{ "cells": [ { "cell_type": "markdown", "id": "89096261-a72a-4aa4-8ee0-10111e612bee", "metadata": {}, "source": [ "# System on Chip design auxiliaries\n", "\n", "For mass generation of bus decoders, a register bit map must be associated with corresponding control, status or data signals.\n", "This SoC concept follows the [*MaSoCist*](https://github.com/hackfin/MaSoCist) register map design rules:\n", "\n", "* Registers are mapped into memory space and are accessed by an address, hence.\n", "* They can be flagged read-only, write-only or volatile:\n", " * READONLY: Writing to the register has no effect\n", " * WRITEONLY: Reading from this register returns an undefined value\n", " * VOLATILE: Write or read access triggers a pulse on the corresponding `select` lines.\n", " This allows to implement `W1C` (write one to clear) behaviour, or optimized data in/out transfers.\n", "* Registers contain bit fields that can be READONLY or WRITEONLY\n", "* Two register definitions (one READONLY, one WRITEONLY) can be mapped to one address. This is used for data I/O.\n", "\n", "**NOTE**: This example supports VHDL output, only" ] }, { "cell_type": "markdown", "id": "f9d9a758-9645-411e-8891-9ee0af0c88bf", "metadata": {}, "source": [ "## Class enhancements\n", "\n", "We import the container extensions `Reg` and `BF` from the library:" ] }, { "cell_type": "code", "execution_count": 1, "id": "4d94057a-4cd1-4ae7-8501-272e98db9ea2", "metadata": {}, "outputs": [], "source": [ "from cyrite.library.soc.mmr import BF, Reg, generate_mmr_decoder" ] }, { "cell_type": "markdown", "id": "f2ac6798-f12e-41af-817a-777c50ec05e5", "metadata": {}, "source": [ "## Register definitions\n", "\n", "Add a few register with bit fields and flags:" ] }, { "cell_type": "code", "execution_count": 2, "id": "8efe074d-fd59-404e-a6c3-39872ce39de7", "metadata": {}, "outputs": [], "source": [ "reg01 = Reg(16,\n", " [\n", " BF(\"im\", 3, 1, flags = BF.READONLY),\n", " BF(\"ex\", 7, 6),\n", " BF(\"inv\", 4, 4, flags = BF.WRITEONLY),\n", " BF(\"mode\", 14, 10, default = 2)\n", " ]\n", ")\n", "\n", "reg02 = Reg(16,\n", " [\n", " BF(\"gna\", 6, 1, default = 8),\n", " BF(\"reset\", 7, 7, default = True)\n", " ],\n", " flags = Reg.VOLATILE | Reg.WRITEONLY\n", ")\n", "\n", "# This is a description for an address map\n", "regdesc = {\n", " 0x01: ['stat', reg01],\n", " 0x02: ['ctrl', reg02],\n", " 0x04: ['TXD', Reg(16, [ BF(\"DATA\", 15, 0)], flags = Reg.WRITEONLY | Reg.VOLATILE) ],\n", " 0x05: ['RXD', Reg(16, [ BF(\"DATA\", 15, 0)], flags = Reg.READONLY | Reg.VOLATILE)]\n", "}" ] }, { "cell_type": "markdown", "id": "1b7cba99-f074-4d40-ab88-d62b071fe3fa", "metadata": {}, "source": [ "The register decoder for this specific memory mapped register has a dynamic `registerbank` dictionary passed to the interface, containing the register in/out wires. This variable argument construct is inferred to a HDL description." ] }, { "cell_type": "markdown", "id": "d4e8bfc3-d7d3-4953-864f-6ae3570984f7", "metadata": {}, "source": [ "## Register map decoder\n", "\n", "The actual register map decoder consists of the code below." ] }, { "cell_type": "code", "execution_count": 3, "id": "952b1db6-32e9-4cb3-9943-17ea968e48ad", "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "from myirl.library.portion import *\n", "\n", "SigType = Signal.Type\n", "\n", "Bool = SigType(bool)\n", "Addr = SigType(intbv, 12)\n", "Data = SigType(intbv, 16)\n", "\n", "@block\n", "def mmr_decode(\n", " clk : ClkSignal,\n", " reset : ResetSignal,\n", " addr : Addr,\n", " wr : Bool,\n", " data_in : Data,\n", " data_out : Data.Output,\n", " REGDESC : dict,\n", " **registerbank\n", "):\n", " # We use a partially assigneable signal:\n", " \n", " idata = PASignal(intbv()[len(data_out):])\n", " \n", " # Then generate the decoder from the register map description passed:\n", " wk = generate_mmr_decoder(REGDESC, registerbank, clk, reset, wr, addr, data_in, idata,\n", " RESET_DEFAULTS = True)\n", "\n", " @always(clk.posedge)\n", " def drive():\n", " data_out.next = idata\n", " \n", " return instances()" ] }, { "cell_type": "markdown", "id": "fc22604c-c692-4b3f-b71c-a7651088b8b2", "metadata": {}, "source": [ "## Test bench\n", "\n", "We define an interface generation function that creates a signal dictionary out of the register description:" ] }, { "cell_type": "code", "execution_count": 4, "id": "8c7936eb-4dcc-4260-a199-a7cf70ef995d", "metadata": {}, "outputs": [], "source": [ "# Interface generation:\n", "from cyrite.library.soc.mmr import RegisterSignal\n", "\n", "def gen_interface(rd):\n", " d = {}\n", " for k, rdesc in rd.items():\n", " n, reg = rdesc[0], rdesc[1]\n", " sig = RegisterSignal(n, reg)\n", " sig.rename(n)\n", " d[n] = sig\n", " \n", " return d" ] }, { "cell_type": "markdown", "id": "df178bba-da7b-43fe-9120-03b330297fb7", "metadata": {}, "source": [ "We might pack all MMR signals into a port structure including auxiliary methods. We need to decorate them with `@hdlmacro` in order to return a generator element usable within the myHDL `@instance`." ] }, { "cell_type": "code", "execution_count": 5, "id": "3149639e-b185-42ee-ab92-8534fe70bcca", "metadata": {}, "outputs": [], "source": [ "@container()\n", "class MMRPort:\n", " _inputs = ['din', 'wr', 'addr']\n", " _outputs = ['dout']\n", " _other = ['clk', 'rst']\n", " \n", " def __init__(self):\n", " self.clk = ClkSignal()\n", " self.wr = Signal(bool())\n", " self.addr = Addr()\n", " self.rst = ResetSignal(0, 1)\n", " self.din, self.dout = [ Data() for _ in range(2) ]\n", " \n", " @hdlmacro\n", " def reset_sequence(self):\n", " p = self\n", " yield [\n", " p.rst.set(True),\n", " simulation.wait(2 * (p.clk.posedge, )),\n", " p.rst.set(False) \n", " ]\n", "\n", " @hdlmacro\n", " def write_sequence(self, a, d):\n", " p = self\n", " yield [\n", " p.addr.set(a),\n", " p.din.set(d),\n", " simulation.wait(p.clk.posedge),\n", " p.wr.set(True),\n", " simulation.wait(p.clk.posedge),\n", " p.wr.set(False),\n", " ]\n", " \n", " @hdlmacro\n", " def assert_read(self, addr, data):\n", " yield [\n", " self.addr.set(addr),\n", " self.wr.set(False),\n", " simulation.wait(2 * (self.clk.posedge,)),\n", " simulation.assert_(self.dout == data, \"Read mismatch\")\n", " ]" ] }, { "cell_type": "markdown", "id": "a465775b-f20d-470d-b060-5dd67c89b4a5", "metadata": {}, "source": [ "### The test bench\n", "\n", "Finally, we run a reset/write on the decoder:" ] }, { "cell_type": "code", "execution_count": 6, "id": "6de11aff-b651-4cdc-9915-d6d7bbc651b7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'ctrl_select.sel_w' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'TXD_select.sel_w' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'RXD_select.sel_r' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'TXD_write.DATA' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'ctrl_write.gna' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'ctrl_write.reset' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_write.ex' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_write.inv' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_write.mode' \u001b[0m\n", " Writing 'mmr_decode' to file /tmp/mmr_decode.vhdl \n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_read.ex' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_read.mode' \u001b[0m\n", "\u001b[7;34m DEBUG: Skip virtual container member: 'stat_read.im' \u001b[0m\n", " Writing 'testbench' to file /tmp/testbench.vhdl \n", " Creating library file /tmp/module_defs.vhdl \n", "==== COSIM stdout ====\n", "START\n", "DONE\n", "simulation stopped @42ns\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `ctrl_select.sel_w` may be ineffective\n", " warnings.warn(msg)\n", "/home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `TXD_select.sel_w` may be ineffective\n", " warnings.warn(msg)\n", "/home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/vhdl.py:1045: UserWarning: Simulation: Delay specification of `RXD_select.sel_r` may be ineffective\n", " warnings.warn(msg)\n" ] } ], "source": [ "@block\n", "def testbench(regdesc : dict):\n", " p = MMRPort()\n", " clk = ClkSignal('clk')\n", " \n", " mon_gna = Signal(intbv()[6:])\n", " mon_select = Signal(bool())\n", " debug = Signal(bool())\n", "\n", " interface = gen_interface(regdesc)\n", " \n", " wires = [\n", " mon_gna.wireup(interface['ctrl'].read.gna),\n", " mon_select.wireup(interface['ctrl'].select.sel_w),\n", " p.clk.wireup(clk)\n", " ]\n", " \n", " inst = mmr_decode(clk, p.rst, p.addr, p.wr, p.din, p.dout, regdesc, **interface )\n", " \n", " @always(delay(2))\n", " def clkgen():\n", " clk.next = ~clk\n", "\n", " ctrl = interface['ctrl']\n", " stat = interface['stat']\n", " \n", " @instance\n", " def stimulus():\n", " print(\"START\")\n", " debug.next = False\n", " p.wr.next = False\n", " p.addr.next = 0x001\n", " \n", " p.reset_sequence()\n", " \n", " stat.read.ex.next = 0\n", " stat.read.mode.next = 4\n", " stat.read.im.next = 2\n", "\n", " p.assert_read(0x001, 0x1004)\n", " \n", " p.write_sequence(0x002, 0xfa)\n", " debug.next = True\n", " \n", " yield clk.posedge\n", " assert ctrl.select.sel_w == True\n", " assert ctrl.write.gna == 0x3d\n", " yield clk.negedge\n", " assert ctrl.select.sel_w == False\n", "\n", " p.write_sequence(0x001, 0x10)\n", " assert stat.write.inv == True\n", "\n", " yield 2 * (clk.posedge, )\n", " \n", " print(\"DONE\")\n", "\n", " raise StopSimulation\n", " \n", " return instances()\n", "\n", "def test():\n", " from myirl.test.common_test import Simulator\n", " tb = testbench(regdesc)\n", " \n", " sim = Simulator()\n", " # Turn 'debug' on for simulation output\n", " sim.run(tb, 200, debug = True, vcdfile = 'testbench.vcd')\n", "\n", "test()" ] }, { "cell_type": "code", "execution_count": 7, "id": "059cfb4a-d7a1-4b23-be01-ca25983771ec", "metadata": {}, "outputs": [], "source": [ "# ! cat {mmr_decode.ctx.path_prefix}module_defs.vhdl" ] }, { "cell_type": "markdown", "id": "95688510-f54f-4f93-aab7-141ffa28d7df", "metadata": {}, "source": [ "## Waveform display\n", "\n", "The `*.vcd` format hides the `MMRPort` record members from the trace. Therefore we need a few monitoring auxiliary signals." ] }, { "cell_type": "code", "execution_count": 8, "id": "cef473a2-447d-4986-9437-957779db8937", "metadata": {}, "outputs": [], "source": [ "import wavedraw\n", "import nbwavedrom" ] }, { "cell_type": "code", "execution_count": 9, "id": "7bffa6d8-2750-4e1d-822f-ad6f4d0f57b3", "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": [ "TB = \"testbench\"\n", "\n", "waveform = wavedraw.vcd2wave(TB+ \".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": 5 }