Teaching-focused digital circuit simulator
This project is a digital circuit simulator implemented in Javascript. It is designed to simulate circuits synthesized by hardware design tools like Yosys, and it has a companion project yosys2digitaljs, which converts Yosys output files to DigitalJS. It is also intended to be a teaching tool, therefore readability and ease of inspection is one of top concerns for the project.
You can try it out online. The web app is a separate Github project.
You can use DigitalJS in your project by installing it from NPM:
npm install digitaljs
Or you can use the Webpack bundle directly.
To simulate a circuit represented using the JSON input format (described later) and display it on a
divnamed
#paper, you need to run the following JS code (see running example):
// create the simulation object const circuit = new digitaljs.Circuit(input_goes_here); // display on #paper const paper = circuit.displayOn($('#paper')); // activate real-time simulation circuit.start();
Circuits are represented using JSON. The top-level object has three keys,
devices,
connectorsand
subcircuits. Under
devicesis a list of all devices forming the circuit, represented as an object, where keys are (unique and internal) device names. Each device has a number of properties, which are represented by an object. A mandatory property is
type, which specifies the type of the device. Example device:
"dev1": { "type": "And", "label": "AND1" }
Under
connectorsis a list of connections between device ports, represented as an array of objects with two keys,
fromand
to. Both keys map to an object with two keys,
idand
port; the first corresponds to a device name, and the second -- to a valid port name for the device. A connection must lead from an output port to an input port, and the bitwidth of both ports must be equal. Example connection:
{ "from": { "id": "dev1", "port": "out" }, "to": { "id": "dev2", "port": "in" } }
Under
subcircuitsis a list of subcircuit definitions, represented as an object, where keys are unique subcircuit names. A subcircuit name can be used as a
celltypefor a device of type
Subcircuit; this instantiates the subcircuit. A subcircuit definition follows the representation of whole circuits, with the exception that subcircuits cannot (currently) define their own subcircuits. A subcircuit can include
Inputand
Outputdevices, these are mapped to ports on a subcircuit instance.
Not,
Repeater
bits(natural number)
in(
bits-bit)
out(
bits-bit)
And,
Nand,
Or,
Nor,
Xor,
Xnor
bits(natural number)
in1,
in2(
bits-bit)
out(
bits-bit)
AndReduce,
NandReduce,
OrReduce,
NorReduce,
XorReduce,
XnorReduce
bits(natural number)
in(
bits-bit)
out(1-bit)
ShiftLeft,
ShiftRight
bits.in1,
bits.in2and
bits.out(natural number),
signed.in1,
signed.in2,
signed.outand
fillx(boolean)
in1(
bits.in1-bit),
in2(
bits.in2-bit)
out(
bits.out-bit)
Eq,
Ne,
Lt,
Le,
Gt,
Ge
bits.in1and
bits.in2(natural number),
signed.in1and
signed.in2(boolean)
in1(
bits.in1-bit),
in2(
bits.in2-bit)
out(1-bit)
Constant
constant(binary string)
out(
constant.length-bit)
Negation,
UnaryPlus
bits.inand
bits.out(natural number),
signed(boolean)
in(
bits.in-bit)
out(
bits.out-bit)
Addition,
Subtraction,
Multiplication,
Division,
Modulo,
Power
bits.in1,
bits.in2and
bits.out(natural number),
signed.in1and
signed.in2(boolean)
in1(
bits.in1-bit),
in2(
bits.in2-bit)
out(
bits.out-bit)
Mux
bits.in,
bits.sel(natural number)
in0...
inN(
bits.in-bit,
N= 2**
bits.sel-1),
sel(
bits.sel-bit)
out(
bits.in-bit)
Mux1Hot
bits.in,
bits.sel(natural number)
in0...
inN(
bits.in-bit,
N=
bits.sel),
sel(
bits.sel-bit)
out(
bits.in-bit)
Dff
bits(natural number),
polarity.clock,
polarity.arst,
polarity.enable(optional booleans),
initial(optional binary string)
in(
bits-bit),
clk(1-bit, if
polarity.clockis present),
arst(1-bit, if
polarity.arstis present),
en(1-bit, if
polarity.enableis present)
out(
bits-bit)
Memory
bits,
abits,
words,
offset(natural number),
rdports(array of read port descriptors),
wrports(array of write port descriptors),
memdata(memory contents description)
enable_polarity,
clock_polarity,
transparent(optional booleans)
enable_polarity,
clock_polarity(optional booleans)
rdKaddr(
abits-bit),
rdKen(1-bit, if
enable_polarityis present),
rdKclk(1-bit, if
clock_polarityis present)
rdKdata(
bits-bit)
wrKaddr(
abits-bit),
wrKdata(
bits-bit),
wrKen(1-bit, if
enable_polarityis present),
wrKclk(1-bit, if
clock_polarityis present)
Clock
out(1-bit)
Button
out(1-bit)
Lamp
in(1-bit)
NumEntry
bits(natural number),
numbase(string)
out(
bits-bit)
NumDisplay
bits(natural number),
numbase(string)
in(
bits-bit)
Input
bits(natural number)
out(
bits-bit)
Output
bits(natural number)
in(
bits-bit)
BusGroup
groups(array of natural numbers)
in0(
groups[0]-bit) ...
inN(
groups[N]-bit)
out(sum-of-
groups-bit)
BusUngroup
groups(array of natural numbers)
in(sum-of-
groups-bit)
out0(
groups[0]-bit) ...
outN(
groups[N]-bit)
BusSlice
slice.first,
slice.count,
slice.total(natural number)
in(
slice.total-bit)
out(
slice.count-bit)
ZeroExtend,
SignExtend
extend.input,
extend.output(natural number)
in(
extend.input-bit)
out(
extend.output-bit)
FSM
bits.in,
bits.out,
states,
init_state,
current_state(natural number),
trans_table(array of transition descriptors)
ctrl_in,
ctrl_out(binary strings),
state_in,
state_out(natural numbers)
clk(1-bit),
arst(1-bit),
in(
bits.in-bit)
out(
bits.out-bit)
Some ideas for further developing the simulator.