{ "cells": [ { "cell_type": "markdown", "id": "12978dc8-fa90-4a29-aaea-a2df6e16dfd4", "metadata": {}, "source": [ "# Conditional coding style issues\n", "\n", "This is specific to RTLIL conversion and does not necessarily apply to HDL targets.\n", "\n", "There are a number of programming caveats involved with conditional statements, arising from the fact that we need to create hardware elements from a sequential programming style construct.\n", "Statements that perform without warning in Python might therefore throw warnings or even errors when inferring to a RTLIL description.\n", "\n", "Plus, implicit behaviour is in place: When a signal is not explicitely assigned to a new value during a process flow graph cycle, it is assumed that it's unaltered, or implicitely: assigned to its previous value. This is tolerated style for synchronous process descriptions.\n", "\n", "However, asynchronous processes would infer a Multiplexer logic with its output fed back to an input, i.e. a *Latch*. Inferring a latch in a clock synchronous design is normally a bad choice alias design flaw." ] }, { "cell_type": "code", "execution_count": 1, "id": "6e858f92-878b-4db1-8584-28d09ae452a3", "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *" ] }, { "cell_type": "markdown", "id": "37863bc8-1687-4c42-abaf-7b76d4712990", "metadata": {}, "source": [ "First, we create a generator auxiliary for conversion of a unit with a 'standard' interface:" ] }, { "cell_type": "code", "execution_count": 2, "id": "9e37207e-a32f-41be-ab79-9e7227b44411", "metadata": {}, "outputs": [], "source": [ "from yosys import display\n", "from myirl.targets import pyosys\n", "\n", "def convert(unit, name = \"test\", optimize = False, ignorefail = False):\n", " tgt = pyosys.RTLIL(name)\n", " if ignorefail:\n", " tgt.warn_combloop = 'warn'\n", " \n", " clk = ClkSignal()\n", " reset = ResetSignal(0, 1)\n", " a, q = [ Signal(intbv()[8:]) for _ in range(2) ]\n", " \n", " inst = unit(clk, reset, q, a)\n", " \n", " d = inst.elab(tgt)\n", " if optimize:\n", " d[0].run(\"opt; opt_clean\")\n", "\n", " d[0].display_rtl(unit, fmt='dot')\n", " return display.display_dot(d[0].name)" ] }, { "cell_type": "markdown", "id": "a6e43de6-091d-40d4-b62d-c1fac2ed7a7b", "metadata": {}, "source": [ "### Implicit default assignment\n", "\n", "The code below increments `w` every clock cycle only when bit 0 of `a` is set. Otherwise, it leaves it as it is. This can be seen as implicit assignment to its current value: `w.next = w`.\n", "\n", "This generates sane hardware, because Flipflops are created. Without a synchronous clock event, a latch would be created." ] }, { "cell_type": "code", "execution_count": 3, "id": "b4ee312d-5e8c-4b85-a57b-84203f8f954d", "metadata": {}, "outputs": [], "source": [ "@block\n", "def implicit_defaults_unit(clk : ClkSignal, r : ResetSignal,\n", " q : Signal.Output, a : Signal):\n", "\n", " w = Signal(intbv(0xaa)[8:])\n", "\n", " @always_seq(clk.posedge, r)\n", " def proceed():\n", " if a[0]:\n", " w.next = w + 1\n", " \n", " wires = [\n", " q.wireup(w)\n", " ]\n", "\n", " return instances()\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "9ceaeea7-719d-4ca0-87ac-ec6671ed8020", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Adding module with name `implicit_defaults_unit` \u001b[0m\n", "\u001b[7;34m FINALIZE implementation `implicit_defaults_unit` of `implicit_defaults_unit` \u001b[0m\n", "\n", "-- Running command `tee -q hierarchy -top \\implicit_defaults_unit' --\n", "\n", "-- Running command `show -format dot -prefix test implicit_defaults_unit' --\n", "\n", "2. Generating Graphviz representation of design.\n", "Writing dot description to `test.dot'.\n", "Dumping module implicit_defaults_unit to page 1.\n" ] }, { "data": { "text/html": [ "\n", "
\n", " \n", " \n", " \n", " \n", "\n", "implicit_defaults_unit\n", "\n", "implicit_defaults_unit\n", "\n", "\n", "n7\n", "\n", "a\n", "\n", "\n", "\n", "x1\n", "\n", "0:0 - 0:0\n", "\n", "\n", "\n", "n7:e->x1:w\n", "\n", "\n", "\n", "\n", "\n", "n8\n", "\n", "q\n", "\n", "\n", "\n", "n9\n", "\n", "r\n", "\n", "\n", "\n", "c20\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$proceed::w_rst_fb97\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "n9:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "n10\n", "\n", "clk\n", "\n", "\n", "\n", "c15\n", "\n", "CLK\n", "\n", "D\n", "\n", "$proceed::w_e6f4\n", "$dff\n", "\n", "Q\n", "\n", "\n", "\n", "n10:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "n11\n", "\n", "w\n", "\n", "\n", "\n", "c22\n", "\n", "A\n", "\n", "B\n", "\n", "$proceed:10::8133/add:_u\n", "$add\n", "\n", "Y\n", "\n", "\n", "\n", "n11:e->c22:w\n", "\n", "\n", "\n", "\n", "\n", "x6\n", "\n", "BUF\n", "\n", "\n", "\n", "n11:e->x6:w\n", "\n", "\n", "\n", "\n", "\n", "n1\n", "\n", "\n", "\n", "\n", "c15:e->n1:w\n", "\n", "\n", "\n", "\n", "\n", "v0\n", "\n", "8'10101010\n", "\n", "\n", "\n", "v0:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "c20:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "c21\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$w:1\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c21:e->c20:w\n", "\n", "\n", "\n", "\n", "\n", "x1:e->c21:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "v2\n", "\n", "8'00000001\n", "\n", "\n", "\n", "v2:e->c22:w\n", "\n", "\n", "\n", "\n", "\n", "x3\n", "\n", "7:0 - 7:0\n", "\n", "\n", "\n", "c22:e->x3:w\n", "\n", "\n", "\n", "\n", "\n", "x3:e->c21:w\n", "\n", "\n", "\n", "\n", "\n", "x4\n", "\n", "BUF\n", "\n", "\n", "\n", "x4:e->n11:w\n", "\n", "\n", "\n", "\n", "\n", "x5\n", "\n", "BUF\n", "\n", "\n", "\n", "x5:e->c21:w\n", "\n", "\n", "\n", "\n", "\n", "x6:e->n8:w\n", "\n", "\n", "\n", "\n", "\n", "n1:e->x4:w\n", "\n", "\n", "\n", "\n", "\n", "n1:e->x5:w\n", "\n", "\n", "\n", "\n", "\n", "\\n\n", "\n", "\n", "\n", "\n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(implicit_defaults_unit, optimize = False)" ] }, { "cell_type": "markdown", "id": "ae4146fa-d2f8-4383-8b35-f08d08251484", "metadata": {}, "source": [ "## Asynchronous implicit assignment: Latches and combinatorial loops\n", "\n", "This may turn up when an `@always` decorator argument lacks the `.posedge` attribute, i.e. is sensitive to anything else than a clock edge. Hence, an asynchronous process is created and the implicit default creates a feedback from the Muxer output to its input.\n", "If `w` is assigned to a combination of its elements, a combinatorial loop would be created that could cause oscillation or excessive power consumption. If the case below would assign a constant, this would not cause oscillation but creation of a latch." ] }, { "cell_type": "code", "execution_count": 5, "id": "c1e37196-adee-4475-993a-326518e48be3", "metadata": {}, "outputs": [], "source": [ "@block\n", "def latch_unit(clk : ClkSignal, r : ResetSignal,\n", " q : Signal.Output, a : Signal):\n", "\n", " z = Signal(intbv(0xaa)[8:])\n", "\n", " @always(clk)\n", " def proceed():\n", " if a[0]:\n", " z.next = z + 1\n", " \n", " wires = [\n", " q.wireup(z)\n", " ]\n", "\n", " return instances()\n" ] }, { "cell_type": "markdown", "id": "12a96ce9-f20e-48fc-b023-6cf9186efd3f", "metadata": {}, "source": [ "To turn the combinatorial loop failure into a warning, we pass `ignorefail = True`:" ] }, { "cell_type": "code", "execution_count": 6, "id": "305dac08-2782-4684-b4e0-430e3519b75e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Adding module with name `latch_unit` \u001b[0m\n", "\u001b[7;31m LOOP_ERROR: z_343545826.py::proceed:10: Combinatorial loop for 'z' \u001b[0m\n", "\u001b[7;31m LATCH_WARNING: Incomplete default assignments, latch created for 'z' \u001b[0m\n", "\u001b[7;34m FINALIZE implementation `latch_unit` of `latch_unit` \u001b[0m\n", "\n", "-- Running command `tee -q hierarchy -top \\latch_unit' --\n", "\n", "-- Running command `show -format dot -prefix test latch_unit' --\n", "\n", "4. Generating Graphviz representation of design.\n", "Writing dot description to `test.dot'.\n", "Dumping module latch_unit to page 1.\n" ] }, { "data": { "text/html": [ "\n", "
\n", " \n", " \n", " \n", " \n", "\n", "latch_unit\n", "\n", "latch_unit\n", "\n", "\n", "n5\n", "\n", "a\n", "\n", "\n", "\n", "x0\n", "\n", "0:0 - 0:0\n", "\n", "\n", "\n", "n5:e->x0:w\n", "\n", "\n", "\n", "\n", "\n", "n6\n", "\n", "q\n", "\n", "\n", "\n", "n7\n", "\n", "r\n", "\n", "\n", "\n", "n8\n", "\n", "clk\n", "\n", "\n", "\n", "n9\n", "\n", "z\n", "\n", "\n", "\n", "c15\n", "\n", "A\n", "\n", "B\n", "\n", "$proceed:10::dd84/add:_u\n", "$add\n", "\n", "Y\n", "\n", "\n", "\n", "n9:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "x4\n", "\n", "BUF\n", "\n", "\n", "\n", "n9:e->x4:w\n", "\n", "\n", "\n", "\n", "\n", "x5\n", "\n", "BUF\n", "\n", "\n", "\n", "n9:e->x5:w\n", "\n", "\n", "\n", "\n", "\n", "c14\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$z:1\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "x3\n", "\n", "BUF\n", "\n", "\n", "\n", "c14:e->x3:w\n", "\n", "\n", "\n", "\n", "\n", "x0:e->c14:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "v1\n", "\n", "8'00000001\n", "\n", "\n", "\n", "v1:e->c15:w\n", "\n", "\n", "\n", "\n", "\n", "x2\n", "\n", "7:0 - 7:0\n", "\n", "\n", "\n", "c15:e->x2:w\n", "\n", "\n", "\n", "\n", "\n", "x2:e->c14:w\n", "\n", "\n", "\n", "\n", "\n", "x3:e->n9:w\n", "\n", "\n", "\n", "\n", "\n", "x4:e->c14:w\n", "\n", "\n", "\n", "\n", "\n", "x5:e->n6:w\n", "\n", "\n", "\n", "\n", "\n", "\\n\n", "\n", "\n", "\n", "\n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(latch_unit, optimize = False, ignorefail = True)" ] }, { "cell_type": "markdown", "id": "4aaab28a-0e4f-4e28-a5c7-866e57448082", "metadata": {}, "source": [ "### Bad style #1: redundant logic\n", "\n", "The example below would create redundant logic, as the `else` clause is commented out. Creation of the Multiplexer for Line `15` is skipped, however you can see in the RTL display below that a comparator with a dangling output signal is left. During synthesis, this will be optimized away." ] }, { "cell_type": "code", "execution_count": 7, "id": "94cdb37e-3662-4220-8c4b-2e7973777897", "metadata": { "tags": [] }, "outputs": [], "source": [ "@block\n", "def pass_clause_unit(clk : ClkSignal, r : ResetSignal,\n", " q : Signal.Output, a : Signal):\n", "\n", " w = Signal(intbv(0xaa)[8:])\n", " b = Signal(bool())\n", "\n", " @always_seq(clk.posedge, r)\n", " def proceed():\n", " if a[0]:\n", " w.next = 12\n", " elif a[1]:\n", " b.next = True\n", " if a[5:2] == 4:\n", " w.next = 4\n", " elif a[5:2] == 2:\n", " pass\n", " # else: # Add an else clause\n", " # w.next = 1\n", " else:\n", " b.next = False\n", " w.next = 99\n", "\n", " wires = [\n", " q.wireup(w)\n", " ]\n", "\n", " return instances()\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "3148bfaa-fade-4601-a22e-9dd036603f6f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "\u001b[32m Adding module with name `pass_clause_unit` \u001b[0m\n", "DEBUG: /tmp/ipykernel_34193/3058877116.py::proceed:16 Ineffective Muxer for `w`, use 'else' clause\n", "\u001b[7;34m FINALIZE implementation `pass_clause_unit` of `pass_clause_unit` \u001b[0m\n", "\n", "-- Running command `tee -q hierarchy -top \\pass_clause_unit' --\n", "\n", "-- Running command `show -format dot -prefix test pass_clause_unit' --\n", "\n", "6. Generating Graphviz representation of design.\n", "Writing dot description to `test.dot'.\n", "Dumping module pass_clause_unit to page 1.\n" ] }, { "data": { "text/html": [ "\n", "
\n", " \n", " \n", " \n", " \n", "\n", "pass_clause_unit\n", "\n", "pass_clause_unit\n", "\n", "\n", "n19\n", "\n", "a\n", "\n", "\n", "\n", "x2\n", "\n", "0:0 - 0:0\n", "\n", "\n", "\n", "n19:e->x2:w\n", "\n", "\n", "\n", "\n", "\n", "x3\n", "\n", "1:1 - 0:0\n", "\n", "\n", "\n", "n19:e->x3:w\n", "\n", "\n", "\n", "\n", "\n", "x4\n", "\n", "0:0 - 0:0\n", "\n", "\n", "\n", "n19:e->x4:w\n", "\n", "\n", "\n", "\n", "\n", "x5\n", "\n", "1:1 - 0:0\n", "\n", "\n", "\n", "n19:e->x5:w\n", "\n", "\n", "\n", "\n", "\n", "x7\n", "\n", "4:2 - 2:0\n", "\n", "\n", "\n", "n19:e->x7:w\n", "\n", "\n", "\n", "\n", "\n", "x9\n", "\n", "4:2 - 2:0\n", "\n", "\n", "\n", "n19:e->x9:w\n", "\n", "\n", "\n", "\n", "\n", "n20\n", "\n", "q\n", "\n", "\n", "\n", "n21\n", "\n", "r\n", "\n", "\n", "\n", "c33\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$proceed::w_rst_2fcd\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "n21:e->c33:w\n", "\n", "\n", "\n", "\n", "\n", "c35\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$proceed::b_rst_6288\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "n21:e->c35:w\n", "\n", "\n", "\n", "\n", "\n", "n22\n", "\n", "clk\n", "\n", "\n", "\n", "c28\n", "\n", "CLK\n", "\n", "D\n", "\n", "$proceed::w_a81a\n", "$dff\n", "\n", "Q\n", "\n", "\n", "\n", "n22:e->c28:w\n", "\n", "\n", "\n", "\n", "\n", "c34\n", "\n", "CLK\n", "\n", "D\n", "\n", "$proceed::b_4a53\n", "$dff\n", "\n", "Q\n", "\n", "\n", "\n", "n22:e->c34:w\n", "\n", "\n", "\n", "\n", "\n", "n23\n", "\n", "w\n", "\n", "\n", "\n", "x24\n", "\n", "BUF\n", "\n", "\n", "\n", "n23:e->x24:w\n", "\n", "\n", "\n", "\n", "\n", "n24\n", "\n", "b\n", "\n", "\n", "\n", "n1\n", "\n", "\n", "\n", "\n", "c28:e->n1:w\n", "\n", "\n", "\n", "\n", "\n", "v0\n", "\n", "8'10101010\n", "\n", "\n", "\n", "v0:e->c33:w\n", "\n", "\n", "\n", "\n", "\n", "c33:e->c28:w\n", "\n", "\n", "\n", "\n", "\n", "n3\n", "\n", "\n", "\n", "\n", "c34:e->n3:w\n", "\n", "\n", "\n", "\n", "\n", "v1\n", "\n", "1'0\n", "\n", "\n", "\n", "v1:e->c35:w\n", "\n", "\n", "\n", "\n", "\n", "c35:e->c34:w\n", "\n", "\n", "\n", "\n", "\n", "c36\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$w:1\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c36:e->c33:w\n", "\n", "\n", "\n", "\n", "\n", "x2:e->c36:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "c37\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$w:1\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c37:e->c36:w\n", "\n", "\n", "\n", "\n", "\n", "x3:e->c37:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "c38\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$pass\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c38:e->c35:w\n", "\n", "\n", "\n", "\n", "\n", "x4:e->c38:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "c39\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$b:1\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c39:e->c38:w\n", "\n", "\n", "\n", "\n", "\n", "x5:e->c39:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "c40\n", "\n", "A\n", "\n", "B\n", "\n", "S\n", "\n", "$w:2\n", "$mux\n", "\n", "Y\n", "\n", "\n", "\n", "c40:e->c37:w\n", "\n", "\n", "\n", "\n", "\n", "v6\n", "\n", "3'100\n", "\n", "\n", "\n", "c41\n", "\n", "A\n", "\n", "B\n", "\n", "$proceed:15::72ae/eq:_u\n", "$eq\n", "\n", "Y\n", "\n", "\n", "\n", "v6:e->c41:w\n", "\n", "\n", "\n", "\n", "\n", "c41:e->c40:w\n", "\n", "\n", "\n", "\n", "\n", "x7:e->c41:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "v8\n", "\n", "3'010\n", "\n", "\n", "\n", "c42\n", "\n", "A\n", "\n", "B\n", "\n", "$proceed:16::f7b0/eq:_u\n", "$eq\n", "\n", "Y\n", "\n", "\n", "\n", "v8:e->c42:w\n", "\n", "\n", "\n", "\n", "\n", "n15\n", "\n", "$10\n", "\n", "\n", "\n", "c42:e->n15:w\n", "\n", "\n", "\n", "\n", "\n", "x9:e->c42:w\n", "\n", "\n", "\n", "\n", "\n", "\n", "v10\n", "\n", "1'0\n", "\n", "\n", "\n", "x11\n", "\n", "BUF\n", "\n", "\n", "\n", "v10:e->x11:w\n", "\n", "\n", "\n", "\n", "\n", "x11:e->c39:w\n", "\n", "\n", "\n", "\n", "\n", "v12\n", "\n", "8'01100011\n", "\n", "\n", "\n", "x13\n", "\n", "BUF\n", "\n", "\n", "\n", "v12:e->x13:w\n", "\n", "\n", "\n", "\n", "\n", "x13:e->c37:w\n", "\n", "\n", "\n", "\n", "\n", "v14\n", "\n", "1'1\n", "\n", "\n", "\n", "x15\n", "\n", "BUF\n", "\n", "\n", "\n", "v14:e->x15:w\n", "\n", "\n", "\n", "\n", "\n", "x15:e->c39:w\n", "\n", "\n", "\n", "\n", "\n", "v16\n", "\n", "8'00000100\n", "\n", "\n", "\n", "x17\n", "\n", "BUF\n", "\n", "\n", "\n", "v16:e->x17:w\n", "\n", "\n", "\n", "\n", "\n", "x17:e->c40:w\n", "\n", "\n", "\n", "\n", "\n", "v18\n", "\n", "8'00001100\n", "\n", "\n", "\n", "x19\n", "\n", "BUF\n", "\n", "\n", "\n", "v18:e->x19:w\n", "\n", "\n", "\n", "\n", "\n", "x19:e->c36:w\n", "\n", "\n", "\n", "\n", "\n", "x20\n", "\n", "BUF\n", "\n", "\n", "\n", "x20:e->n24:w\n", "\n", "\n", "\n", "\n", "\n", "x21\n", "\n", "BUF\n", "\n", "\n", "\n", "x21:e->c38:w\n", "\n", "\n", "\n", "\n", "\n", "x22\n", "\n", "BUF\n", "\n", "\n", "\n", "x22:e->n23:w\n", "\n", "\n", "\n", "\n", "\n", "x23\n", "\n", "BUF\n", "\n", "\n", "\n", "x23:e->c40:w\n", "\n", "\n", "\n", "\n", "\n", "x24:e->n20:w\n", "\n", "\n", "\n", "\n", "\n", "n1:e->x22:w\n", "\n", "\n", "\n", "\n", "\n", "n1:e->x23:w\n", "\n", "\n", "\n", "\n", "\n", "n3:e->x20:w\n", "\n", "\n", "\n", "\n", "\n", "n3:e->x21:w\n", "\n", "\n", "\n", "\n", "\n", "\\n\n", "\n", "\n", "\n", "\n", "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(pass_clause_unit)" ] }, { "cell_type": "markdown", "id": "4cca1924-50dc-4ce3-b79f-2b83e9361409", "metadata": {}, "source": [ "With the `else` clause effective, the statements make sense and complete into multiplexers, although it's not considered the best style." ] } ], "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 }