Some myHDL libraries provide classes to generate @block
components. These are currently under scrutiny.
For the time being, factory classes have be decorated using the @factory
decorator in order to be translated to IRL.
One very important thing to note is that factory classes in myHDL emulation mode inherit the global namespace of the caller. Anything that is local to the factory class should be stored as an attribute during initialization.
So, when referring to external HDL objects inside a @block_component
that are imported at the header, a NameError
may occur. It is best to avoid reference to external HDL objects and use class constructs, only.
Not that the following class makes sense in the real world, but as an example:
@block
factory¶from myirl.emulation.myhdl import *
@factory
class Bingo:
def __init__(self, parameter = 8):
self.n = parameter
T = Signal.Type(intbv, parameter)
@block
def my_unit(clk : ClkSignal, a : T, b : T.Output):
@always(clk.posedge)
def worker():
b.next = a
return instances()
self.my_unit = my_unit
def test():
b = Bingo(6)
u, v = [Signal(intbv()[6:]) for _ in range(2)]
c = ClkSignal()
u = b.my_unit(c, u, v)
f = u.elab(targets.VHDL)
return f
f = test()
FALLBACK: UNHANDLED ROOT CLASS <class 'ipykernel.zmqshell.ZMQInteractiveShell'>, create new context Writing 'my_unit' to file /tmp/myirl_my_unit_bj7ydc5l/my_unit.vhdl
!cat {f[0]}
-- File generated from source: -- /usr/local/lib/python3.9/dist-packages/IPython/core/interactiveshell.py -- (c) 2016-2022 section5.ch -- Modifications may be lost, edit the source file instead. library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library work; use work.txt_util.all; use work.myirl_conversion.all; entity my_unit is port ( clk : in std_ulogic; a : in unsigned(5 downto 0); b : out unsigned(5 downto 0) ); end entity my_unit; architecture myhdl_emulation of my_unit is -- Local type declarations -- Signal declarations begin worker: process(clk) begin if rising_edge(clk) then b <= a; end if; end process; end architecture myhdl_emulation;
@block_component
member¶When specified as a static unit inside this class, the class will serve as its context and the unit requires a @block_component
decoration (without arguments), plus the self
argument, representing a component method describing hardware inference.
In this case, the factory class is derived from the DesignModule
(or minimum the BareModule
class). This construct helps to encapsulate common blocks that depend on a complex configuration.
from myirl.test import common_test
@factory
class Bongo(LibraryModule):
def __init__(self, parameter = 8, name = "Bongo"):
self.n = parameter
super().__init__(name)
self.files = []
self.inst = None
# Here, we use `@block_component` without arguments, as the context is 'self'
@block_component
def my_unit1(self, clk : ClkSignal, a : Signal, b : Signal.Output):
"A component somewhere in the hierarchy"
ls = Signal(intbv()[self.n:])
@always(clk.posedge)
def worker():
ls.next = a
b.next = ls
return instances()
@block_component
def my_tb(self):
"A built-in test bench"
u, v = [Signal(intbv()[self.n:]) for _ in range(2)]
c = ClkSignal()
@always(delay(4))
def clkgen():
c.next = ~c
@instance
def stim():
u.next = 2
yield c.posedge
yield c.posedge
yield c.posedge
print(v)
assert v == 2
raise StopSimulation
uut = self.my_unit1(c, u, v)
return instances()
def build(self):
t = self.my_tb()
self.files = t.elab(targets.VHDL, elab_all = True)
self.inst = t
print("Resulting files", self.files)
def simulate(self):
common_test.run_ghdl(self.files, self.inst, debug = True)
print(Bongo.unparse())
============================== Unparsing unit Bongo ============================== class Bongo(LibraryModule): def __init__(self, parameter=8, name='Bongo'): self.n = parameter super().__init__(name) self.files = [] self.inst = None @block_component def my_unit1(self, clk: ClkSignal, a: Signal, b: Signal.Output): 'A component somewhere in the hierarchy' ls = Signal(intbv()[self.n:]) @always_(clk.posedge) def worker(): (yield [ls.set(a), b.set(ls)]) return instances() @block_component def my_tb(self): 'A built-in test bench' (u, v) = [Signal(intbv()[self.n:]) for _ in range(2)] c = ClkSignal() @always_(delay(4)) def clkgen(): (yield [c.set((~ c))]) @sequential def stim(_sequence): _sequence += [u.set(2), wait(c.posedge), wait(c.posedge), wait(c.posedge), print_(v), assert_((v == 2), 'Failed in /tmp/ipykernel_30472/1829234184.py:Bongo():41'), raise_(StopSimulation)] uut = self.my_unit1(c, u, v) return instances() def build(self): t = self.my_tb() self.files = t.elab(targets.VHDL, elab_all=True) self.inst = t print('Resulting files', self.files) def simulate(self): common_test.run_ghdl(self.files, self.inst, debug=True)
Running this design component, OO style:
def test():
b = Bongo(6)
b.build()
b.simulate()
b = Bongo(7)
b.build()
test()
Using default for name: Bongo Declare obj 'my_tb' in context '(LIB: Bongo 'Bongo')' Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' Writing 'my_unit1' to file /tmp/myirl_Bongo_mjvhac89/my_unit1.vhdl Writing 'my_tb' to file /tmp/myirl_Bongo_mjvhac89/my_tb.vhdl Creating library file /tmp/myirl_module_defs_a3f3b5nx/module_defs.vhdl Resulting files ['/tmp/myirl_Bongo_mjvhac89/my_unit1.vhdl', '/tmp/myirl_Bongo_mjvhac89/my_tb.vhdl', '/tmp/myirl_module_defs_a3f3b5nx/module_defs.vhdl'] ==== COSIM stdout ==== 0x02 simulation stopped @20ns Using default for name: Bongo Declare obj 'my_tb' in context '(LIB: Bongo 'Bongo')' Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' Writing 'my_unit1' to file /tmp/myirl_Bongo_zreky7gt/my_unit1.vhdl Writing 'my_tb' to file /tmp/myirl_Bongo_zreky7gt/my_tb.vhdl Creating library file /tmp/myirl_module_defs_wy2ybdix/module_defs.vhdl Resulting files ['/tmp/myirl_Bongo_zreky7gt/my_unit1.vhdl', '/tmp/myirl_Bongo_zreky7gt/my_tb.vhdl', '/tmp/myirl_module_defs_wy2ybdix/module_defs.vhdl']
For new designs, usage of the new factory class is encouraged. This variant is more flexible with respect to arbitrary generation of units and testbenches for various targets: