Direct RTL via yosys

The former 'jupyosys' fork from myHDL is in process of migrating into this development tree. However, it is regarded unstable, as the Python API towards yosys is likely going to be redesigned.

In [1]:
import sys
sys.path.insert(0, "../..")
In [2]:
from myirl.emulation.myhdl import *

Byte = Signal.Type(intbv, 8)
Bool = Signal.Type(bool)

@block
def lfsr8(clk : ClkSignal, ce : Bool, reset : ResetSignal, dout : Byte.Output,
          RVAL : int = 1):
    """LFSR with all states"""
    
    v = Signal(intbv(RVAL)[8:])
     
    fb = Signal(bool())
    
    e = v[7:0] == 0

    @always_seq(clk.posedge, reset)
    def worker():
        if ce == 1:
            v.next = concat(v[6], v[5], v[4], v[3] ^ fb, v[2] ^ fb, v[1] ^ fb, v[0], fb)

    @always_comb
    def assign():
        fb.next = v[7] ^ e
        dout.next = v

    return instances()

# Wrapper hack to use local dictionary for instance naming
def use_local_names(arg):
    arg.use_local_names = True
    return arg

@use_local_names
@block
def unit_count(clk : ClkSignal, ce: Signal, reset : ResetSignal, q : Signal.Output):
    
    c, d = [ Signal(intbv(0)[8:]) for _ in range(2) ]
    
    inst_lfsr = lfsr8(clk, ce, reset, d, RVAL = 0xfa)

    @always_seq(clk.posedge, reset)
    def counter():
        c.next = c + 1
#        q.next = d ^ c

    wires = [ q.wireup(d ^ c) ]
    
    return instances()
In [3]:
from myirl.targets import pyosys

def test_expr(tgt):
    ce = Signal(bool())
    clk = ClkSignal()
    reset = ResetSignal(0, 1, isasync = True)
    q = Signal(intbv()[8:])

    t = unit_count(clk, ce, reset, q)
    designs = t.elab(tgt, elab_all = True)

    return designs[0]
In [4]:
tgt = pyosys.RTLIL("top")

