{ "cells": [ { "cell_type": "markdown", "id": "d6c71bb1", "metadata": {}, "source": [ "# Pipeline generators\n", "\n", "This demonstrates manually elaborated Pipeline support for native (unmodified) `intbv` types. Drawbacks:\n", "* No automated bit width\n", "* No automated inference of ALU types\n", "* Limited pipeline delay tracking for signals" ] }, { "cell_type": "code", "execution_count": 1, "id": "2d6079fd", "metadata": {}, "outputs": [], "source": [ "from myirl import *\n", "from myhdl import intbv\n", "# Create VHDL module context with stdout output:\n", "d = DummyVHDLModule()" ] }, { "cell_type": "markdown", "id": "b4225439", "metadata": {}, "source": [ "## Delay tracking\n", "\n", "To enable latency accounting for any signal type, the following construct generates a Signal class derived from the given class argument that adds a `_latency` member.\n", "**May be not implemented in this branch.**" ] }, { "cell_type": "code", "execution_count": 2, "id": "63270f0f", "metadata": {}, "outputs": [], "source": [ "class PipelineTracker:\n", " def __init__(self, *args, **kwargs):\n", " self._latency = 0\n", " super().__init__(*args, **kwargs)\n", "\n", "def pipelined(sig):\n", " base = sig\n", "\n", " return type(\"Pipelined\" + base.__name__, (PipelineTracker, base), {})" ] }, { "cell_type": "markdown", "id": "f97ffaad", "metadata": {}, "source": [ "## A Pipeline stage process (element)" ] }, { "cell_type": "markdown", "id": "8cb27f14", "metadata": {}, "source": [ "First, derive a PipelineProcess class from `Process`:" ] }, { "cell_type": "code", "execution_count": 3, "id": "b1fbb8bb", "metadata": {}, "outputs": [], "source": [ "class PipelineProcess(kernel.sensitivity.Process):\n", " def __init__(self, func, clk, logic, stage):\n", " def f():\n", " return logic\n", " \n", " self.func = f\n", " f.__name__ = func.__name__ + \"_stage%d\" % stage\n", " self.sensors = [clk]\n", " self.edge = clk.POS\n", " self.reset = None\n", " self.logic = LogicContext()\n", " self.logic += f()\n" ] }, { "cell_type": "markdown", "id": "415a35cf", "metadata": {}, "source": [ "Then implement a Pipeline class for the `@pipeline` decorator magic:" ] }, { "cell_type": "code", "execution_count": 4, "id": "56fa49e3", "metadata": {}, "outputs": [], "source": [ "from myirl.kernel import sensitivity\n", "\n", "class Pipeline(Generator):\n", " def __init__(self, func, clk, reset, enable, valid):\n", " self.clk = clk\n", " self.valid = valid\n", " self.reset = reset\n", " self.enable = enable\n", " self.depth = 0\n", " super().__init__(func)\n", "\n", " def __call__(self, ctx):\n", " n = 0\n", " for i, g in enumerate(self.func()):\n", " self.logic += [\n", " PipelineProcess(self.func, self.clk, g, i)\n", " ]\n", " signals = {}\n", " for stmt in g:\n", " stmt.get_drivers(signals)\n", " for name, s in signals.items():\n", " try:\n", " s._latency = i + 1\n", " except AttributeError:\n", " raise TypeError(\"Signal %s must be of type PipelineSignal\" % name)\n", " n += 1\n", " self.depth = n\n", " \n", " n += 1\n", " en = [ pipelined(Signal)(bool(), name = self.func.__name__ + \"_enable%d\" % i) for i in range(n) ]\n", " \n", " @genprocess(self.clk, EDGE=self.clk.POS, RESET=self.reset)\n", " def delay_queue():\n", " for i in range(1, n):\n", " yield [\n", " en[i].set(en[i-1]) \n", " ]\n", "\n", " # Important to call that process within the\n", " # actual context:\n", " delay_queue(ctx)\n", "\n", " self.logic += [\n", " delay_queue,\n", " self.valid.wireup(en[n-1]), # Ugly hack: abuse en0 for reset\n", " en[0].wireup(self.enable)\n", " ]\n", " \n", " def collect_sources(self):\n", " signals = {}\n", " for i in self.logic:\n", " signals.update(i.collect_sources())\n", " \n", " return signals\n", "\n", " def collect_drivers(self):\n", " signals = {}\n", " for i in self.logic:\n", " lsigs = i.collect_drivers()\n", " signals.update(lsigs)\n", " \n", " return signals \n", " \n", "def pipeline(clk, reset, enable, valid, *kwargs):\n", " def pipeline_dec(func):\n", " return Pipeline(func, clk, reset, enable, valid)\n", "\n", " return pipeline_dec " ] }, { "cell_type": "markdown", "id": "1c0d6c6f", "metadata": {}, "source": [ "Now create a pipeline test:" ] }, { "cell_type": "code", "execution_count": 5, "id": "05c55a54-8af5-4971-9147-a069bfcafbd7", "metadata": {}, "outputs": [], "source": [ "from myirl.vector import VectorSig\n", "\n", "@block\n", "def dummy(clk: ClkSignal, reset : ResetSignal, en : Signal, din : Signal,\n", " dout: Signal.Output, valid : Signal.Output):\n", " \n", " PS = pipelined(Signal)\n", " \n", " a = PS(intbv()[7:], name = 'a')\n", " \n", " q = PS(intbv()[8:], name = 'q')\n", " p = PS(intbv()[5:], name = 'p')\n", "\n", " @pipeline(clk, reset, en, valid)\n", " def pipe():\n", " # u = Variable('u', intbv()[22:])\n", " v = Variable('v', intbv(5)[7:])\n", " # First stage\n", " yield [\n", " v.assign(din * base.ConstSig(2, 2)),\n", " a.set(v),\n", " ]\n", " \n", " # Second stage\n", " yield [\n", " q.set(a + 4)\n", " ]\n", " \n", " # Third stage\n", " yield [ p.set(q[6:1]) ] # value >> 1\n", " \n", " wires = [ dout.wireup(p) ]\n", " \n", " return locals()" ] }, { "cell_type": "code", "execution_count": 6, "id": "78ce0408-74d7-43e3-8c7d-a3ec6c722dc9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", " Writing 'dummy' to file /tmp/dummy.vhdl \n", " Writing 'tb' to file /tmp/tb.vhdl \n", " Creating library file /tmp/module_defs.vhdl \n", "==== COSIM stdout ====\n", "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\n", "analyze /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/libmyirl.vhdl\n", "analyze /tmp/dummy.vhdl\n", "analyze /tmp/tb.vhdl\n", "elaborate tb\n", "\n", "==== COSIM stdout ====\n", "data out: 0x04\n", "simulation stopped @165ns\n", "\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from myirl.test.common_test import *\n", "from myirl.simulation import *\n", "from myirl.test.ghdl import GHDL\n", "from yosys.simulator import CXXRTL\n", "\n", "from simulation import sim\n", "\n", "@sim.testbench(GHDL)\n", "@block\n", "def tb():\n", " clk = ClkSignal(name = 'clk')\n", " en, valid = [ Signal(bool()) for _ in range(2) ]\n", " data_in = Signal(intbv()[5:])\n", " data_out = Signal(intbv()[5:])\n", " reset = ResetSignal(ResetSignal.POS_ASYNC)\n", "\n", " inst = dummy(clk, reset, en, data_in, data_out, valid)\n", " \n", " osc = gen_osc(clk, CYCLE=5)\n", " \n", " @generator\n", " def seq():\n", " \n", " yield [\n", " reset.set(True), en.set(False),\n", " wait(2 * [clk.posedge]), reset.set(False),\n", " wait(4 * [clk.posedge])\n", " ]\n", " \n", " it = Iterator([0xa, 0x5, 0x2])\n", " \n", " yield [\n", " For(it)(\n", " data_in.set(it),\n", " wait('1 ns'),\n", " en.set(True),\n", "\n", " wait(clk.posedge),\n", " )\n", " ]\n", " \n", " yield [ wait(4 * [clk.posedge] ) ]\n", " \n", " yield [\n", " print_(\"data out:\", data_out),\n", " ]\n", " yield [\n", " wait(4 * [clk.posedge])\n", " ]\n", " yield [ raise_(StopSimulation) ]\n", " \n", " return locals()\n", " \n", "inst = tb()\n", "inst.run(180, wavetrace= \"/tmp/\" + tb.name + \".vcd\")" ] }, { "cell_type": "code", "execution_count": 7, "id": "71e30043", "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; import nbwavedrom\n", "TB = tb.uut_name;\n", "\n", "cfg = wavedraw.config(TB, \n", " {\n", " 'clk' : None, 'en' : None, 'reset' : None,\n", " 'valid' : None, 'data_out[4:0]' : None, 'data_in[4:0]' : None, \n", " 'inst_dummy_0.q[7:0]' : None, 'inst_dummy_0.a[6:0]' : None,\n", " }\n", ")\n", "\n", "waveform = wavedraw.vcd2wave(\"/tmp/tb.vcd\", TB + '.clk', None)\n", "nbwavedrom.draw(waveform)" ] }, { "cell_type": "code", "execution_count": 8, "id": "90cf7df1", "metadata": {}, "outputs": [], "source": [ "# !cat -n {f[0]}" ] } ], "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 }