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']
}
}