{ "cells": [ { "cell_type": "markdown", "id": "902680b9-afe0-4057-a944-fa5ca9f37884", "metadata": {}, "source": [ "# Avoiding arithmetic pitfalls\n", "\n", "**No longer maintained. May be outdated, requires a myHDL setup**\n", "\n", "MyHDL arithmetics, in particular addition/subtraction of `intbv()` signals does not account for bit widths within a chain of additions/subtractions. Therefore it is possible to create scenarios where certain values that never occur in the MyHDL model (due to intbv() min max restrictions) are left unconvered (such as truncated results) in the resulting HDL.\n", "\n", "The IRL kernel however does account for bit widths and is stricter with respect to truncation, plus it allows expressions that are not valid using MyHDL intbvs, as elaborated below. However, the bit width accounted for is always the hard amount of bits used for a binary value, not a logical limit as applied to an intbv().\n", "\n", "From this release on, the arithmetic handling is entirely wire type specific, with exception of `intbv` which is in most scenarios compatible to the MyHDL intbv." ] }, { "cell_type": "code", "execution_count": 2, "id": "c8426ec1-098a-4cb9-ab3f-35bc0b6ee829", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Defaulting to user installation because normal site-packages is not writeable\n", "Collecting myhdl\n", " Downloading myhdl-0.11.49-py3-none-any.whl (157 kB)\n", "\u001b[K |████████████████████████████████| 157 kB 720 kB/s eta 0:00:01\n", "\u001b[?25hInstalling collected packages: myhdl\n", "Successfully installed myhdl-0.11.49\n", "\u001b[33mWARNING: You are using pip version 21.2.4; however, version 24.1.2 is available.\n", "You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.\u001b[0m\n" ] } ], "source": [ "# ! pip install myhdl" ] }, { "cell_type": "code", "execution_count": 3, "id": "9f67056f-a00e-47e9-bb4c-28dbed176c29", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from myhdl import *\n", "\n", "@block\n", "def calc(a, b):\n", " @always_comb\n", " def worker():\n", " b.next = a + a - 8\n", " \n", " return instances()\n", "\n", "a = Signal(intbv(min=0, max=9))\n", "b = Signal(intbv(min=-8, max=9))\n", "inst = calc(a, b)\n", "inst.convert(\"VHDL\")" ] }, { "cell_type": "code", "execution_count": 4, "id": "dfc8046e-3d14-4a5b-ad46-9941373edf0f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b <= signed((resize(a, 5) + a) - 8);\n", "\n", "end architecture MyHDL;\n" ] } ], "source": [ "! grep -A 4 resize calc.vhd" ] }, { "cell_type": "markdown", "id": "68b39b5e-5934-4e9e-90d4-fce4ffd35594", "metadata": {}, "source": [ "Let's recapitulate a few intbv properties:" ] }, { "cell_type": "code", "execution_count": 5, "id": "cb3760f5-8719-47bc-9c25-20df4b03b02c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(intbv(8)[4:], '1000', intbv(-8)[4:])" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = intbv(8)[4:]\n", "assert int(a.signed()) == -8\n", "a, bin(a), a.signed()" ] }, { "cell_type": "markdown", "id": "e36ee291-2710-4174-8516-fda1a5dffb8f", "metadata": {}, "source": [ "We observe this being bit accurate: since the MSB is set, casting it to a Signed type will yield its negated value." ] }, { "cell_type": "markdown", "id": "5cd7debb-9e89-47f3-8824-1082ef60aefb", "metadata": {}, "source": [ "### Implicit truncation\n", "\n", "This case may appear constructed, but is an example of 'boundaries gone wrong' or 'testing with insufficient values'. Fortunately, we get a GHDL warning on the truncated vectors, but due to lack of static bit width accounting, it will be left unnoticed in the translation stage." ] }, { "cell_type": "code", "execution_count": 6, "id": "e63c9635-571b-4091-95cf-79f7f5a0d8b4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "08\n", "ERROR DETECTED intbv value -24 < minimum -8\n", "==== COSIM stdout ====\n", "../../src/ieee2008/numeric_std-body.vhdl:3089:7:@0ms:(assertion warning): NUMERIC_STD.TO_UNSIGNED: vector truncated\n", "../../src/ieee2008/numeric_std-body.vhdl:3089:7:@0ms:(assertion warning): NUMERIC_STD.TO_UNSIGNED: vector truncated\n", "08\n", "../../src/ieee2008/numeric_std-body.vhdl:3089:7:@1ns:(assertion warning): NUMERIC_STD.TO_UNSIGNED: vector truncated\n", "08\n", "\n" ] } ], "source": [ "from myirl.test.common_test import run_ghdl\n", "\n", "@block\n", "def test_arith1():\n", " c = Signal(intbv(15)[4:])\n", " a = Signal(intbv(0, min=0, max=9))\n", " b = Signal(intbv(min=-8, max=9))\n", "\n", " @always_comb\n", " def worker():\n", " b.next = a + a + c + c - 36\n", " \n", " @instance\n", " def feed():\n", " a.next = 7\n", " c.next = 15\n", " yield delay(1)\n", " print(b)\n", " assert b == 8\n", " # These values will also yield the same result in the HDL transfer,\n", " # however, MyHDL simulation will notice\n", " a.next = 6\n", " c.next = 0\n", " yield delay(1)\n", " print(b)\n", " assert b == 8\n", " \n", " return instances()\n", "\n", "def run():\n", " import os\n", " pwd = os.getcwd()\n", "\n", " inst = test_arith1()\n", " # Simulation would detect the above overflow in this case:\n", " try:\n", " inst.run_sim(10)\n", " except ValueError as e:\n", " print(\"ERROR DETECTED\", e)\n", " \n", " inst = test_arith1()\n", " inst.convert(\"VHDL\")\n", "\n", " run_ghdl([pwd + \"/test_arith1.vhd\", pwd + \"/pck_myhdl_011.vhd\"], inst, debug = True)\n", " \n", "run()" ] }, { "cell_type": "code", "execution_count": 7, "id": "481c2195-e53b-4570-8cbe-fd9266dee628", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b <= signed((((resize(a, 5) + a) + c) + c) - 36);\n" ] } ], "source": [ "! grep resize test_arith1.vhd" ] }, { "cell_type": "markdown", "id": "533c91ca-0d5a-4fcb-9f6c-47d995cea0b7", "metadata": {}, "source": [ "### intbv behaviour\n", "\n", "Important to keep in mind: an addition or subtraction involving an intbv will no longer be an intbv:" ] }, { "cell_type": "code", "execution_count": 8, "id": "a9db3e6b-caa1-431a-b866-56427f946d55", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t = a + a\n", "type(t)" ] }, { "cell_type": "markdown", "id": "33287682-cd3c-4303-ac9a-7c0d9cccb8e4", "metadata": {}, "source": [ "So this will not work:" ] }, { "cell_type": "code", "execution_count": 9, "id": "f78cc51f-3819-4f7f-a4e5-d348acf57e22", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'int' object has no attribute 'signed'\n" ] } ], "source": [ "try:\n", " t = (a + 1).signed()\n", " assert False # Never hit\n", "except AttributeError as e:\n", " print(e)" ] }, { "cell_type": "markdown", "id": "ae7d91a6-6586-4032-844e-d00682134f09", "metadata": {}, "source": [ "In fact this is not a deficiency of the intbv concept, rather, this property elegantly offloads the boundary checks to the simulation. However, apart from non-supported constructs as the above, it does not support static checking or bit accounting for pipelines from the HLS library." ] }, { "cell_type": "markdown", "id": "678f7419-f2ed-4d99-95a7-21eb96b17d27", "metadata": {}, "source": [ "## MyIRL / emulation variant\n", "\n", "The IRL kernel does not implicitely truncate, unless the bit size of the result is one more than the signal it is assigned to. In this case, a warning is emitted.\n", "If the bit size is larger, a size mismatch error will be thrown.\n", "\n", "When the result is signed, the arguments however are unsigned, the IRL requires a more explicit specification which part is to be assumed 'signed', otherwise, an exception is thrown:" ] }, { "cell_type": "code", "execution_count": 10, "id": "f2925235-5af4-4039-9641-01a92ff1b281", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "EXPECTED ERROR <= (): requires explicit casting\n" ] } ], "source": [ "from myirl.emulation.myhdl import *\n", "from myirl.kernel.components import DesignModule\n", "from myirl.targets.dummy import DummyVHDLModule\n", "\n", "a = Signal(intbv()[4:])\n", "sa = Signal(intbv(0, min=-16, max=17))\n", "ctx = DummyVHDLModule()\n", "op = sa.set(a)\n", "try:\n", " op.emit(ctx)\n", " assert False # Should never get here\n", "except TypeError as e:\n", " print(\"EXPECTED ERROR\", e)" ] }, { "cell_type": "markdown", "id": "75933c62-bc27-4868-bc06-8e268227ff20", "metadata": {}, "source": [ "Since it is size-sensitive, a `.signed()` cast will result in a negative number if the MSB of the signal wire is set. This may result in a number of pitfalls, see `MODE`s below. One of them produces the wrong result. What we're trying to achieve, is the bit-correct operation for" ] }, { "cell_type": "code", "execution_count": 11, "id": "b3bcf449-949a-4ebf-a397-c17b02494c0e", "metadata": {}, "outputs": [], "source": [ "result = a + a - 8" ] }, { "cell_type": "markdown", "id": "2355f78c-fed8-4f42-b58c-688ddcbd0030", "metadata": {}, "source": [ "where `result` is obviously signed and `a` is unsigned. Some of the following logic constructs are incorrect. Which `MODE`s would that be?" ] }, { "cell_type": "code", "execution_count": 12, "id": "f6a0edf8-0fce-4987-808f-547894160827", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Size a = 4 Signed: False , Size b = 6 Signed: True\n", " Writing 'calc' to file /tmp/myirl_calc_plfht1p8/calc.vhdl \n", "\u001b[32m Module calc: Existing instance calc, rename to calc_1 \u001b[0m\n", " Writing 'calc_1' to file /tmp/myirl_calc_gdowsb99/calc_1.vhdl \n", "\u001b[32m Module calc: Existing instance calc, rename to calc_2 \u001b[0m\n", " Writing 'calc_2' to file /tmp/myirl_calc_wtqk8dmu/calc_2.vhdl \n", "Warning: Implicit truncation of SUB(ADD(SGN(R(a, 5)), SGN(R(a, 5))), C:8) result\n", "\u001b[32m Module calc: Existing instance calc, rename to calc_3 \u001b[0m\n", " Writing 'calc_3' to file /tmp/myirl_calc_sfs8jgrd/calc_3.vhdl \n" ] } ], "source": [ "@block\n", "def calc(a : Signal, b : Signal.Output, MODE = 0):\n", " \n", " if MODE == 0:\n", " @always_comb\n", " def worker():\n", " b.next = a.signed() + a - 8\n", " elif MODE == 1:\n", " @always_comb\n", " def worker():\n", " b.next = a + (a - 8).signed()\n", " elif MODE == 2:\n", " a1 = a.resize(a.size() + 1)\n", " @always_comb\n", " def worker():\n", " b.next = a1.signed() + a1.signed() - 8\n", " elif MODE == 3:\n", " @always_comb\n", " def worker():\n", " b.next = (a + a - 8).signed() \n", " return instances()\n", "\n", "def test():\n", " a = Signal(intbv(min=0, max=9))\n", " b = Signal(intbv(min=-2*8, max=2*8+1))\n", " print(\"Size a =\", len(a), \"Signed:\", a.is_signed(), \", Size b =\", len(b), \"Signed:\", b.is_signed())\n", " for mode in [0, 1, 2, 3]:\n", " inst = calc(a, b, MODE = mode)\n", " f = inst.elab(targets.VHDL)\n", " \n", "test()" ] }, { "cell_type": "markdown", "id": "5869201b-f108-4fa7-9134-cd6e0b4e018b", "metadata": {}, "source": [ "## Simulation of VHDL transfer\n", "\n", "To verify our assumption on incorrect implementations, we run the simulation for all four modes:" ] }, { "cell_type": "code", "execution_count": 13, "id": "3276d8f4-6d4c-4aa8-984a-6217407dc594", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[7;34m =========== TESTING MODE 0 =========== \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance calc, rename to calc_4 \u001b[0m\n", " Writing 'calc_4' to file /tmp/myirl_testbench_sum_svql7no9/calc_4.vhdl \n", " Writing 'testbench_sum' to file /tmp/myirl_testbench_sum_svql7no9/testbench_sum.vhdl \n", " Creating library file module_defs.vhdl \n", "WORK DIR of instance [Instance testbench_sum I/F: [// ID: testbench_sum_0 ]] /tmp/myirl_testbench_sum_svql7no9/\n", "==== COSIM stdout ====\n", "0x38\n", "/tmp/myirl_testbench_sum_svql7no9/testbench_sum.vhdl:41:13:@1ns:(assertion failure): Failed in /tmp/ipykernel_97258/2637647312.py:testbench_sum():15\n", "/tmp/testbench_sum:error: assertion failed\n", "in process .testbench_sum(cyritehdl).stim\n", "/tmp/testbench_sum:error: simulation failed\n", "\n", "\u001b[7;31m TEST FAIL \u001b[0m\n", "\u001b[7;34m =========== TESTING MODE 1 =========== \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance testbench_sum, rename to testbench_sum_1 \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance calc, rename to calc_5 \u001b[0m\n", " Writing 'calc_5' to file /tmp/myirl_testbench_sum_h36n3mi6/calc_5.vhdl \n", " Writing 'testbench_sum_1' to file /tmp/myirl_testbench_sum_h36n3mi6/testbench_sum_1.vhdl \n", " Creating library file module_defs.vhdl \n", "WORK DIR of instance [Instance testbench_sum_1 I/F: [// ID: testbench_sum_0 ]] /tmp/myirl_testbench_sum_h36n3mi6/\n", "==== COSIM stdout ====\n", "0x08\n", "0x38\n", "0x10\n", "\n", " TEST PASS \n", "\u001b[7;34m =========== TESTING MODE 2 =========== \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance testbench_sum, rename to testbench_sum_2 \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance calc, rename to calc_6 \u001b[0m\n", " Writing 'calc_6' to file /tmp/myirl_testbench_sum_c0mnfqow/calc_6.vhdl \n", "Warning: Implicit truncation of SUB(ADD(SGN(R(a, 5)), SGN(R(a, 5))), C:8) result\n", " Writing 'testbench_sum_2' to file /tmp/myirl_testbench_sum_c0mnfqow/testbench_sum_2.vhdl \n", " Creating library file module_defs.vhdl \n", "WORK DIR of instance [Instance testbench_sum_2 I/F: [// ID: testbench_sum_0 ]] /tmp/myirl_testbench_sum_c0mnfqow/\n", "==== COSIM stdout ====\n", "0x08\n", "0x38\n", "0x10\n", "\n", " TEST PASS \n", "\u001b[7;34m =========== TESTING MODE 3 =========== \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance testbench_sum, rename to testbench_sum_3 \u001b[0m\n", "\u001b[32m Module testbench_sum: Existing instance calc, rename to calc_7 \u001b[0m\n", " Writing 'calc_7' to file /tmp/myirl_testbench_sum_15hp75ih/calc_7.vhdl \n", " Writing 'testbench_sum_3' to file /tmp/myirl_testbench_sum_15hp75ih/testbench_sum_3.vhdl \n", " Creating library file module_defs.vhdl \n", "WORK DIR of instance [Instance testbench_sum_3 I/F: [// ID: testbench_sum_0 ]] /tmp/myirl_testbench_sum_15hp75ih/\n", "==== COSIM stdout ====\n", "0x08\n", "0x38\n", "0x10\n", "\n", " TEST PASS \n" ] } ], "source": [ "ctx = DesignModule(\"test_addsub\", debug = True)\n", "\n", "@block\n", "def testbench_sum(mode):\n", " a = Signal(intbv(min=0, max=9))\n", " b = Signal(intbv(min=-2*8, max=2*8+1))\n", " uut = calc(a, b, mode)\n", " \n", " @instance\n", " def stim():\n", " for it in [ (8, 8), (0, -8), (12, 16)]:\n", " a.next = it[0]\n", " yield delay(1)\n", " print(b)\n", " assert b == it[1]\n", " yield delay(10)\n", "\n", " return instances()\n", "\n", "from myirl.test import ghdl\n", "\n", "def test_tb(mode):\n", " tb = testbench_sum(mode)\n", " f = tb.elab(targets.VHDL, elab_all = True)\n", " run_ghdl(f, tb, debug = True)\n", " return f\n", "\n", "\n", "for mode in range(4):\n", " ctx.log(\"=========== TESTING MODE %d ===========\" % mode, annotation = 'info')\n", "\n", " try:\n", " f = test_tb(mode)\n", " ctx.log(\"TEST PASS\")\n", " except (ghdl.RuntimeError, ghdl.AnalysisError):\n", " ctx.log(\"TEST FAIL\", annotation = 'warn')\n" ] }, { "cell_type": "markdown", "id": "20ec82cf-5c17-4724-ac64-06cfedbbbe66", "metadata": {}, "source": [ "### Result sizes and truncation\n", "\n", "The above calculation `a + a - 8` presents a few more cases specific to intbv().\n", "\n", "We define again `a`, because we include '8', we need four bits. This would allow representing 15, in synthesized or simulated hardware. Because we restricted `a` to maximum 8, we can, however, impose some boundaries on the result." ] }, { "cell_type": "code", "execution_count": 14, "id": "f0e753ba-08e2-408e-8d1f-693f263b4c73", "metadata": {}, "outputs": [], "source": [ "a = Signal(intbv(min=0, max=9), name = 'a')\n", "b = Signal(intbv(0, min = -8, max = 9), name = 'b')\n", "\n", "op0 = (a + a - 8).signed()\n", "op1 = a + (a - 8).signed()" ] }, { "cell_type": "markdown", "id": "ea40667a-97f7-4cc6-b6c9-48662dec318f", "metadata": {}, "source": [ "Verify size of a:" ] }, { "cell_type": "code", "execution_count": 15, "id": "fe88d7cd-4815-44b7-860a-70e14c55d98c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a.size()" ] }, { "cell_type": "markdown", "id": "8f057193-d86c-4d91-af80-740466aead62", "metadata": {}, "source": [ "By default, the operation sizes match the 'hard' bit properties and don't respect the intbv() restrictions:" ] }, { "cell_type": "code", "execution_count": 16, "id": "0d8077c9-8624-46b3-8d14-58759f8210df", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(6, 6, 6)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "op0.size(), op1.size(), len(intbv(0, min=-8, max=22))" ] }, { "cell_type": "markdown", "id": "d8fdf5c8-b57b-4145-807d-e97fdd6e0cce", "metadata": {}, "source": [ "However, for a maximum result of `8`, five bits would suffice:" ] }, { "cell_type": "code", "execution_count": 17, "id": "46efdb25-aff5-425c-95d8-cfe66b840f2a", "metadata": {}, "outputs": [], "source": [ "assert len(intbv(0, min=-8, max=8+1)) == 5" ] }, { "cell_type": "markdown", "id": "8f173e4b-7378-4af0-9ea7-7b4efe447f30", "metadata": {}, "source": [ "We note: the **IRL** kernel does not account for the effectively needed bits. It will however truncate with a warning, in the special case of an assignment where the source is an explicit addition.\n", "\n", "If it's not, an exception will throw. See below:" ] }, { "cell_type": "code", "execution_count": 18, "id": "384d27d6-9086-4044-a0a3-341edc8624b0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "*** EMIT b <= SGN(SUB(ADD(a, a), C:8))\n", "Failed Expression SGN(SUB(ADD(a, a), C:8))/ exceeds bit size of signal (6 > 5)\n", "*** EMIT b <= ADD(a, SGN(SUB(a, C:8)))\n", "Warning: Implicit truncation of ADD(a, SGN(SUB(a, C:8))) result\n", "\u001b[94mb <= signed(resize((signed(resize(a, 6)) + signed((resize(a, 5) - 8))), 5));\n", "\u001b[0m" ] } ], "source": [ "d = DummyVHDLModule()\n", "\n", "gens = [\n", " b.set(op0), b.set(op1)\n", " ]\n", " \n", "for g in gens:\n", " print(\"*** EMIT \", g)\n", " try:\n", " g.emit(d)\n", " except base.SizeMismatch as e:\n", " print(\"Failed\", e)" ] }, { "cell_type": "markdown", "id": "3f9f653e-8371-4c99-824f-851ae1952514", "metadata": {}, "source": [ "## Negation\n", "\n", "Negation of a value will increment the number of bits by one. This obviously turns an unsigned signal into signed, and applies to the special case where a signed signal wire contains the most negative possible value." ] }, { "cell_type": "code", "execution_count": 19, "id": "106ae65d-adb8-4765-8c64-a4a383a9edac", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, False)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sa.is_signed(), a.is_signed()" ] }, { "cell_type": "code", "execution_count": 20, "id": "788e09d9-d906-4ec2-aca1-71af02a63485", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(6, 7, 4, 5)" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sb = -sa\n", "b = - a\n", "sa.size(), sb.size(), a.size(), b.size()" ] }, { "cell_type": "markdown", "id": "423e089c-0b4b-4433-b496-e8fc1a0ad970", "metadata": {}, "source": [ "### Further issues\n", "\n", "* Explicit 'inline' result truncating may be necessary to handle bit size overflows. These don't always transfer correctly to the target HDL yet and require explicit split ups of the signal operations and assignments.\n", "* As elaborated above, no special intbv handling for specified boundaries is in place. The kernel will always use the minimum bit width necessary to express an operation\n", "* Sign extension: Bug #322 in the MyHDL main tree accounts for a number of arithmetic scenarios going wrong during VHDL transfer." ] }, { "cell_type": "markdown", "id": "59da70ad-b347-40f2-acd2-a4d1b2797294", "metadata": {}, "source": [ "# Bit shifting\n", "\n", "Unlike the above [intbv behaviour](#intbv-behaviour), a shift operation on an intbv returns again an intbv, however it is unlimited:" ] }, { "cell_type": "code", "execution_count": 21, "id": "2024a4a7-8849-470c-9a97-d79217195ac7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['-0x1000000', '0xdead', '0x40000']" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from myhdl import *\n", "\n", "vec = intbv\n", "\n", "a = Signal(vec(0xdeadbeef)[32:])\n", "c = Signal(vec(1)[32:])\n", "\n", "\n", "b = Signal(intbv(0x80000000)[32:]).signed()\n", "\n", "u = b >> 7\n", "y = a >> 16\n", "z = c << 18\n", "\n", "[ hex(x) for x in [u, y, z] ]" ] }, { "cell_type": "code", "execution_count": 22, "id": "95788675-8e9d-4227-8b12-a81ab750c3f4", "metadata": {}, "outputs": [], "source": [ "a = vec(0x8000)[16:].signed()\n", "z = a >> 5\n", "# assert len(z) == 11" ] }, { "cell_type": "code", "execution_count": 23, "id": "81683766-ff35-439e-8d99-73fd95de5108", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('0xdead', 0, '-0x400', 0)" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hex(y), len(y), hex(z), len(z)" ] }, { "cell_type": "code", "execution_count": 24, "id": "c21e474a-76af-4517-a585-9a1a91d3aa76", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/cyrite/.local/lib/python3.10/site-packages/myhdl/conversion/_toVHDL.py:513: ToVHDLWarning: Signal is not driven: a\n", " warnings.warn(\"%s: %s\" % (_error.UndrivenSignal, s._name),\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@block\n", "def tb():\n", " q = Signal(modbv()[16:])\n", " a = Signal(intbv(0xbeef)[12:])\n", " \n", " @instance\n", " def stim():\n", " yield delay(5)\n", " \n", " q.next = a << 4\n", " yield delay(16)\n", " print(q)\n", " assert q == 0xeef0\n", " \n", " return instances()\n", "# print(tb.unparse())\n", "uut = tb()\n", "uut.convert('VHDL')\n", "\n" ] }, { "cell_type": "markdown", "id": "b7340c37-6ba0-47c7-b5a8-7e8de49ca0de", "metadata": {}, "source": [ "When assigning to a signal, the built-in `intbv` limit check will kick in and handle overflows.\n", "However, this can lead to errors in the conversion." ] }, { "cell_type": "code", "execution_count": 25, "id": "d02fc151-a03c-43f9-a6ec-c075ddbc9990", "metadata": {}, "outputs": [], "source": [ "# !cat tb.vhd -n" ] }, { "cell_type": "markdown", "id": "8918f77f-a2db-4d55-b2b4-f8f855491621", "metadata": {}, "source": [ "Due to discrepancies with the myIRL kernel, it was decided to not support shift operations with the base Signal class for the time being.\n", "\n", "Therefore, bit shifting is currently covered using various options:\n", "\n", "* Inline blackbox from a library: instancing an explicit plug-in hardware barrel shifter unit for each dynamic bit shift occurence (shift value non-constant)\n", "* Translation to shift operations in the target language\n", "* Using the dynamic-sized *flexbv* wire type from HLS package\n", "\n", "For static shift operations, the MyHDL emulation currently uses the `SSignal` class from the `shift` library.\n", "\n", "\n", "## Example\n", "\n", "The myHDL emulation default behaviour was changed such that dynamic shifts track the maximum size results.\n", "Hence it is necessary to explicitely truncate (i.e. slice) the result of a left shift expression as below, in order to avoid a SizeMismatch exception:" ] }, { "cell_type": "code", "execution_count": 26, "id": "660c2223-add6-46f7-865c-b4e96a435755", "metadata": {}, "outputs": [], "source": [ "from myirl.emulation.myhdl import *\n", "\n", "@block\n", "def unit_shift(clk : ClkSignal, a : Signal, sh : Signal, q : Signal.Output,\n", " SHIFT_RIGHT = False):\n", " @always(clk.posedge)\n", " def worker():\n", " if SHIFT_RIGHT:\n", " q.next = a >> sh\n", " else:\n", " q.next = (a << sh)[len(q):]\n", "\n", " return instances()" ] }, { "cell_type": "code", "execution_count": 27, "id": "6095dc7e-1faa-4921-837b-6efc57838f87", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==============================\n", "Unparsing unit unit_shift\n", "==============================\n", "\n", "\n", "@block\n", "def unit_shift(clk: ClkSignal, a: Signal, sh: Signal, q: Signal.Output, SHIFT_RIGHT=False):\n", "\n", " @always_(clk.posedge)\n", " def worker(_context):\n", " (yield [_context.If(SHIFT_RIGHT).Then(q.set((a >> sh))).Else(q.set((a << sh)[len(q):]))])\n", " return instances()\n", "\n" ] } ], "source": [ "print(unit_shift.unparse())" ] }, { "cell_type": "code", "execution_count": 28, "id": "76d2f6ad-9088-43c2-a0ab-011230ab8812", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Using default for SHIFT_RIGHT: False\n", " SHIFT_RIGHT: use default False \n", "\u001b[32m DEBUG Inline builtin instance [block_inline 'bshifter_inline/bshifter_inline'] \u001b[0m\n", "\u001b[7;35m Declare obj 'bshifter_inline' in context '(EmulationModule 'unit_shift')'() \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_1 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_2 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_3 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_4 \u001b[0m\n", "\u001b[32m DEBUG Inline builtin instance [block_inline 'bshifter_inline/bshifter_inline'] \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bshifter_inline, rename to bshifter_inline_1 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance barrel_shifter_async, rename to barrel_shifter_async_1 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_5 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_6 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_7 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_8 \u001b[0m\n", "\u001b[32m Module unit_shift: Existing instance bs_stage, rename to bs_stage_9 \u001b[0m\n", " Writing 'unit_shift' to file /tmp/myirl_unit_shift_qeef2l_a/unit_shift.vhdl \n", "\u001b[32m DEBUG Inline builtin instance [block_inline 'bshifter_inline/bshifter_inline'] \u001b[0m\n", "\u001b[32m DEBUG Inline builtin instance [block_inline 'bshifter_inline/bshifter_inline'] \u001b[0m\n", " Writing 'unit_shift' to file /tmp/myirl_unit_shift_qeef2l_a/unit_shift.v \n", "DEBUG NAME q \n", "DEBUG Fallback wire for s_19c7\n", "DEBUG Fallback wire for s_f773\n" ] } ], "source": [ "a, q = [ Signal(intbv()[32:]) for _ in range(2) ]\n", "sh = Signal(intbv()[5:])\n", "clk = ClkSignal()\n", "\n", "uut = unit_shift(clk, a, sh, q)\n", "\n", "vhdl = uut.elab(targets.VHDL)\n", "verilog = uut.elab(targets.Verilog)" ] }, { "cell_type": "code", "execution_count": 29, "id": "bfe55cf5-b3c2-4748-a4ae-5672d64e36bc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-- File generated from source:\n", "-- /tmp/ipykernel_97258/554209513.py\n", "-- (c) 2016-2022 section5.ch\n", "-- Modifications may be lost, edit the source file instead.\n", "\n", "library IEEE;\n", "use IEEE.std_logic_1164.all;\n", "use IEEE.numeric_std.all;\n", "\n", "library work;\n", "\n", "use work.txt_util.all;\n", "use work.myirl_conversion.all;\n", "\n", "entity unit_shift is\n", " port (\n", " clk : in std_ulogic;\n", " a : in unsigned(31 downto 0);\n", " sh : in unsigned(4 downto 0);\n", " q : out unsigned(31 downto 0)\n", " );\n", "end entity unit_shift;\n", "\n", "architecture cyriteHDL of unit_shift is\n", " -- Local type declarations\n", " -- Signal declarations\n", " signal s_ce16 : unsigned(31 downto 0);\n", " signal s_9148 : unsigned(31 downto 0);\n", "begin\n", " \n", "worker:\n", " process(clk)\n", " begin\n", " if rising_edge(clk) then\n", " if FALSE then\n", " q <= s_ce16;\n", " else\n", " q <= s_9148(32-1 downto 0);\n", " end if;\n", " end if;\n", " end process;\n", " \n", " -- Instance bshifter_inline\n", " inst_bshifter_inline_1: entity work.bshifter_inline\n", " port map (\n", " d => a,\n", " sh => sh,\n", " result => s_ce16\n", " );\n", " \n", " -- Instance bshifter_inline_1\n", " inst_bshifter_inline_3: entity work.bshifter_inline_1\n", " port map (\n", " d => a,\n", " sh => sh,\n", " result => s_9148\n", " );\n", "end architecture cyriteHDL;\n", "\n" ] } ], "source": [ "!cat {vhdl[0]}" ] }, { "cell_type": "code", "execution_count": 29, "id": "aeac9a88-5021-42fc-94ba-5db9b3d5638b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "// File generated from source:\r\n", "// /tmp/ipykernel_599/554209513.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 cyriteHDL\r\n", "\r\n", "module unit_shift\r\n", " (\r\n", " input wire /* std_ulogic */ clk,\r\n", " input wire [31:0] a,\r\n", " input wire [4:0] sh,\r\n", " output reg [31:0] q\r\n", " );\r\n", " // Local type declarations\r\n", " // Signal declarations\r\n", " wire [31:0] s_ce16;\r\n", " wire [31:0] s_9148;\r\n", " wire [31:0] s_8a50;\r\n", " wire [31:0] s_9a6a;\r\n", " \r\n", " always @ (posedge clk ) begin : WORKER\r\n", " if (1'b0) begin\r\n", " q <= s_8a50;\r\n", " end else begin\r\n", " q <= s_9a6a[32-1:0];\r\n", " end\r\n", " end\r\n", " \r\n", " // Instance bshifter_inline\r\n", " bshifter_inline\r\n", " bshifter_inline_1\r\n", " (\r\n", " .d(a),\r\n", " .sh(sh),\r\n", " .result(s_ce16)\r\n", " );\r\n", " \r\n", " // Instance bshifter_inline_1\r\n", " bshifter_inline_1\r\n", " bshifter_inline_3\r\n", " (\r\n", " .d(a),\r\n", " .sh(sh),\r\n", " .result(s_9148)\r\n", " );\r\n", " \r\n", " // Instance bshifter_inline\r\n", " bshifter_inline\r\n", " bshifter_inline_5\r\n", " (\r\n", " .d(a),\r\n", " .sh(sh),\r\n", " .result(s_8a50)\r\n", " );\r\n", " \r\n", " // Instance bshifter_inline_1\r\n", " bshifter_inline_1\r\n", " bshifter_inline_7\r\n", " (\r\n", " .d(a),\r\n", " .sh(sh),\r\n", " .result(s_9a6a)\r\n", " );\r\n", "endmodule // unit_shift\r\n" ] } ], "source": [ "!cat {verilog[0]}" ] }, { "cell_type": "markdown", "id": "073f0267-e59c-45ba-a535-c84a252dace0", "metadata": {}, "source": [ "See also arithmetic tests (myirl/test/test_arith.py) for reference." ] } ], "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.10.0" } }, "nbformat": 4, "nbformat_minor": 5 }