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()
Creating process 'my_unit/worker' with sensitivity (clk'rising,)
 Writing 'my_unit' to file /tmp/myirl_top_my_unit_robmnu6w/my_unit.vhdl 
In [3]:
!cat {f[0]}
-- File generated from source:
--     /usr/local/lib/python3.9/dist-packages/IPython/core/interactiveshell.py
-- (c) 2016-2021 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 MyIRL 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 MyIRL;

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())
==============================
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_65678/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:

In [5]:
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')' 
Creating process 'my_tb/clkgen' with sensitivity ([ DeltaT 4 ns ],)
Creating sequential 'my_tb/stim' 
 Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' 
Creating process 'my_unit1/worker' with sensitivity (clk'rising,)
 Elaborating component my_unit1__Bongo_s1_s6_s6 
 Writing 'my_unit1' to file /tmp/myirl_Bongo_js42d7gy/my_unit1.vhdl 
 Elaborating component my_tb__Bongo 
 Writing 'my_tb' to file /tmp/myirl_Bongo_js42d7gy/my_tb.vhdl 
 Creating library file /tmp/myirl_module_defs_ikc0aolm/module_defs.vhdl 
Resulting files ['/tmp/myirl_Bongo_js42d7gy/my_unit1.vhdl', '/tmp/myirl_Bongo_js42d7gy/my_tb.vhdl', '/tmp/myirl_module_defs_ikc0aolm/module_defs.vhdl']
==== COSIM stdout ====

==== COSIM stderr ====

==== COSIM stdout ====
analyze /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/../test/vhdl/txt_util.vhdl
analyze /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/libmyirl.vhdl
analyze /tmp/myirl_Bongo_js42d7gy/my_unit1.vhdl
analyze /tmp/myirl_Bongo_js42d7gy/my_tb.vhdl
elaborate my_tb

==== COSIM stderr ====

==== COSIM stdout ====
0x02
/tmp/myirl_Bongo_js42d7gy/my_tb.vhdl:42:9:@20ns:(assertion failure): Stop Simulation
/tmp/my_tb:error: assertion failed
in process .my_tb(myirl).stim
/tmp/my_tb:error: simulation failed

==== COSIM stderr ====

Using default for name: Bongo
 Declare obj 'my_tb' in context '(LIB: Bongo 'Bongo')' 
Creating process 'my_tb/clkgen' with sensitivity ([ DeltaT 4 ns ],)
Creating sequential 'my_tb/stim' 
 Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' 
Creating process 'my_unit1/worker' with sensitivity (clk'rising,)
 Elaborating component my_unit1__Bongo_s1_s7_s7 
 Writing 'my_unit1' to file /tmp/myirl_Bongo_8z9jrnhz/my_unit1.vhdl 
 Elaborating component my_tb__Bongo 
 Writing 'my_tb' to file /tmp/myirl_Bongo_8z9jrnhz/my_tb.vhdl 
 Creating library file /tmp/myirl_module_defs_p8drlq06/module_defs.vhdl 
Resulting files ['/tmp/myirl_Bongo_8z9jrnhz/my_unit1.vhdl', '/tmp/myirl_Bongo_8z9jrnhz/my_tb.vhdl', '/tmp/myirl_module_defs_p8drlq06/module_defs.vhdl']

Examples