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 (Github repo here), 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 div
named #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
,
connectors
and subcircuits
. Under devices
is 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 connectors
is a list of connections between device ports, represented as an
array of objects with two keys, from
and to
. Both keys map to an object with two
keys, id
and 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 subcircuits
is a list of subcircuit definitions, represented as an object,
where keys are unique subcircuit names. A subcircuit name can be used as
a celltype
for 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
Input
and Output
devices, 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), inputs
(natural number, default 2)in1
, in2
... inN
(bits
-bit, N
= inputs
)out
(bits
-bit)AndReduce
, NandReduce
, OrReduce
, NorReduce
, XorReduce
, XnorReduce
bits
(natural number)in
(bits
-bit)out
(1-bit)ShiftLeft
, ShiftRight
bits.in1
, bits.in2
and bits.out
(natural number), signed.in1
, signed.in2
, signed.out
and fillx
(boolean)in1
(bits.in1
-bit), in2
(bits.in2
-bit)out
(bits.out
-bit)Eq
, Ne
, Lt
, Le
, Gt
, Ge
bits.in1
and bits.in2
(natural number), signed.in1
and 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.in
and bits.out
(natural number), signed
(boolean)in
(bits.in
-bit)out
(bits.out
-bit)Addition
, Subtraction
, Multiplication
, Division
, Modulo
, Power
bits.in1
, bits.in2
and bits.out
(natural number), signed.in1
and 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)MuxSparse
bits.in
, bits.sel
(natural number), inputs
(list of natural numbers), default_input
(optional boolean)in0
... inN
(bits.in
-bit, N
= inputs.length
, +1 if default_input
is true)out
(bits.in
-bit)Dff
bits
(natural number), polarity.clock
, polarity.arst
, polarity.srst
, polarity.aload
, polarity.set
, polarity.clr
, polarity.enable
, enable_srst
(optional booleans), initial
(optional binary string), arst_value
, srst_value
(optional binary string), no_data
(optional boolean)in
(bits
-bit), clk
(1-bit, if polarity.clock
is present), arst
(1-bit, if polarity.arst
is present), srst
(1-bit, if polarity.srst
is present), en
(1-bit, if polarity.enable
is present), set
(1-bit, if polarity.set
is present), clr
(1-bit, if polarity.clr
is present), ain
(bits
-bit, if polarity.aload
is present), aload
(1-bit, if polarity.aload
is 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
, arst_polarity
, srst_polarity
(optional booleans), init_value
, arst_value
, srst_value
(optional binary strings), transparent
, collision
(optional booleans or arrays of booleans)enable_polarity
, clock_polarity
, no_bit_enable
(optional booleans)rdKaddr
(abits
-bit), rdKen
(1-bit, if enable_polarity
is present), rdKclk
(1-bit, if clock_polarity
is present), rdKarst
(1-bit, if arst_polarity
is present), rdKsrst
(1-bit, if srst_polarity
is present)rdKdata
(bits
-bit)wrKaddr
(abits
-bit), wrKdata
(bits
-bit), wrKen
(1-bit (when no_bit_enable
is true) or bits
-bit (otherwise), if enable_polarity
is present), wrKclk
(1-bit, if clock_polarity
is 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)Display7
bits
(8-bit only - most significant bit controls decimal point LED)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.