{ "cells": [ { "cell_type": "markdown", "metadata": { "tags": [] }, "source": [ "# Gray counter\n", "\n", "There are cases where you construct a logic out of a chain of primitive elements.\n", "Take a gray counter, for example. It is used often for cross-clock-domain negotations\n", "(dual clock FIFO fill counters).\n", "\n", "Note that most of the work below is simply done by logical gate connections without using a process." ] }, { "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", "\n", "@block\n", "def gray_counter1(clk : ClkSignal,\n", " enable : Signal,\n", " reset : ResetSignal,\n", " gray_count :Signal.Output):\n", "\n", " n = len(gray_count)\n", " gray_bits = [ Signal(bool(), name=\"u%d\" % i) for i in range(n) ]\n", " code, work = [ Signal(modbv()[n:]) for _ in range(2) ]\n", " flags = [ Signal(bool(), name=\"flags%d\" % i) for i in range(n + 1) ]\n", " toggle = Signal(bool(1))\n", "\n", " # This creates connection instances:\n", " connections = [\n", " flags[0].wireup(False),\n", " code.wireup(concat(*reversed(gray_bits))),\n", " work.wireup(concat(\"1\", code[n-2:], toggle))\n", " ] \n", " \n", " for i in range(n):\n", " v = work[i] & ~flags[i]\n", " connections += [\n", " gray_bits[i].wireup(v ^ gray_count[i]),\n", " flags[i + 1].wireup(flags[i] | v )\n", " ]\n", " \n", " @always_seq(clk.posedge, reset)\n", " def worker():\n", " if enable == True:\n", " gray_count.next = code\n", " toggle.next = ~toggle\n", "\n", " return instances()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulation\n", "\n", "This particular implementation translates via the yosys' CXXRTL simulator backend, using the 'new style' factory class.\n", "\n", "The above code is elaborated implicitely using direct RTL transfer, compiled into C++ code and executed. The result of this is a trace dump into a VCD file.\n", "\n", "For fun, we create a `Waveform` class from an Iterator first:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "from myirl.loops import Iterator\n", "\n", "class Waveform(Iterator):\n", " def __init__(self, val, name = 'waveform'):\n", " it = [ True if i == '1' else False for i in val]\n", " super().__init__(it, name)\n", " def convert(self, tgt, tsz = None):\n", " return \"True\" if self.val else \"False\"\n", " def resolve_type(self):\n", " return bool" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "from myirl.emulation.factory_class import factory \n", "\n", "class WaveformCreatorMixin:\n", " def waveform(self, top, name, waveconfig):\n", "\n", " # Configure waveform:\n", " cfg = wavedraw.config(top, \n", " waveconfig\n", " )\n", "\n", " u = [ top + \".u%d\" % i for i in range(self.size) ]\n", "\n", " for s in u:\n", " cfg[s] = None\n", "\n", " return wavedraw.vcd2wave(\"%s.vcd\" % name, top + '.clk', cfg, delta = 1)\n", "\n", "class example_design(factory.Module, WaveformCreatorMixin):\n", " # Note: in ipython, it is mandatory for a class to have an __init__ function\n", " # in order to retrieve the source for compilation\n", " def __init__(self, name, simclass, SIZE, *args):\n", " super().__init__(name, simclass, *args)\n", " self.size = SIZE\n", "\n", " @factory.testbench('ns')\n", " def tb_gray1(self):\n", " SIZE = self.size\n", " clk = self.ClkSignal(name = 'clk')\n", " ce = self.Signal(bool(), name='ce')\n", "\n", " reset = self.ResetSignal(0, 1, isasync = True)\n", "\n", " dout = self.Signal(intbv()[SIZE:]) \n", "\n", " @self.always(delay(2))\n", " def clkgen():\n", " clk.next = ~clk\n", "\n", " gc = gray_counter1(clk, ce, reset, dout)\n", "\n", " @self.sequence\n", " def stim():\n", " ce.next = False\n", " reset.next = True\n", " yield delay(6)\n", " reset.next = False\n", " for i in Waveform(\"0011111111111111111111111111111111000\"):\n", " ce.next = i \n", " yield clk.negedge\n", "\n", " raise StopSimulation\n", "\n", " return instances() \n", " \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Run the test bench for a few cycles." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m Insert unit gray_counter1_s1_s1_s1_s5 \u001b[0m\n", "DEBUG: Skip non-simulation type \n", "\u001b[32m Adding module with name `gray_counter1` \u001b[0m\n", "\u001b[7;34m FINALIZE implementation `gray_counter1` of `gray_counter1` \u001b[0m\n", "Compiling /tmp/gray_counter1_d977.pyx because it changed.\n", "[1/1] Cythonizing /tmp/gray_counter1_d977.pyx\n", "running build_ext\n", "building 'gray_counter1_d977' extension\n", "x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DCOSIM_NAMESPACE=gray_counter1_d977 -I../../myirl/../ -I/tmp/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/gray_counter1_d977.cpp -o build/temp.linux-x86_64-3.9/tmp/gray_counter1_d977.o\n", "x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DCOSIM_NAMESPACE=gray_counter1_d977 -I../../myirl/../ -I/tmp/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/gray_counter1_d977_rtl.cpp -o build/temp.linux-x86_64-3.9/tmp/gray_counter1_d977_rtl.o\n", "x86_64-linux-gnu-g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fwrapv -O2 -Wl,-z,relro -g -fwrapv -O2 -g -ffile-prefix-map=/build/python3.9-RNBry6/python3.9-3.9.2=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.9/tmp/gray_counter1_d977.o build/temp.linux-x86_64-3.9/tmp/gray_counter1_d977_rtl.o -o build/lib.linux-x86_64-3.9/gray_counter1_d977.cpython-39-x86_64-linux-gnu.so\n", "copying build/lib.linux-x86_64-3.9/gray_counter1_d977.cpython-39-x86_64-linux-gnu.so -> \n", "Open for writing: testbench.vcd\n", "STOP SIMULATION @152 \n" ] } ], "source": [ "from yosys.simulator import CXXRTL\n", "\n", "# Alternatively, we can fall back to GHDL:\n", "from myirl.test.ghdl import GHDL\n", "\n", "SIM = CXXRTL\n", "\n", "\n", "d = example_design(\"myhdl_sim\", SIM, 5)\n", "t = d.tb_gray1()\n", "t.run(180, wavetrace = t.name + \".vcd\")\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Waveform display\n", "\n", "The resulting waveform is displayed using a signal filter configuration:" ] }, { "cell_type": "code", "execution_count": 6, "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 nbwavedrom, wavedraw\n", "waveconfig = { '.reset' : None, '.clk' : None, '.enable' : None }\n", "waveform = d.waveform(t.uut_name, t.name, waveconfig)\n", "nbwavedrom.draw(waveform)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Notes\n", "\n", "This is not so much a recommended design style, as the above `gray_counter1` implementation can be owned by several contexts, as it's defined as a global function.\n", "If several different contexts are referring to it as a top level object, unwanted effects may occur.\n", "\n", "Therefore it is better to stick all kind of parametrizable top level objects into a design context class." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## HDL translation\n", "\n", "To explicitely convert to HDL, we create an instance and elaborate:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Module gray_counter1: Existing instance gray_counter1, rename to gray_counter1_1 \u001b[0m\n", " Writing 'gray_counter1_1' to file /tmp/myirl_gray_counter1_0wci4dqa/gray_counter1_1.v \n" ] } ], "source": [ "def convert(SIZE):\n", " clk = ClkSignal()\n", " ce = Signal(bool())\n", "\n", " reset = ResetSignal(0, 1, isasync = True)\n", "\n", " dout = Signal(intbv()[SIZE:]) \n", "\n", " gc = gray_counter1(clk, ce, reset, dout)\n", " \n", " f = gc.elab(targets.Verilog)\n", " return f\n", "\n", "f = convert(8)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "// File generated from source:\r\n", "// /tmp/ipykernel_30160/733657572.py\r\n", "// (c) 2016-2022 section5.ch\r\n", "// Modifications may be lost, edit the source file instead.\r\n", "\r\n", "`timescale 1 ns / 1 ps\r\n", "`include \"aux.v\"\r\n", "// Architecture myhdl_emulation\r\n", "\r\n", "module gray_counter1_1\r\n", " (\r\n", " input wire clk,\r\n", " input wire enable,\r\n", " input wire /* std_ulogic */ reset,\r\n", " output reg [7:0] gray_count\r\n", " );\r\n", " // Local type declarations\r\n", " // Signal declarations\r\n", " reg toggle;\r\n", " wire [7:0] code;\r\n", " wire flags0;\r\n", " wire u7;\r\n", " wire u6;\r\n", " wire u5;\r\n", " wire u4;\r\n", " wire u3;\r\n", " wire u2;\r\n", " wire u1;\r\n", " wire u0;\r\n", " wire [7:0] work;\r\n", " wire flags1;\r\n", " wire flags2;\r\n", " wire flags3;\r\n", " wire flags4;\r\n", " wire flags5;\r\n", " wire flags6;\r\n", " wire flags7;\r\n", " wire flags8;\r\n", " \r\n", " always @ (posedge clk or posedge reset) begin : WORKER\r\n", " if (reset == 1'b1) begin\r\n", " gray_count <= 8'h00;\r\n", " toggle <= 1'b1;\r\n", " end else begin\r\n", " if ((enable == 1'b1)) begin\r\n", " gray_count <= code;\r\n", " toggle <= ~toggle;\r\n", " end\r\n", " end\r\n", " end\r\n", " assign flags0 = 1'b0;\r\n", " assign code = {u7, u6, u5, u4, u3, u2, u1, u0};\r\n", " assign work = {1'b1, code[6-1:0], toggle};\r\n", " assign u0 = ((work[0] & ~flags0) ^ gray_count[0]);\r\n", " assign flags1 = (flags0 | (work[0] & ~flags0));\r\n", " assign u1 = ((work[1] & ~flags1) ^ gray_count[1]);\r\n", " assign flags2 = (flags1 | (work[1] & ~flags1));\r\n", " assign u2 = ((work[2] & ~flags2) ^ gray_count[2]);\r\n", " assign flags3 = (flags2 | (work[2] & ~flags2));\r\n", " assign u3 = ((work[3] & ~flags3) ^ gray_count[3]);\r\n", " assign flags4 = (flags3 | (work[3] & ~flags3));\r\n", " assign u4 = ((work[4] & ~flags4) ^ gray_count[4]);\r\n", " assign flags5 = (flags4 | (work[4] & ~flags4));\r\n", " assign u5 = ((work[5] & ~flags5) ^ gray_count[5]);\r\n", " assign flags6 = (flags5 | (work[5] & ~flags5));\r\n", " assign u6 = ((work[6] & ~flags6) ^ gray_count[6]);\r\n", " assign flags7 = (flags6 | (work[6] & ~flags6));\r\n", " assign u7 = ((work[7] & ~flags7) ^ gray_count[7]);\r\n", " assign flags8 = (flags7 | (work[7] & ~flags7));\r\n", "endmodule // gray_counter1_1\r\n" ] } ], "source": [ "!cat {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": 4 }