{ "cells": [ { "cell_type": "markdown", "id": "12dd8deb-ce5d-4118-b517-6671eaabeaa2", "metadata": {}, "source": [ "# Factory classes\n", "\n", "Some myHDL libraries provide classes to generate `@block` components. These are currently under scrutiny. " ] }, { "cell_type": "markdown", "id": "aff5ca49-b134-41be-854d-f619cf3fe94c", "metadata": {}, "source": [ "## MyHDL factory classes (emulation)\n", "\n", "For the time being, factory classes have be decorated using the `@factory` decorator in order to be translated to IRL.\n", "\n", "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.\n", "\n", "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.\n", "\n", "### Examples\n", "\n", "Not that the following class makes sense in the real world, but as an example:" ] }, { "cell_type": "markdown", "id": "b7cad36a-b609-4972-86e8-a6eda9fb2920", "metadata": {}, "source": [ "### `@block` factory" ] }, { "cell_type": "code", "execution_count": 1, "id": "25722f8e-af44-4fde-a247-88f9feb993c2", "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "\n", "@factory\n", "class Bingo:\n", " def __init__(self, parameter = 8):\n", " self.n = parameter\n", " \n", " T = Signal.Type(intbv, parameter)\n", " \n", " @block\n", " def my_unit(clk : ClkSignal, a : T, b : T.Output):\n", " \n", " @always(clk.posedge)\n", " def worker():\n", " b.next = a\n", "\n", " return instances()\n", "\n", " self.my_unit = my_unit " ] }, { "cell_type": "code", "execution_count": 2, "id": "c9b32a4c-de2c-46c6-bf0d-3d3c118823d8", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", " Writing 'my_unit' to file /tmp/myirl_my_unit_bj7ydc5l/my_unit.vhdl \n" ] } ], "source": [ "def test():\n", " b = Bingo(6)\n", " u, v = [Signal(intbv()[6:]) for _ in range(2)]\n", " c = ClkSignal()\n", " u = b.my_unit(c, u, v)\n", " \n", " f = u.elab(targets.VHDL)\n", " return f\n", "f = test()" ] }, { "cell_type": "code", "execution_count": 3, "id": "3fece99e-1108-4da9-9f58-a4cfca3ba6bd", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-- File generated from source:\r\n", "-- /usr/local/lib/python3.9/dist-packages/IPython/core/interactiveshell.py\r\n", "-- (c) 2016-2022 section5.ch\r\n", "-- Modifications may be lost, edit the source file instead.\r\n", "\r\n", "library IEEE;\r\n", "use IEEE.std_logic_1164.all;\r\n", "use IEEE.numeric_std.all;\r\n", "\r\n", "library work;\r\n", "\r\n", "use work.txt_util.all;\r\n", "use work.myirl_conversion.all;\r\n", "\r\n", "entity my_unit is\r\n", " port (\r\n", " clk : in std_ulogic;\r\n", " a : in unsigned(5 downto 0);\r\n", " b : out unsigned(5 downto 0)\r\n", " );\r\n", "end entity my_unit;\r\n", "\r\n", "architecture myhdl_emulation of my_unit is\r\n", " -- Local type declarations\r\n", " -- Signal declarations\r\n", "begin\r\n", " \r\n", "worker:\r\n", " process(clk)\r\n", " begin\r\n", " if rising_edge(clk) then\r\n", " b <= a;\r\n", " end if;\r\n", " end process;\r\n", "end architecture myhdl_emulation;\r\n", "\r\n" ] } ], "source": [ "!cat {f[0]}" ] }, { "cell_type": "markdown", "id": "5835fc0c-9eab-4a1c-9b4b-22cf00fd1ede", "metadata": {}, "source": [ "### Class with static `@block_component` member\n", "\n", "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.\n", "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." ] }, { "cell_type": "code", "execution_count": 4, "id": "e613bb15-1234-42a6-9b80-25c6ad9c4bed", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==============================\n", "Unparsing unit Bongo\n", "==============================\n", "\n", "\n", "class Bongo(LibraryModule):\n", "\n", " def __init__(self, parameter=8, name='Bongo'):\n", " self.n = parameter\n", " super().__init__(name)\n", " self.files = []\n", " self.inst = None\n", "\n", " @block_component\n", " def my_unit1(self, clk: ClkSignal, a: Signal, b: Signal.Output):\n", " 'A component somewhere in the hierarchy'\n", " ls = Signal(intbv()[self.n:])\n", "\n", " @always_(clk.posedge)\n", " def worker():\n", " (yield [ls.set(a), b.set(ls)])\n", " return instances()\n", "\n", " @block_component\n", " def my_tb(self):\n", " 'A built-in test bench'\n", " (u, v) = [Signal(intbv()[self.n:]) for _ in range(2)]\n", " c = ClkSignal()\n", "\n", " @always_(delay(4))\n", " def clkgen():\n", " (yield [c.set((~ c))])\n", "\n", " @sequential\n", " def stim(_sequence):\n", " _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)]\n", " uut = self.my_unit1(c, u, v)\n", " return instances()\n", "\n", " def build(self):\n", " t = self.my_tb()\n", " self.files = t.elab(targets.VHDL, elab_all=True)\n", " self.inst = t\n", " print('Resulting files', self.files)\n", "\n", " def simulate(self):\n", " common_test.run_ghdl(self.files, self.inst, debug=True)\n", "\n" ] } ], "source": [ "from myirl.test import common_test\n", "\n", "@factory\n", "class Bongo(LibraryModule):\n", " def __init__(self, parameter = 8, name = \"Bongo\"):\n", " self.n = parameter\n", " super().__init__(name)\n", " self.files = []\n", " self.inst = None\n", " \n", " # Here, we use `@block_component` without arguments, as the context is 'self'\n", " @block_component\n", " def my_unit1(self, clk : ClkSignal, a : Signal, b : Signal.Output):\n", " \"A component somewhere in the hierarchy\"\n", " ls = Signal(intbv()[self.n:])\n", " \n", " @always(clk.posedge)\n", " def worker():\n", " ls.next = a\n", " b.next = ls\n", "\n", " return instances()\n", " \n", " @block_component\n", " def my_tb(self):\n", " \"A built-in test bench\"\n", " u, v = [Signal(intbv()[self.n:]) for _ in range(2)]\n", " c = ClkSignal()\n", " \n", " @always(delay(4))\n", " def clkgen():\n", " c.next = ~c\n", " \n", " @instance\n", " def stim():\n", " u.next = 2\n", " yield c.posedge\n", " yield c.posedge\n", " yield c.posedge\n", " print(v)\n", " assert v == 2\n", " raise StopSimulation\n", " \n", " uut = self.my_unit1(c, u, v)\n", " return instances()\n", " \n", " def build(self):\n", " t = self.my_tb()\n", " self.files = t.elab(targets.VHDL, elab_all = True)\n", " self.inst = t\n", " print(\"Resulting files\", self.files)\n", " \n", " def simulate(self):\n", " common_test.run_ghdl(self.files, self.inst, debug = True)\n", " \n", "print(Bongo.unparse())" ] }, { "cell_type": "markdown", "id": "03a6ac37-d19e-4162-843e-a256a0ad433a", "metadata": {}, "source": [ "Running this design component, *OO* style:" ] }, { "cell_type": "code", "execution_count": 5, "id": "d795f0b1-be86-4425-9c43-0a5abdce7163", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using default for name: Bongo\n", "\u001b[7;35m Declare obj 'my_tb' in context '(LIB: Bongo 'Bongo')' \u001b[0m\n", "\u001b[7;35m Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' \u001b[0m\n", " Writing 'my_unit1' to file /tmp/myirl_Bongo_mjvhac89/my_unit1.vhdl \n", " Writing 'my_tb' to file /tmp/myirl_Bongo_mjvhac89/my_tb.vhdl \n", " Creating library file /tmp/myirl_module_defs_a3f3b5nx/module_defs.vhdl \n", "Resulting files ['/tmp/myirl_Bongo_mjvhac89/my_unit1.vhdl', '/tmp/myirl_Bongo_mjvhac89/my_tb.vhdl', '/tmp/myirl_module_defs_a3f3b5nx/module_defs.vhdl']\n", "==== COSIM stdout ====\n", "0x02\n", "simulation stopped @20ns\n", "\n", "Using default for name: Bongo\n", "\u001b[7;35m Declare obj 'my_tb' in context '(LIB: Bongo 'Bongo')' \u001b[0m\n", "\u001b[7;35m Declare obj 'my_unit1' in context '(LIB: Bongo 'Bongo')' \u001b[0m\n", " Writing 'my_unit1' to file /tmp/myirl_Bongo_zreky7gt/my_unit1.vhdl \n", " Writing 'my_tb' to file /tmp/myirl_Bongo_zreky7gt/my_tb.vhdl \n", " Creating library file /tmp/myirl_module_defs_wy2ybdix/module_defs.vhdl \n", "Resulting files ['/tmp/myirl_Bongo_zreky7gt/my_unit1.vhdl', '/tmp/myirl_Bongo_zreky7gt/my_tb.vhdl', '/tmp/myirl_module_defs_wy2ybdix/module_defs.vhdl']\n" ] } ], "source": [ "def test():\n", " b = Bongo(6)\n", " b.build()\n", " b.simulate()\n", " \n", " b = Bongo(7)\n", " b.build()\n", "test()" ] }, { "cell_type": "markdown", "id": "b290abbe-4f99-4e39-a3d6-c1232024cef3", "metadata": {}, "source": [ "## Examples\n", "\n", "* DVI 10b8b encoder and decoder [library example](codec10b8b.ipynb)" ] }, { "cell_type": "markdown", "id": "b0b0c598-bf6a-460a-a861-8a41b82a7af9", "metadata": {}, "source": [ "## Notes\n", "\n", "For new designs, usage of the new [factory class](../notebooks/factory_class_arch.ipynb) is encouraged.\n", "This variant is more flexible with respect to arbitrary generation of units and testbenches for various targets:\n", "* Switchable Simulation/Co-Simulation\n", " * Emit test bench code to HDL to fully simulate in native HDL simulator\n", " * Compile synthesizable code into executable, co-simulate against test bench code executed in Python environment\n", "* Mixing of IRL and MyHDL notation" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.2" } }, "nbformat": 4, "nbformat_minor": 5 }