Mapping tests: A simple multiplier¶

We create a simple parametrizable multiplier element, convert it to RTL and call synthesis and mapping for the Gatemate target architecture.

In [1]:
import sys
sys.path.insert(0, '../../../')
In [2]:
from myirl.emulation.myhdl import *
In [3]:
Bool = Signal.Type(bool)

@block
def aluelement(clk : ClkSignal, en : Bool, a : Signal, b : Signal, dout : Signal.Output, reset : ResetSignal, DEPTH=18, DFF=False):
    DEPTH -= 1
    res = Signal(intbv(0, min=-2**DEPTH, max=2**DEPTH))

    
    if DFF:
        @always_seq(clk.posedge, reset)
        def worker():
            if en:
                res.next = a * b  
    else:
        @always_comb
        def worker():
            res.next = a * b


    @always_comb
    def assign():
        dout.next = res.signed()
        
    return instances()
In [4]:
from myirl.targets.pyosys import RTLIL

tgt = RTLIL("multest")

def convert(unit, size):
    clk = ClkSignal()
    en = Signal(bool(0))
    
    reset = ResetSignal(0, 1, False)
    s = size - 1
    a, b = [ Signal(intbv(0, min=-2**s, max=2**s)) for i in range(2) ]

    s = 2 * size - 1
    y = Signal(intbv(0, min=-2**s, max=2**s))

    
    unit_to_synthesize = unit(clk, en, a, b, y, reset, DEPTH=size*2, DFF=True)
    design = unit_to_synthesize.elab(tgt, elab_all = True)
    return design[0]
