{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Memory\n", "\n", "Describing memory and getting it synthesized correctly is cumbersome.\n", "Therefore, this will only demonstrate a simple approach to generate memory.\n", "Using [Signal arrays](arrays.ipynb), a simple `r1w1` simplex dual port memory can be inferred to HDL.\n", "This modification enables shared variable output for VHDL to support true dual port behaviour.\n", "The general strategy with the MyIRL synthesis is to use a blackbox element (rely on the library)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from myhdl import intbv\n", "from myirl import *\n", "import random" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Dual port memories\n", "\n", "We can not use a `SigArray` for the RAM, because a true dual port RAM would provoke unresolved multiple drivers.\n", "Therefore we hack the `myirl.lists.SigArray` class by deriving and choosing a different element in return of the `__getitem__` member which uses a shared variable.\n", "\n", "**Note** Not working in VHDL-2008" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from myirl import lists\n", "from myirl.kernel import extensions, utils\n", "\n", "from myirl import targets\n", "\n", "class ArrayElem(lists.SigIndexed):\n", " decl_type_vhdl = \"shared variable\"\n", "\n", " def set(self, val):\n", " w = self.size()\n", " return CellAssign(self.seq, self.index, val, w)\n", "\n", "class CellAssign: # extensions.ElemAssign\n", " def __init__(self, parent, portion, val, width):\n", " self._vref = parent\n", " self.portion = portion\n", " self.value = val\n", " self.width = width\n", " \n", " def emit(self, context):\n", " tgt = context.target\n", " n, v = self._vref.identifier, self.value\n", " p = self.portion\n", " sz = self.width\n", " if tgt.lang == 'VHDL':\n", " context.output(\"%s(to_integer(%s)) := %s;\\n\" % (n, p, base.convert(v, tgt, sz))) \n", " elif tgt.lang == 'Verilog':\n", " context.output(\"%s[%s] <= %s;\\n\" % (n, p, base.convert(v, tgt, sz))) \n", " else:\n", " raise TypeError(\"Unsupported target %s\" % type(tgt))\n", "\n", "\n", " def get_sources(self, srcs):\n", " if isinstance(self.value, base.Sig):\n", " self.value.get_sources(srcs)\n", " \n", " def get_drivers(self, drvs):\n", " pass\n", " \n", "\n", "class RamBuffer(lists.SigArray): \n", " def __getitem__(self, item):\n", " if isinstance(item, (Sig, int)):\n", " # We can not just return self.val[item], as the iterator\n", " # has not initialized yet.\n", " return ArrayElem(self, item)\n", " else:\n", " raise TypeError(\"Multi item slicing of iterator not supported\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then define a RAM port (legacy class constructs will suffice) and the actual RAM implementation:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "class RamPort:\n", " _inputs = ['we', 'wa', 'ra', 'wd']\n", " _outputs = ['rd']\n", " _other = ['clk']\n", " def __init__(self, AWIDTH, DWIDTH):\n", " self.clk = ClkSignal()\n", " self.we = Signal(bool())\n", " self.ra, self.wa = [ Signal(intbv()[AWIDTH:]) for i in range(2) ]\n", " self.rd, self.wd = [ Signal(intbv()[DWIDTH:]) for i in range(2) ] \n", " \n", "@block\n", "def tdp_ram(pa, pb, INITDATA):\n", " inst = []\n", " \n", " def gen_logic(p, i):\n", " \"Generate port mechanics inline\"\n", " \n", " rd = p.rd.clone(\"rd%d\" % i)\n", " \n", " @genprocess(p.clk, EDGE=p.clk.POS)\n", " def proc():\n", " yield [\n", " proc.If(p.we == True).Then(\n", " buf[p.wa].set(p.wd)\n", " ),\n", " rd.set(buf[p.ra])\n", " ]\n", " proc.rename(\"proc%d\" % i)\n", " wirings = [\n", " p.rd.wireup(rd)\n", " ]\n", " return proc, wirings\n", "\n", " buf = RamBuffer(INITDATA)\n", " \n", " for i, p in enumerate([pa, pb]):\n", " inst += (gen_logic(p, i))\n", " \n", " return instances()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Basic checks\n", "\n", "Verify we can evaluate the content:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "intbv(138)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = RamBuffer([intbv(i + 0x80)[8:] for i in range(200) ])\n", "b = Signal(intbv(10)[5:])\n", "a[b].evaluate()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "intbv(128)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a[0].wire" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "from myirl import targets\n", "from myirl.test.common_test import run_ghdl, run_icarus\n", "\n", "random.seed(0)\n", "\n", "def test_vhdl():\n", " RAM_CONTENT = [ intbv(random.randint(0, 2 ** 9))[8:] for i in range(2 ** 9) ]\n", " pa, pb = [ RamPort(AWIDTH=9, DWIDTH=8) for i in range(2) ]\n", " inst = tdp_ram(pa, pb, RAM_CONTENT)\n", " \n", " f = inst.elab(targets.VHDL)\n", " run_ghdl(f, inst, std = \"93\", debug = True) # Note we run with std '93'\n", " return f" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "Adding INITDATA to port dict (fallback for type )\n", " Writing 'tdp_ram' to file /tmp/myirl_tdp_ram__rhdj3y3/tdp_ram.vhdl \n" ] } ], "source": [ "f = test_vhdl()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "! grep -A 20 MyIRL {f[0]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Verilog variant\n", "\n", "The same design translated to Verilog, run through the icarus simulator." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def test_verilog():\n", " RAM_CONTENT = [ intbv(random.randint(0, 2 ** 9))[8:] for i in range(2 ** 9) ]\n", " pa, pb = [ RamPort(AWIDTH=9, DWIDTH=8) for i in range(2) ]\n", " inst = tdp_ram(pa, pb, RAM_CONTENT)\n", " \n", " f = inst.elab(targets.Verilog)\n", " run_icarus(f, inst, debug = True)\n", " return f" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "FALLBACK: UNHANDLED ROOT CLASS , create new context\n", "Adding INITDATA to port dict (fallback for type )\n", "\u001b[32m Module tdp_ram: Existing instance tdp_ram, rename to tdp_ram_1 \u001b[0m\n", " Writing 'tdp_ram_1' to file /tmp/myirl_tdp_ram_83l01cbr/tdp_ram_1.v \n", "DEBUG SKIP INST [pa_rd <= s_8a50] \n", "DEBUG SKIP INST [pb_rd <= s_9a6a] \n" ] } ], "source": [ "f = test_verilog()" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "// File generated from source:\n", "// /tmp/ipykernel_7487/3504810182.py\n", "// (c) 2016-2022 section5.ch\n", "// Modifications may be lost, edit the source file instead.\n", "\n", "`timescale 1 ns / 1 ps\n", "`include \"aux.v\"\n", "// Architecture myIRL\n", "\n", "module tdp_ram_1\n", " (\n", " input wire pa_clk,\n", " input wire pa_we,\n", " input wire [8:0] pa_ra,\n", " input wire [8:0] pa_wa,\n", " output wire [7:0] pa_rd,\n", " input wire [7:0] pa_wd,\n", " input wire pb_clk,\n", " input wire pb_we,\n", " input wire [8:0] pb_ra,\n", " input wire [8:0] pb_wa,\n", " output wire [7:0] pb_rd,\n", " input wire [7:0] pb_wd\n", " );\n", " // Local type declarations\n", " // Signal declarations\n", " reg [7:0] s_8a50;\n", " // Dummy array definition v_8c25\n", " reg [7:0] v_8c25[511:0];\n", " reg [7:0] s_9a6a;\n", " \n", " always @ (posedge pa_clk ) begin : PROC0\n", " if ((pa_we == 1'b1)) begin\n", " v_8c25[pa_wa] <= pa_wd;\n", " end\n", " s_8a50 <= v_8c25[pa_ra];\n", " end\n", " assign pa_rd = s_8a50;\n", " \n", " always @ (posedge pb_clk ) begin : PROC1\n", " if ((pb_we == 1'b1)) begin\n", " v_8c25[pb_wa] <= pb_wd;\n", " end\n", " s_9a6a <= v_8c25[pb_ra];\n", " end\n", " assign pb_rd = s_9a6a;\n", "endmodule // tdp_ram_1\n" ] } ], "source": [ "!cat {f[0]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# TDPRAM verification\n", "\n", "TDPRAM verification can be done on various levels:\n", "\n", "* Functional verification without collision detection via CXXRTL\n", "* HDL based model simulation against a Verilog or VHDL primitive vendor model with collision detection\n", "* Post-map simulation after mapping using a synthesis step\n", "\n", "The fine detail in the above HDL models: depending on the RAM instance (as VHDL shared variable or a Verilog signal), as well as the specific assignment mode (blocking or non-blocking) in the Verilog inference do have a **crucial impact** on the read/write priorities of such RAMs.\n", "\n", "Most TDP RAM models allow the following priority modes.\n", "* TRANSPARENCY: When a value is written on a port, it is available on the data output on the next clock cycle\n", "* READ_BEFORE_WRITE: The previous value of the memory cell at the given address is available on the output on the next clock cycle\n", "\n", "For concurrent accesses from two ports, even more complexity is introduced. For example, a WRITE action on one port and a READ on the other from the same address can also be subject to the priority configuration, moreover, different clock domains are getting into the play.\n", "\n", "## Different data I/O widths\n", "\n", "Several combinations of input and output port widths are supported by TDPRAM architectures in general. If both data widths are in an integer ratio, only address translation and mapping is relevant for either bitwise slicing or concatenation. If not, alignment rules may apply.\n", "\n", "As these properties are very technology dependent, it turns out to be complex to generalize test routines.\n", "This is addressed by a generic `tdp_tester` base class, containing a few common test routines. By derivation, customized TDP RAM testers are created that run a customized test for a particular architecture.\n", "\n", "**Note** This may require a particular installation of the cyrite tdpram library" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from cyrite.library.ram.tdpram import TDPRamGenerator\n", "\n", "from myirl.test.ghdl import GHDL93\n", "from myirl.test.icarus import ICARUS\n", "\n", "class my_tester(TDPRamGenerator):\n", " def __init__(self, SIMULATOR = ICARUS, delta = 0.2,\n", " name = \"tb_priorities\"):\n", " SIZE = 320*64\n", "\n", " super().__init__(name, SIMULATOR, size = SIZE)\n", "\n", " # Enable strict transparency check\n", " self.STRICT_XTRANSPARENCY_CHECK = True\n", " # Use generic wrapper as Unit Under Test:\n", " self.uut = self.tdpram_wrapper\n", " self.MODE_RAW = 'NORMAL'\n", " self.MODE_WT = 'WRITETHROUGH'\n", " self.undefined = Undefined\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Priority tests\n", "\n", "The `tb_priorities` test uses two particularely detuned clocks for each RAM port and a small DELTA window for coincidence checks. It verifies that the RAM model reports an `Undefined` signal value when simultaneous writes/reads occur in `WRITETHROUGH` (Transparency) mode." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[7;35m Declare obj 'tb_priorities' in context '(my_tester 'tb_priorities')' \u001b[0m\n", " DSIZE: use default 16 \n", " ASIZE: use default 10 \n", "Adding WMODE to port dict (fallback for type )\n", " CLKA_PERIOD: use default 3.02 \n", " CLKB_PERIOD: use default 3.61 \n", " OUT_REG: use default False \n", "\u001b[7;35m Declare obj 'trigger_pulse' in context '(my_tester 'tb_priorities')' \u001b[0m\n", "Adding DELTA to port dict (fallback for type )\n", "\u001b[32m Insert unit trigger_pulse__my_tester_s1_s1_s1_s1_0.200000 \u001b[0m\n", "\u001b[7;35m Declare obj 'coincidence_prio' in context '(my_tester 'tb_priorities')' \u001b[0m\n", " DELTA: use default 0.2 \n", "\u001b[7;35m Declare obj 'clkpulse' in context '(my_tester 'tb_priorities')' \u001b[0m\n", "Adding DELTA to port dict (fallback for type )\n", "\u001b[32m Insert unit clkpulse__my_tester_s1_s1_0.200000 \u001b[0m\n", "Adding DELTA to port dict (fallback for type )\n", "\u001b[32m DEBUG: Cached unit clkpulse__my_tester_s1_s1_0.200000 \u001b[0m\n", "\u001b[32m Insert unit coincidence_prio__my_tester_s1_s1_s1_s1_s1_0.200000 \u001b[0m\n", "DEBUG: INSTANCING uut with wmode = WRITETHROUGH\n", "\u001b[7;35m Declare obj 'tdpram_wrapper' in context '(my_tester 'tb_priorities')' \u001b[0m\n", "Adding wmode to port dict (fallback for type )\n", "\u001b[7;35m Declare obj 'tdpram' in context '(my_tester 'tb_priorities')' \u001b[0m\n", " INITDATA: use default None \n", "\u001b[32m Insert unit tdpram__my_tester__RAMPort__RAMPort__NoneType_20480_1 \u001b[0m\n", " DELTA: use default 0.2 \n", "\u001b[32m DEBUG: Cached unit trigger_pulse__my_tester_s1_s1_s1_s1_0.200000 \u001b[0m\n", " DELTA: use default 0.2 \n", "\u001b[32m DEBUG: Cached unit trigger_pulse__my_tester_s1_s1_s1_s1_0.200000 \u001b[0m\n", " DELTA: use default 0.2 \n", "\u001b[32m DEBUG: Cached unit coincidence_prio__my_tester_s1_s1_s1_s1_s1_0.200000 \u001b[0m\n", "\u001b[32m Insert unit tdpram_wrapper__my_tester__SimRamPort__SimRamPort_s1__str_0 \u001b[0m\n", "\u001b[32m Insert unit gray_counter_s1_s1_s1_s4 \u001b[0m\n", "\u001b[32m DEBUG: Cached unit gray_counter_s1_s1_s1_s4 \u001b[0m\n", "\u001b[32m Insert unit tb_priorities__my_tester_16_10__str_3.020000_3.610000_0 \u001b[0m\n", " Writing 'gray_counter' to file /tmp/gray_counter.v \n", " Writing 'tdpram' to file /tmp/tdpram.v \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/test/icarus.py:33: UserWarning: Ignoring wavetrace argument for Verilog simulator\n", " warnings.warn(\"Ignoring wavetrace argument for Verilog simulator\")\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " Writing 'tdpram_wrapper' to file /tmp/tdpram_wrapper.v \n", " Writing 'clkpulse' to file /tmp/clkpulse.v \n", " Writing 'coincidence_prio' to file /tmp/coincidence_prio.v \n", " Writing 'trigger_pulse' to file /tmp/trigger_pulse.v \n", " Renaming reserved identifier: reg -> reg_8557 \n", " Writing 'tb_priorities' to file /tmp/tb_priorities.v \n", "Warning: Implicit truncation of ADD(a_addr_auto, C:1) result\n", "Warning: Implicit truncation of ADD(b_addr_auto, C:1) result\n", "==== COSIM stdout ====\n", "VCD info: dumpfile tb_priorities.vcd opened for output.\n", "is: 0x640f wants: 0x640f \n", "is: 0xab82 wants: 0xab82 \n", "is: 0xa957 wants: 0xa957 \n", "RUN WRITE SEQUENCE... \n", "====== REPORT ====== \n", "Coincidence occured. GOOD. \n", "Coincidence detected. GOOD. \n", "Stop Simulation\n", "\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "d = my_tester()\n", "tb = d.tb_priorities(WMODE = \"WRITETHROUGH\")\n", "\n", "tb.run(2000, debug = True, wavetrace = \"test_ghdl.vcd\")\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "// File generated from source:\n", "// \n", "// (c) 2016-2022 section5.ch\n", "// Modifications may be lost, edit the source file instead.\n", "\n", "`timescale 1 ns / 1 ps\n", "`include \"aux.v\"\n", "// Architecture myIRL\n", "\n", "\n", "\n", "module tb_priorities\n", " ();\n", " // Local type declarations\n", " // Signal declarations\n", " reg a_clk;\n", " initial a_clk = 1'b0;\n", " reg b_clk;\n", " initial b_clk = 1'b0;\n", " wire pa;\n", " wire pb;\n", " wire a_is_first;\n", " wire coincidence_reg;\n", " wire co_en;\n", " wire coincidence;\n", " wire [3:0] gca;\n", " wire fill_en;\n", " reg /* std_ulogic */ r;\n", " wire [3:0] gcb;\n", " wire valid;\n", " reg stat_raw;\n", " reg stat_rbw;\n", " reg stat_wt;\n", " reg stat_wto;\n", " reg stat_coincidence;\n", " reg stat_prio;\n", " reg stat_coll_wra_rdb;\n", " reg fill;\n", " reg read;\n", " reg a_ce;\n", " reg a_we;\n", " reg a_re;\n", " reg a_sel;\n", " reg [9:0] a_addr_user;\n", " reg [9:0] a_addr_auto;\n", " reg a_areset;\n", " reg [15:0] a_wdata;\n", " reg b_ce;\n", " reg b_we;\n", " reg b_re;\n", " reg b_sel;\n", " reg [9:0] b_addr_user;\n", " reg [9:0] b_addr_auto;\n", " reg b_areset;\n", " reg [15:0] b_wdata;\n", " wire [15:0] a_rdata;\n", " wire [15:0] b_rdata;\n", " reg [9:0] a_addr;\n", " reg s_ffda;\n", " reg [9:0] b_addr;\n", " reg s_155e;\n", " wire qen;\n", " \n", " always begin : CLKGEN_A\n", " #3.020000 a_clk <= ~a_clk;\n", " end\n", " \n", " always begin : CLKGEN_B\n", " #3.610000 b_clk <= ~b_clk;\n", " end\n", " \n", " // Instance coincidence_prio\n", " coincidence_prio\n", " coincidence_prio_0\n", " (\n", " .clka(a_clk),\n", " .clkb(b_clk),\n", " .pulse_a(pa),\n", " .pulse_b(pb),\n", " .a_b4_b(a_is_first)\n", " );\n", " \n", " // Instance trigger_pulse\n", " trigger_pulse\n", " trigger_pulse_0\n", " (\n", " .clk(a_clk),\n", " .en(co_en),\n", " .coincidence(coincidence),\n", " .coincidence_reg(coincidence_reg)\n", " );\n", " \n", " // Instance gray_counter\n", " gray_counter\n", " gray_counter_0\n", " (\n", " .clk(a_clk),\n", " .enable(fill_en),\n", " .reset(r),\n", " .gray_count(gca)\n", " );\n", " \n", " // Instance gray_counter\n", " gray_counter\n", " gray_counter_1\n", " (\n", " .clk(b_clk),\n", " .enable(valid),\n", " .reset(r),\n", " .gray_count(gcb)\n", " );\n", " \n", " initial begin : MAIN\n", " $dumpfile(\"tb_priorities.vcd\");\n", " $dumpvars(0, tb_priorities);\n", " stat_raw <= 1'b0;\n", " stat_rbw <= 1'b0;\n", " stat_wt <= 1'b0;\n", " stat_wto <= 1'b0;\n", " stat_coincidence <= 1'b0;\n", " stat_prio <= 1'b0;\n", " stat_coll_wra_rdb <= 1'b0;\n", " r = 1'b1;\n", " fill <= 1'b0;\n", " read <= 1'b0;\n", " #30.000000;\n", " r = 1'b0;\n", " a_ce <= 1'b0;\n", " a_we <= 1'b0;\n", " a_re <= 1'b0;\n", " a_sel <= 1'b0;\n", " a_addr_user <= 10'b0000000000;\n", " a_addr_auto <= 10'b0000000000;\n", " a_areset <= 1'b0;\n", " a_wdata <= 16'h0000;\n", " b_ce <= 1'b0;\n", " b_we <= 1'b0;\n", " b_re <= 1'b0;\n", " b_sel <= 1'b0;\n", " b_addr_user <= 10'b0000000000;\n", " b_addr_auto <= 10'b0000000000;\n", " b_areset <= 1'b0;\n", " b_wdata <= 16'h0000;\n", " #100.000000;\n", " a_ce <= 1'b1;\n", " b_ce <= 1'b1;\n", " @ (negedge a_clk);\n", " a_we <= 1'b1;\n", " a_addr_user <= 10'b0000000000;\n", " a_wdata <= 16'h640f;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000000001;\n", " a_wdata <= 16'hab82;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000000010;\n", " a_wdata <= 16'ha957;\n", " @ (negedge a_clk);\n", " a_we <= 1'b0;\n", " a_re <= 1'b1;\n", " read <= 1'b1;\n", " a_addr_user <= 10'b0000000000;\n", " @ (negedge a_clk);\n", " $write(\"%s \", \"is: \");\n", " $write(\"0x%h \", a_rdata);\n", " $write(\"%s \", \" wants: \");\n", " $write(\"%s \", \"0x640f\");\n", " $write(\"\\n\");\n", " `assert((a_rdata == 16'h640f), \"Failed memory read\") \n", " a_addr_user <= 10'b0000000001;\n", " @ (negedge a_clk);\n", " $write(\"%s \", \"is: \");\n", " $write(\"0x%h \", a_rdata);\n", " $write(\"%s \", \" wants: \");\n", " $write(\"%s \", \"0xab82\");\n", " $write(\"\\n\");\n", " `assert((a_rdata == 16'hab82), \"Failed memory read\") \n", " a_addr_user <= 10'b0000000010;\n", " @ (negedge a_clk);\n", " $write(\"%s \", \"is: \");\n", " $write(\"0x%h \", a_rdata);\n", " $write(\"%s \", \" wants: \");\n", " $write(\"%s \", \"0xa957\");\n", " $write(\"\\n\");\n", " `assert((a_rdata == 16'ha957), \"Failed memory read\") \n", " @ (negedge a_clk);\n", " #7.000000;\n", " while ((stat_prio == 1'b0)) begin\n", " $write(\"%s \", \"RUN WRITE SEQUENCE...\");\n", " $write(\"\\n\");\n", " while ((stat_coincidence == 1'b0)) begin\n", " b_addr_user <= 10'b0000100000;\n", " @ (negedge a_clk);\n", " a_we <= 1'b1;\n", " a_addr_user <= 10'b0000000000;\n", " a_wdata <= 16'h640f;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000000001;\n", " a_wdata <= 16'hab82;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000000010;\n", " a_wdata <= 16'ha957;\n", " @ (negedge a_clk);\n", " a_we <= 1'b0;\n", " #12.080000;\n", " @ (negedge a_clk);\n", " b_addr_user <= 10'b0000000000;\n", " a_we <= 1'b1;\n", " a_addr_user <= 10'b0000000000;\n", " a_wdata <= 16'h31cd;\n", " @ (negedge a_clk);\n", " a_we <= 1'b0;\n", " if ((coincidence_reg == 1'b1)) begin\n", " stat_coincidence <= 1'b1;\n", " stat_prio <= a_is_first;\n", " if ((b_rdata === 16'bxxxxxxxxxxxxxxxx)) begin\n", " stat_coll_wra_rdb <= 1'b1;\n", " end else if ((b_rdata == 16'h31cd)) begin\n", " stat_coll_wra_rdb <= 1'b0;\n", " $write(\"%s \", \"OTHER PORT READ \");\n", " $write(\"0x%h \", b_rdata);\n", " $write(\"\\n\");\n", " stat_wto <= 1'b1;\n", " end else begin\n", " $write(\"%s \", \"OTHER PORT READ \");\n", " $write(\"0x%h \", b_rdata);\n", " $write(\"\\n\");\n", " end\n", " end\n", " @ (posedge a_clk);\n", " if ((a_rdata == 16'h31cd)) begin\n", " stat_wt <= 1'b1;\n", " end else if ((a_rdata == 16'h640f)) begin\n", " @ (posedge a_clk);\n", " if ((a_rdata == 16'h31cd)) begin\n", " stat_rbw <= 1'b1;\n", " end else begin\n", " `assert(1'b0, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():576\") \n", " end\n", " end else if ((a_rdata == 16'ha957)) begin\n", " @ (posedge a_clk);\n", " if ((a_rdata == 16'h31cd)) begin\n", " stat_raw <= 1'b1;\n", " end else begin\n", " $write(\"%s \", \"p0.rdata:\");\n", " $write(\"0x%h \", a_rdata);\n", " $write(\"%s \", \"WRITETHROUGH\");\n", " $write(\"\\n\");\n", " end\n", " end else begin\n", " `assert(1'b0, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():585\") \n", " end\n", " @ (posedge a_clk);\n", " if ((stat_raw == 1'b1)) begin\n", " `assert(1'b0, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():590\") \n", " $write(\"%s \", \"WMODE: READ AFTER WRITE\");\n", " $write(\"\\n\");\n", " end\n", " if ((stat_wt == 1'b1)) begin\n", " `assert(1'b1, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():594\") \n", " end\n", " if ((stat_rbw == 1'b1)) begin\n", " `assert(1'b0, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():597\") \n", " end\n", " if ((stat_wto == 1'b1)) begin\n", " $write(\"%s \", \"WMODE: WRITETHROUGH TRANSPARENCY TO OTHER PORT\");\n", " $write(\"\\n\");\n", " `assert(1'b1, \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():600\") \n", " end\n", " `assert((((stat_raw | stat_wt) | stat_rbw) == 1'b1), \"Failed in /home/testing/src/myhdl2/library/ram/ramtest.py:tdp_tester():605\") \n", " end\n", " $write(\"%s \", \"====== REPORT ======\");\n", " $write(\"\\n\");\n", " $write(\"%s \", \"Coincidence occured. GOOD.\");\n", " $write(\"\\n\");\n", " #5.000000;\n", " if ((stat_coll_wra_rdb == 1'b1)) begin\n", " if ((stat_prio == 1'b0)) begin\n", " $write(\"%s \", \"Coincidence detected, but PRIO flag not set.\");\n", " $write(\"\\n\");\n", " $write(\"%s \", \"Check `clkpulse` DELTA settings.\");\n", " $write(\"\\n\");\n", " end else begin\n", " $write(\"%s \", \"Coincidence detected. GOOD.\");\n", " $write(\"\\n\");\n", " end\n", " end else if ((stat_wto == 1'b1)) begin\n", " $write(\"%s \", \"Coincidence: WT transparency to other port.\");\n", " $write(\"\\n\");\n", " if (1'b1) begin\n", " $fatal(0, \"Cross transparency check (strict) FAILED\");\n", " end else begin\n", " $write(\"%s \", \"This is unreal, but tolerated.\");\n", " $write(\"\\n\");\n", " end\n", " end else if ((stat_prio == 1'b0)) begin\n", " $write(\"%s \", \"Coincidence: Read before write\");\n", " $write(\"\\n\");\n", " end else begin\n", " $write(\"%s \", \"Coincidence not handled. BAD.\");\n", " $write(\"\\n\");\n", " end\n", " stat_coincidence <= 1'b0;\n", " #10.000000;\n", " end\n", " fill <= 1'b1;\n", " b_areset <= 1'b1;\n", " @ (posedge b_clk);\n", " b_areset <= 1'b0;\n", " b_sel <= 1'b1;\n", " #20.000000;\n", " @ (negedge a_clk);\n", " a_we <= 1'b1;\n", " a_addr_user <= 10'b0000100000;\n", " a_wdata <= 16'h640f;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000100001;\n", " a_wdata <= 16'hab82;\n", " @ (negedge a_clk);\n", " a_addr_user <= 10'b0000100010;\n", " a_wdata <= 16'ha957;\n", " @ (negedge a_clk);\n", " a_we <= 1'b0;\n", " #100.000000;\n", " #50.000000;\n", " $display(\"Stop Simulation\");\n", " $finish;\n", " end\n", " \n", " // Instance tdpram_wrapper\n", " tdpram_wrapper\n", " tdpram_wrapper_0\n", " (\n", " .a_sel(a_sel),\n", " .a_addr_auto(a_addr_auto),\n", " .a_addr_user(a_addr_user),\n", " .a_areset(a_areset),\n", " .a_clk(a_clk),\n", " .a_addr(a_addr),\n", " .a_wdata(a_wdata),\n", " .a_rdata(a_rdata),\n", " .a_ce(a_ce),\n", " .a_re(a_re),\n", " .a_we(a_we),\n", " .b_sel(b_sel),\n", " .b_addr_auto(b_addr_auto),\n", " .b_addr_user(b_addr_user),\n", " .b_areset(b_areset),\n", " .b_clk(b_clk),\n", " .b_addr(b_addr),\n", " .b_wdata(b_wdata),\n", " .b_rdata(b_rdata),\n", " .b_ce(b_ce),\n", " .b_re(b_re),\n", " .b_we(b_we),\n", " .rst(r)\n", " );\n", " \n", " always @ (a_sel or a_addr_auto or a_addr_user) begin : MUXER_A\n", " if ((a_sel == 1'b1)) begin\n", " a_addr <= a_addr_auto;\n", " end else begin\n", " a_addr <= a_addr_user;\n", " end\n", " end\n", " \n", " always @ (negedge a_clk ) begin : INC_A\n", " s_ffda <= valid;\n", " if ((a_areset == 1'b1)) begin\n", " a_addr_auto <= 10'b0000000000;\n", " end else if ((s_ffda == 1'b1)) begin\n", " a_addr_auto <= (a_addr_auto + 10'b0000000001) & 10'b1111111111 /* resize 11 -> 10 */ ;\n", " end\n", " end\n", " \n", " always @ (b_sel or b_addr_auto or b_addr_user) begin : MUXER_B\n", " if ((b_sel == 1'b1)) begin\n", " b_addr <= b_addr_auto;\n", " end else begin\n", " b_addr <= b_addr_user;\n", " end\n", " end\n", " \n", " always @ (negedge b_clk ) begin : INC_B\n", " s_155e <= valid;\n", " if ((b_areset == 1'b1)) begin\n", " b_addr_auto <= 10'b0000100000;\n", " end else if ((s_155e == 1'b1)) begin\n", " b_addr_auto <= (b_addr_auto + 10'b0000000001) & 10'b1111111111 /* resize 11 -> 10 */ ;\n", " end\n", " end\n", " assign coincidence = (pa & pb);\n", " assign qen = (read & valid);\n", " assign co_en = (a_we & ((a_addr == b_addr)));\n", " assign fill_en = (a_we & fill);\n", " assign valid = ((gca !== gcb));\n", "endmodule // tb_priorities\n" ] } ], "source": [ "!cat /tmp/tb_priorities.v" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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 }