{ "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/MaSoCisthttps://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", "To skip the entire elaboration on *myIRL* auxiliaries, jump to the actual [register map decoder implementation](#Register-map-decoder)" ] }, { "cell_type": "markdown", "id": "f9d9a758-9645-411e-8891-9ee0af0c88bf", "metadata": {}, "source": [ "## Class enhancements\n", "\n", "The `NamedBitfield` and `Register` class is extended with flags: " ] }, { "cell_type": "code", "execution_count": 1, "id": "7674515d-f12e-4d9f-a974-9b6ba2de71fb", "metadata": {}, "outputs": [], "source": [ "import sys\n", "sys.path.insert(0, \"../../\")" ] }, { "cell_type": "code", "execution_count": 2, "id": "65678c0f-f7ce-4087-afdc-a15e64c5dd20", "metadata": {}, "outputs": [], "source": [ "from myirl.library.registers import Register, NamedBitfield\n", "from myirl.kernel.components import ContainerExtension, ContainerBase, Signal, ChildAlias\n", "from myirl.library.bulksignals import BulkWrapperSig\n", "from myhdl import intbv\n", "from myirl.kernel.sensitivity import hdlmacro\n", "from myirl import targets\n", "\n", "class BF(NamedBitfield):\n", " READONLY = 0x01\n", " WRITEONLY = 0x02\n", " \n", " def __init__(self, name, msb, lsb, flags = 0, default = None):\n", " super().__init__(name, msb, lsb)\n", " self.init = default # Initialization value when reset\n", " self.flags = flags # Access flags\n", "\n", "class Reg(Register):\n", " VOLATILE = 0x04\n", " READONLY = 0x01\n", " WRITEONLY = 0x02\n", "\n", " def __init__(self, size, bitfields, flags = 0):\n", " super().__init__(size, bitfields)\n", " self.flags = flags\n", " \n", " def has_select(self, flag):\n", " if (self.flags & Reg.VOLATILE) and not (self.flags & flag):\n", " return True\n", " \n", " return False" ] }, { "cell_type": "markdown", "id": "be7d97f6-6221-476e-a7d0-19a4fb5abf3b", "metadata": {}, "source": [ "We then create a `RegisterSignal` derived from the `ContainerExtension` that takes a register as argument. To allow aliasing, we also need to be able to pass a signal dictionary, optionally.\n", "\n", "This class also adds a few special assignment methods for its signal members, according to their WRITEONLY/READONLY configuration." ] }, { "cell_type": "code", "execution_count": 3, "id": "cc9a96b6-c351-458f-83cb-7c5b9924690c", "metadata": {}, "outputs": [], "source": [ "class RegisterSignal(ContainerExtension):\n", " def __init__(self, name, reg, signals = None, virtual = None):\n", " if name is None:\n", " name = kernel.utils.get_uuid('reg_')\n", " \n", " # Hack to allow cloning/aliasing\n", " if signals is not None:\n", " super().__init__(name, signals, template = reg, virtual = True, twoway = True)\n", " return\n", " \n", " if not isinstance(reg, Register):\n", " raise TypeError(\"expects register as argument\")\n", " \n", " bitfields = reg.members().items()\n", " \n", " signals = {\n", " n : Signal(intbv(ival := i.init if i.init is not None else 0)[i.msb + 1-i.lsb:] if i.msb != i.lsb else bool(ival),\n", " name = i.name)\n", " for n, i in bitfields\n", " }\n", " # Selection signals ('W1C' etc.)\n", " \n", " select_sigs = {}\n", "\n", " if reg.flags & reg.VOLATILE:\n", " if not reg.flags & reg.READONLY:\n", " select_sigs[\"sel_w\"] = Signal(bool(0))\n", " if not reg.flags & reg.WRITEONLY:\n", " select_sigs[\"sel_r\"] = Signal(bool(0))\n", "\n", " inputs = filter(lambda t: (t[1].flags & BF.WRITEONLY) == False, bitfields)\n", " outputs = filter(lambda t: (t[1].flags & BF.READONLY) == False, bitfields)\n", " \n", " input_container = self.create(name, 'read', { n : signals[n] for n, _ in inputs }, ())\n", " output_container = self.create(name, 'write', { n : signals[n] for n, _ in outputs }, ())\n", "\n", " select_container = self.create(name, 'sel', select_sigs, ())\n", " \n", " sigs = {}\n", " \n", " if input_container is not None:\n", " sigs['read'] = input_container\n", " if output_container is not None:\n", " output_container.driven = True\n", " output_container._aux = False\n", " sigs['write'] = output_container\n", " if select_container is not None:\n", " select_container.driven = True\n", " sigs['select'] = select_container\n", "\n", " for n, s in sigs.items():\n", " s.rename(name + '_' + n)\n", " \n", " super().__init__(name, sigs, template = reg, virtual = True, twoway = True)\n", " \n", " def create(self, name, mname, children, bases = tuple()):\n", " \"Dynamically create a subclass from self._type\"\n", " if len(children) == 0:\n", " return None\n", " d = { '_templ' : children, '_virtual' : True,\n", " '_rank' : ContainerBase._rank }\n", "\n", " sign = name + '_' + mname\n", " \n", " container = type(sign, (BulkWrapperSig, *bases), d)\n", " targets.vhdl.register_type(container, sign)\n", " inst = container()\n", " inst._populate(children)\n", " return inst\n", " \n", " def alias(self, name):\n", " \"We need a different .alias() method, as we pass a Register for a new object\"\n", " signals = {}\n", " for n, s in self._members.items():\n", " nn = n\n", " sig = ChildAlias(self, s, n, is_member = False)\n", " signals[nn] = sig\n", " \n", " new = type(self)(name, self._template, signals)\n", " new.rename(name) # Need explicit rename here XXX\n", " return new\n", "\n", " # Here we don't need a @hdlmacro, because Register.assign() already returns\n", " # a generator\n", " def assign(self, data):\n", " d = {}\n", " try:\n", " readport_members = self.get_children()['read'].members().items()\n", " for n, i in readport_members:\n", " bf = self._template.bfmap[n]\n", " d[n] = i\n", " \n", " gen = self._template.assign(data, **d)\n", " return gen\n", " except KeyError:\n", " return None\n", "\n", " @hdlmacro\n", " def select_reset(self):\n", " try:\n", " members = self.get_children()['select'].members().items()\n", " gen = []\n", " for n, i in members:\n", " gen.append(i.set(False))\n", " yield gen\n", " except KeyError:\n", " yield []\n", " \n", " @hdlmacro\n", " def set(self, other):\n", " gen = []\n", " try:\n", " w = self.get_children()['write']\n", " m = w.members()\n", " for n, i in m.items():\n", " bf = self._template.bfmap[n]\n", " if bf.msb == bf.lsb:\n", " gen.append(i.set(other[bf.msb]))\n", " else:\n", " gen.append(i.set(other[bf.msb + 1: bf.lsb]))\n", "\n", " yield gen\n", " except KeyError:\n", " yield []" ] }, { "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": 4, "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": "3773d975-98df-42ac-acc3-04c14d428ede", "metadata": {}, "source": [ "## Register decoder table generator\n", "\n", "To turn this into a register decoder circuit, we create a factory function, returning a `worker` process:" ] }, { "cell_type": "code", "execution_count": 5, "id": "14dbdec4-d4ed-4794-84ff-2927d65fa580", "metadata": {}, "outputs": [], "source": [ "from myirl.kernel import sensitivity\n", "from myirl.kernel.sig import ConstSig\n", "from myirl import simulation\n", "\n", "def gen_regdec(regmap, port, clk, reset, wr, addr, idata, odata,\n", " REPORT_ILLEGAL_ACCESS = False,\n", " RESET_DEFAULTS = False):\n", " \n", " kwargs = { 'EDGE' : clk.POS }\n", " if RESET_DEFAULTS:\n", " kwargs['RESET'] = reset\n", " \n", " @sensitivity.process(clk, **kwargs)\n", " def worker(logic):\n", " \"\"\"Creates a case/when flow the procedural way\"\"\"\n", " cw, cr = [ logic.Case(addr) for _ in range(2) ]\n", " N = addr.size()\n", " \n", " _reset = []\n", " for k, rdesc in regmap.items():\n", " name, rd = rdesc[0], rdesc[1]\n", " if rd.has_select(Reg.READONLY):\n", " maybe_write_sel = port[name].select.sel_w.set(True)\n", " else:\n", " maybe_write_sel = None\n", " if rd.has_select(Reg.WRITEONLY):\n", " maybe_read_sel = port[name].select.sel_r.set(True)\n", " else:\n", " maybe_read_sel = None\n", " \n", " reg = port[name] \n", " _reset += [ reg.select_reset(), ]\n", " s = ConstSig(k, N)\n", " if not rd.flags & Reg.READONLY:\n", " cw = cw.When(s)(reg.set(idata), maybe_write_sel)\n", " if not rd.flags & Reg.WRITEONLY:\n", " cr = cr.When(s)(reg.assign(odata), maybe_read_sel)\n", " \n", " if REPORT_ILLEGAL_ACCESS:\n", " cw = cw.Other(sim.raise_(ValueError(\"Illegal WRITE address\")))\n", " cr = cr.Other(sim.raise_(ValueError(\"Illegal READ address\")))\n", " else:\n", " cw = cw.Other(None)\n", " cr = cr.Other(None)\n", " \n", " _if = logic.If(wr == True).Then(cw).Else(cr)\n", " \n", " logic += _reset\n", " logic += [_if ]\n", " \n", " return worker\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": 6, "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,\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 = gen_regdec(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": 7, "id": "8c7936eb-4dcc-4260-a199-a7cf70ef995d", "metadata": {}, "outputs": [], "source": [ "# Interface generation:\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": 8, "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": 9, "id": "6de11aff-b651-4cdc-9915-d6d7bbc651b7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " DEBUG CREATE wrapper module for testbench (EmulationModule 'top_testbench') \n", "Creating process 'mmr_decode/drive' with sensitivity (clk'rising,)\n", "Creating process 'testbench/clkgen' with sensitivity ([ DeltaT 2 ns ],)\n", "Creating sequential 'testbench/stimulus' \n", " Elaborating component mmr_decode_s1_s1_s12_s1_s16_s16_d_1_2_4_5 \n", " DEBUG: Skip virtual/interface: 'ctrl_select.sel_w' \n", " DEBUG: Skip virtual/interface: 'TXD_select.sel_w' \n", " DEBUG: Skip virtual/interface: 'RXD_select.sel_r' \n", " DEBUG: Skip virtual/interface: 'TXD_write.DATA' \n", " DEBUG: Skip virtual/interface: 'ctrl_write.gna' \n", " DEBUG: Skip virtual/interface: 'ctrl_write.reset' \n", " DEBUG: Skip virtual/interface: 'stat_write.ex' \n", " DEBUG: Skip virtual/interface: 'stat_write.inv' \n", " DEBUG: Skip virtual/interface: 'stat_write.mode' \n", " Writing 'mmr_decode' to file /tmp/myirl_top_testbench_6qys188j/mmr_decode.vhdl \n", " Elaborating component testbench_d_1_2_4_5 \n", " DEBUG: Skip virtual/interface: 'stat_read.ex' \n", " DEBUG: Skip virtual/interface: 'stat_read.mode' \n", " DEBUG: Skip virtual/interface: 'stat_read.im' \n", " Writing 'testbench' to file /tmp/myirl_top_testbench_6qys188j/testbench.vhdl \n", " Creating library file /tmp/myirl_module_defs_5bphdbsz/module_defs.vhdl \n", "==== COSIM stdout ====\n", "\n", "==== COSIM stderr ====\n", "\n", "==== COSIM stdout ====\n", "analyze /home/testing/src/myhdl2/myirl/targets/../test/vhdl/txt_util.vhdl\n", "analyze /home/testing/src/myhdl2/myirl/targets/libmyirl.vhdl\n", "analyze /tmp/myirl_module_defs_5bphdbsz/module_defs.vhdl\n", "analyze /tmp/myirl_top_testbench_6qys188j/mmr_decode.vhdl\n", "analyze /tmp/myirl_top_testbench_6qys188j/testbench.vhdl\n", "elaborate testbench\n", "\n", "==== COSIM stderr ====\n", "\n", "==== COSIM stdout ====\n", "START\n", "DONE\n", "/tmp/myirl_top_testbench_6qys188j/testbench.vhdl:114:9:@42ns:(assertion failure): Stop Simulation\n", "/tmp/testbench:error: assertion failed\n", "in process .testbench(myirl).stimulus\n", "/tmp/testbench:error: simulation failed\n", "\n", "==== COSIM stderr ====\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "../../myirl/kernel/components.py:138: UserWarning: Base `dict` (Port 'regdesc') does not add to the interface.\n", "Local signal instances will be created. Use BulkSignal classes instead.\n", " base.warnings.warn(\"\"\"Base `dict` (Port '%s') does not add to the interface.\n", "../../myirl/kernel/components.py:138: UserWarning: Base `dict` (Port 'REGDESC') does not add to the interface.\n", "Local signal instances will be created. Use BulkSignal classes instead.\n", " base.warnings.warn(\"\"\"Base `dict` (Port '%s') does not add to the interface.\n" ] } ], "source": [ "@block\n", "def testbench(regdesc):\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", "\n", " tb = testbench(regdesc)\n", " f = tb.elab(targets.VHDL, elab_all = True)\n", " # Turn 'debug' on for simulation output\n", " run_ghdl(f, tb, debug = True, vcdfile = 'testbench.vcd')\n", " return f\n", "\n", "f = test()" ] }, { "cell_type": "code", "execution_count": 10, "id": "0be20f2b-fc11-49ae-badc-bc763104ea71", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 1\t-- File generated from source:\r\n", " 2\t-- /tmp/ipykernel_42989/810171885.py\r\n", " 3\t-- (c) 2016-2021 section5.ch\r\n", " 4\t-- Modifications may be lost, edit the source file instead.\r\n", " 5\t\r\n", " 6\tlibrary IEEE;\r\n", " 7\tuse IEEE.std_logic_1164.all;\r\n", " 8\tuse IEEE.numeric_std.all;\r\n", " 9\t\r\n", " 10\tlibrary work;\r\n", " 11\t\r\n", " 12\tuse work.module_defs.all;\r\n", " 13\tuse work.txt_util.all;\r\n", " 14\tuse work.myirl_conversion.all;\r\n", " 15\t\r\n", " 16\tentity mmr_decode is\r\n", " 17\t port (\r\n", " 18\t clk : in std_ulogic;\r\n", " 19\t reset : in std_ulogic;\r\n", " 20\t addr : in unsigned(11 downto 0);\r\n", " 21\t wr : in std_ulogic;\r\n", " 22\t data_in : in unsigned(15 downto 0);\r\n", " 23\t data_out : out unsigned(15 downto 0);\r\n", " 24\t stat_read : in t_stat_read;\r\n", " 25\t stat_write : out t_stat_write;\r\n", " 26\t ctrl_read : in t_ctrl_read;\r\n", " 27\t ctrl_write : out t_ctrl_write;\r\n", " 28\t ctrl_select : out t_ctrl_sel;\r\n", " 29\t TXD_read : in t_TXD_read;\r\n", " 30\t TXD_write : out t_TXD_write;\r\n", " 31\t TXD_select : out t_TXD_sel;\r\n", " 32\t RXD_read : in t_RXD_read;\r\n", " 33\t RXD_write : out t_RXD_write;\r\n", " 34\t RXD_select : out t_RXD_sel\r\n", " 35\t );\r\n", " 36\tend entity mmr_decode;\r\n", " 37\t\r\n", " 38\tarchitecture MyIRL of mmr_decode is\r\n", " 39\t -- Local type declarations\r\n", " 40\t -- Signal declarations\r\n", " 41\t signal idata : unsigned(15 downto 0);\r\n", " 42\tbegin\r\n", " 43\t \r\n", " 44\tworker:\r\n", " 45\t process(clk, reset)\r\n", " 46\t begin\r\n", " 47\t if rising_edge(clk) then\r\n", " 48\t if reset = '1' then\r\n", " 49\t ctrl_select.sel_w <= '0';\r\n", " 50\t TXD_select.sel_w <= '0';\r\n", " 51\t RXD_select.sel_r <= '0';\r\n", " 52\t idata <= x\"0000\";\r\n", " 53\t TXD_write.DATA <= x\"0000\";\r\n", " 54\t ctrl_write.gna <= \"001000\";\r\n", " 55\t ctrl_write.reset <= '1';\r\n", " 56\t stat_write.ex <= \"00\";\r\n", " 57\t stat_write.inv <= '0';\r\n", " 58\t stat_write.mode <= \"00010\";\r\n", " 59\t else\r\n", " 60\t ctrl_select.sel_w <= '0';\r\n", " 61\t TXD_select.sel_w <= '0';\r\n", " 62\t RXD_select.sel_r <= '0';\r\n", " 63\t if (wr = '1') then\r\n", " 64\t case addr is\r\n", " 65\t when x\"001\" =>\r\n", " 66\t stat_write.ex <= data_in(8-1 downto 6);\r\n", " 67\t stat_write.inv <= data_in(4);\r\n", " 68\t stat_write.mode <= data_in(15-1 downto 10);\r\n", " 69\t when x\"002\" =>\r\n", " 70\t ctrl_write.gna <= data_in(7-1 downto 1);\r\n", " 71\t ctrl_write.reset <= data_in(7);\r\n", " 72\t ctrl_select.sel_w <= '1';\r\n", " 73\t when x\"004\" =>\r\n", " 74\t TXD_write.DATA <= data_in(16-1 downto 0);\r\n", " 75\t TXD_select.sel_w <= '1';\r\n", " 76\t when others =>\r\n", " 77\t end case;\r\n", " 78\t else\r\n", " 79\t case addr is\r\n", " 80\t when x\"001\" =>\r\n", " 81\t idata(3 downto 1) <= stat_read.im;\r\n", " 82\t idata(7 downto 6) <= stat_read.ex;\r\n", " 83\t idata(14 downto 10) <= stat_read.mode;\r\n", " 84\t when x\"005\" =>\r\n", " 85\t idata(15 downto 0) <= RXD_read.DATA;\r\n", " 86\t RXD_select.sel_r <= '1';\r\n", " 87\t when others =>\r\n", " 88\t end case;\r\n", " 89\t end if;\r\n", " 90\t end if;\r\n", " 91\t end if;\r\n", " 92\t end process;\r\n", " 93\t \r\n", " 94\tdrive:\r\n", " 95\t process(clk)\r\n", " 96\t begin\r\n", " 97\t if rising_edge(clk) then\r\n", " 98\t data_out <= idata;\r\n", " 99\t end if;\r\n", " 100\t end process;\r\n", " 101\tend architecture MyIRL;\r\n", " 102\t\r\n" ] } ], "source": [ "! cat -n {f[0]}" ] }, { "cell_type": "code", "execution_count": 11, "id": "05a66665-e410-491a-9d62-4c187b91b81f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-- File generated from source:\r\n", "-- /tmp/ipykernel_42989/385898099.py\r\n", "-- (c) 2016-2021 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.module_defs.all;\r\n", "use work.txt_util.all;\r\n", "use work.myirl_conversion.all;\r\n", "\r\n", "entity testbench is\r\n", "end entity testbench;\r\n", "\r\n", "architecture MyIRL of testbench is\r\n", " -- Local type declarations\r\n", " -- Signal declarations\r\n", " signal bulkc209_out : t_MMRPort_out;\r\n", " signal stat_write : t_stat_write;\r\n", " signal ctrl_write : t_ctrl_write;\r\n", " signal ctrl_select : t_ctrl_sel;\r\n", " signal TXD_write : t_TXD_write;\r\n", " signal TXD_select : t_TXD_sel;\r\n", " signal RXD_write : t_RXD_write;\r\n", " signal RXD_select : t_RXD_sel;\r\n", " signal clk : std_ulogic := '0';\r\n", " signal stat_read : t_stat_read;\r\n", " signal ctrl_read : t_ctrl_read;\r\n", " signal TXD_read : t_TXD_read;\r\n", " signal RXD_read : t_RXD_read;\r\n", " signal debug : std_ulogic;\r\n", " signal bulkc209_in : t_MMRPort_in;\r\n", " signal bulkc209_aux : t_MMRPort_aux;\r\n", " signal mon_gna : unsigned(5 downto 0);\r\n", " signal mon_select : std_ulogic;\r\n", "begin\r\n", " \r\n", " -- Instance mmr_decode\r\n", " inst_mmr_decode_0: entity work.mmr_decode\r\n", " port map (\r\n", " clk => clk,\r\n", " reset => bulkc209_aux.rst,\r\n", " addr => bulkc209_in.addr,\r\n", " wr => bulkc209_in.wr,\r\n", " data_in => bulkc209_in.din,\r\n", " data_out => bulkc209_out.dout,\r\n", " stat_read => stat_read,\r\n", " stat_write => stat_write,\r\n", " ctrl_read => ctrl_read,\r\n", " ctrl_write => ctrl_write,\r\n", " ctrl_select => ctrl_select,\r\n", " TXD_read => TXD_read,\r\n", " TXD_write => TXD_write,\r\n", " TXD_select => TXD_select,\r\n", " RXD_read => RXD_read,\r\n", " RXD_write => RXD_write,\r\n", " RXD_select => RXD_select\r\n", " );\r\n", " \r\n", "clkgen:\r\n", " clk <= not clk after 2 ns;\r\n", " \r\n", " \r\n", "stimulus:\r\n", " process\r\n", " begin\r\n", " print(\"START\");\r\n", " debug <= '0';\r\n", " bulkc209_in.wr <= '0';\r\n", " bulkc209_in.addr <= x\"001\";\r\n", " bulkc209_aux.rst <= '1';\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " bulkc209_aux.rst <= '0';\r\n", " stat_read.ex <= \"00\";\r\n", " stat_read.mode <= \"00100\";\r\n", " stat_read.im <= \"010\";\r\n", " bulkc209_in.addr <= x\"001\";\r\n", " bulkc209_in.wr <= '0';\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " assert (bulkc209_out.dout = x\"1004\")\r\n", " report \"Read mismatch\" severity failure;\r\n", " bulkc209_in.addr <= x\"002\";\r\n", " bulkc209_in.din <= x\"00fa\";\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " bulkc209_in.wr <= '1';\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " bulkc209_in.wr <= '0';\r\n", " debug <= '1';\r\n", " wait until rising_edge(clk);\r\n", " assert (ctrl_select.sel_w = '1')\r\n", " report \"Failed in /tmp/ipykernel_42989/385898099.py:testbench():46\" severity failure;\r\n", " assert (ctrl_write.gna = \"111101\")\r\n", " report \"Failed in /tmp/ipykernel_42989/385898099.py:testbench():47\" severity failure;\r\n", " wait until falling_edge(clk);\r\n", " assert (ctrl_select.sel_w = '0')\r\n", " report \"Failed in /tmp/ipykernel_42989/385898099.py:testbench():49\" severity failure;\r\n", " bulkc209_in.addr <= x\"001\";\r\n", " bulkc209_in.din <= x\"0010\";\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " bulkc209_in.wr <= '1';\r\n", " wait until rising_edge(bulkc209_aux.clk);\r\n", " bulkc209_in.wr <= '0';\r\n", " assert (stat_write.inv = '1')\r\n", " report \"Failed in /tmp/ipykernel_42989/385898099.py:testbench():52\" severity failure;\r\n", " wait until rising_edge(clk);\r\n", " wait until rising_edge(clk);\r\n", " print(\"DONE\");\r\n", " assert false\r\n", " report \"Stop Simulation\" severity failure;\r\n", " wait;\r\n", " end process;\r\n", " mon_gna <= ctrl_read.gna;\r\n", " mon_select <= ctrl_select.sel_w;\r\n", " bulkc209_aux.clk <= clk;\r\n", "end architecture MyIRL;\r\n", "\r\n" ] } ], "source": [ "! cat {f[1]}" ] }, { "cell_type": "code", "execution_count": 12, "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": 13, "id": "cef473a2-447d-4986-9437-957779db8937", "metadata": {}, "outputs": [], "source": [ "import wavedraw\n", "import nbwavedrom" ] }, { "cell_type": "code", "execution_count": 14, "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)" ] }, { "cell_type": "code", "execution_count": null, "id": "2d07298a-222a-4bf9-adf7-a7573a49533e", "metadata": {}, "outputs": [], "source": [] } ], "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 }