In [5]:
d = convert(aluelement, 4)
Creating process 'aluelement/worker' with sensitivity (clk'rising, <reset>)
 Adding module with name `aluelement` 
 DEBUG: SKIP NON-SIGNAL ARGUMENT `DEPTH` : <class 'int'> 
 DEBUG: SKIP NON-SIGNAL ARGUMENT `DFF` : <class 'bool'> 
 FINALIZE implementation `aluelement` of `aluelement` 

Synthesize for GateMate target¶

We simply call the synth_gatemate script on the design:

In [6]:
d.write_rtlil("pre")
-- Running command `tee -q hierarchy -top \aluelement' --

-- Running command `write_rtlil pre.il' --

2. Executing RTLIL backend.

For some constructs to sythesize, the yosys-abc executable is required, otherwise the Notebook kernel may crash without error report.

In [7]:
! [ -e `which yosys-abc` ] || echo "It may be necessary to `sudo apt install yosys`"
In [8]:
d.run("synth_gatemate")
In [9]:
# d.addSource("/usr/share/yosys/gatemate/cells_sim.v")
In [10]:
for cmd in [ 'proc', 'hierarchy -check', 'flatten']:
    d.run(cmd)
In [11]:
d.run("opt")
d.write_rtlil("post")
Output filename: pre.il

-- Running command `tee -q synth_gatemate' --

-- Running command `tee -q proc' --

-- Running command `tee -q hierarchy -check' --

-- Running command `tee -q flatten' --

-- Running command `tee -q opt' --

-- Running command `write_rtlil post.il' --

8. Executing RTLIL backend.
In [12]:
! echo "" >multest.log
In [13]:
d.run("stat", capture = True)

Then we dump the result as DOT file:

In [14]:
!cat multest.log

9. Printing statistics.

=== aluelement ===

   Number of wires:                 21
   Number of wire bits:             57
   Number of public wires:           7
   Number of public wire bits:      27
   Number of memories:               0
   Number of memory bits:            0
   Number of processes:              0
   Number of cells:                 37
     CC_BUFG                         1
     CC_DFF                          8
     CC_IBUF                        11
     CC_LUT4                         8
     CC_MULT                         1
     CC_OBUF                         8

In [15]:
d.display_rtl("*", fmt='dot')
Output filename: post.il

-- Running command `tee -q -a multest.log stat' --

-- Running command `show -format dot -prefix multest *' --

10. Generating Graphviz representation of design.
Writing dot description to `multest.dot'.
Dumping module aluelement to page 1.

Display the dot schematic for the multiplier:

In [16]:
from yosys import display
display.display_dot("multest")
Out[16]:
aluelement aluelement n15 a x56 0:0 - 0:0 n15:e->x56:w x58 1:1 - 0:0 n15:e->x58:w x60 2:2 - 0:0 n15:e->x60:w x62 3:3 - 0:0 n15:e->x62:w n16 b x64 0:0 - 0:0 n16:e->x64:w x66 1:1 - 0:0 n16:e->x66:w x68 2:2 - 0:0 n16:e->x68:w x70 3:3 - 0:0 n16:e->x70:w n17 clk c59 I $aluelement.clk CC_IBUF Y n17:e->c59:w n18 dout n19 en c69 I $aluelement.en CC_IBUF Y n19:e->c69:w n20 res x1 1:1 - 0:0 n20:e->x1:w x4 3:3 - 0:0 n20:e->x4:w x8 0:0 - 0:0 n20:e->x8:w x12 4:4 - 0:0 n20:e->x12:w x16 6:6 - 0:0 n20:e->x16:w x20 2:2 - 0:0 n20:e->x20:w x24 5:5 - 0:0 n20:e->x24:w x28 7:7 - 0:0 n20:e->x28:w x72 0:0 - 0:0 n20:e->x72:w x74 1:1 - 0:0 n20:e->x74:w x76 2:2 - 0:0 n20:e->x76:w x78 3:3 - 0:0 n20:e->x78:w x80 4:4 - 0:0 n20:e->x80:w x82 5:5 - 0:0 n20:e->x82:w x84 6:6 - 0:0 n20:e->x84:w x86 7:7 - 0:0 n20:e->x86:w x90 1:1 - 1:1 1:1 - 0:0 n20:e->x90:w n21 reset c70 I $aluelement.reset CC_IBUF Y n21:e->c70:w c27 I0 I1 I2 I3 $808 CC_LUT4 O c43 CLK D EN SR $743 CC_DFF Q c27:e->c43:w x0 1:1 - 0:0 x0:e->c27:w x1:e->c27:w x2 2:2 - 0:0 x2:e->c27:w x3 3:3 - 0:0 x3:e->c27:w c28 I0 I1 I2 I3 $809 CC_LUT4 O c45 CLK D EN SR $745 CC_DFF Q c28:e->c45:w x4:e->c28:w x5 3:3 - 0:0 x5:e->c28:w x6 2:2 - 0:0 x6:e->c28:w x7 3:3 - 0:0 x7:e->c28:w c29 I0 I1 I2 I3 $810 CC_LUT4 O c42 CLK D EN SR $742 CC_DFF Q c29:e->c42:w x8:e->c29:w x9 0:0 - 0:0 x9:e->c29:w x10 2:2 - 0:0 x10:e->c29:w x11 3:3 - 0:0 x11:e->c29:w c30 I0 I1 I2 I3 $811 CC_LUT4 O c46 CLK D EN SR $746 CC_DFF Q c30:e->c46:w x12:e->c30:w x13 4:4 - 0:0 x13:e->c30:w x14 2:2 - 0:0 x14:e->c30:w x15 3:3 - 0:0 x15:e->c30:w c31 I0 I1 I2 I3 $812 CC_LUT4 O c48 CLK D EN SR $748 CC_DFF Q c31:e->c48:w x16:e->c31:w x17 6:6 - 0:0 x17:e->c31:w x18 2:2 - 0:0 x18:e->c31:w x19 3:3 - 0:0 x19:e->c31:w c32 I0 I1 I2 I3 $813 CC_LUT4 O c44 CLK D EN SR $744 CC_DFF Q c32:e->c44:w x20:e->c32:w x21 2:2 - 0:0 x21:e->c32:w x22 2:2 - 0:0 x22:e->c32:w x23 3:3 - 0:0 x23:e->c32:w c33 I0 I1 I2 I3 $814 CC_LUT4 O c47 CLK D EN SR $747 CC_DFF Q c33:e->c47:w x24:e->c33:w x25 5:5 - 0:0 x25:e->c33:w x26 2:2 - 0:0 x26:e->c33:w x27 3:3 - 0:0 x27:e->c33:w c34 I0 I1 I2 I3 $815 CC_LUT4 O c49 CLK D EN SR $749 CC_DFF Q c34:e->c49:w x28:e->c34:w x29 7:7 - 0:0 x29:e->c34:w x30 2:2 - 0:0 x30:e->c34:w x31 3:3 - 0:0 x31:e->c34:w c36 I $824 CC_BUFG O n12 c36:e->n12:w v32 1'1 v32:e->c42:w v34 1'0 v34:e->c42:w x33 0:0 - 0:0 c42:e->x33:w x33:e->n20:w v35 1'1 v35:e->c43:w v37 1'0 v37:e->c43:w x36 0:0 - 1:1 c43:e->x36:w x36:e->n20:w v38 1'1 v38:e->c44:w v40 1'0 v40:e->c44:w x39 0:0 - 2:2 c44:e->x39:w x39:e->n20:w v41 1'1 v41:e->c45:w v43 1'0 v43:e->c45:w x42 0:0 - 3:3 c45:e->x42:w x42:e->n20:w v44 1'1 v44:e->c46:w v46 1'0 v46:e->c46:w x45 0:0 - 4:4 c46:e->x45:w x45:e->n20:w v47 1'1 v47:e->c47:w v49 1'0 v49:e->c47:w x48 0:0 - 5:5 c47:e->x48:w x48:e->n20:w v50 1'1 v50:e->c48:w v52 1'0 v52:e->c48:w x51 0:0 - 6:6 c48:e->x51:w x51:e->n20:w v53 1'1 v53:e->c49:w v55 1'0 v55:e->c49:w x54 0:0 - 7:7 c49:e->x54:w x54:e->n20:w c51 I $aluelement.a CC_IBUF Y x57 0:0 - 0:0 c51:e->x57:w x56:e->c51:w n10 x57:e->n10:w c52 I $aluelement.a_1 CC_IBUF Y x59 0:0 - 1:1 c52:e->x59:w x58:e->c52:w x59:e->n10:w c53 I $aluelement.a_2 CC_IBUF Y x61 0:0 - 2:2 c53:e->x61:w x60:e->c53:w x61:e->n10:w c54 I $aluelement.a_3 CC_IBUF Y x63 0:0 - 3:3 c54:e->x63:w x62:e->c54:w x63:e->n10:w c55 I $aluelement.b CC_IBUF Y x65 0:0 - 0:0 c55:e->x65:w x64:e->c55:w n11 x65:e->n11:w c56 I $aluelement.b_1 CC_IBUF Y x67 0:0 - 1:1 c56:e->x67:w x66:e->c56:w x67:e->n11:w c57 I $aluelement.b_2 CC_IBUF Y x69 0:0 - 2:2 c57:e->x69:w x68:e->c57:w x69:e->n11:w c58 I $aluelement.b_3 CC_IBUF Y x71 0:0 - 3:3 c58:e->x71:w x70:e->c58:w x71:e->n11:w c59:e->c36:w c61 A $aluelement.dout CC_OBUF O x73 0:0 - 0:0 c61:e->x73:w x72:e->c61:w x73:e->n18:w c62 A $aluelement.dout_1 CC_OBUF O x75 0:0 - 1:1 c62:e->x75:w x74:e->c62:w x75:e->n18:w c63 A $aluelement.dout_2 CC_OBUF O x77 0:0 - 2:2 c63:e->x77:w x76:e->c63:w x77:e->n18:w c64 A $aluelement.dout_3 CC_OBUF O x79 0:0 - 3:3 c64:e->x79:w x78:e->c64:w x79:e->n18:w c65 A $aluelement.dout_4 CC_OBUF O x81 0:0 - 4:4 c65:e->x81:w x80:e->c65:w x81:e->n18:w c66 A $aluelement.dout_5 CC_OBUF O x83 0:0 - 5:5 c66:e->x83:w x82:e->c66:w x83:e->n18:w c67 A $aluelement.dout_6 CC_OBUF O x85 0:0 - 6:6 c67:e->x85:w x84:e->c67:w x85:e->n18:w c68 A $aluelement.dout_7 CC_OBUF O x87 0:0 - 7:7 c68:e->x87:w x86:e->c68:w x87:e->n18:w x88 0:0 - 3:3 c69:e->x88:w n14 x88:e->n14:w x89 0:0 - 2:2 c70:e->x89:w x89:e->n14:w c73 A B $worker:13::4021/mul:_s CC_MULT P n13 c73:e->n13:w x91 1:0 - 1:0 x90:e->x91:w x91:e->n14:w n10:e->c73:w n11:e->c73:w n12:e->c42:w n12:e->c43:w n12:e->c44:w n12:e->c45:w n12:e->c46:w n12:e->c47:w n12:e->c48:w n12:e->c49:w n13:e->x0:w n13:e->x5:w n13:e->x9:w n13:e->x13:w n13:e->x17:w n13:e->x21:w n13:e->x25:w n13:e->x29:w n13:e->x90:w n14:e->x2:w n14:e->x3:w n14:e->x6:w n14:e->x7:w n14:e->x10:w n14:e->x11:w n14:e->x14:w n14:e->x15:w n14:e->x18:w n14:e->x19:w n14:e->x22:w n14:e->x23:w n14:e->x26:w n14:e->x27:w n14:e->x30:w n14:e->x31:w \n

Simulation¶

Let's run this construct through the CXXRTL simulator to see if it behaves as we expect:

In [17]:
from simulation import *
from yosys.simulator import CXXRTL

@sim.testbench(CXXRTL, time_unit = 'ns')
def tb_mul(size, VALUES):
    clk = ClkSignal(init = 0)
    reset = ResetSignal(1, 1, isasync = False)

    en = Signal(bool(0))

    reset = ResetSignal(0, 1, False)
    s = size - 1
    a, b = [ Signal(intbv(0, min=-2**s, max=2**s)) for i in range(2) ]

    s = 2 * size - 1
    y = Signal(intbv(0, min=-2**s, max=2**s))

    uut_mul = aluelement(clk, en, a, b, y, reset, DEPTH=size*2, DFF=True)
    
    @always(delay(5))
    def clkgen():
        clk.next = ~clk

    @sequence
    def reset_seq():
        reset.next = True
        for _ in range(5):
            yield clk.posedge
        reset.next = False
        yield clk.negedge
        for va, vb, vy in VALUES:
            a.next = va
            b.next = vb
            en.next = True
            yield clk.negedge
            en.next = False
            yield clk.negedge
            print(va, vb, int(y))
            assert int(y) == vy
        
        yield delay(200)

    return instances()
In [18]:
def test_simulation(size, VALUES):
    t = tb_mul(size, VALUES)
    t.run_synthcmd("write_ilang test.il")
    # t.run_synthcmd("synth_gatemate")
    # t.run_synthcmd("read_verilog /usr/share/yosys/gatemate/cells_sim.v")
    # t._force_compile = True
    t.run(150)
In [19]:
VALUES = [
    (4, 5, 20),
    (7, 6, 42),
    (-1, 2, -2),
    (-2, -2, 4)
]
test_simulation(4, VALUES)
DEBUG: Skip non-simulation type <class 'list'>
 Adding module with name `aluelement` 
 DEBUG: SKIP NON-SIGNAL ARGUMENT `DEPTH` : <class 'int'> 
 DEBUG: SKIP NON-SIGNAL ARGUMENT `DFF` : <class 'bool'> 
 FINALIZE implementation `aluelement` of `aluelement` 
Tolerate exception: Module with name `aluelement` already existing
Compiling /tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0.pyx because it changed.
[1/1] Cythonizing /tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0.pyx
running build_ext
building 'aluelement_f7b0' extension
creating build
creating build/temp.linux-x86_64-3.9
creating build/temp.linux-x86_64-3.9/tmp
creating build/temp.linux-x86_64-3.9/tmp/myirl_top_aluelement_5f6ix9un
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I../../../myirl/../ -I/tmp/myirl_top_aluelement_5f6ix9un/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0.o
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I../../../myirl/../ -I/tmp/myirl_top_aluelement_5f6ix9un/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0_rtl.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0_rtl.o
creating build/lib.linux-x86_64-3.9
x86_64-linux-gnu-g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 -Wl,-z,relro -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.9/tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0.o build/temp.linux-x86_64-3.9/tmp/myirl_top_aluelement_5f6ix9un/aluelement_f7b0_rtl.o -o build/lib.linux-x86_64-3.9/aluelement_f7b0.cpython-39-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-3.9/aluelement_f7b0.cpython-39-x86_64-linux-gnu.so -> 
Open for writing: tb_mul.vcd
 CXXRTL context: SKIP INTERFACE ITEM `DEPTH` 
 CXXRTL context: SKIP INTERFACE ITEM `DFF` 
4 5 20
7 6 42
-1 2 -2
-2 -2 4

Wave trace¶

The commands below display a schematic waveform of the above.

Note: This display is not timing accurate.

In [20]:
import wavedraw; import nbwavedrom
waveform = wavedraw.vcd2wave("tb_mul.vcd", '.clk', None)
nbwavedrom.draw(waveform)