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 digitaljsOr 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.
- Unary gates: Not,Repeater- Attributes: bits(natural number)
- Inputs: in(bits-bit)
- Outputs: out(bits-bit)
 
- Attributes: 
- N-ary gates: And,Nand,Or,Nor,Xor,Xnor- Attributes: bits(natural number),inputs(natural number, default 2)
- Inputs: in1,in2...inN(bits-bit,N=inputs)
- Outputs: out(bits-bit)
 
- Attributes: 
- Reducing gates: AndReduce,NandReduce,OrReduce,NorReduce,XorReduce,XnorReduce- Attributes: bits(natural number)
- Inputs: in(bits-bit)
- Outputs: out(1-bit)
 
- Attributes: 
- Bit shifts: ShiftLeft,ShiftRight- Attributes: bits.in1,bits.in2andbits.out(natural number),signed.in1,signed.in2,signed.outandfillx(boolean)
- Inputs: in1(bits.in1-bit),in2(bits.in2-bit)
- Outputs: out(bits.out-bit)
 
- Attributes: 
- Comparisons: Eq,Ne,Lt,Le,Gt,Ge- Attributes: bits.in1andbits.in2(natural number),signed.in1andsigned.in2(boolean)
- Inputs: in1(bits.in1-bit),in2(bits.in2-bit)
- Outputs: out(1-bit)
 
- Attributes: 
- Number constant: Constant- Attributes: constant(binary string)
- Outputs: out(constant.length-bit)
 
- Attributes: 
- Unary arithmetic: Negation,UnaryPlus- Attributes: bits.inandbits.out(natural number),signed(boolean)
- Inputs: in(bits.in-bit)
- Outputs: out(bits.out-bit)
 
- Attributes: 
- Binary arithmetic: Addition,Subtraction,Multiplication,Division,Modulo,Power- Attributes: bits.in1,bits.in2andbits.out(natural number),signed.in1andsigned.in2(boolean)
- Inputs: in1(bits.in1-bit),in2(bits.in2-bit)
- Outputs: out(bits.out-bit)
 
- Attributes: 
- Multiplexer: Mux- Attributes: bits.in,bits.sel(natural number)
- Inputs: in0...inN(bits.in-bit,N= 2**bits.sel-1),sel(bits.sel-bit)
- Outputs: out(bits.in-bit)
 
- Attributes: 
- One-hot multiplexer: Mux1Hot- Attributes: bits.in,bits.sel(natural number)
- Inputs: in0...inN(bits.in-bit,N=bits.sel),sel(bits.sel-bit)
- Outputs: out(bits.in-bit)
 
- Attributes: 
- Sparse multiplexer: MuxSparse- Attributes: bits.in,bits.sel(natural number),inputs(list of natural numbers),default_input(optional boolean)
- Inputs: in0...inN(bits.in-bit,N=inputs.length, +1 ifdefault_inputis true)
- Outputs: out(bits.in-bit)
 
- Attributes: 
- D flip-flop: Dff- Attributes: 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)
- Inputs: in(bits-bit),clk(1-bit, ifpolarity.clockis present),arst(1-bit, ifpolarity.arstis present),srst(1-bit, ifpolarity.srstis present),en(1-bit, ifpolarity.enableis present),set(1-bit, ifpolarity.setis present),clr(1-bit, ifpolarity.clris present),ain(bits-bit, ifpolarity.aloadis present),aload(1-bit, ifpolarity.aloadis present)
- Outputs: out(bits-bit)
 
- Attributes: 
- Memory: Memory- Attributes: bits,abits,words,offset(natural number),rdports(array of read port descriptors),wrports(array of write port descriptors),memdata(memory contents description)
- Read port descriptor attributes: 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)
- Write port descriptor attributes: enable_polarity,clock_polarity,no_bit_enable(optional booleans)
- Inputs (per read port): rdKaddr(abits-bit),rdKen(1-bit, ifenable_polarityis present),rdKclk(1-bit, ifclock_polarityis present),rdKarst(1-bit, ifarst_polarityis present),rdKsrst(1-bit, ifsrst_polarityis present)
- Outputs (per read port): rdKdata(bits-bit)
- Inputs (per write port): wrKaddr(abits-bit),wrKdata(bits-bit),wrKen(1-bit (whenno_bit_enableis true) orbits-bit (otherwise), ifenable_polarityis present),wrKclk(1-bit, ifclock_polarityis present)
 
- Attributes: 
- Clock source: Clock- Outputs: out(1-bit)
 
- Outputs: 
- Button input: Button- Outputs: out(1-bit)
 
- Outputs: 
- Lamp output: Lamp- Inputs: in(1-bit)
 
- Inputs: 
- Number input: NumEntry- Attributes: bits(natural number),numbase(string)
- Outputs: out(bits-bit)
 
- Attributes: 
- Number output: NumDisplay- Attributes: bits(natural number),numbase(string)
- Inputs: in(bits-bit)
 
- Attributes: 
- Subcircuit input: Input- Attributes: bits(natural number)
- Outputs: out(bits-bit)
 
- Attributes: 
- Subcircuit output: Output- Attributes: bits(natural number)
- Inputs: in(bits-bit)
 
- Attributes: 
- 7 segment display output: Display7- Inputs: bits(8-bit only - most significant bit controls decimal point LED)
 
- Inputs: 
- Bus grouping: BusGroup- Attributes: groups(array of natural numbers)
- Inputs: in0(groups[0]-bit) ...inN(groups[N]-bit)
- Outputs: out(sum-of-groups-bit)
 
- Attributes: 
- Bus ungrouping: BusUngroup- Attributes: groups(array of natural numbers)
- Inputs: in(sum-of-groups-bit)
- Outputs: out0(groups[0]-bit) ...outN(groups[N]-bit)
 
- Attributes: 
- Bus slicing: BusSlice- Attributes: slice.first,slice.count,slice.total(natural number)
- Inputs: in(slice.total-bit)
- Outputs: out(slice.count-bit)
 
- Attributes: 
- Zero- and sign-extension: ZeroExtend,SignExtend- Attributes: extend.input,extend.output(natural number)
- Inputs: in(extend.input-bit)
- Outputs: out(extend.output-bit)
 
- Attributes: 
- Finite state machines: FSM- Attributes: bits.in,bits.out,states,init_state,current_state(natural number),trans_table(array of transition descriptors)
- Transition descriptor attributes: ctrl_in,ctrl_out(binary strings),state_in,state_out(natural numbers)
- Inputs: clk(1-bit),arst(1-bit),in(bits.in-bit)
- Outputs: out(bits.out-bit)
 
- Attributes: 
Some ideas for further developing the simulator.
- Use JointJS elementTools for configuring/removing gates.
- RAM/ROM import/export for Verilog format and Intel HEX.
- Framebuffer element with character/bitmap display.
- More editing capability: adding and removing blocks, modifying some of blocks' properties.
- Undo-redo capability.
- Saving and loading circuits, including layout and state.
- Generic handling of negation for unary/binary gates (negation on inputs/outputs) for better clarity.
- SVG export.
- Verilog export.
- Smartphone and tablet compatible UI.