185 lines
No EOL
5.9 KiB
JavaScript
185 lines
No EOL
5.9 KiB
JavaScript
import CircuitElement from '../circuitElement';
|
|
import Node, { findNode } from '../node';
|
|
import simulationArea from '../simulationArea';
|
|
import { lineTo, moveTo, fillText, correctWidth, rect2 } from '../canvasApi';
|
|
import { colors } from '../themer/themer';
|
|
|
|
|
|
/**
|
|
* @class
|
|
* Counter component.
|
|
* @extends CircuitElement
|
|
* @param {number} x - x coordinate of element.
|
|
* @param {number} y - y coordinate of element.
|
|
* @param {Scope=} scope - Cirucit on which element is drawn
|
|
* @param {number=} rows - number of rows
|
|
* @param {number=} cols - number of columns.
|
|
* Counts from zero to a particular maximum value, which is either
|
|
* specified by an input pin or determined by the Counter's bitWidth.
|
|
* The counter outputs its current value and a flag that indicates
|
|
* when the output value is zero and the clock is 1.
|
|
* The counter can be reset to zero at any point using the RESET pin.
|
|
* @category modules
|
|
*/
|
|
export default class Counter extends CircuitElement {
|
|
constructor(x, y, scope = globalScope, bitWidth = 8) {
|
|
super(x, y, scope, "RIGHT", bitWidth);
|
|
/* this is done in this.baseSetup() now
|
|
this.scope['Counter'].push(this);
|
|
*/
|
|
this.directionFixed = true;
|
|
this.rectangleObject = true;
|
|
|
|
this.setDimensions(20, 20);
|
|
|
|
this.maxValue = new Node(-20, -10, 0, this, this.bitWidth, "MaxValue");
|
|
this.clock = new Node(-20, +10, 0, this, 1, "Clock");
|
|
this.reset = new Node(0, 20, 0, this, 1, "Reset");
|
|
this.output = new Node(20, -10, 1, this, this.bitWidth, "Value");
|
|
this.zero = new Node(20, 10, 1, this, 1, "Zero");
|
|
|
|
this.value = 0;
|
|
this.prevClockState = undefined;
|
|
}
|
|
|
|
customSave() {
|
|
return {
|
|
nodes: {
|
|
maxValue: findNode(this.maxValue),
|
|
clock: findNode(this.clock),
|
|
reset: findNode(this.reset),
|
|
output: findNode(this.output),
|
|
zero: findNode(this.zero),
|
|
},
|
|
constructorParamaters: [this.bitWidth]
|
|
};
|
|
}
|
|
|
|
newBitWidth(bitWidth) {
|
|
this.bitWidth = bitWidth;
|
|
this.maxValue.bitWidth = bitWidth;
|
|
this.output.bitWidth = bitWidth;
|
|
}
|
|
|
|
isResolvable() {
|
|
return true;
|
|
}
|
|
|
|
resolve() {
|
|
// Max value is either the value in the input pin or the max allowed by the bitWidth.
|
|
var maxValue = this.maxValue.value != undefined ? this.maxValue.value : (1 << this.bitWidth) - 1;
|
|
var outputValue = this.value;
|
|
|
|
// Increase value when clock is raised
|
|
if (this.clock.value != this.prevClockState && this.clock.value == 1) {
|
|
outputValue++;
|
|
}
|
|
this.prevClockState = this.clock.value;
|
|
|
|
// Limit to the effective maximum value; this also accounts for bitWidth changes.
|
|
outputValue = outputValue % (maxValue + 1);
|
|
|
|
// Reset to zero if RESET pin is on
|
|
if (this.reset.value == 1) {
|
|
outputValue = 0;
|
|
}
|
|
|
|
// Output the new value
|
|
this.value = outputValue;
|
|
if (this.output.value != outputValue) {
|
|
this.output.value = outputValue;
|
|
simulationArea.simulationQueue.add(this.output);
|
|
}
|
|
|
|
// Output the zero signal
|
|
var zeroValue = this.clock.value == 1 && outputValue == 0 ? 1 : 0;
|
|
if (this.zero.value != zeroValue) {
|
|
this.zero.value = zeroValue;
|
|
simulationArea.simulationQueue.add(this.zero);
|
|
}
|
|
}
|
|
|
|
customDraw() {
|
|
var ctx = simulationArea.context;
|
|
var xx = this.x;
|
|
var yy = this.y;
|
|
|
|
ctx.beginPath();
|
|
ctx.font = "20px Raleway";
|
|
ctx.fillStyle = colors['input_text'];
|
|
ctx.textAlign = "center";
|
|
fillText(ctx, this.value.toString(16), this.x, this.y + 5);
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = colors['stroke'];
|
|
ctx.beginPath();
|
|
moveTo(ctx, -20, 5, xx, yy, this.direction);
|
|
lineTo(ctx, -15, 10, xx, yy, this.direction);
|
|
lineTo(ctx, -20, 15, xx, yy, this.direction);
|
|
ctx.stroke();
|
|
}
|
|
|
|
// Draws the element in the subcuircuit. Used in layout mode
|
|
subcircuitDraw(xOffset = 0, yOffset = 0) {
|
|
var ctx = simulationArea.context;
|
|
var xx = this.subcircuitMetadata.x + xOffset;
|
|
var yy = this.subcircuitMetadata.y + yOffset;
|
|
|
|
ctx.beginPath();
|
|
ctx.font = "20px Raleway";
|
|
ctx.fillStyle = "green";
|
|
ctx.textAlign = "center";
|
|
fillText(ctx, this.value.toString(16), xx + 10, yy + 17);
|
|
ctx.fill();
|
|
|
|
ctx.beginPath();
|
|
ctx.lineWidth = correctWidth(1);
|
|
rect2(ctx, 0, 0, 20, 20, xx, yy, this.direction);
|
|
ctx.stroke();
|
|
|
|
if ((this.hover && !simulationArea.shiftDown) || simulationArea.lastSelected == this || simulationArea.multipleObjectSelections.contains(this)) {
|
|
ctx.fillStyle = "rgba(255, 255, 32,0.6)";
|
|
ctx.fill();
|
|
}
|
|
}
|
|
static moduleVerilog() {
|
|
return `
|
|
module Counter(val, zero, max, clk, rst);
|
|
parameter WIDTH = 1;
|
|
output reg [WIDTH-1:0] val;
|
|
output reg zero;
|
|
input [WIDTH-1:0] max;
|
|
input clk, rst;
|
|
|
|
initial
|
|
val = 0;
|
|
|
|
always @ (val)
|
|
if (val == 0)
|
|
zero = 1;
|
|
else
|
|
zero = 0;
|
|
|
|
always @ (posedge clk or posedge rst) begin
|
|
if (rst)
|
|
val <= 0;
|
|
else
|
|
if (val == max)
|
|
val <= 0;
|
|
else
|
|
val <= val + 1;
|
|
end
|
|
endmodule`;
|
|
}
|
|
}
|
|
|
|
Counter.prototype.tooltipText = "Counter: a binary counter from zero to a given maximum value";
|
|
Counter.prototype.helplink = "https://docs.circuitverse.org/#/inputElements?id=counter"; Counter.prototype.objectType = 'Counter';
|
|
Counter.prototype.objectType = 'Counter';
|
|
Counter.prototype.canShowInSubcircuit = true;
|
|
Counter.prototype.layoutProperties = {
|
|
rightDimensionX : 20,
|
|
leftDimensionX : 0,
|
|
upDimensionY : 0,
|
|
downDimensionY: 20
|
|
} |