{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Barrel Shifter\n", "\n", "A Barrel shifter performs the operation `v << s` (or `v >> s` with flipped bits) where `s` is not constant.\n", "An elegant implementation is the approach via cascaded multiplexer elements." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This `cshift` primitive is later instanced procedurally, iterating through the variable `i` " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Shifter element in MyHDL" ] }, { "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", "@block\n", "def cshift(q : Signal.Output, a : Signal, b : Signal, sbit : Signal, \n", " msb : Signal,\n", " # These can take a signal or a fixed bool:\n", " asr, rotate,\n", " WRAP : bool):\n", " carry = Signal(bool()) # Carry bit\n", " u, v = [ Signal(bool()) for i in range(2) ]\n", " \n", " @always_comb\n", " def assign_carry():\n", " if asr: # arithmetic shift right\n", " carry.next = sbit & msb\n", " else:\n", " carry.next = 0\n", "\n", " @always_comb\n", " def assign():\n", " u.next = a & ~sbit\n", " \n", " if rotate == False: \n", " if WRAP:\n", " v.next = carry\n", " else:\n", " v.next = b & sbit\n", " else:\n", " v.next = b & sbit\n", " \n", " @always_comb\n", " def assign_q(): \n", " q.next = u | v\n", " \n", " return instances()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Shifter stage" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "@block\n", "def shifter_stage(\n", " shifter,\n", " w_in : Signal,\n", " w_out : Signal.Output,\n", " msb : Signal,\n", " nmux : int, sbit : Signal, DATA_WIDTH : int, W_POWER : int, asr : bool, rotate : bool\n", "):\n", " inst = []\n", "\n", " # Create signal array\n", " w = [ Signal(bool()) for i in range(DATA_WIDTH) ]\n", " wo = concat(*reversed(w))\n", " wi = [ w_in[i] for i in range(DATA_WIDTH) ]\n", "\n", " MUX_W = DATA_WIDTH // nmux\n", "\n", " for imux in range(nmux):\n", " tmp = imux * MUX_W\n", " # print(imux)\n", " for i in range(tmp, tmp + MUX_W):\n", " j = i + MUX_W//2\n", " m = j % DATA_WIDTH\n", " inst.append(shifter(w[m], wi[m], wi[i], sbit, msb, asr, rotate,\n", " j >= DATA_WIDTH ))\n", "\n", " @always_comb\n", " def assign():\n", " w_out.next = wo\n", "\n", " return instances()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The barrel shifter implementation" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "@block\n", "def barrel_shifter(shifter, clk : ClkSignal, ce : Signal, val : Signal, s : Signal, result : Signal.Output, \\\n", " rotate = False, W_POWER = 5 ):\n", " \n", " DATA_WIDTH = 2 ** W_POWER\n", " print(\"DATA WIDTH\", DATA_WIDTH, \"ROTATE\", rotate)\n", " \n", " worker = [ val ]\n", " worker = worker + [ Signal(intbv()[DATA_WIDTH:]) for i in range(W_POWER) ]\n", " msb = val[DATA_WIDTH-1]\n", "\n", " sbit = [ s[i] for i in range(len(s))]\n", " \n", " shifter_stages = []\n", " for stage in range(W_POWER):\n", " K = W_POWER - stage - 1\n", " print(\"Stage %d\" % stage)\n", " shifter_stages.append( \\\n", " shifter_stage(shifter, worker[stage], worker[stage + 1], msb, 2 ** stage, sbit[K], \\\n", " DATA_WIDTH, W_POWER, False, rotate) \\\n", " )\n", " \n", " @always(clk.posedge)\n", " def assign():\n", " if ce == True:\n", " result.next = worker[W_POWER]\n", " \n", " return instances()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Translate and test\n", "\n", "The translation into the target RTL or HDL takes place via a test bench factory function. This allows us to switch between different simulator back end methods. Here, we can choose between `GHDL` and `CXXRTL`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "from simulation import *\n", "\n", "from myirl.simulation import print_\n", "from myirl.emulation.factory_class import factory\n", "from myirl.targets import pyosys\n", "\n", "class example_design(factory.Module):\n", " def __init__(self, name, simclass, *args, **kwargs):\n", " super().__init__(name, simclass, *args, **kwargs)\n", " self.W_POWER = 4\n", " self.debug = False\n", "\n", " # Top level signal set\n", " self.clk = self.ClkSignal(name=\"clk\")\n", " self.ce = self.Signal(bool())\n", " self.val, self.result = [ self.Signal(intbv(0xaa00)[2 ** self.W_POWER:]) for i in range(2) ]\n", " self.result.rename(\"result\")\n", " self.s = self.Signal(intbv()[self.W_POWER:])\n", " \n", " def build(self):\n", " return barrel_shifter(cshift,\n", " clk = self.clk,\n", " ce = self.ce,\n", " val = self.val,\n", " s = self.s,\n", " result = self.result,\n", " rotate = False,\n", " W_POWER = self.W_POWER\n", " )\n", " \n", " def emit_rtlil(self, fileprefix):\n", " tgt = pyosys.RTLIL(\"barrel_shifter\")\n", " inst = self.build()\n", " d = inst.elab(tgt)\n", " d[0].write_rtlil(fileprefix)\n", "\n", " @factory.testbench('ns')\n", " def tb_rtl(self):\n", " \n", " inst = self.build()\n", "\n", " clk = self.clk\n", " ce = self.ce\n", " val = self.val\n", " result = self.result\n", " s = self.s\n", " \n", " @self.always(delay(2))\n", " def clkgen():\n", " clk.next = ~clk\n", "\n", " TEST_VALUES = [\n", " (0xdead, 8, 0xad00),\n", " (0x8f01, 15, 0x8000),\n", " ]\n", "\n", " @self.sequence\n", " def stim():\n", " for item in TEST_VALUES:\n", " ce.next = False\n", " s.next = item[1]\n", " val.next = item[0]\n", " yield(clk.posedge)\n", " ce.next = True\n", " yield(clk.posedge)\n", " yield(clk.posedge)\n", "\n", " print(result, type(result))\n", " assert result == item[2]\n", "\n", " raise StopSimulation\n", "\n", " return instances() " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "DATA WIDTH 16 ROTATE False\n", "Stage 0\n", "\u001b[32m Module rtlil_bs: Existing instance cshift, rename to cshift_1 \u001b[0m\n", "Stage 1\n", "\u001b[32m Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_1 \u001b[0m\n", "Stage 2\n", "\u001b[32m Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_2 \u001b[0m\n", "Stage 3\n", "\u001b[32m Module rtlil_bs: Existing instance shifter_stage, rename to shifter_stage_3 \u001b[0m\n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_8_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_3` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_4_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_2` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_2_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_1` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component cshift_s1_s1_s1_s1_s1_0_0_1 \n", "\u001b[32m Adding module with name `cshift_1` \u001b[0m\n", " Elaborating component cshift_s1_s1_s1_s1_s1_0_0_0 \n", "\u001b[32m Adding module with name `cshift` \u001b[0m\n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_1_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", "\u001b[32m Adding module with name `barrel_shifter` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", "\u001b[7;34m FINALIZE implementation `barrel_shifter` of `barrel_shifter` \u001b[0m\n" ] } ], "source": [ "from yosys.simulator import CXXRTL\n", "\n", "d = example_design(\"rtlil_bs\", CXXRTL)\n", "d.emit_rtlil(\"test\")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m Module testbench: Existing instance barrel_shifter, rename to barrel_shifter_1 \u001b[0m\n", "DATA WIDTH 16 ROTATE False\n", "Stage 0\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_4 \u001b[0m\n", "\u001b[32m Module testbench: Existing instance cshift, rename to cshift_2 \u001b[0m\n", "\u001b[32m Module testbench: Existing instance cshift, rename to cshift_3 \u001b[0m\n", "Stage 1\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_5 \u001b[0m\n", "Stage 2\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_6 \u001b[0m\n", "Stage 3\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_7 \u001b[0m\n", "DEBUG: Skip non-simulation type \n", "DEBUG: Skip non-simulation type \n", "============================================================================\n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_8_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_7` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_4_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_6` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_2_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_5` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", " Elaborating component cshift_s1_s1_s1_s1_s1_0_0_1 \n", "\u001b[32m Adding module with name `cshift_3` \u001b[0m\n", " Elaborating component cshift_s1_s1_s1_s1_s1_0_0_0 \n", "\u001b[32m Adding module with name `cshift_2` \u001b[0m\n", " Elaborating component shifter_stage__wrapped_wrapper_s16_s16_s1_1_s1_16_4_0_0 \n", "\u001b[32m Adding module with name `shifter_stage_4` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", "\u001b[32m Adding module with name `barrel_shifter_1` \u001b[0m\n", " DEBUG: SKIP NON-SIGNAL ARGUMENT `shifter` : \n", "\u001b[7;34m FINALIZE implementation `barrel_shifter_1` of `barrel_shifter` \u001b[0m\n", "Compiling /tmp/barrel_shifter_1_2922.pyx because it changed.\n", "[1/1] Cythonizing /tmp/barrel_shifter_1_2922.pyx\n", "running build_ext\n", "building 'barrel_shifter_1_2922' 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=barrel_shifter_1_2922 -I../../myirl/../ -I/tmp/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/barrel_shifter_1_2922.cpp -o build/temp.linux-x86_64-3.9/tmp/barrel_shifter_1_2922.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=barrel_shifter_1_2922 -I../../myirl/../ -I/tmp/ -I/usr/share/yosys/include -I/usr/include/python3.9 -c /tmp/barrel_shifter_1_2922_rtl.cpp -o build/temp.linux-x86_64-3.9/tmp/barrel_shifter_1_2922_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/barrel_shifter_1_2922.o build/temp.linux-x86_64-3.9/tmp/barrel_shifter_1_2922_rtl.o -o build/lib.linux-x86_64-3.9/barrel_shifter_1_2922.cpython-39-x86_64-linux-gnu.so\n", "copying build/lib.linux-x86_64-3.9/barrel_shifter_1_2922.cpython-39-x86_64-linux-gnu.so -> \n", "Open for writing: tb_rtl.vcd\n", "\u001b[7;35m CXXRTL context: SKIP INTERFACE ITEM `shifter` \u001b[0m\n", "\u001b[7;35m CXXRTL context: SKIP INTERFACE ITEM `rotate` \u001b[0m\n", "\u001b[7;35m CXXRTL context: SKIP INTERFACE ITEM `W_POWER` \u001b[0m\n", " : 0xad00 \n", " : 0x8000 \n", "STOP SIMULATION @22 \n", "\u001b[7;35m Declare obj 'tb_rtl' in context '(example_design 'testbench')' \u001b[0m\n", "\u001b[32m Module testbench: Existing instance barrel_shifter, rename to barrel_shifter_2 \u001b[0m\n", "DATA WIDTH 16 ROTATE False\n", "Stage 0\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_8 \u001b[0m\n", "\u001b[32m Module testbench: Existing instance cshift, rename to cshift_4 \u001b[0m\n", "\u001b[32m Module testbench: Existing instance cshift, rename to cshift_5 \u001b[0m\n", "Stage 1\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_9 \u001b[0m\n", "Stage 2\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_10 \u001b[0m\n", "Stage 3\n", "\u001b[32m Module testbench: Existing instance shifter_stage, rename to shifter_stage_11 \u001b[0m\n", "============================================================================\n", " Writing 'shifter_stage_11' to file /tmp/shifter_stage_11.vhdl \n", " Writing 'shifter_stage_10' to file /tmp/shifter_stage_10.vhdl \n", " Writing 'shifter_stage_9' to file /tmp/shifter_stage_9.vhdl \n", " Writing 'cshift_5' to file /tmp/cshift_5.vhdl \n", " Writing 'cshift_4' to file /tmp/cshift_4.vhdl \n", " Writing 'shifter_stage_8' to file /tmp/shifter_stage_8.vhdl \n", " Writing 'barrel_shifter_2' to file /tmp/barrel_shifter_2.vhdl \n", " Writing 'tb_rtl' to file /tmp/tb_rtl.vhdl \n", " Creating library file /tmp/module_defs.vhdl \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/cshift_4.vhdl\n", "analyze /tmp/cshift_5.vhdl\n", "analyze /tmp/shifter_stage_8.vhdl\n", "analyze /tmp/shifter_stage_9.vhdl\n", "analyze /tmp/shifter_stage_10.vhdl\n", "analyze /tmp/shifter_stage_11.vhdl\n", "analyze /tmp/barrel_shifter_2.vhdl\n", "analyze /tmp/tb_rtl.vhdl\n", "elaborate tb_rtl\n", "\n", "==== COSIM stdout ====\n", "0xAD00 \n", "0x8000 \n", "simulation stopped @22ns\n", "\n" ] } ], "source": [ "from myirl.test.ghdl import GHDL\n", "\n", "# XXX Note: we must carry out the simulation backend sequence in this order. Otherwise,\n", "# a yet undetermined sticky effect will report a combinatorial loop error.\n", "for sim in CXXRTL, GHDL:\n", " d = example_design(\"testbench\", sim)\n", " tb = d.tb_rtl()\n", " print(76 * '=')\n", " tb.run(60)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Further Optimization\n", "\n", "A bit more optimization can be done by moving the code into a cythonizeable library. See\n", "[Cythonized barrel shifter](example_cython_barrelshifter.ipynb)\n", "\n", "This primitive approach however is not very compact and this design should not be mass-instanced using CXXRTL due to issues with combinatorial logic.\n", "\n", "See [Compact barrel shifter implementation](bshift_compact.ipynb) for an improved version." ] } ], "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 }