Skip to content

Python Examples

The examples folder of the repository has examples that build and simulate circuits in code. Most of them write a VCD file with all signals. The ready-made circuits for the DigSim application are on GUI Examples.

To run them, clone the repository and run the commands below from its root folder (see Run from source):

git clone https://github.com/freand76/digsim.git
cd digsim

The waveform images on this page are rendered from the VCD files that the examples write. To look at the waveforms yourself, open the VCD file in GTKWave or another VCD viewer, for example gtkwave sr.vcd.

SR latch

examples/example_sr.py connects two push buttons to the set and reset inputs of an SR latch (built from two NOR gates) and the latch output to an LED. The script presses the buttons in a fixed sequence, and a callback prints the LED state every time it changes.

SR latch circuit

uv run python examples/example_sr.py

The left column is the simulation time in nanoseconds. Pressing set a second time does not change the output, the latch is already set:

        0:LED: 'D1' is OFF
Reset
 10000001:LED: 'D1' is OFF
Set
 30000002:LED: 'D1' is ON
Set (again)
Reset
 60000001:LED: 'D1' is OFF

The waveforms are written to sr.vcd. The latch output is unknown (red) until the first reset:

SR latch waveforms

Clock

examples/example_clock.py connects a 20 Hz clock to an LED and simulates one second.

Clock circuit

uv run python examples/example_clock.py

The LED toggles every 25 ms (half a 20 Hz period):

        0:LED: 'D1' is OFF
        0:LED: 'D1' is OFF
 25000000:LED: 'D1' is ON
 50000000:LED: 'D1' is OFF
 75000000:LED: 'D1' is ON
...
975000000:LED: 'D1' is ON
1000000000:LED: 'D1' is OFF

The first 300 ms of clock.vcd:

Clock waveforms

Save a circuit (pulse generator)

examples/load_save/example_save_circuit.py builds a pulse generator: a push button drives one AND input directly and the other through three NOT gates. The circuit is simulated, saved to pulse_circuit.json, loaded into a new circuit and simulated again.

Pulse generator circuit

uv run python examples/load_save/example_save_circuit.py

When the button is pressed, both AND inputs are high for a moment, until the change has propagated through the three NOT gates (1 ns each). The LED is on for 3 ns. The second half of the output comes from the loaded copy of the circuit:

        0:LED: 'D' is OFF
        1:LED: 'D' is OFF
ON
 10000001:LED: 'D' is ON
 10000004:LED: 'D' is OFF
OFF
        1:LED: 'D' is OFF
  5000001:LED: 'D' is ON
  5000004:LED: 'D' is OFF

The waveforms in pulse.vcd, zoomed in on the button press at 10 ms, show the change moving through the gates:

Pulse generator waveforms

Load a circuit (logic gates)

examples/load_save/example_load_circuit.py loads the circuit in example_circuit.json: two buttons connected to an AND, a NOT and an XOR gate, each driving an LED. The script presses the buttons in a sequence and prints the LED changes.

Logic gates circuit

uv run python examples/load_save/example_load_circuit.py

The ------------ lines show the button states (button1 button2) for the next step:

        1:LED: 'and_led' is OFF
        1:LED: 'xor_led' is OFF
        1:LED: 'not_led' is ON
------------ 10
  5000001:LED: 'not_led' is OFF
  5000001:LED: 'xor_led' is ON
------------ 00
 10000001:LED: 'not_led' is ON
 10000001:LED: 'xor_led' is OFF
------------ 10
 15000001:LED: 'not_led' is OFF
 15000001:LED: 'xor_led' is ON
------------ 11
 20000001:LED: 'and_led' is ON
 20000001:LED: 'xor_led' is OFF

The waveforms in circuit.vcd:

Logic gates waveforms

Verilog counter

The two examples in examples/yosys_counter simulate the 4-bit counter in counter.v:

  • example_yosys_counter_netlist.py loads the pre-synthesized netlist counter.json.
  • example_yosys_counter_verilog.py loads counter.v and synthesizes it on the fly.

Both connect push buttons to the clock and reset inputs and tie the up input high, reset the counter and then clock it 16 times.

Counter circuit

uv run python examples/yosys_counter/example_yosys_counter_netlist.py
uv run python examples/yosys_counter/example_yosys_counter_verilog.py

Both print the same output, the counter value before each clock pulse:

===================== Reset ==========================

