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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..4928c948 --- /dev/null +++ b/v0/src/simulator/src/sequential/ShiftRegister.js @@ -0,0 +1,279 @@ +/* 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' + +/** + * @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=} 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, registerType = "PIPO") { + super(x, y, scope, dir, bitWidth) + + this.message = 'ShiftRegister' + + this.width = 60; + + this.noOfStages = noOfStages || parseInt(prompt('Enter number of stages:')) + + + this.registerType = registerType ?? "PIPO"; + + 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') + 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.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.registerType !== "PIPO" && this.registerType != "SIPO") { + 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) + } + + /** + * Draw the ShiftRegister component + */ + 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) + 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 ==== + 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 logic based on clock edge and shift/load/reset signals + */ + 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 (isLoad === 1) { + for (let i = 0; i < this.noOfStages; i++) { + this.cell[i] = this.inp[i].value; + } + } + else if (isShift === 1) { + this.cell.unshift(this.firstInput.value) + this.cell.length = this.noOfStages + } + 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 + } + + /** + * 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; + this.out[i].bitWidth = bitWidth; + } + } + + /** + * Generate JSON save object for component + * @returns {Object} + */ + 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 + } + + /** + * 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.registerType) + this.delete() + simulationArea.lastSelected = obj + return obj + } + + /** + * 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; + } + + } + +} + + +ShiftRegister.prototype.mutableProperties = { + noOfStages: { + name: 'Number of Stages: ', + type: 'number', + max: '32', + min: '1', + func: 'changeNumberofStages', + }, + registerType: { + name: 'Register Type: ', + type: 'dropdown', + func: 'changeRegisterType', + dropdownArray: ['SISO', 'SIPO', "PISO", 'PIPO'] + } +} + +ShiftRegister.prototype.helplink = + 'https://docs.circuitverse.org/#/chapter4/' + +ShiftRegister.prototype.tooltipText = "ShiftRegister"; + +ShiftRegister.prototype.objectType = 'ShiftRegister'