diff --git a/umi/sumi/__init__.py b/umi/sumi/__init__.py index f7095977..e07bdb26 100644 --- a/umi/sumi/__init__.py +++ b/umi/sumi/__init__.py @@ -13,6 +13,7 @@ from .umi_pipeline.umi_pipeline import Pipeline from .umi_ram.umi_ram import RAM from .umi_regif.umi_regif import Regif +from .umi_stream.umi_stream import Stream from .umi_switch.umi_switch import Switch from .umi_tester.umi_tester import Tester from .umi_unpack.umi_unpack import Unpack @@ -34,6 +35,7 @@ 'Pipeline', 'RAM', 'Regif', + 'Stream', 'Switch', 'Tester', 'Unpack', diff --git a/umi/sumi/include/umi_messages.vh b/umi/sumi/include/umi_messages.vh index dc08f005..2094a07e 100644 --- a/umi/sumi/include/umi_messages.vh +++ b/umi/sumi/include/umi_messages.vh @@ -27,6 +27,9 @@ localparam UMI_OPCODE_LSB = 0; localparam UMI_OPCODE_MSB = 4; +localparam UMI_OPTIONS_LSB = 5; +localparam UMI_OPTIONS_MSB = 31; + localparam UMI_SIZE_LSB = 5; localparam UMI_SIZE_MSB = 7; diff --git a/umi/sumi/umi_stream/rtl/umi_stream.v b/umi/sumi/umi_stream/rtl/umi_stream.v new file mode 100644 index 00000000..b5f9df40 --- /dev/null +++ b/umi/sumi/umi_stream/rtl/umi_stream.v @@ -0,0 +1,294 @@ +/******************************************************************************* + * Copyright 2026 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. + ****************************************************************************** + * + * This module converts between UMI memory mapped transactions and streaming + * transactions dynamically via the devicemode signal + * + * When configured as a device, posted writes go through a fifo unmodified + * from the umi domain to the streaming domain. Write requests have acks + * sent back immediately, ie the write to the fifo indicates completion. + * Read requests pull data from the fifo whenever not empty. + * + * When configure as a non-device full duplex channel, only posted writes + * are allowed. + * + ******************************************************************************/ + +module umi_stream + #(parameter AW = 64, // UMI data width + parameter CW = 32, // UMI command width + parameter DW = 256, // UMI data width + parameter S2MM_DEPTH = 4, // S2MM FIFO depth + parameter MM2S_DEPTH = 4 // MM2S FIFO depth + ) + (// operating mode + input devicemode, // 1 = device endpoint, 0=full duplex link + // S2MM addresses (from external controller) + input [AW-1:0] s2mm_dstaddr, + input [AW-1:0] s2mm_srcaddr, + input [CW-1:0] s2mm_cmd, + // UMI memory mapped interface + input umi_nreset, + input umi_clk, + input umi_in_valid, + input [CW-1:0] umi_in_cmd, + input [AW-1:0] umi_in_dstaddr, + input [AW-1:0] umi_in_srcaddr, + input [DW-1:0] umi_in_data, + output umi_in_ready, + output umi_out_valid, + output [CW-1:0] umi_out_cmd, + output [AW-1:0] umi_out_dstaddr, + output [AW-1:0] umi_out_srcaddr, + output [DW-1:0] umi_out_data, + input umi_out_ready, + // USI streaming interface + input usi_clk, + input usi_nreset, + output usi_out_valid, + output usi_out_last, + output [DW-1:0] usi_out_data, + input usi_out_ready, + input usi_in_valid, + input usi_in_last, + input [DW-1:0] usi_in_data, + output usi_in_ready + ); + +`include "umi_messages.vh" + + // M2SS signals + wire mm2s_fifo_full; + wire mm2s_fifo_almost_full; + wire mm2s_fifo_empty; + wire mm2s_fifo_write; + wire mm2s_fifo_read; + wire [DW:0] mm2s_fifo_din; + wire [DW:0] mm2s_fifo_dout; + + // S2MM signals + wire s2mm_fifo_full; + wire s2mm_fifo_almost_full; + wire s2mm_fifo_empty; + wire s2mm_fifo_write; + wire s2mm_fifo_read; + wire [DW:0] s2mm_fifo_din; + wire [DW:0] s2mm_fifo_dout; + + // Command decode signals (direct decode from umi_in_cmd) + wire cmd_read; + wire cmd_write; + wire cmd_write_posted; + + // Transaction control signals + wire umi_resp; + wire request_stall; + wire resp_vld_out; + + // Response packet signals + wire [CW-1:0] resp_cmd; + + // Response pipeline registers (following umi_endpoint pattern) + reg resp_vld_r; + reg resp_vld_keep; + reg [CW-1:0] resp_cmd_r; + reg [AW-1:0] resp_dstaddr_r; + reg [AW-1:0] resp_srcaddr_r; + reg [DW-1:0] resp_data_r; + reg [DW-1:0] resp_data_keep; + wire [DW-1:0] resp_data_out; + + //################################################### + // UMI Command Decode (direct decode using umi_messages.vh) + //################################################### + + assign cmd_read = (umi_in_cmd[4:0] == UMI_REQ_READ); + assign cmd_write = (umi_in_cmd[4:0] == UMI_REQ_WRITE); + assign cmd_write_posted = (umi_in_cmd[4:0] == UMI_REQ_POSTED); + + //################################################### + // UMI Transaction Decode + //################################################### + + // Stall when there's a pending response and consumer not ready + assign resp_vld_out = resp_vld_r | resp_vld_keep; + assign request_stall = resp_vld_out & ~umi_out_ready; + + // Ready to accept new UMI transactions (device mode only for response-generating ops) + // Posted writes: only needs FIFO not full (works in both modes) + // Write with ack: need FIFO not full AND no stall (device mode only) + // Read: need s2mm not empty AND no stall (device mode only) + assign umi_in_ready = (cmd_write_posted & ~mm2s_fifo_full) | + (cmd_write & devicemode & ~mm2s_fifo_full & ~request_stall) | + (cmd_read & devicemode & ~s2mm_fifo_empty & ~request_stall); + + // Transaction accepted that generates a response (device mode only) + // Write with ack (cmd_write) generates response, posted write does NOT + // Read generates response + assign umi_resp = umi_in_valid & umi_in_ready & devicemode & + (cmd_write | cmd_read); + + //################################################### + // MM2S FIFO Write Control + //################################################### + + // Write to mm2s fifo on any write transaction (posted or with ack) + assign mm2s_fifo_write = umi_in_valid & umi_in_ready & + (cmd_write | cmd_write_posted); + + assign mm2s_fifo_din[DW-1:0] = umi_in_data[DW-1:0]; + + assign mm2s_fifo_din[DW] = umi_in_cmd[UMI_EOM_BIT]; + + //################################################### + // S2MM FIFO Read Control (for read responses) + //################################################### + + // In device mode: read when servicing a read request + // In non-device mode, auto drain when umi_out is ready + assign s2mm_fifo_read = devicemode ? (umi_in_valid & umi_in_ready & cmd_read): + (~s2mm_fifo_empty & umi_out_ready); + + //################################################### + // Response Generation + //################################################### + + assign resp_cmd[31:0] = {umi_in_cmd[31:5], + cmd_read ? UMI_RESP_READ : UMI_RESP_WRITE}; + + // Response valid register + always @(posedge umi_clk or negedge umi_nreset) + if (!umi_nreset) + resp_vld_r <= 1'b0; + else + resp_vld_r <= umi_resp; + + // Response (capture when response is generated) + always @(posedge umi_clk or negedge umi_nreset) + if (!umi_nreset) + begin + resp_cmd_r[CW-1:0] <= {CW{1'b0}}; + resp_dstaddr_r[AW-1:0] <= {AW{1'b0}}; + resp_srcaddr_r[AW-1:0] <= {AW{1'b0}}; + end + else if (umi_resp) + begin + resp_cmd_r[CW-1:0] <= resp_cmd[CW-1:0]; + resp_dstaddr_r[AW-1:0] <= umi_in_srcaddr[AW-1:0]; + resp_srcaddr_r[AW-1:0] <= {AW{1'b0}}; + end + + // Data storage in case ready is low + always @(posedge umi_clk or negedge umi_nreset) + if (!umi_nreset) + resp_data_keep[DW-1:0] <= {DW{1'b0}}; + else if (resp_vld_r) + resp_data_keep[DW-1:0] <= resp_data_r[DW-1:0]; + + // Response data register (for reads, capture from s2mm fifo) + always @(posedge umi_clk or negedge umi_nreset) + if (!umi_nreset) + resp_data_r[DW-1:0] <= {DW{1'b0}}; + else if (umi_resp) + resp_data_r[DW-1:0] <= cmd_read ? s2mm_fifo_dout[DW-1:0] : + {DW{1'b0}}; + + // Valid keep - holds response when consumer not ready + always @(posedge umi_clk or negedge umi_nreset) + if (!umi_nreset) + resp_vld_keep <= 1'b0; + else if (resp_vld_r & ~umi_out_ready) + resp_vld_keep <= 1'b1; + else if (umi_out_ready) + resp_vld_keep <= 1'b0; + + // Output data mux + assign resp_data_out[DW-1:0] = resp_vld_r ? resp_data_r[DW-1:0] : + resp_data_keep[DW-1:0]; + + //################################################### + // UMI Output Mux + //################################################### + + assign umi_out_valid = devicemode ? resp_vld_out : ~s2mm_fifo_empty; + assign umi_out_cmd = devicemode ? resp_cmd_r : s2mm_cmd; + assign umi_out_dstaddr = devicemode ? resp_dstaddr_r : s2mm_dstaddr; + assign umi_out_srcaddr = devicemode ? resp_srcaddr_r : s2mm_srcaddr; + assign umi_out_data = devicemode ? resp_data_out : s2mm_fifo_dout[DW-1:0]; + + //################################################### + // Memory Mapped to Stream (MM2S) + //################################################### + + assign mm2s_fifo_read = ~mm2s_fifo_empty & usi_out_ready; + assign usi_out_valid = ~mm2s_fifo_empty; + assign usi_out_data = mm2s_fifo_dout[DW-1:0]; + assign usi_out_last = mm2s_fifo_dout[DW]; + + la_asyncfifo #(.DW(DW+1), + .DEPTH(MM2S_DEPTH)) + ififo_mm2s (// write side + .wr_clk (umi_clk), + .wr_nreset (umi_nreset), + .wr_full (mm2s_fifo_full), + .wr_almost_full (mm2s_fifo_almost_full), + .wr_din (mm2s_fifo_din[DW:0]), + .wr_en (mm2s_fifo_write), + .wr_chaosmode (1'b0), + // read side + .rd_clk (usi_clk), + .rd_nreset (usi_nreset), + .rd_dout (mm2s_fifo_dout[DW:0]), + .rd_empty (mm2s_fifo_empty), + .rd_en (mm2s_fifo_read), + // misc + .vss (1'b0), + .vdd (1'b1), + .ctrl (1'b0), + .test (1'b0)); + + //###################################################### + // Stream to Memory Mapped (S2MM) + //###################################################### + + assign s2mm_fifo_write = usi_in_valid & ~s2mm_fifo_full; + assign s2mm_fifo_din[DW-1:0] = usi_in_data[DW-1:0]; + assign s2mm_fifo_din[DW] = usi_in_last; + assign usi_in_ready = ~s2mm_fifo_full; + + la_asyncfifo #(.DW(DW+1), + .DEPTH(S2MM_DEPTH)) + ififo_s2mm (// write side (stream domain) + .wr_clk (usi_clk), + .wr_nreset (usi_nreset), + .wr_full (s2mm_fifo_full), + .wr_almost_full (s2mm_fifo_almost_full), + .wr_din (s2mm_fifo_din[DW:0]), + .wr_en (s2mm_fifo_write), + .wr_chaosmode (1'b0), + // read side (umi domain) + .rd_clk (umi_clk), + .rd_nreset (umi_nreset), + .rd_dout (s2mm_fifo_dout[DW:0]), + .rd_empty (s2mm_fifo_empty), + .rd_en (s2mm_fifo_read), + // misc + .vss (1'b0), + .vdd (1'b1), + .ctrl (1'b0), + .test (1'b0)); + +endmodule diff --git a/umi/sumi/umi_stream/testbench/tb_umi_stream.v b/umi/sumi/umi_stream/testbench/tb_umi_stream.v new file mode 100644 index 00000000..81bf53bc --- /dev/null +++ b/umi/sumi/umi_stream/testbench/tb_umi_stream.v @@ -0,0 +1,506 @@ +/******************************************************************************* + * Copyright 2026 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. + * + * Testbench for umi_stream module + * Tests both device mode and non-device (full duplex) mode + * + ******************************************************************************/ + +`timescale 1ns/1ps + +module tb_umi_stream; + + // Parameters + parameter AW = 64; + parameter CW = 32; + parameter DW = 64; + parameter RW = 32; + parameter RAW = 32; + parameter DEPTH = 4; + + // UMI command opcodes (from umi_messages.vh) + localparam UMI_REQ_READ = 5'h01; + localparam UMI_REQ_WRITE = 5'h03; + localparam UMI_REQ_POSTED = 5'h05; + localparam UMI_RESP_READ = 5'h02; + localparam UMI_RESP_WRITE = 5'h04; + + // UMI command bit positions + localparam UMI_EOM_BIT = 22; + + // Clock periods (ns) + parameter UMI_CLK_PERIOD = 10; + parameter USI_CLK_PERIOD = 12; + + // Test control + reg devicemode; + reg umi_nreset; + reg umi_clk; + reg usi_nreset; + reg usi_clk; + + // UMI input interface + reg umi_in_valid; + reg [CW-1:0] umi_in_cmd; + reg [AW-1:0] umi_in_dstaddr; + reg [AW-1:0] umi_in_srcaddr; + reg [DW-1:0] umi_in_data; + wire umi_in_ready; + + // UMI output interface + wire umi_out_valid; + wire [CW-1:0] umi_out_cmd; + wire [AW-1:0] umi_out_dstaddr; + wire [AW-1:0] umi_out_srcaddr; + wire [DW-1:0] umi_out_data; + reg umi_out_ready; + + // S2MM control interface (for non-device mode) + reg [AW-1:0] s2mm_dstaddr; + reg [AW-1:0] s2mm_srcaddr; + reg [CW-1:0] s2mm_cmd; + + // USI streaming output interface + wire usi_out_valid; + wire usi_out_last; + wire [DW-1:0] usi_out_data; + reg usi_out_ready; + + // USI streaming input interface + reg usi_in_valid; + reg usi_in_last; + reg [DW-1:0] usi_in_data; + wire usi_in_ready; + + // Test tracking + integer errors; + integer test_num; + + //################################################### + // DUT Instantiation + //################################################### + + umi_stream #( + .AW(AW), + .CW(CW), + .DW(DW), + .S2MM_DEPTH(DEPTH), + .MM2S_DEPTH(DEPTH)) + dut ( + // Operating mode + .devicemode (devicemode), + // UMI interface + .umi_nreset (umi_nreset), + .umi_clk (umi_clk), + .umi_in_valid (umi_in_valid), + .umi_in_cmd (umi_in_cmd), + .umi_in_dstaddr (umi_in_dstaddr), + .umi_in_srcaddr (umi_in_srcaddr), + .umi_in_data (umi_in_data), + .umi_in_ready (umi_in_ready), + .umi_out_valid (umi_out_valid), + .umi_out_cmd (umi_out_cmd), + .umi_out_dstaddr(umi_out_dstaddr), + .umi_out_srcaddr(umi_out_srcaddr), + .umi_out_data (umi_out_data), + .umi_out_ready (umi_out_ready), + // S2MM control + .s2mm_dstaddr (s2mm_dstaddr), + .s2mm_srcaddr (s2mm_srcaddr), + .s2mm_cmd (s2mm_cmd), + // USI interface + .usi_clk (usi_clk), + .usi_nreset (usi_nreset), + .usi_out_valid (usi_out_valid), + .usi_out_last (usi_out_last), + .usi_out_data (usi_out_data), + .usi_out_ready (usi_out_ready), + .usi_in_valid (usi_in_valid), + .usi_in_last (usi_in_last), + .usi_in_data (usi_in_data), + .usi_in_ready (usi_in_ready) + ); + + //################################################### + // Clock Generation + //################################################### + + initial begin + umi_clk = 1'b0; + forever #(UMI_CLK_PERIOD/2) umi_clk = ~umi_clk; + end + + initial begin + usi_clk = 1'b0; + forever #(USI_CLK_PERIOD/2) usi_clk = ~usi_clk; + end + + //################################################### + // VCD Dump + //################################################### + + initial begin + $dumpfile("tb_umi_stream.vcd"); + $dumpvars(0, tb_umi_stream); + end + + //################################################### + // Test Tasks + //################################################### + + // Initialize all signals + task init_signals; + begin + devicemode = 1'b1; + umi_nreset = 1'b0; + usi_nreset = 1'b0; + umi_in_valid = 1'b0; + umi_in_cmd = {CW{1'b0}}; + umi_in_dstaddr = {AW{1'b0}}; + umi_in_srcaddr = {AW{1'b0}}; + umi_in_data = {DW{1'b0}}; + umi_out_ready = 1'b1; + s2mm_dstaddr = {AW{1'b0}}; + s2mm_srcaddr = {AW{1'b0}}; + s2mm_cmd = {CW{1'b0}}; + usi_out_ready = 1'b1; + usi_in_valid = 1'b0; + usi_in_last = 1'b0; + usi_in_data = {DW{1'b0}}; + errors = 0; + test_num = 0; + end + endtask + + // Apply reset + task apply_reset; + begin + umi_nreset = 1'b0; + usi_nreset = 1'b0; + repeat (10) @(posedge umi_clk); + umi_nreset = 1'b1; + usi_nreset = 1'b1; + repeat (5) @(posedge umi_clk); + end + endtask + + // Build UMI command + function [CW-1:0] build_umi_cmd; + input [4:0] opcode; + input [2:0] size; + input [7:0] len; + input eom; + begin + build_umi_cmd = {CW{1'b0}}; + build_umi_cmd[4:0] = opcode; + build_umi_cmd[7:5] = size; + build_umi_cmd[15:8] = len; + build_umi_cmd[UMI_EOM_BIT] = eom; + end + endfunction + + // Send UMI write transaction (posted or with ack) + task send_umi_write; + input posted; + input [AW-1:0] dstaddr; + input [AW-1:0] srcaddr; + input [DW-1:0] data; + input eom; + begin + umi_in_valid = 1'b1; + umi_in_cmd = build_umi_cmd(posted ? UMI_REQ_POSTED : UMI_REQ_WRITE, 3'd3, 8'd0, eom); + umi_in_dstaddr = dstaddr; + umi_in_srcaddr = srcaddr; + umi_in_data = data; + @(posedge umi_clk); + while (!umi_in_ready) @(posedge umi_clk); + @(posedge umi_clk); + umi_in_valid = 1'b0; + end + endtask + + // Send UMI read request + task send_umi_read; + input [AW-1:0] dstaddr; + input [AW-1:0] srcaddr; + integer wait_count; + begin + umi_in_valid = 1'b1; + umi_in_cmd = build_umi_cmd(UMI_REQ_READ, 3'd3, 8'd0, 1'b1); + umi_in_dstaddr = dstaddr; + umi_in_srcaddr = srcaddr; + umi_in_data = {DW{1'b0}}; + wait_count = 0; + @(posedge umi_clk); + while (!umi_in_ready) begin + @(posedge umi_clk); + wait_count = wait_count + 1; + if (wait_count == 10) + $display("DEBUG read: s2mm_empty=%b request_stall=%b devicemode=%b cmd_read=%b resp_vld_out=%b", + dut.s2mm_fifo_empty, dut.request_stall, devicemode, dut.cmd_read, dut.resp_vld_out); + if (wait_count > 50) begin + $display("ERROR: Timeout in send_umi_read, umi_in_ready stuck low"); + umi_in_valid = 1'b0; + disable send_umi_read; + end + end + @(posedge umi_clk); + umi_in_valid = 1'b0; + end + endtask + + // Wait for and check UMI response + task wait_umi_response; + input [4:0] expected_opcode; + input [DW-1:0] expected_data; + integer timeout; + begin + timeout = 0; + while (!umi_out_valid && timeout < 100) begin + @(posedge umi_clk); + timeout = timeout + 1; + end + if (timeout >= 100) begin + $display("ERROR: Timeout waiting for UMI response"); + errors = errors + 1; + end else begin + if (umi_out_cmd[4:0] !== expected_opcode) begin + $display("ERROR: Expected opcode %h, got %h", expected_opcode, umi_out_cmd[4:0]); + errors = errors + 1; + end + if (expected_data !== {DW{1'bx}} && umi_out_data !== expected_data) begin + $display("ERROR: Expected data %h, got %h", expected_data, umi_out_data); + errors = errors + 1; + end + @(posedge umi_clk); + end + end + endtask + + // Send streaming data to S2MM FIFO (from stream side) + task send_usi_data; + input [DW-1:0] data; + input last; + integer wait_count; + begin + usi_in_valid = 1'b1; + usi_in_data = data; + usi_in_last = last; + wait_count = 0; + @(posedge usi_clk); + while (!usi_in_ready) begin + @(posedge usi_clk); + wait_count = wait_count + 1; + if (wait_count == 10) + $display("DEBUG send_usi: s2mm_full=%b usi_in_ready=%b", dut.s2mm_fifo_full, usi_in_ready); + if (wait_count > 50) begin + $display("ERROR: Timeout in send_usi_data, s2mm FIFO full?"); + usi_in_valid = 1'b0; + disable send_usi_data; + end + end + @(posedge usi_clk); + usi_in_valid = 1'b0; + usi_in_last = 1'b0; + end + endtask + + // Wait for and check streaming output from MM2S + task wait_usi_data; + input [DW-1:0] expected_data; + input expected_last; + integer timeout; + begin + timeout = 0; + while (!usi_out_valid && timeout < 100) begin + @(posedge usi_clk); + timeout = timeout + 1; + end + if (timeout >= 100) begin + $display("ERROR: Timeout waiting for USI data"); + errors = errors + 1; + end else begin + if (usi_out_data !== expected_data) begin + $display("ERROR: Expected USI data %h, got %h", expected_data, usi_out_data); + errors = errors + 1; + end + if (usi_out_last !== expected_last) begin + $display("ERROR: Expected USI last %b, got %b", expected_last, usi_out_last); + errors = errors + 1; + end + @(posedge usi_clk); + end + end + endtask + + //################################################### + // Main Test Sequence + //################################################### + + initial begin + $display("========================================"); + $display("Starting umi_stream testbench"); + $display("========================================"); + + init_signals(); + apply_reset(); + + //-------------------------------------------- + // Test 1: Device Mode - Posted Write to Stream + //-------------------------------------------- + test_num = 1; + $display("\nTest %0d: Device Mode - Posted Write", test_num); + + devicemode = 1'b1; + + send_umi_write(1'b1, 64'h1000, 64'h2000, 64'hDEADBEEF_CAFEBABE, 1'b1); + wait_usi_data(64'hDEADBEEF_CAFEBABE, 1'b1); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test 2: Device Mode - Write with Ack + //-------------------------------------------- + test_num = 2; + errors = 0; + $display("\nTest %0d: Device Mode - Write with Ack", test_num); + + send_umi_write(1'b0, 64'h3000, 64'h4000, 64'h12345678_AABBCCDD, 1'b0); + wait_umi_response(UMI_RESP_WRITE, {DW{1'bx}}); + wait_usi_data(64'h12345678_AABBCCDD, 1'b0); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test 3: Device Mode - Read from S2MM FIFO + //-------------------------------------------- + test_num = 3; + errors = 0; + $display("\nTest %0d: Device Mode - Read Request", test_num); + + // Push data into S2MM FIFO from streaming side + send_usi_data(64'hFEEDFACE_BEEFCAFE, 1'b1); + repeat (20) @(posedge umi_clk); + + // Send read request - will read the data we just pushed + send_umi_read(64'h5000, 64'h6000); + wait_umi_response(UMI_RESP_READ, 64'hFEEDFACE_BEEFCAFE); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test 4: Device Mode - Multiple Posted Writes + //-------------------------------------------- + test_num = 4; + errors = 0; + $display("\nTest %0d: Device Mode - Multiple Posted Writes", test_num); + + send_umi_write(1'b1, 64'h7000, 64'h8000, 64'h1111111111111111, 1'b0); + wait_usi_data(64'h1111111111111111, 1'b0); + + send_umi_write(1'b1, 64'h7008, 64'h8000, 64'h2222222222222222, 1'b0); + wait_usi_data(64'h2222222222222222, 1'b0); + + send_umi_write(1'b1, 64'h7010, 64'h8000, 64'h3333333333333333, 1'b1); + wait_usi_data(64'h3333333333333333, 1'b1); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test 5: Non-Device Mode (Full Duplex) + //-------------------------------------------- + test_num = 5; + errors = 0; + $display("\nTest %0d: Non-Device Mode - Stream to UMI", test_num); + + // Switch to non-device mode + devicemode = 1'b0; + repeat (10) @(posedge umi_clk); + + // Setup S2MM control signals for pass-through + s2mm_cmd = build_umi_cmd(UMI_RESP_READ, 3'd3, 8'd0, 1'b1); + s2mm_dstaddr = 64'hAAAA_0000; + s2mm_srcaddr = 64'hBBBB_0000; + + // Send streaming data - should appear on UMI output + send_usi_data(64'hDDDDDDDD_EEEEEEEE, 1'b1); + + // Wait for data on UMI output + while (!umi_out_valid) @(posedge umi_clk); + + if (umi_out_data !== 64'hDDDDDDDD_EEEEEEEE) begin + $display("ERROR: Expected data %h, got %h", 64'hDDDDDDDD_EEEEEEEE, umi_out_data); + errors = errors + 1; + end + if (umi_out_dstaddr !== 64'hAAAA_0000) begin + $display("ERROR: Expected dstaddr %h, got %h", 64'hAAAA_0000, umi_out_dstaddr); + errors = errors + 1; + end + @(posedge umi_clk); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test 6: Non-Device Mode - UMI Write to Stream + //-------------------------------------------- + test_num = 6; + errors = 0; + $display("\nTest %0d: Non-Device Mode - UMI Write to Stream", test_num); + + // Posted writes should still go to stream in non-device mode + send_umi_write(1'b1, 64'h9000, 64'hA000, 64'hFFFFFFFF_00000000, 1'b1); + wait_usi_data(64'hFFFFFFFF_00000000, 1'b1); + + if (errors == 0) $display("Test %0d PASSED", test_num); + else $display("Test %0d FAILED", test_num); + + repeat (20) @(posedge umi_clk); + + //-------------------------------------------- + // Test Summary + //-------------------------------------------- + $display("\n========================================"); + $display("All tests completed"); + $display("========================================\n"); + + $finish; + end + + //################################################### + // Timeout watchdog + //################################################### + + initial begin + #200000; + $display("ERROR: Simulation timeout!"); + $finish; + end + +endmodule diff --git a/umi/sumi/umi_stream/umi_stream.py b/umi/sumi/umi_stream/umi_stream.py new file mode 100644 index 00000000..baedf4ab --- /dev/null +++ b/umi/sumi/umi_stream/umi_stream.py @@ -0,0 +1,23 @@ +from umi.common import UMI +from lambdalib.ramlib import Asyncfifo + + +class Stream(UMI): + def __init__(self): + super().__init__('umi_stream', + files=['rtl/umi_stream.v'], + deps=[Asyncfifo()]) + + +class StreamTB(UMI): + def __init__(self): + super().__init__('tb_umi_stream', + files=['testbench/tb_umi_stream.v'], + deps=[Stream()]) + + +if __name__ == "__main__": + d = Stream() + d.write_fileset(f"{d.name}.f", fileset="rtl") + d = StreamTB() + d.write_fileset(f"{d.name}.f", fileset="rtl")