#!/usr/bin/env python # coding: utf-8 # # Direct RTL via yosys # # The former 'jupyosys' fork from myHDL is in process of migrating into this development tree. # # Status notes: # * Do not rely on pyosys API # * Crashes and irregularities may occur # # 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') # ### 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) # ## 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) # The resulting test bench file: [testbench.vcd](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, objs = None): 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) return [design] # The `.finalize()` function must return the created elements in a list, which is in turn returned from the `.elab()` call. # In[9]: tgt = MyRTL("top2") design = test_expr(tgt) # In[10]: get_ipython().system('cat unit_count_mapped.v') # ## 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)