{ "cells": [ { "cell_type": "markdown", "id": "dfd7651d-06e5-482e-9a54-e086f7094cea", "metadata": {}, "source": [ "# Direct RTL via yosys\n", "\n", "The former 'jupyosys' fork from myHDL is in process of migrating into this development tree.\n", "\n", "Status notes:\n", " * Do not rely on pyosys API\n", " * Crashes and irregularities may occur\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "39577a8d-4df7-4f2a-ad84-c4cb65fa81a2", "metadata": {}, "outputs": [], "source": [ "import sys\n", "sys.path.insert(0, \"../..\")" ] }, { "cell_type": "code", "execution_count": 2, "id": "f3a927f3-aba0-4662-a83b-3077b8c78c82", "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "\n", "Byte = Signal.Type(intbv, 8)\n", "Bool = Signal.Type(bool)\n", "\n", "@block\n", "def lfsr8(clk : ClkSignal, ce : Bool, reset : ResetSignal, dout : Byte.Output,\n", " RVAL : int = 1):\n", " \"\"\"LFSR with all states\"\"\"\n", " \n", " v = Signal(intbv(RVAL)[8:])\n", " \n", " fb = Signal(bool())\n", " \n", " e = v[7:0] == 0\n", "\n", " @always_seq(clk.posedge, reset)\n", " def worker():\n", " if ce == 1:\n", " v.next = concat(v[6], v[5], v[4], v[3] ^ fb, v[2] ^ fb, v[1] ^ fb, v[0], fb)\n", "\n", " @always_comb\n", " def assign():\n", " fb.next = v[7] ^ e\n", " dout.next = v\n", "\n", " return instances()\n", "\n", "# Wrapper hack to use local dictionary for instance naming\n", "def use_local_names(arg):\n", " arg.use_local_names = True\n", " return arg\n", "\n", "@use_local_names\n", "@block\n", "def unit_count(clk : ClkSignal, ce: Signal, reset : ResetSignal, q : Signal.Output):\n", " \n", " c, d = [ Signal(intbv(0)[8:]) for _ in range(2) ]\n", " \n", " inst_lfsr = lfsr8(clk, ce, reset, d, RVAL = 0xfa)\n", "\n", " @always_seq(clk.posedge, reset)\n", " def counter():\n", " c.next = c + 1\n", "# q.next = d ^ c\n", "\n", " wires = [ q.wireup(d ^ c) ]\n", " \n", " return instances()" ] }, { "cell_type": "code", "execution_count": 3, "id": "c476cce3-5621-4f1d-86ed-50545e94cff7", "metadata": {}, "outputs": [], "source": [ "from myirl.targets import pyosys\n", "\n", "def test_expr(tgt):\n", " ce = Signal(bool())\n", " clk = ClkSignal()\n", " reset = ResetSignal(0, 1, isasync = True)\n", " q = Signal(intbv()[8:])\n", "\n", " t = unit_count(clk, ce, reset, q)\n", " designs = t.elab(tgt, elab_all = True)\n", "\n", " return designs[0]" ] }, { "cell_type": "code", "execution_count": 4, "id": "17f66eb8-0222-4083-aff3-1577e6ba05a7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", " Elaborating component lfsr8_s1_s1_s1_s8_250 \n", "\u001b[32m Adding module with name `lfsr8` \u001b[0m\n", "\u001b[32m Adding module with name `unit_count` \u001b[0m\n", "\u001b[7;34m FINALIZE implementation `unit_count` of `unit_count` \u001b[0m\n", "\n", "-- Running command `tee -q hierarchy -top \\unit_count' --\n", "\n", "-- Running command `show -format dot -prefix top unit_count' --\n", "\n", "2. Generating Graphviz representation of design.\n", "Writing dot description to `top.dot'.\n", "Dumping module unit_count to page 1.\n" ] } ], "source": [ "tgt = pyosys.RTLIL(\"top\")\n", "\n", "design = test_expr(tgt)\n", "design.display_rtl(selection = \"unit_count\", fmt='dot')\n", "# design.display_rtl(selection = \"lfsr8\", fmt='dot')" ] }, { "cell_type": "markdown", "id": "f66bb0eb-feca-4b75-8050-02cc7d15dc16", "metadata": {}, "source": [ "### RTL Display\n", "\n", "The `@use_local_names` construct sets the myHDL instance variable names for the identifier.\n", "\n", "Note: Pan and zoom may not work on some browsers." ] }, { "cell_type": "code", "execution_count": 5, "id": "fc8b26c2-c4d0-4874-b9df-cb05bc5bcff2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " \n", " \n", " \n", "\n", "unit_count\n", "\n", "unit_count\n", "\n", "\n", "n6\n", "\n", "q\n", "\n", "\n", "\n", "n7\n", "\n", "reset\n", "\n", "\n", "\n", "c20\n", "\n", "ARST\n", "\n", "CLK\n", "\n", "D\n", "\n", "$counter::c_6619\n", "$adff\n", "\n", "Q\n", "\n", "\n", "\n", "n7:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "c23\n", "\n", "ce\n", "\n", "clk\n", "\n", "reset\n", "\n", "inst_lfsr\n", "lfsr8\n", "\n", "dout\n", "\n", "\n", "\n", "n7:e->c23:w\n", "\n", "\n", "\n", "\n", "\n", "n8\n", "\n", "ce\n", "\n", "\n", "\n", "n8:e->c23:w\n", "\n", "\n", "\n", "\n", "\n", "n9\n", "\n", "clk\n", "\n", "\n", "\n", "n9:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "n9:e->c23:w\n", "\n", "\n", "\n", "\n", "\n", "n10\n", "\n", "c\n", "\n", "\n", "\n", "c15\n", "\n", "A\n", "\n", "B\n", "\n", "$counter:44::ab0c/xor:_u\n", "$xor\n", "\n", "Y\n", "\n", "\n", "\n", "n10:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "c21\n", "\n", "A\n", "\n", "B\n", "\n", "$counter:44::0398/add:_u\n", "$add\n", "\n", "Y\n", "\n", "\n", "\n", "n10:e->c21:w\n", "\n", "\n", "\n", "\n", "\n", "n11\n", "\n", "d\n", "\n", "\n", "\n", "n11:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "x3\n", "\n", "BUF\n", "\n", "\n", "\n", "c15:e->x3:w\n", "\n", "\n", "\n", "\n", "\n", "x2\n", "\n", "BUF\n", "\n", "\n", "\n", "c20:e->x2:w\n", "\n", "\n", "\n", "\n", "\n", "v0\n", "\n", "8'00000001\n", "\n", "\n", "\n", "v0:e->c21:w\n", "\n", "\n", "\n", "\n", "\n", "x1\n", "\n", "7:0 - 7:0\n", "\n", "\n", "\n", "c21:e->x1:w\n", "\n", "\n", "\n", "\n", "\n", "c23:e->n11:w\n", "\n", "\n", "\n", "\n", "\n", "x1:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "x2:e->n10:w\n", "\n", "\n", "\n", "\n", "\n", "x3:e->n6:w\n", "\n", "\n", "\n", "\n", "\n", "\\n\n", "\n", "\n", "\n", "\n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from yosys import display\n", "display.display_dot(design.name)" ] }, { "cell_type": "markdown", "id": "ee9de89e-781f-4646-a962-5928cd8d3b4b", "metadata": {}, "source": [ "## Test bench (myHDL style)\n", "\n", "Note that `and`, `or` and `not` boolean constructs are no longer allowed with signals." ] }, { "cell_type": "code", "execution_count": 6, "id": "21365dab-9b35-4e8c-927e-68a00d004bc9", "metadata": {}, "outputs": [], "source": [ "from simulation import *\n", "\n", "from yosys.simulator import CXXRTL as Sim\n", "\n", "\n", "@sim.testbench(Sim, time_unit = 'ns')\n", "def testbench():\n", " clk = ClkSignal(init = 0)\n", " reset = ResetSignal(1, 1, isasync = False)\n", " ce = Signal(bool())\n", "\n", " a = Signal(intbv()[8:])\n", "\n", " inst = unit_count(clk, ce, reset, a)\n", "\n", " @always(delay(2))\n", " def clkgen():\n", " clk.next = ~ clk\n", "\n", " @sequence\n", " def reset_seq():\n", " yield delay(21)\n", " reset.next = False\n", " yield delay(1)\n", " ce.next = True\n", " yield delay(20)\n", "\n", " return instances()" ] }, { "cell_type": "markdown", "id": "31861a7c-7740-4896-8754-b870ef17e7fe", "metadata": {}, "source": [ "The simulation is executed using the `.run` method below. Note that the simulation may not be fully 'delta' accurate and will only serve for synchronous designs.\n", "\n", "**Note**: It is mandatory to yield an initial delay in the sequential code to properly arm the concurrent process scheduling." ] }, { "cell_type": "code", "execution_count": 7, "id": "441f6d7e-da0f-4175-8087-98f00d72e64f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Module unit_count: Existing instance unit_count, rename to unit_count_1 \u001b[0m\n", "\u001b[32m Module unit_count: Existing instance lfsr8, rename to lfsr8_1 \u001b[0m\n", " Elaborating component lfsr8_s1_s1_s1_s8_250 \n", "\u001b[32m Adding module with name `lfsr8_1` \u001b[0m\n", "\u001b[32m Adding module with name `unit_count_1` \u001b[0m\n", "\u001b[7;34m FINALIZE implementation `unit_count_1` of `unit_count` \u001b[0m\n", "Compiling /tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f.pyx because it changed.\n", "[1/1] Cythonizing /tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f.pyx\n", "running build_ext\n", "building 'unit_count_1_468f' extension\n", "creating build/temp.linux-x86_64-3.9/tmp/myirl_unit_count_s99a9uf8\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=unit_count_1_468f -I../../myirl/../ -I/tmp/myirl_unit_count_s99a9uf8/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f.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=unit_count_1_468f -I../../myirl/../ -I/tmp/myirl_unit_count_s99a9uf8/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f_rtl.cpp -o build/temp.linux-x86_64-3.9/tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f_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/myirl_unit_count_s99a9uf8/unit_count_1_468f.o build/temp.linux-x86_64-3.9/tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f_rtl.o -o build/lib.linux-x86_64-3.9/unit_count_1_468f.cpython-39-x86_64-linux-gnu.so\n", "copying build/lib.linux-x86_64-3.9/unit_count_1_468f.cpython-39-x86_64-linux-gnu.so -> \n", "Open for writing: testbench.vcd\n", "DEBUG STOP PROCESS reset_seq\n" ] } ], "source": [ "def test_simulation(n):\n", " t = testbench()\n", " assert t._uut.obj.ctx == unit_count.ctx\n", " t.run(n)\n", " return t\n", "\n", "t = test_simulation(2000)" ] }, { "cell_type": "markdown", "id": "b48fab93-be1e-4b1a-b2cd-a4981a931e70", "metadata": {}, "source": [ "The resulting test bench file: [testbench.vcd](testbench.vcd)" ] }, { "cell_type": "markdown", "id": "6a145b11-29ac-4a55-9580-5e8da1178d33", "metadata": {}, "source": [ "## Customizing RTLIL targets\n", "\n", "When the `.elab()` method is called, the design is elaborated as RTLIL and a list of design elements is returned, the first being a RTLIL Design handle.\n", "The `.finalize()` method is called last inside elaboration, which can perform some optimizations or emissions to specific targets.\n", "\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "f472f6fd-6039-45f6-bda2-219c94cbca33", "metadata": {}, "outputs": [], "source": [ "class MyRTL(pyosys.RTLIL):\n", " def finalize(self, top, objs = None):\n", " tname = top.name\n", " design = self._design\n", " design.run(\"hierarchy -top %s\" % tname)\n", " print(80 * '=')\n", " design.write_verilog(name = top.obj.name)\n", " design.run(\"flatten; ls; select %s; stat\" % tname)\n", " \n", " return [design]" ] }, { "cell_type": "markdown", "id": "54e56e82-4571-4a9a-9887-8545e94616ce", "metadata": {}, "source": [ "The `.finalize()` function must return the created elements in a list, which is in turn returned from the `.elab()` call." ] }, { "cell_type": "code", "execution_count": 9, "id": "490b03b6-6cfb-4465-832c-b5ebfa8b5a44", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Module unit_count: Existing instance unit_count, rename to unit_count_2 \u001b[0m\n", "\u001b[32m Module unit_count: Existing instance lfsr8, rename to lfsr8_2 \u001b[0m\n", " Elaborating component lfsr8_s1_s1_s1_s8_250 \n", "\u001b[32m Adding module with name `lfsr8_2` \u001b[0m\n", "\u001b[32m Adding module with name `unit_count_2` \u001b[0m\n", "================================================================================\n", "\n", "-- Running command `tee -q hierarchy -top \\unit_count_1' --\n", "\n", "-- Running command `tee -q write_cxxrtl -namespace unit_count_1_468f -header /tmp/myirl_unit_count_s99a9uf8/unit_count_1_468f_rtl.cpp' --\n", "\n", "-- Running command `tee -q hierarchy -top unit_count_2' --\n", "\n", "-- Running command `ls; check' --\n", "\n", "6. Executing CHECK pass (checking for obvious problems).\n", "Found and reported 0 problems.\n", "\n", "-- Running command `hierarchy -check' --\n", "\n", "7. Executing HIERARCHY pass (managing design hierarchy).\n", "\n", "-- Running command `write_verilog unit_count_mapped.v' --\n", "\n", "8. Executing Verilog backend.\n", "Dumping module `\\lfsr8_2'.\n", "Dumping module `\\unit_count_2'.\n", "\n", "-- Running command `tee -q flatten; ls; select unit_count_2; stat' --\n", "\n", "1 modules:\n", " unit_count_2\n", "\n", "10. Printing statistics.\n" ] } ], "source": [ "tgt = MyRTL(\"top2\")\n", "\n", "design = test_expr(tgt)" ] }, { "cell_type": "code", "execution_count": 10, "id": "7195d6f9-aeaf-4464-ac62-6dc5ad7dba64", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/* Generated by Yosys 0.13+3 (git sha1 4656b0171, gcc 10.2.1-6 -Og -fPIC) */\n", "\n", "module lfsr8_2(clk, ce, reset, dout);\n", " wire [7:0] _00_;\n", " wire [-1:0] _01_;\n", " wire _02_;\n", " wire _03_;\n", " wire _04_;\n", " wire _05_;\n", " wire [7:0] _06_;\n", " wire _07_;\n", " wire _08_;\n", " wire _09_;\n", " wire [7:0] _10_;\n", " reg [7:0] _11_;\n", " wire [7:0] _12_;\n", " wire [7:0] _13_;\n", " input ce;\n", " input clk;\n", " output [7:0] dout;\n", " wire fb;\n", " input reset;\n", " wire [7:0] v;\n", " assign _08_ = v[6:0] == 7'h00;\n", " assign _09_ = v[7] ^ _08_;\n", " assign _05_ = ce == 1'h1;\n", " assign _03_ = v[2] ^ fb;\n", " assign _04_ = v[3] ^ fb;\n", " assign _06_ = _05_ ? _00_ : _13_;\n", " assign _02_ = v[1] ^ fb;\n", " always @(posedge clk, posedge reset)\n", " if (reset) _11_ <= 8'hfa;\n", " else _11_ <= _06_;\n", " assign _00_ = { v[6:4], _04_, _03_, _02_, v[0], fb };\n", " assign v = _11_;\n", " assign _13_ = _11_;\n", " assign _07_ = _09_;\n", " assign _10_ = v;\n", " assign fb = _07_;\n", " assign dout = _10_;\n", "endmodule\n", "\n", "(* top = 1 *)\n", "module unit_count_2(clk, ce, reset, q);\n", " wire [7:0] _0_;\n", " wire [8:0] _1_;\n", " wire [7:0] _2_;\n", " reg [7:0] _3_;\n", " wire [7:0] _4_;\n", " wire [7:0] c;\n", " input ce;\n", " input clk;\n", " wire [7:0] d;\n", " output [7:0] q;\n", " input reset;\n", " assign _2_ = d ^ c;\n", " assign _1_ = c + 8'h01;\n", " always @(posedge clk, posedge reset)\n", " if (reset) _3_ <= 8'h00;\n", " else _3_ <= _0_;\n", " lfsr8_2 inst_lfsr (\n", " .ce(ce),\n", " .clk(clk),\n", " .dout(d),\n", " .reset(reset)\n", " );\n", " assign _0_ = _1_[7:0];\n", " assign c = _3_;\n", " assign q = _2_;\n", "endmodule\n" ] } ], "source": [ "!cat unit_count_mapped.v" ] }, { "cell_type": "markdown", "id": "54ffaa3d-e31b-4520-9599-9bedb011c2d6", "metadata": {}, "source": [ "## Limitations\n", "\n", "Some constructs that work for the VHDL target are not yet supported:\n", "\n", "* No partial assignments of the type `p[0].next = x`\n", "* Custom generators (bulk signal assignment, @hdlmacro constructs)" ] } ], "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 }