Unlike translation via AST from Python to a target, logic generators are lists of functions returning a generator
item. These generators can either evaluate to a current state or value or emit a target HDL.
As an example how a MyHDL snippet translates:
import sys
sys.path.insert(0, "../..")
from myirl.emulation.myhdl import *
@block
def unit(clk, en, a, q):
@always(clk.posedge)
def worker():
if en:
q.next = a
return instances()
print(unit.unparse())
============================== Unparsing unit unit ============================== @block def unit(clk, en, a, q): @always_(clk.posedge) def worker(): (yield [unit.ctx.If(en).Then(q.set(a))]) return instances()
In many cases you might want to interject statements (without using kludgy if __debug__
constructs) that don't end up in the resulting HDL. For instance, your HDL may not support a specific iteration as part of its language. MyIRL is more strict in separating synthesizeable elements from debug statements, for instance, inserting a print
command in a @process
is not valid. The @generator
style allows by separating in situ-execution from HDL output via yield
, but is still limited to simple combinatorial logic for now.
The following example generates an inverse wiring order by unrolling a loop:
from myirl.kernel import sensitivity
@block
def unit_x():
s = Signal(intbv()[8:])
z = [ Signal(bool(), name = "z%d" % i) for i in range(8) ]
@sensitivity.generator
def wireup():
for i in range(8):
j = 7 - i
print("DEBUG: Assign z[%d] = s[%d]" % (i, j))
yield [ z[i].set(s[j]) ]
return instances()
../../myirl/emulation/myhdl2irl.py:542: UserWarning: Not translating decorator `generator` warnings.warn("Not translating decorator `%s`" % n)
def convert():
inst = unit_x()
f = inst.elab(targets.VHDL)
return f
f = convert()
FALLBACK: UNHANDLED ROOT CLASS <class 'ipykernel.zmqshell.ZMQInteractiveShell'>, create new context DEBUG: Assign z[0] = s[7] DEBUG: Assign z[1] = s[6] DEBUG: Assign z[2] = s[5] DEBUG: Assign z[3] = s[4] DEBUG: Assign z[4] = s[3] DEBUG: Assign z[5] = s[2] DEBUG: Assign z[6] = s[1] DEBUG: Assign z[7] = s[0] Writing 'unit_x' to file /tmp/myirl_unit_x_uo9vd6v_/unit_x.vhdl
! grep -A 20 architecture {f[0]}
architecture myhdl_emulation of unit_x is -- Local type declarations -- Signal declarations signal z0 : std_ulogic; signal z1 : std_ulogic; signal z2 : std_ulogic; signal z3 : std_ulogic; signal z4 : std_ulogic; signal z5 : std_ulogic; signal z6 : std_ulogic; signal z7 : std_ulogic; signal s : unsigned(7 downto 0); begin z0 <= s(7); z1 <= s(6); z2 <= s(5); z3 <= s(4); z4 <= s(3); z5 <= s(2); z6 <= s(1); z7 <= s(0); end architecture myhdl_emulation;
Likewise, the @simulator.generator
supports this style for sequential, simulation specific commands.
from myirl import simulation
from myirl.test import common_test
@block
def testbench():
s = Signal(intbv()[8:])
rst = ResetSignal(0, 1, isasync = True)
clk = ClkSignal(name = 'clk')
clk.init = True
a, b = [ Signal(intbv()[5:]) for _ in range(2) ]
c = Signal(intbv()[6:])
@always(delay(1))
def clkgen():
clk.next = ~clk
@simulation.generator
def stimulus():
# These are the operations to be tested. We create references
# for evaluation below:
init = a.set(0xf), b.set(0x8),
add_operation = c.set(a + b + 2)
# Evaluate the operations:
init[0].evaluate(), init[1].evaluate()
v = add_operation.evaluate()
# Generate HDL:
yield [
rst.set(True),
simulation.wait('10 ns'),
rst.set(False),
*init,
simulation.wait('1 ns'),
add_operation, simulation.wait(clk.posedge),
simulation.assert_(c == v, "Test failed"),
simulation.print_("Test ok")
]
# Unroll a loop:
for i in range(4):
reassign = a.set(i)
reassign.evaluate()
add_operation = c.set(a + b - 1)
v = add_operation.evaluate()
yield [
reassign, simulation.wait(clk.posedge),
add_operation,
simulation.wait("1 ns"), simulation.print_(a, b, c, hex(v)),
simulation.assert_(c == v, "Test failed")
]
for i in range(6):
yield [ simulation.wait(clk.posedge ) ]
yield [ simulation.raise_(simulation.StopSimulation)]
return instances()
def convert():
tb = testbench()
files = tb.elab(targets.VHDL)
common_test.run_ghdl(files, tb, vcdfile = "testbench.vcd", debug = True)
convert()
FALLBACK: UNHANDLED ROOT CLASS <class 'ipykernel.zmqshell.ZMQInteractiveShell'>, create new context Writing 'testbench' to file /tmp/myirl_testbench_w158uouo/testbench.vhdl Warning: Implicit truncation of ADD(ADD(a, b), C:2) result Warning: Implicit truncation of SUB(ADD(a, b), C:1) result Warning: Implicit truncation of SUB(ADD(a, b), C:1) result Warning: Implicit truncation of SUB(ADD(a, b), C:1) result Warning: Implicit truncation of SUB(ADD(a, b), C:1) result ==== COSIM stdout ==== Test ok 0x00 0x08 0x07 0x7 0x01 0x08 0x08 0x8 0x02 0x08 0x09 0x9 0x03 0x08 0x0A 0xa simulation stopped @33ns
../../myirl/kernel/instance.py:424: TranslationWarning: @component `testbench`: DEBUG UNUSED 's' base.warn("@component `%s`: DEBUG UNUSED '%s'" % (self.obj.func.__name__, n), category = base.TranslationWarning)
import wavedraw
import nbwavedrom
TB = "testbench"
waveform = wavedraw.vcd2wave("testbench.vcd", TB + '.clk', None)
nbwavedrom.draw(waveform)