{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Logic generators\n", "\n", "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.\n", "\n", "As an example how a MyHDL snippet translates:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import sys\n", "sys.path.insert(0, \"../..\")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "\n", "@block\n", "def unit(clk, en, a, q):\n", " @always(clk.posedge)\n", " def worker():\n", " if en:\n", " q.next = a\n", " \n", " return instances()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==============================\n", "Unparsing unit unit\n", "==============================\n", "\n", "\n", "@block\n", "def unit(clk, en, a, q):\n", "\n", " @always_(clk.posedge)\n", " def worker():\n", " (yield [unit.ctx.If(en).Then(q.set(a))])\n", " return instances()\n", "\n" ] } ], "source": [ "print(unit.unparse())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## @generator : co-routine style coding\n", "\n", "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.\n", "\n", "The following example generates an inverse wiring order by unrolling a loop:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "../../myirl/emulation/myhdl2irl.py:542: UserWarning: Not translating decorator `generator`\n", " warnings.warn(\"Not translating decorator `%s`\" % n)\n" ] } ], "source": [ "from myirl.kernel import sensitivity\n", "@block\n", "def unit_x():\n", " s = Signal(intbv()[8:])\n", " \n", " z = [ Signal(bool(), name = \"z%d\" % i) for i in range(8) ]\n", " \n", " @sensitivity.generator\n", " def wireup():\n", " for i in range(8):\n", " j = 7 - i\n", " print(\"DEBUG: Assign z[%d] = s[%d]\" % (i, j))\n", " yield [ z[i].set(s[j]) ]\n", " \n", " return instances()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "DEBUG: Assign z[0] = s[7]\n", "DEBUG: Assign z[1] = s[6]\n", "DEBUG: Assign z[2] = s[5]\n", "DEBUG: Assign z[3] = s[4]\n", "DEBUG: Assign z[4] = s[3]\n", "DEBUG: Assign z[5] = s[2]\n", "DEBUG: Assign z[6] = s[1]\n", "DEBUG: Assign z[7] = s[0]\n", " Writing 'unit_x' to file /tmp/myirl_unit_x_uo9vd6v_/unit_x.vhdl \n" ] } ], "source": [ "def convert():\n", " inst = unit_x()\n", " f = inst.elab(targets.VHDL)\n", " return f\n", "\n", "f = convert()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "architecture myhdl_emulation of unit_x is\r\n", " -- Local type declarations\r\n", " -- Signal declarations\r\n", " signal z0 : std_ulogic;\r\n", " signal z1 : std_ulogic;\r\n", " signal z2 : std_ulogic;\r\n", " signal z3 : std_ulogic;\r\n", " signal z4 : std_ulogic;\r\n", " signal z5 : std_ulogic;\r\n", " signal z6 : std_ulogic;\r\n", " signal z7 : std_ulogic;\r\n", " signal s : unsigned(7 downto 0);\r\n", "begin\r\n", " z0 <= s(7);\r\n", " z1 <= s(6);\r\n", " z2 <= s(5);\r\n", " z3 <= s(4);\r\n", " z4 <= s(3);\r\n", " z5 <= s(2);\r\n", " z6 <= s(1);\r\n", " z7 <= s(0);\r\n", "end architecture myhdl_emulation;\r\n", "\r\n" ] } ], "source": [ "! grep -A 20 architecture {f[0]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Likewise, the `@simulator.generator` supports this style for sequential, simulation specific commands." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", " Writing 'testbench' to file /tmp/myirl_testbench_w158uouo/testbench.vhdl \n", "Warning: Implicit truncation of ADD(ADD(a, b), C:2) result\n", "Warning: Implicit truncation of SUB(ADD(a, b), C:1) result\n", "Warning: Implicit truncation of SUB(ADD(a, b), C:1) result\n", "Warning: Implicit truncation of SUB(ADD(a, b), C:1) result\n", "Warning: Implicit truncation of SUB(ADD(a, b), C:1) result\n", "==== COSIM stdout ====\n", "Test ok\n", "0x00 0x08 0x07 0x7\n", "0x01 0x08 0x08 0x8\n", "0x02 0x08 0x09 0x9\n", "0x03 0x08 0x0A 0xa\n", "simulation stopped @33ns\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "../../myirl/kernel/instance.py:424: TranslationWarning: @component `testbench`: DEBUG UNUSED 's'\n", " base.warn(\"@component `%s`: DEBUG UNUSED '%s'\" % (self.obj.func.__name__, n), category = base.TranslationWarning)\n" ] } ], "source": [ "from myirl import simulation\n", "from myirl.test import common_test\n", "\n", "@block\n", "def testbench():\n", " s = Signal(intbv()[8:])\n", " rst = ResetSignal(0, 1, isasync = True)\n", " clk = ClkSignal(name = 'clk')\n", " clk.init = True\n", " a, b = [ Signal(intbv()[5:]) for _ in range(2) ]\n", " c = Signal(intbv()[6:])\n", " \n", " \n", " @always(delay(1))\n", " def clkgen():\n", " clk.next = ~clk\n", " \n", " @simulation.generator\n", " def stimulus():\n", " \n", " # These are the operations to be tested. We create references\n", " # for evaluation below:\n", " init = a.set(0xf), b.set(0x8),\n", " add_operation = c.set(a + b + 2)\n", " \n", " # Evaluate the operations:\n", " init[0].evaluate(), init[1].evaluate()\n", " v = add_operation.evaluate()\n", "\n", " # Generate HDL:\n", " yield [\n", " rst.set(True),\n", " simulation.wait('10 ns'),\n", " rst.set(False),\n", " *init,\n", " simulation.wait('1 ns'),\n", " add_operation, simulation.wait(clk.posedge),\n", " simulation.assert_(c == v, \"Test failed\"),\n", " simulation.print_(\"Test ok\")\n", " ]\n", "\n", " # Unroll a loop:\n", " for i in range(4):\n", " reassign = a.set(i)\n", " reassign.evaluate()\n", " add_operation = c.set(a + b - 1)\n", "\n", " v = add_operation.evaluate()\n", " \n", " yield [\n", " reassign, simulation.wait(clk.posedge),\n", " add_operation,\n", " simulation.wait(\"1 ns\"), simulation.print_(a, b, c, hex(v)),\n", " simulation.assert_(c == v, \"Test failed\") \n", " ]\n", " \n", " for i in range(6):\n", " yield [ simulation.wait(clk.posedge ) ]\n", " yield [ simulation.raise_(simulation.StopSimulation)]\n", " \n", " return instances()\n", " \n", "def convert():\n", " tb = testbench()\n", " files = tb.elab(targets.VHDL)\n", " common_test.run_ghdl(files, tb, vcdfile = \"testbench.vcd\", debug = True)\n", " \n", "convert()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Waveform trace" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "new Promise(function(resolve, reject) {\n", "\tvar script = document.createElement(\"script\");\n", "\tscript.onload = resolve;\n", "\tscript.onerror = reject;\n", "\tscript.src = \"https://wavedrom.com/wavedrom.min.js\";\n", "\tdocument.head.appendChild(script);\n", "}).then(() => {\n", "new Promise(function(resolve, reject) {\n", "\tvar script = document.createElement(\"script\");\n", "\tscript.onload = resolve;\n", "\tscript.onerror = reject;\n", "\tscript.src = \"https://wavedrom.com/skins/narrow.js\";\n", "\tdocument.head.appendChild(script);\n", "}).then(() => {\n", "WaveDrom.ProcessAll();\n", "});\n", "});" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import wavedraw\n", "import nbwavedrom\n", "\n", "TB = \"testbench\"\n", "\n", "waveform = wavedraw.vcd2wave(\"testbench.vcd\", TB + '.clk', None)\n", " \n", "nbwavedrom.draw(waveform)" ] } ], "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": 4 }