counter
 - I:clk=0
 - I:reset=0
 - I:up=1
 - O:cnt=0

===================== Start ==========================

OUT 0
OUT 1
OUT 2
...
OUT 14
OUT 15

counter.vcd is started after the reset:

Counter waveforms

Synthesis (Fibonacci sequence)

examples/synthesis/example_synthesis.py synthesizes fibonacci.v, a Fibonacci sequence generator, with Yosys from Python, loads the netlist as a component and drives its clock and reset inputs directly from the script.

Fibonacci circuit

uv run python examples/synthesis/example_synthesis.py
Start synthesis of 'examples/synthesis/fibonacci.v'
Synthesis done!
...
Fibonacci sequence [0] value is 0
Fibonacci sequence [1] value is 1
Fibonacci sequence [2] value is 1
Fibonacci sequence [3] value is 2
Fibonacci sequence [4] value is 3
Fibonacci sequence [5] value is 5
...
Fibonacci sequence [14] value is 377
Fibonacci sequence [15] value is 610

The netlist is written to examples/synthesis/fibonacci.json and the waveforms to fibonacci.vcd:

Fibonacci waveforms

6502 CPU

examples/yosys_6502/example_yosys_6502.py simulates a synthesized 6502 CPU (6502_primitives.json) connected to a 64 kB RAM and a memory-mapped text output at address 0x8000. The program is preloaded into the RAM at 0xF800 from code.bin, built from code.s. It writes "Hello World" to the text output, which prints each character.

6502 circuit

uv run python examples/yosys_6502/example_yosys_6502.py

The simulation of 300 clock cycles takes less than a second, and the example prints how many clock cycles it simulated per second:

cpu
 - I:clk=0
 - I:reset=0
 - I:DI=X
 - I:IRQ=0
 - I:NMI=0
 - I:RDY=1
 - O:AB=X
 - O:DO=X
 - O:WE=X
StringOutput [char]: 'H'
StringOutput [char]: 'e'
StringOutput [char]: 'l'
StringOutput [char]: 'l'
StringOutput [char]: 'o'
StringOutput [char]: ' '
StringOutput [char]: 'W'
StringOutput [char]: 'o'
StringOutput [char]: 'r'
StringOutput [char]: 'l'
StringOutput [char]: 'd'
StringOutput [line]: 'Hello World'
Simulated 302 clock cycles of 6502_primitives.json in 0.41 s: 728 clocks/second

6502_primitives.json is the CPU synthesized into gates. Add --functional to run 6502_functional.json instead, the same CPU synthesized into functional blocks like adders and multiplexers (see Gates or functional blocks):

uv run python examples/yosys_6502/example_yosys_6502.py --functional
...
StringOutput [line]: 'Hello World'
Simulated 302 clock cycles of 6502_functional.json in 0.26 s: 1147 clocks/second

verilog/synth.sh in the repository synthesizes both netlists.

Add --signals to make the internal signals of the CPU readable (see Internal signals and states). The example then prints the registers after the simulation, and with --vcd the waveforms have the signals inside the CPU too, with the state of the CPU as the names of its states:

uv run python examples/yosys_6502/example_yosys_6502.py --functional --signals
...
CPU: PC=F82E A=00 X=00 Y=0C S=FF P=37 state=DECODE

Waveforms are not written by default, since writing every signal in the CPU makes the simulation slower. Add --vcd to write them to 6502.vcd, or --vcd <file> to choose the file name:

uv run python examples/yosys_6502/example_yosys_6502.py --vcd

The waveforms below (values in hex) show the CPU fetching the program from F823 and onwards, then writing 48, the character H, to the text output at address 8000:

6502 waveforms

To change the program, edit the assembly files and rebuild code.bin with compile.sh, which needs the cc65 assembler and linker.

State machine (traffic light)

examples/yosys_fsm/example_yosys_fsm.py simulates a traffic light, a state machine in traffic_light.sv with its states as a SystemVerilog enum. A request turns the light from red to green, and back after a while. The component is created with signals=True, so its internal signals, the state and a timer, are readable, and the state has the names of the enum:

light = YosysComponent(circuit, path="traffic_light.sv", functional=True, signals=True)
light.state("state")  # "RED", "RED_YELLOW", "GREEN" or "YELLOW"
uv run python examples/yosys_fsm/example_yosys_fsm.py
Internal signals: state, timer
   10 ms: RED
  200 ms: RED after the reset
  260 ms: RED_YELLOW
  460 ms: GREEN
  960 ms: YELLOW
 1160 ms: RED

