diff --git a/umi/umi/rtl/umi_mem_agent.v b/umi/umi/rtl/umi_mem_agent.v index ff14e4d..d23cd1d 100644 --- a/umi/umi/rtl/umi_mem_agent.v +++ b/umi/umi/rtl/umi_mem_agent.v @@ -16,10 +16,7 @@ * ---- * * Documentation: - * - Implements a simple memory array for use in simulation - * - * Known limitation/bugs: - * - Transaction cannot cross the DW boundary + * - Implements a simple memory array * ******************************************************************************/ diff --git a/umi/umi/rtl/umi_ram.v b/umi/umi/rtl/umi_ram.v new file mode 100644 index 0000000..089aba7 --- /dev/null +++ b/umi/umi/rtl/umi_ram.v @@ -0,0 +1,156 @@ +/****************************************************************************** + * Copyright 2020 Zero ASIC Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ---- + * + * Documentation: + * - Implements a simple memory array with multiple UMI access ports + * - The array allows only a single read or write per cycle + * + ****************************************************************************/ + +module umi_ram + #(parameter N = 1, // number of UMI ports + parameter DW = 256, // umi packet width + parameter AW = 64, // address width + parameter CW = 32, // command width + parameter RAMDEPTH = 512, + parameter CTRLW = 8, + parameter SRAMTYPE = "DEFAULT" + ) + (// global controls + input clk, // clock signals + input nreset, // async active low reset + input [CTRLW-1:0] sram_ctrl, // Control signal for SRAM + // Device port + input [N-1:0] udev_req_valid, + input [N*CW-1:0] udev_req_cmd, + input [N*AW-1:0] udev_req_dstaddr, + input [N*AW-1:0] udev_req_srcaddr, + input [N*DW-1:0] udev_req_data, + output [N-1:0] udev_req_ready, + output [N-1:0] udev_resp_valid, + output [N*CW-1:0] udev_resp_cmd, + output [N*AW-1:0] udev_resp_dstaddr, + output [N*AW-1:0] udev_resp_srcaddr, + output [N*DW-1:0] udev_resp_data, + input [N-1:0] udev_resp_ready + ); + + /*AUTOREG*/ + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [CW-1:0] mem_resp_cmd; + wire [DW-1:0] mem_resp_data; + wire [AW+N-1:0] mem_resp_dstaddr; + wire [AW+N-1:0] mem_resp_srcaddr; + wire mem_resp_valid; + wire [N-1:0] umi_in_ready; + wire [CW-1:0] umi_out_cmd; + wire [DW-1:0] umi_out_data; + wire [AW-1:0] umi_out_dstaddr; + wire umi_out_ready; + wire [AW-1:0] umi_out_srcaddr; + wire umi_out_valid; + wire [N-1:0] umi_req_grants; + // End of automatics + wire mem_resp_ready; + + //################################################################## + //# UMI ENDPOINT (Pipelined Request/Response) + //################################################################## + + /*umi_arbiter AUTO_TEMPLATE( + .mode (2'b10), + .mask ({@"vl-width"{1'b0}}), + .requests (udev_req_valid[]), + .grants (umi_req_grants[]), + );*/ + + umi_arbiter #(.N(N)) + umi_arbiter (/*AUTOINST*/ + // Outputs + .grants (umi_req_grants[N-1:0]), // Templated + // Inputs + .clk (clk), + .nreset (nreset), + .mode (2'b10), // Templated + .mask ({N{1'b0}}), // Templated + .requests (udev_req_valid[N-1:0])); // Templated + + assign udev_req_ready[N-1:0] = umi_in_ready[N-1:0] & umi_req_grants[N-1:0]; + + /*umi_mux AUTO_TEMPLATE( + .umi_in_valid (umi_req_grants[]), + .umi_in_ready (umi_in_ready[]), + .umi_in_\(.*\) (udev_req_\1[]), + );*/ + + umi_mux #(.CW(CW), + .AW(AW), + .DW(DW), + .N(N)) + umi_mux(/*AUTOINST*/ + // Outputs + .umi_in_ready (umi_in_ready[N-1:0]), // Templated + .umi_out_valid (umi_out_valid), + .umi_out_cmd (umi_out_cmd[CW-1:0]), + .umi_out_dstaddr (umi_out_dstaddr[AW-1:0]), + .umi_out_srcaddr (umi_out_srcaddr[AW-1:0]), + .umi_out_data (umi_out_data[DW-1:0]), + // Inputs + .umi_in_valid (umi_req_grants[N-1:0]), // Templated + .umi_in_cmd (udev_req_cmd[N*CW-1:0]), // Templated + .umi_in_dstaddr (udev_req_dstaddr[N*AW-1:0]), // Templated + .umi_in_srcaddr (udev_req_srcaddr[N*AW-1:0]), // Templated + .umi_in_data (udev_req_data[N*DW-1:0]), // Templated + .umi_out_ready (umi_out_ready)); + + assign udev_resp_valid[N-1:0] = mem_resp_srcaddr[AW+:N] & {N{mem_resp_valid}}; + assign mem_resp_ready = |(mem_resp_srcaddr[AW+:N] & udev_resp_ready[N-1:0]) | ~mem_resp_valid; + assign udev_resp_cmd[N*CW-1:0] = {N{mem_resp_cmd[CW-1:0]}}; + assign udev_resp_dstaddr[N*AW-1:0] = {N{mem_resp_dstaddr[AW-1:0]}}; + assign udev_resp_srcaddr[N*AW-1:0] = {N{mem_resp_srcaddr[AW-1:0]}}; + assign udev_resp_data[N*DW-1:0] = {N{mem_resp_data[DW-1:0]}}; + + umi_mem_agent #(.CW(CW), + .AW(AW+N), + .DW(DW), + .RAMDEPTH(RAMDEPTH), + .CTRLW(CTRLW), + .SRAMTYPE(SRAMTYPE)) + umi_mem_agent(.clk (clk), + .nreset (nreset), + .sram_ctrl (sram_ctrl), + + .udev_req_valid (umi_out_valid), + .udev_req_cmd (umi_out_cmd[CW-1:0]), + .udev_req_dstaddr ({umi_req_grants[N-1:0], umi_out_dstaddr[AW-1:0]}), + .udev_req_srcaddr ({{N{1'b0}}, umi_out_srcaddr[AW-1:0]}), + .udev_req_data (umi_out_data[DW-1:0]), + .udev_req_ready (umi_out_ready), + + .udev_resp_valid (mem_resp_valid), + .udev_resp_cmd (mem_resp_cmd[CW-1:0]), + .udev_resp_dstaddr (mem_resp_dstaddr[AW+N-1:0]), + .udev_resp_srcaddr (mem_resp_srcaddr[AW+N-1:0]), + .udev_resp_data (mem_resp_data[DW-1:0]), + .udev_resp_ready (mem_resp_ready)); + +endmodule +// Local Variables: +// verilog-library-directories:("./") +// End: diff --git a/umi/umi/testbench/test_umi_ram.py b/umi/umi/testbench/test_umi_ram.py new file mode 100755 index 0000000..81a1bb3 --- /dev/null +++ b/umi/umi/testbench/test_umi_ram.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2023 Zero ASIC +# This code is licensed under Apache License 2.0 (see LICENSE for details) + +import numpy as np +from argparse import ArgumentParser +from switchboard import SbDut, UmiTxRx, verilator_run +import umi + + +def build_testbench(): + dut = SbDut('testbench', default_main=True) + + # Set up inputs + dut.input('umi/testbench/testbench_umi_ram.sv', package='umi') + + dut.use(umi) + dut.add('option', 'library', 'umi') + dut.add('option', 'library', 'lambdalib_stdlib') + dut.add('option', 'library', 'lambdalib_ramlib') + dut.add('option', 'library', 'lambdalib_vectorlib') + + # Verilator configuration + dut.set('tool', 'verilator', 'task', 'compile', 'file', 'config', 'umi/testbench/config.vlt', package='umi') +# dut.set('option', 'relax', True) + dut.add('tool', 'verilator', 'task', 'compile', 'option', '--prof-cfuncs') + dut.add('tool', 'verilator', 'task', 'compile', 'option', '-CFLAGS') + dut.add('tool', 'verilator', 'task', 'compile', 'option', '-DVL_DEBUG') + dut.add('tool', 'verilator', 'task', 'compile', 'option', '-Wall') + + # Settings - enable tracing + dut.set('option', 'trace', True) + dut.set('tool', 'verilator', 'task', 'compile', 'var', 'trace_type', 'fst') + + # Build simulator + dut.run() + + return dut.find_result('vexe', step='compile') + + +def apply_atomic(origdata, atomicdata, operation, maxrange): + tempval = origdata + if (operation == 0): + tempval = origdata + atomicdata + if (tempval >= maxrange): + tempval = tempval - maxrange + elif (operation == 1): + tempval = origdata & atomicdata + elif (operation == 2): + tempval = origdata | atomicdata + elif (operation == 3): + tempval = origdata ^ atomicdata + elif (operation == 4): + if (origdata & (maxrange >> 1)): + origdata = int(origdata) - int(maxrange) + else: + origdata = int(origdata) + if (atomicdata & (maxrange >> 1)): + atomicdata = int(atomicdata) - int(maxrange) + else: + atomicdata = int(atomicdata) + tempval = origdata if (origdata > atomicdata) else atomicdata + elif (operation == 5): + if (origdata & (maxrange >> 1)): + origdata = int(origdata) - int(maxrange) + else: + origdata = int(origdata) + if (atomicdata & (maxrange >> 1)): + atomicdata = int(atomicdata) - int(maxrange) + else: + atomicdata = int(atomicdata) + tempval = atomicdata if (origdata > atomicdata) else origdata + elif (operation == 6): + tempval = origdata if (origdata > atomicdata) else atomicdata + elif (operation == 7): + tempval = atomicdata if (origdata > atomicdata) else origdata + elif (operation == 8): + tempval = atomicdata + else: + tempval = atomicdata + + return tempval + + +def main(vldmode="2", rdymode="2", n=100): + + verilator_bin = build_testbench() + + # launch the simulation + verilator_run(verilator_bin, plusargs=['trace', ('valid_mode', vldmode), ('ready_mode', rdymode)]) + + # instantiate TX and RX queues. note that these can be instantiated without + # specifying a URI, in which case the URI can be specified later via the + # "init" method + + host = [UmiTxRx(f'host2dut_{x}.q', f'dut2host_{x}.q', fresh=True) for x in range(5)] + + print("### Statring test ###") + + avail_datatype = [np.uint8, np.uint16, np.uint32] + + # un-aligned accesses + for _ in range(n): + psel = np.random.choice([0, 1, 2, 3, 4]) + addr = np.random.randint(0, 512) + # length should not cross the DW boundary - umi_mem_agent limitation + length = np.random.randint(0, 256) + wordindexer = np.random.choice([0, 1, 2]) + maxrange = 2**(8*(2**wordindexer)) + data = np.random.randint(0, maxrange, size=(length+1), dtype=avail_datatype[wordindexer]) + addr = addr*(2**wordindexer) & 0x1FF + + print(f"umi{psel} writing {length+1} words of type {avail_datatype[wordindexer]} to addr 0x{addr:08x}") + host[psel].write(addr, data) + + psel = np.random.choice([0, 1, 2, 3, 4]) + atomicopcode = np.random.randint(0, 9) + atomicdata = np.random.randint(0, 256, dtype=avail_datatype[wordindexer]) + print(f"umi{psel} atomic opcode: {atomicopcode} of type {avail_datatype[wordindexer]} to addr 0x{addr:08x}") + atomicval = host[psel].atomic(addr, atomicdata, atomicopcode) + if not (atomicval == data[0]): + print(f"ERROR umi atomic from addr 0x{addr:08x} expected {data[0]} actual {atomicval}") + assert (atomicval == data[0]) + data[0] = apply_atomic(data[0], atomicdata, atomicopcode, maxrange) + + psel = np.random.choice([0, 1, 2, 3, 4]) + print(f"umi{psel} read from addr 0x{addr:08x}") + val = host[psel].read(addr, length+1, dtype=avail_datatype[wordindexer]) + if not (np.array_equal(val, data)): + print(f"ERROR umi read from addr 0x{addr:08x} expected {data} actual {val}") + assert (np.array_equal(val, data)) + + print("### TEST PASS ###") + + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('--vldmode', default='2') + parser.add_argument('--rdymode', default='2') + parser.add_argument('-n', type=int, default=10, + help='Number of transactions to send during the test.') + args = parser.parse_args() + + main(vldmode=args.vldmode, + rdymode=args.rdymode, + n=args.n) diff --git a/umi/umi/testbench/testbench_umi_ram.sv b/umi/umi/testbench/testbench_umi_ram.sv new file mode 100644 index 0000000..5f27889 --- /dev/null +++ b/umi/umi/testbench/testbench_umi_ram.sv @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright 2020 Zero ASIC Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ---- + * + * Documentation: + * - Simple umi_ram testbench + * + ******************************************************************************/ + +`default_nettype none + +module testbench ( + input clk + ); + + parameter integer N=5; + parameter integer CW=32; + parameter integer AW=64; + parameter integer DW=256; + parameter integer CTRLW=8; + parameter integer RAMDEPTH=512; + + reg nreset; + reg go; + + wire [N-1:0] udev_req_valid; + wire [N*CW-1:0] udev_req_cmd; + wire [N*AW-1:0] udev_req_dstaddr; + wire [N*AW-1:0] udev_req_srcaddr; + wire [N*DW-1:0] udev_req_data; + wire [N-1:0] udev_req_ready; + + wire [N-1:0] udev_resp_valid; + wire [N*CW-1:0] udev_resp_cmd; + wire [N*AW-1:0] udev_resp_dstaddr; + wire [N*AW-1:0] udev_resp_srcaddr; + wire [N*DW-1:0] udev_resp_data; + wire [N-1:0] udev_resp_ready; + + wire [CTRLW-1:0] sram_ctrl = 8'b0; + + /////////////////////////////////////////// + // Host side umi agents + /////////////////////////////////////////// + + genvar i; + generate + for (i = 0; i < N; i = i + 1) begin : UMI_AGENTS_GEN + umi_rx_sim #(.VALID_MODE_DEFAULT(2), + .DW(DW) + ) + host_umi_rx_i (.clk(clk), + .valid(udev_req_valid[i]), + .cmd(udev_req_cmd[i*CW+:CW]), + .dstaddr(udev_req_dstaddr[i*AW+:AW]), + .srcaddr(udev_req_srcaddr[i*AW+:AW]), + .data(udev_req_data[i*DW+:DW]), + .ready(udev_req_ready[i]) + ); + + umi_tx_sim #(.READY_MODE_DEFAULT(2), + .DW(DW) + ) + host_umi_tx_i (.clk(clk), + .valid(udev_resp_valid[i]), + .cmd(udev_resp_cmd[i*CW+:CW]), + .dstaddr(udev_resp_dstaddr[i*AW+:AW]), + .srcaddr(udev_resp_srcaddr[i*AW+:AW]), + .data(udev_resp_data[i*DW+:DW]), + .ready(udev_resp_ready[i]) + ); + end + endgenerate + + // instantiate dut with UMI ports + /* umi_ram AUTO_TEMPLATE( + );*/ + umi_ram #(.N(N), + .CW(CW), + .AW(AW), + .DW(DW), + .RAMDEPTH(RAMDEPTH), + .CTRLW(CTRLW)) + umi_ram_i(/*AUTOINST*/ + // Outputs + .udev_req_ready (udev_req_ready[N-1:0]), + .udev_resp_valid (udev_resp_valid[N-1:0]), + .udev_resp_cmd (udev_resp_cmd[N*CW-1:0]), + .udev_resp_dstaddr (udev_resp_dstaddr[N*AW-1:0]), + .udev_resp_srcaddr (udev_resp_srcaddr[N*AW-1:0]), + .udev_resp_data (udev_resp_data[N*DW-1:0]), + // Inputs + .clk (clk), + .nreset (nreset), + .sram_ctrl (sram_ctrl[CTRLW-1:0]), + .udev_req_valid (udev_req_valid[N-1:0]), + .udev_req_cmd (udev_req_cmd[N*CW-1:0]), + .udev_req_dstaddr (udev_req_dstaddr[N*AW-1:0]), + .udev_req_srcaddr (udev_req_srcaddr[N*AW-1:0]), + .udev_req_data (udev_req_data[N*DW-1:0]), + .udev_resp_ready (udev_resp_ready[N-1:0])); + + // Initialize UMI + integer valid_mode, ready_mode; + + initial begin + /* verilator lint_off IGNOREDRETURN */ + if (!$value$plusargs("valid_mode=%d", valid_mode)) begin + valid_mode = 2; // default if not provided as a plusarg + end + + if (!$value$plusargs("ready_mode=%d", ready_mode)) begin + ready_mode = 2; // default if not provided as a plusarg + end + + UMI_AGENTS_GEN[0].host_umi_rx_i.init("host2dut_0.q"); + UMI_AGENTS_GEN[0].host_umi_rx_i.set_valid_mode(valid_mode); + UMI_AGENTS_GEN[1].host_umi_rx_i.init("host2dut_1.q"); + UMI_AGENTS_GEN[1].host_umi_rx_i.set_valid_mode(valid_mode); + UMI_AGENTS_GEN[2].host_umi_rx_i.init("host2dut_2.q"); + UMI_AGENTS_GEN[2].host_umi_rx_i.set_valid_mode(valid_mode); + UMI_AGENTS_GEN[3].host_umi_rx_i.init("host2dut_3.q"); + UMI_AGENTS_GEN[3].host_umi_rx_i.set_valid_mode(valid_mode); + UMI_AGENTS_GEN[4].host_umi_rx_i.init("host2dut_4.q"); + UMI_AGENTS_GEN[4].host_umi_rx_i.set_valid_mode(valid_mode); + + UMI_AGENTS_GEN[0].host_umi_tx_i.init("dut2host_0.q"); + UMI_AGENTS_GEN[0].host_umi_tx_i.set_ready_mode(ready_mode); + UMI_AGENTS_GEN[1].host_umi_tx_i.init("dut2host_1.q"); + UMI_AGENTS_GEN[1].host_umi_tx_i.set_ready_mode(ready_mode); + UMI_AGENTS_GEN[2].host_umi_tx_i.init("dut2host_2.q"); + UMI_AGENTS_GEN[2].host_umi_tx_i.set_ready_mode(ready_mode); + UMI_AGENTS_GEN[3].host_umi_tx_i.init("dut2host_3.q"); + UMI_AGENTS_GEN[3].host_umi_tx_i.set_ready_mode(ready_mode); + UMI_AGENTS_GEN[4].host_umi_tx_i.init("dut2host_4.q"); + UMI_AGENTS_GEN[4].host_umi_tx_i.set_ready_mode(ready_mode); + /* verilator lint_on IGNOREDRETURN */ + end + + // VCD + + initial + begin + nreset = 1'b0; + go = 1'b0; + end // initial begin + + // Bring up reset and the go signal on the first clock cycle + always @(negedge clk) + begin + nreset <= nreset | 1'b1; + go <= 1'b1; + end + + // control block + initial + begin + if ($test$plusargs("trace")) + begin + $dumpfile("testbench.fst"); + $dumpvars(0, testbench); + end + end + + // auto-stop + + auto_stop_sim auto_stop_sim_i (.clk(clk)); + +endmodule +// Local Variables: +// verilog-library-directories:("../rtl" ) +// End: + +`default_nettype wire