design = test_expr(tgt)
design.display_rtl(selection = "unit_count", fmt='dot')
# design.display_rtl(selection = "lfsr8", fmt='dot')
Creating process 'lfsr8/worker' with sensitivity (clk'rising, <reset>)
Creating process 'unit_count/counter' with sensitivity (clk'rising, <reset>)
 Elaborating component lfsr8_s1_s1_s1_s8_250 
 Adding module with name `lfsr8` 
 Adding module with name `unit_count` 
 FINALIZE implementation `unit_count` of `unit_count` 

-- Running command `tee -q hierarchy -top \unit_count' --

-- Running command `show -format dot -prefix top unit_count' --

2. Generating Graphviz representation of design.
Writing dot description to `top.dot'.
Dumping module unit_count to page 1.

RTL Display

The @use_local_names construct sets the myHDL instance variable names for the identifier.

Note: Pan and zoom may not work on some browsers.

In [5]:
from yosys import display
display.display_dot(design.name)
Out[5]:
unit_count unit_count n7 q n8 reset c21 ARST CLK D $counter::c_6619 $adff Q n8:e->c21:w c24 ce clk reset inst_lfsr lfsr8 dout n8:e->c24:w n9 ce n9:e->c24:w n10 clk n10:e->c21:w n10:e->c24:w n11 c c16 A B $counter:44::ab0c/xor:_u $xor Y n11:e->c16:w x2 BUF n11:e->x2:w n12 d n12:e->c16:w x5 BUF c16:e->x5:w x4 BUF c21:e->x4:w v0 9'000000001 c22 A B $counter:44::0398/add:_u $add Y v0:e->c22:w x3 7:0 - 7:0 c22:e->x3:w x1 0 -> 8:8 7:0 - 7:0 x1:e->c22:w c24:e->n12:w x2:e->x1:w x3:e->c21:w x4:e->n11:w x5:e->n7:w \n

Test bench (myHDL style)

Note that and, or and not boolean constructs are no longer allowed with signals.

In [6]:
from simulation import *

from yosys.simulator import CXXRTL as Sim


@sim.testbench(Sim, time_unit = 'ns')
def testbench():
    clk = ClkSignal(init = 0)
    reset = ResetSignal(1, 1, isasync = False)
    ce = Signal(bool())

    a = Signal(intbv()[8:])

    inst = unit_count(clk, ce, reset, a)

    @always(delay(2))
    def clkgen():
        clk.next = ~ clk

    @sequence
    def reset_seq():
        yield delay(21)
        reset.next = False
        yield delay(1)
        ce.next = True
        yield delay(20)

    return instances()

The simulation is executed using the .run method below. Note that the simulation may not be fully 'delta' accurate and will only serve for synchronous designs.

Note: It is mandatory to yield an initial delay in the sequential code to properly arm the concurrent process scheduling.

In [7]:
def test_simulation(n):
    t = testbench()
    assert t._uut.obj.ctx == unit_count.ctx
    t.run(n)
    return t

t = test_simulation(2000)
 Elaborating component lfsr8_s1_s1_s1_s8_250 
 Adding module with name `lfsr8` 
 Adding module with name `unit_count` 
 FINALIZE implementation `unit_count` of `unit_count` 
Compiling /tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a.pyx because it changed.
[1/1] Cythonizing /tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a.pyx
running build_ext
building 'unit_count_a81a' extension
creating build/temp.linux-x86_64-3.9/tmp/myirl_top_unit_count_y0bv_vwv
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 -DCOSIM_NAMESPACE=unit_count_a81a -I../../myirl/../ -I/tmp/myirl_top_unit_count_y0bv_vwv/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a.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 -DCOSIM_NAMESPACE=unit_count_a81a -I../../myirl/../ -I/tmp/myirl_top_unit_count_y0bv_vwv/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a_rtl.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a_rtl.o
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_unit_count_y0bv_vwv/unit_count_a81a.o build/temp.linux-x86_64-3.9/tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a_rtl.o -o build/lib.linux-x86_64-3.9/unit_count_a81a.cpython-39-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-3.9/unit_count_a81a.cpython-39-x86_64-linux-gnu.so -> 
Open for writing: testbench.vcd
DEBUG STOP PROCESS reset_seq

The resulting test bench file: testbench.vcd

Customizing RTLIL targets

When the .elab() method is called, the design is elaborated as RTLIL and a list of design elements is returned, the first being a RTLIL Design handle. The .finalize() method is called last inside elaboration, which can perform some optimizations or emissions to specific targets.

In [8]:
class MyRTL(pyosys.RTLIL):
    def finalize(self, top):
        tname = top.name
        design = self._design
        design.run("hierarchy -top %s" % tname)
        print(80 * '=')
        design.write_verilog(name = top.obj.name)
        design.run("flatten; ls; select %s; stat" % tname)
In [9]:
tgt = MyRTL("top2")

design = test_expr(tgt)
 Elaborating component lfsr8_s1_s1_s1_s8_250 
 Adding module with name `lfsr8` 
 Adding module with name `unit_count` 
================================================================================

-- Running command `tee -q hierarchy -top \unit_count' --

-- Running command `tee -q write_cxxrtl -namespace unit_count_a81a  -header /tmp/myirl_top_unit_count_y0bv_vwv/unit_count_a81a_rtl.cpp' --

-- Running command `tee -q hierarchy -top unit_count' --

-- Running command `ls; check' --

6. Executing CHECK pass (checking for obvious problems).
Found and reported 0 problems.

-- Running command `hierarchy -check' --

7. Executing HIERARCHY pass (managing design hierarchy).

-- Running command `write_verilog unit_count_mapped.v' --

8. Executing Verilog backend.
Dumping module `\lfsr8'.
Dumping module `\unit_count'.

-- Running command `tee -q flatten; ls; select unit_count; stat' --

1 modules:
  unit_count

10. Printing statistics.
In [10]:
!cat unit_count_mapped.v
/* Generated by Yosys 0.13+3 (git sha1 4656b0171, gcc 10.2.1-6 -Og -fPIC) */

module lfsr8(clk, ce, reset, dout);
  wire [7:0] _00_;
  wire [-1:0] _01_;
  wire _02_;
  wire _03_;
  wire _04_;
  wire _05_;
  wire [7:0] _06_;
  wire _07_;
  wire _08_;
  wire _09_;
  wire [7:0] _10_;
  reg [7:0] _11_;
  wire [7:0] _12_;
  wire [7:0] _13_;
  input ce;
  input clk;
  output [7:0] dout;
  wire fb;
  input reset;
  wire [7:0] v;
  assign _08_ = v[6:0] == 7'h00;
  assign _09_ = v[7] ^ _08_;
  assign _03_ = v[2] ^ fb;
  assign _02_ = v[1] ^ fb;
  assign _05_ = ce == 1'h1;
  assign _06_ = _05_ ? _00_ : _13_;
  assign _04_ = v[3] ^ fb;
  always @(posedge clk, posedge reset)
    if (reset) _11_ <= 8'hfa;
    else _11_ <= _06_;
  assign _00_ = { v[6:4], _04_, _03_, _02_, v[0], fb };
  assign v = _11_;
  assign _13_ = _11_;
  assign _07_ = _09_;
  assign _10_ = v;
  assign fb = _07_;
  assign dout = _10_;
endmodule

(* top =  1  *)
module unit_count(clk, ce, reset, q);
  wire [7:0] _0_;
  wire [7:0] _1_;
  wire [9:0] _2_;
  wire [7:0] _3_;
  reg [7:0] _4_;
  wire [7:0] _5_;
  wire [7:0] c;
  input ce;
  input clk;
  wire [7:0] d;
  output [7:0] q;
  input reset;
  assign _3_ = d ^ c;
  assign _2_ = { 1'h0, _1_ } + 9'h001;
  always @(posedge clk, posedge reset)
    if (reset) _4_ <= 8'h00;
    else _4_ <= _0_;
  lfsr8 inst_lfsr (
    .ce(ce),
    .clk(clk),
    .dout(d),
    .reset(reset)
  );
  assign _1_ = c;
  assign _0_ = _2_[7:0];
  assign c = _4_;
  assign q = _3_;
endmodule

Limitations

Some constructs that work for the VHDL target are not yet supported:

  • No partial assignments of the type p[0].next = x
  • Custom generators (bulk signal assignment, @hdlmacro constructs)