The example writes traffic_light.vcd, with the state as a number and, in state_name, as text, the name of the state:

Traffic light waveforms

In the VCD file the state is a wire, and its name a string variable:

$var wire 2 0! state $end
$var string 1 1! state_name $end
...
#250000001
b1 0!
sRED_YELLOW 1!
...
#450000001
b10 0!
sGREEN 1!

Bus registers (inout ports)

examples/yosys_inout/example_yosys_inout.py puts two registers on a shared 8-bit data bus, with a tristate buffer that drives the bus like a CPU. The register, bus_register.v, has a bidirectional inout data port: it drives its value on the bus while oe is high, and stores the value on the bus at a clock edge while we is high. The inout ports drive each other, see Inout ports:

cpu.Y.wire = reg_a.data
cpu.Y.wire = reg_b.data
reg_a.data.wire = reg_b.data
reg_b.data.wire = reg_a.data

The CPU writes 0x5a to the first register, which then drives it on the bus for the second register to store. bus is the value on the bus that the first register reads, and a drives what it drives itself:

uv run python examples/yosys_inout/example_yosys_inout.py
Start, nothing drives the bus      bus=Z    a drives=Z    a=0x00 b=0x00
CPU drives 0x5a                    bus=0x5a a drives=Z    a=0x00 b=0x00
Register a stores it               bus=0x5a a drives=Z    a=0x5a b=0x00
Register a drives the bus          bus=0x5a a drives=0x5a a=0x5a b=0x00
Register b stores it               bus=0x5a a drives=0x5a a=0x5a b=0x5a
Register b drives the bus          bus=0x5a a drives=Z    a=0x5a b=0x5a
CPU drives 0x33 too (contention)   bus=X    a drives=Z    a=0x5a b=0x5a
Nothing drives the bus             bus=Z    a drives=Z    a=0x5a b=0x5a

pytest testbenches

examples/pytest_tb tests two Verilog designs with pytest: an 8-bit ALU (alu.v) and a 4-bit up/down counter (up_down_counter.v). Each test module synthesizes its design in a fixture, drives the inputs from Python and asserts on the outputs. See Verilog Testbench with pytest for how they work.

Testbench setup

uv run --with pytest pytest -v examples/pytest_tb
examples/pytest_tb/test_alu_testbench.py::test_add PASSED
examples/pytest_tb/test_alu_testbench.py::test_sub PASSED
examples/pytest_tb/test_alu_testbench.py::test_and PASSED
examples/pytest_tb/test_alu_testbench.py::test_or PASSED
examples/pytest_tb/test_alu_testbench.py::test_xor PASSED
examples/pytest_tb/test_alu_testbench.py::test_logic_lshift PASSED
examples/pytest_tb/test_alu_testbench.py::test_logic_rshift PASSED
examples/pytest_tb/test_alu_testbench.py::test_arith_rshift PASSED
examples/pytest_tb/test_up_down_counter_testbench.py::test_no_count PASSED
examples/pytest_tb/test_up_down_counter_testbench.py::test_count_up PASSED
examples/pytest_tb/test_up_down_counter_testbench.py::test_count_down PASSED
examples/pytest_tb/test_up_down_counter_testbench.py::test_up_precedence PASSED
examples/pytest_tb/test_up_down_counter_testbench.py::test_reset_after_count PASSED

The tests write alu_test.vcd and up_down_counter_test.vcd. The counter tests, one after the other: no counting, counting up to 15 and wrapping to 0, counting down, and resets:

Up/down counter testbench waveforms

Subcircuit (full adder)

examples/subcircuit/example_subcircuit.py uses the full adder of the GUI example circuits, circuits/subcircuits/full_adder.circuit, as a SubCircuit, a component made from a circuit file. The full adder is made from two half adder subcircuits itself. Its ports are the In and Out ports of the file, and the example prints its truth table:

uv run python examples/subcircuit/example_subcircuit.py
 a  b cin |  s cout
 0  0  0  |  0   0
 0  0  1  |  1   0
 0  1  0  |  1   0
 0  1  1  |  0   1
 1  0  0  |  1   0
 1  0  1  |  0   1
 1  1  0  |  0   1
 1  1  1  |  1   1