From 7ae0b8342fcf0b906905a25dc069b147f1160bf0 Mon Sep 17 00:00:00 2001 From: Nihal Rajpal Date: Tue, 1 Jul 2025 08:06:03 +0530 Subject: [PATCH 1/4] feat(gsoc2025): add shift register --- .../ElementProperty/ElementProperty.vue | 8 + v0/src/simulator/src/metadata.json | 4 +- v0/src/simulator/src/moduleSetup.js | 2 + .../simulator/src/sequential/ShiftRegister.js | 222 ++++++++++++++++++ 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 v0/src/simulator/src/sequential/ShiftRegister.js diff --git a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue index fec2c571..62a6531a 100644 --- a/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue +++ b/v0/src/components/Panels/PropertiesPanel/ModuleProperty/ElementProperty/ElementProperty.vue @@ -107,6 +107,14 @@ {{ obj[name] }}

+ diff --git a/v0/src/simulator/src/metadata.json b/v0/src/simulator/src/metadata.json index 8ed4f225..22b762f3 100644 --- a/v0/src/simulator/src/metadata.json +++ b/v0/src/simulator/src/metadata.json @@ -60,7 +60,8 @@ "Dlatch", "TB_Input", "TB_Output", - "ForceGate" + "ForceGate", + "ShiftRegister" ], "annotationList": ["Text", "Rectangle", "Arrow", "ImageAnnotation"], "inputList": [ @@ -137,6 +138,7 @@ { "name": "Decoder", "label": "Decoder" } ], "Sequential Elements": [ + { "name": "ShiftRegister", "label": "ShiftRegister" }, { "name": "DflipFlop", "label": "D flip Flop" }, { "name": "Dlatch", "label": "D latch" }, { "name": "TflipFlop", "label": "T flip Flop" }, diff --git a/v0/src/simulator/src/moduleSetup.js b/v0/src/simulator/src/moduleSetup.js index 5002bc69..8d5a3f4c 100644 --- a/v0/src/simulator/src/moduleSetup.js +++ b/v0/src/simulator/src/moduleSetup.js @@ -63,6 +63,7 @@ import verilogPower from './modules/verilogPower' import verilogShiftLeft from './modules/verilogShiftLeft' import verilogShiftRight from './modules/verilogShiftRight' import verilogRAM from './sequential/verilogRAM' +import ShiftRegister from './sequential/ShiftRegister' export default function setupModules() { var moduleSet = { @@ -130,6 +131,7 @@ export default function setupModules() { TB_Input, TB_Output, ForceGate, + ShiftRegister } Object.assign(modules, moduleSet) } diff --git a/v0/src/simulator/src/sequential/ShiftRegister.js b/v0/src/simulator/src/sequential/ShiftRegister.js new file mode 100644 index 00000000..184a0f47 --- /dev/null +++ b/v0/src/simulator/src/sequential/ShiftRegister.js @@ -0,0 +1,222 @@ +/* eslint-disable no-bitwise */ +import CircuitElement from '../circuitElement' +import Node, { findNode } from '../node' +import simulationArea from '../simulationArea' +import { correctWidth, lineTo, moveTo, fillText4 } from '../canvasApi' +import { colors } from '../themer/themer' + +export default class ShiftRegister extends CircuitElement { + + constructor(x, y, scope = globalScope, dir = 'DOWN', bitWidth = 1, noOfStages = 4, isParallelLoad = "Yes") { + super(x, y, scope, dir, bitWidth) + + this.message = 'ShiftRegister' + + this.width = 60; + + this.noOfStages = noOfStages || parseInt(prompt('Enter number of stages:')) + + + this.isParallelLoad = isParallelLoad ?? "Yes"; + + const baseHeight = 230; + const extraHeight = this.noOfStages * 20; + this.height = baseHeight + extraHeight; + + // Set half-width, half-height for bounding box + this.setDimensions(this.width / 2, baseHeight / 2) + + this.rectangleObject = false + + this.inp = [] + this.out = [] + + this.firstInput = new Node(30, 0, 0, this, this.bitWidth, 'First Input') + + + this.reset = new Node(30, -90, 0, this, 1, 'Reset') + this.shiftLoad = new Node(30, -70, 0, this, 1, 'Shift/Load') + this.clk = new Node(30, -50, 0, this, 1, "Clock"); + + let i = 0; + + while (i < this.noOfStages) { + if (this.isParallelLoad == "Yes") { + const a = new Node(30, 20 * (i + 1), 0, this, this.bitWidth) + this.inp.push(a) + + const b = new Node(-30, 20 * (i + 1), 1, this, this.bitWidth) + b.value = 1; + this.out.push(b) + } + ++i; + } + if (this.isParallelLoad == "No") { + const b = new Node(-30, 20 * i, 1, this, this.bitWidth) + b.value = 1; + this.out.push(b) + } + this.lastClk = 0 + this.cell = new Array(this.noOfStages) + } + + customDraw() { + const ctx = simulationArea.context + const xx = this.x + const yy = this.y + + const width = this.width + const baseHeight = 230; // fixed top height + const extraHeight = this.noOfStages * 20; // dynamic growth below + + const partitionY = -30 + + // Draw main rectangle + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.beginPath(); + + // top-left + moveTo(ctx, -width / 2, -baseHeight / 2, xx, yy, this.direction); + + // top-right + lineTo(ctx, width / 2, -baseHeight / 2, xx, yy, this.direction); + + // bottom-right (extends with noOfStages) + lineTo(ctx, width / 2, extraHeight + 20, xx, yy, this.direction); + + // bottom-left + lineTo(ctx, -width / 2, extraHeight + 20, xx, yy, this.direction); + + ctx.closePath(); + ctx.fill(); + ctx.strokeStyle = colors['stroke'] + ctx.fillStyle = colors['fill'] + ctx.lineWidth = correctWidth(3) + ctx.stroke(); + + // Partition line + ctx.beginPath() + moveTo(ctx, -width / 2, partitionY, xx, yy, this.direction) + lineTo(ctx, width / 2, partitionY, xx, yy, this.direction) + + ctx.setLineDash([4, 2]) + ctx.stroke() + ctx.setLineDash([]) + + // Section labels + ctx.fillStyle = 'black' + ctx.textAlign = 'center' + fillText4(ctx, '>>>', 0, 0, xx, yy, this.direction, 10) + + // ==== Label control inputs (inside component) ==== + fillText4(ctx, 'Reset', 16, -90, xx, yy, this.direction, 6) + fillText4(ctx, 'S/L', 16, -70, xx, yy, this.direction, 6) + fillText4(ctx, 'Clock', 16, -50, xx, yy, this.direction, 6) + + // ==== Label data i/o ==== + for (let i = 0; i < this.noOfStages; i++) { + fillText4(ctx, `In${i}`, 20, 20 * (i + 1), xx, yy, this.direction, 6) + fillText4(ctx, `Out${i}`, -20, 20 * (i + 1), xx, yy, this.direction, 6) + fillText4(ctx, this.cell[i] == undefined ? 'x' : this.cell[i], 0, 20 * (i + 1), xx, yy, this.direction, 11) + } + + } + + isResolvable() { + return true; + } + + resolve() { + const clkValue = this.clk.value; + // Rising edge detection + if (this.lastClk === 0 && clkValue === 1) { + if (this.reset.value === 1) { + this.cell.fill(0); + } + else if (this.shiftLoad.value === 1) { + for (let i = 0; i < this.noOfStages; i++) { + this.cell[i] = this.inp[i].value; + } + } + else { + this.cell.unshift(this.firstInput.value) + this.cell.length = this.noOfStages + } + + for (let i = 0; i < this.noOfStages; i++) { + this.out[i].value = this.cell[i]; + simulationArea.simulationQueue.add(this.out[i]) + } + } + this.lastClk = clkValue + } + + newBitWidth(bitWidth) { + for (let i = 0; i < this.noOfStages; i++) { + this.inp[i].bitWidth = bitWidth; + this.out[i].bitWidth = bitWidth; + } + } + + customSave() { + const data = { + nodes: { + reset: findNode(this.reset), + shiftLoad: findNode(this.shiftLoad), + clk: findNode(this.clk), + out: this.out.map(findNode), + }, + values: { + cell: this.cell, + }, + constructorParamaters: [this.direction, this.bitWidth, this.noOfStages], + } + return data + } + + changeNumberofStages(noOfStages) { + if (noOfStages == undefined || noOfStages < 1 || noOfStages > 32) return; + if (this.noOfStages == noOfStages) return; + var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, noOfStages, this.isParallelLoad) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + changeParallelLoad(parallelLoad) { + if (parallelLoad !== undefined && this.isParallelLoad !== parallelLoad) { + this.isParallelLoad = parallelLoad; + var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, this.noOfStages, this.isParallelLoad) + this.delete() + simulationArea.lastSelected = obj + return obj; + } + + } + +} + + +ShiftRegister.prototype.mutableProperties = { + noOfStages: { + name: 'Number of Stages: ', + type: 'number', + max: '32', + min: '1', + func: 'changeNumberofStages', + }, + isParallelLoad: { + name: 'Parallel Load: ', + type: 'dropdown', + func: 'changeParallelLoad', + dropdownArray: ['Yes', 'No'] + } +} + +ShiftRegister.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/' + +ShiftRegister.prototype.tooltipText = "ShiftRegister"; + +ShiftRegister.prototype.objectType = 'ShiftRegister' From 1419df97d3b20abc245abeb3a82b75a2bad00012 Mon Sep 17 00:00:00 2001 From: Nihal Rajpal Date: Tue, 1 Jul 2025 23:40:51 +0530 Subject: [PATCH 2/4] feat(gsoc2025): add shift register svg --- v0/src/assets/img/ShiftRegister.svg | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 v0/src/assets/img/ShiftRegister.svg diff --git a/v0/src/assets/img/ShiftRegister.svg b/v0/src/assets/img/ShiftRegister.svg new file mode 100644 index 00000000..8cd47cd8 --- /dev/null +++ b/v0/src/assets/img/ShiftRegister.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 7f929a3fb7ace1c66b64b15e703ecd47e658e0ac Mon Sep 17 00:00:00 2001 From: Nihal Rajpal Date: Fri, 4 Jul 2025 01:15:43 +0530 Subject: [PATCH 3/4] feat(gsoc2025): add jsdoc style comments --- .../simulator/src/sequential/ShiftRegister.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/v0/src/simulator/src/sequential/ShiftRegister.js b/v0/src/simulator/src/sequential/ShiftRegister.js index 184a0f47..3b30123f 100644 --- a/v0/src/simulator/src/sequential/ShiftRegister.js +++ b/v0/src/simulator/src/sequential/ShiftRegister.js @@ -5,6 +5,19 @@ import simulationArea from '../simulationArea' import { correctWidth, lineTo, moveTo, fillText4 } from '../canvasApi' import { colors } from '../themer/themer' +/** + * @class + * ShiftRegister + * @extends CircuitElement + * @param {number} x - x coordinate of the element + * @param {number} y - y coordinate of the element + * @param {Scope=} scope - Circuit scope where the element exists + * @param {string=} dir - Direction of the element (default: DOWN) + * @param {number=} bitWidth - Bit width per data node (default: 1) + * @param {number=} noOfStages - Number of shift register stages (default: 4) + * @param {string=} isParallelLoad - If parallel loading is enabled ("Yes" or "No") + * @category modules + */ export default class ShiftRegister extends CircuitElement { constructor(x, y, scope = globalScope, dir = 'DOWN', bitWidth = 1, noOfStages = 4, isParallelLoad = "Yes") { @@ -60,6 +73,9 @@ export default class ShiftRegister extends CircuitElement { this.cell = new Array(this.noOfStages) } + /** + * Draw the ShiftRegister component + */ customDraw() { const ctx = simulationArea.context const xx = this.x @@ -127,6 +143,9 @@ export default class ShiftRegister extends CircuitElement { return true; } + /** + * Resolve logic based on clock edge and shift/load/reset signals + */ resolve() { const clkValue = this.clk.value; // Rising edge detection @@ -152,6 +171,10 @@ export default class ShiftRegister extends CircuitElement { this.lastClk = clkValue } + /** + * Change the bit width of all input/output nodes + * @param {number} bitWidth - New bit width + */ newBitWidth(bitWidth) { for (let i = 0; i < this.noOfStages; i++) { this.inp[i].bitWidth = bitWidth; @@ -159,6 +182,10 @@ export default class ShiftRegister extends CircuitElement { } } + /** + * Generate JSON save object for component + * @returns {Object} + */ customSave() { const data = { nodes: { @@ -175,6 +202,10 @@ export default class ShiftRegister extends CircuitElement { return data } +/** + * Update the number of stages in the register + * @param {number} noOfStages - New number of stages (1–32) + */ changeNumberofStages(noOfStages) { if (noOfStages == undefined || noOfStages < 1 || noOfStages > 32) return; if (this.noOfStages == noOfStages) return; @@ -184,6 +215,10 @@ export default class ShiftRegister extends CircuitElement { return obj } + /** + * Update the parallel load configuration + * @param {string} parallelLoad - "Yes" or "No" + */ changeParallelLoad(parallelLoad) { if (parallelLoad !== undefined && this.isParallelLoad !== parallelLoad) { this.isParallelLoad = parallelLoad; From 3ff05482b00c4daa1b96ae124ec84b1cf769d5d5 Mon Sep 17 00:00:00 2001 From: Nihal Rajpal Date: Sun, 13 Jul 2025 18:14:46 +0530 Subject: [PATCH 4/4] feat(gsoc2025): introduce 4 modes in shift register --- .../simulator/src/sequential/ShiftRegister.js | 90 ++++++++++++------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/v0/src/simulator/src/sequential/ShiftRegister.js b/v0/src/simulator/src/sequential/ShiftRegister.js index 3b30123f..4928c948 100644 --- a/v0/src/simulator/src/sequential/ShiftRegister.js +++ b/v0/src/simulator/src/sequential/ShiftRegister.js @@ -15,12 +15,12 @@ import { colors } from '../themer/themer' * @param {string=} dir - Direction of the element (default: DOWN) * @param {number=} bitWidth - Bit width per data node (default: 1) * @param {number=} noOfStages - Number of shift register stages (default: 4) - * @param {string=} isParallelLoad - If parallel loading is enabled ("Yes" or "No") + * @param {string=} registerType - If parallel loading is enabled ("Yes" or "No") * @category modules */ export default class ShiftRegister extends CircuitElement { - constructor(x, y, scope = globalScope, dir = 'DOWN', bitWidth = 1, noOfStages = 4, isParallelLoad = "Yes") { + constructor(x, y, scope = globalScope, dir = 'DOWN', bitWidth = 1, noOfStages = 4, registerType = "PIPO") { super(x, y, scope, dir, bitWidth) this.message = 'ShiftRegister' @@ -30,7 +30,7 @@ export default class ShiftRegister extends CircuitElement { this.noOfStages = noOfStages || parseInt(prompt('Enter number of stages:')) - this.isParallelLoad = isParallelLoad ?? "Yes"; + this.registerType = registerType ?? "PIPO"; const baseHeight = 230; const extraHeight = this.noOfStages * 20; @@ -48,23 +48,31 @@ export default class ShiftRegister extends CircuitElement { this.reset = new Node(30, -90, 0, this, 1, 'Reset') - this.shiftLoad = new Node(30, -70, 0, this, 1, 'Shift/Load') + const labelText = (this.registerType === "PIPO" || this.registerType === "PISO") ? "Shift/Load" : "Shift"; + this.shiftLoad = new Node(30, -70, 0, this, 1, labelText); this.clk = new Node(30, -50, 0, this, 1, "Clock"); let i = 0; while (i < this.noOfStages) { - if (this.isParallelLoad == "Yes") { + if (this.registerType == "PIPO") { const a = new Node(30, 20 * (i + 1), 0, this, this.bitWidth) this.inp.push(a) + const b = new Node(-30, 20 * (i + 1), 1, this, this.bitWidth) + b.value = 1; + this.out.push(b) + } else if (this.registerType == "PISO") { + const a = new Node(30, 20 * (i + 1), 0, this, this.bitWidth) + this.inp.push(a) + } else if (this.registerType == "SIPO") { const b = new Node(-30, 20 * (i + 1), 1, this, this.bitWidth) b.value = 1; this.out.push(b) } ++i; } - if (this.isParallelLoad == "No") { + if (this.registerType !== "PIPO" && this.registerType != "SIPO") { const b = new Node(-30, 20 * i, 1, this, this.bitWidth) b.value = 1; this.out.push(b) @@ -73,9 +81,9 @@ export default class ShiftRegister extends CircuitElement { this.cell = new Array(this.noOfStages) } - /** - * Draw the ShiftRegister component - */ + /** + * Draw the ShiftRegister component + */ customDraw() { const ctx = simulationArea.context const xx = this.x @@ -88,7 +96,7 @@ export default class ShiftRegister extends CircuitElement { const partitionY = -30 // Draw main rectangle - ctx.strokeStyle = colors['stroke'] + ctx.strokeStyle = colors['stroke'] ctx.fillStyle = colors['fill'] ctx.beginPath(); @@ -127,7 +135,8 @@ export default class ShiftRegister extends CircuitElement { // ==== Label control inputs (inside component) ==== fillText4(ctx, 'Reset', 16, -90, xx, yy, this.direction, 6) - fillText4(ctx, 'S/L', 16, -70, xx, yy, this.direction, 6) + const labelText = (this.registerType === "PIPO" || this.registerType === "PISO") ? "S/L" : "S"; + fillText4(ctx, labelText, 16, -70, xx, yy, this.direction, 6) fillText4(ctx, 'Clock', 16, -50, xx, yy, this.direction, 6) // ==== Label data i/o ==== @@ -148,25 +157,38 @@ export default class ShiftRegister extends CircuitElement { */ resolve() { const clkValue = this.clk.value; + let isShift = 0; + let isLoad = 0; + if (this.registerType == "SISO" || this.registerType == "SIPO") { + isShift = this.shiftLoad.value; + } else { + isShift = +!this.shiftLoad.value; + isLoad = this.shiftLoad.value; + } // Rising edge detection if (this.lastClk === 0 && clkValue === 1) { if (this.reset.value === 1) { this.cell.fill(0); } - else if (this.shiftLoad.value === 1) { + else if (isLoad === 1) { for (let i = 0; i < this.noOfStages; i++) { this.cell[i] = this.inp[i].value; } } - else { + else if (isShift === 1) { this.cell.unshift(this.firstInput.value) this.cell.length = this.noOfStages } - - for (let i = 0; i < this.noOfStages; i++) { - this.out[i].value = this.cell[i]; - simulationArea.simulationQueue.add(this.out[i]) + if (this.registerType == 'SISO' || this.registerType == 'PISO') { + this.out[0].value = this.cell[this.cell.length - 1]; + simulationArea.simulationQueue.add(this.out[0]) + } else { + for (let i = 0; i < this.noOfStages; i++) { + this.out[i].value = this.cell[i]; + simulationArea.simulationQueue.add(this.out[i]) + } } + } this.lastClk = clkValue } @@ -202,27 +224,27 @@ export default class ShiftRegister extends CircuitElement { return data } -/** - * Update the number of stages in the register - * @param {number} noOfStages - New number of stages (1–32) - */ + /** + * Update the number of stages in the register + * @param {number} noOfStages - New number of stages (1–32) + */ changeNumberofStages(noOfStages) { if (noOfStages == undefined || noOfStages < 1 || noOfStages > 32) return; if (this.noOfStages == noOfStages) return; - var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, noOfStages, this.isParallelLoad) + var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, noOfStages, this.registerType) this.delete() simulationArea.lastSelected = obj return obj } - /** - * Update the parallel load configuration - * @param {string} parallelLoad - "Yes" or "No" - */ - changeParallelLoad(parallelLoad) { - if (parallelLoad !== undefined && this.isParallelLoad !== parallelLoad) { - this.isParallelLoad = parallelLoad; - var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, this.noOfStages, this.isParallelLoad) + /** + * Update the parallel load configuration + * @param {string} registerType - "Yes" or "No" + */ + changeRegisterType(registerType) { + if (registerType !== undefined && this.registerType !== registerType) { + this.registerType = registerType; + var obj = new ShiftRegister(this.x, this.y, this.scope, this.dir, this.bitWidth, this.noOfStages, this.registerType) this.delete() simulationArea.lastSelected = obj return obj; @@ -241,11 +263,11 @@ ShiftRegister.prototype.mutableProperties = { min: '1', func: 'changeNumberofStages', }, - isParallelLoad: { - name: 'Parallel Load: ', + registerType: { + name: 'Register Type: ', type: 'dropdown', - func: 'changeParallelLoad', - dropdownArray: ['Yes', 'No'] + func: 'changeRegisterType', + dropdownArray: ['SISO', 'SIPO', "PISO", 'PIPO'] } }