#!/usr/bin/env python # coding: utf-8 # # Factory classes # # Some myHDL libraries provide classes to generate `@block` components. These are currently under scrutiny. # ## MyHDL factory classes (emulation) # # 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. # # ### Examples # # Not that the following class makes sense in the real world, but as an example: # ### `@block` factory # In[1]: 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 # In[2]: 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() # In[3]: get_ipython().system('cat {f[0]}') # ### Class with static `@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. # In[4]: 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()) # Running this design component, *OO* style: # In[5]: def test(): b = Bongo(6) b.build() b.simulate() b = Bongo(7) b.build() test() # ## Examples # # * DVI 10b8b encoder and decoder [library example](codec10b8b.ipynb) # ## Notes # # For new designs, usage of the new [factory class](../notebooks/factory_class_arch.ipynb) is encouraged. # This variant is more flexible with respect to arbitrary generation of units and testbenches for various targets: # * Switchable Simulation/Co-Simulation # * Emit test bench code to HDL to fully simulate in native HDL simulator # * Compile synthesizable code into executable, co-simulate against test bench code executed in Python environment # * Mixing of IRL and MyHDL notation