diff --git a/.github/authors-cfg.yaml b/.github/authors-cfg.yaml index 3d0098d5..0d5d4f43 100644 --- a/.github/authors-cfg.yaml +++ b/.github/authors-cfg.yaml @@ -41,6 +41,7 @@ allowed-years: allowed-authors: Axel Vanoni: axvanoni@ethz.ch + Daniel Keller: dankeller@iis.ee.ethz.ch Michael Rogenmoser: michaero@iis.ee.ethz.ch Samuel Riedel: sriedel@iis.ee.ethz.ch Thomas Benz: tbenz@iis.ee.ethz.ch diff --git a/idma.mk b/idma.mk index 06d6c4b8..f5e7ed20 100644 --- a/idma.mk +++ b/idma.mk @@ -16,6 +16,7 @@ SPHINXBUILD ?= sphinx-build VCS ?= vcs VERILATOR ?= verilator VLOGAN ?= vlogan +VSIM ?= vsim # Shell SHELL := /bin/bash @@ -308,6 +309,12 @@ endef $(IDMA_VSIM_DIR)/compile.tcl: $(IDMA_BENDER_FILES) $(IDMA_FULL_TB) $(IDMA_FULL_RTL) $(IDMA_INCLUDE_ALL) $(IDMA_WAVE_ALL) $(call idma_generate_vsim, $@, -t sim -t test -t idma_test -t synth -t rtl -t asic -t snitch_cluster,../../..) +.PHONY: idma_sim_tb_idma_rt_midend + +idma_sim_tb_idma_rt_midend: $(IDMA_VSIM_DIR)/compile.tcl + cd $(IDMA_VSIM_DIR); $(VSIM) -c -do "source compile.tcl; quit" + cd $(IDMA_VSIM_DIR); $(VSIM) -c -t 1ps -voptargs=+acc tb_idma_rt_midend -do "run -all; quit" + idma_sim_clean: rm -rf $(IDMA_VSIM_DIR)/compile.tcl rm -rf $(IDMA_VSIM_DIR)/work diff --git a/src/midend/idma_rt_midend.sv b/src/midend/idma_rt_midend.sv index 1ab81cc4..2d11e98b 100644 --- a/src/midend/idma_rt_midend.sv +++ b/src/midend/idma_rt_midend.sv @@ -111,6 +111,9 @@ module idma_rt_midend #( idma_rsp_t int_rsp; logic int_valid; + // choice-FIFO backpressure signal + logic choice_fifo_ready; + // generate the counters timing the events and assemble the transfers for (genvar c = 0; c < NumEvents; c++) begin : gen_counters // counter instance @@ -180,7 +183,7 @@ module idma_rt_midend #( .inp_ready_o ( { nd_req_ready_o, nd_req_ready_int } ), .oup_data_o ( out_req ), .oup_valid_o ( nd_req_valid_o ), - .oup_ready_i ( nd_req_ready_i ) + .oup_ready_i ( nd_req_ready_i & choice_fifo_ready ) ); // assemble arbiter inputs @@ -206,11 +209,11 @@ module idma_rt_midend #( .testmode_i ( 1'b0 ), .usage_o ( /* NC */ ), .data_i ( choice ), - .valid_i ( nd_req_valid_i & nd_req_ready_o ), - .ready_o ( /* HACK: NC */ ), + .valid_i ( nd_req_valid_o & nd_req_ready_i & choice_fifo_ready ), + .ready_o ( choice_fifo_ready ), .data_o ( choice_head ), - .valid_o ( /* HACK: NC */ ), - .ready_i ( burst_rsp_valid_o & burst_rsp_ready_i ) + .valid_o ( /* NC */ ), + .ready_i ( burst_rsp_valid_i & burst_rsp_ready_o ) ); // arbitration of responses diff --git a/test/midend/tb_idma_rt_midend.sv b/test/midend/tb_idma_rt_midend.sv index 463a27e2..3b11c1da 100644 --- a/test/midend/tb_idma_rt_midend.sv +++ b/test/midend/tb_idma_rt_midend.sv @@ -4,17 +4,19 @@ // Authors: // - Thomas Benz +// - Daniel Keller `include "idma/typedef.svh" -/// Sanity testbench for the RT midend +/// Sanity testbench for the RT midend: checks bypass responses are routed +/// to the bypass output, not lost to the internal sink. module tb_idma_rt_midend; logic clk; logic rst_n; localparam int unsigned NumEvents = 5; - localparam int unsigned NumDim = 3; + localparam int unsigned NumDim = 3; typedef logic [5:0] axi_id_t; typedef logic [31:0] tf_len_t; @@ -29,6 +31,29 @@ module tb_idma_rt_midend; tf_len_t [NumEvents-1:0] event_counts = '0; logic [NumEvents-1:0] event_ena = '0; + // Downstream side: drains nd_req_o and supplies burst_rsp. + idma_nd_req_t out_req; + logic out_req_valid; + logic out_req_ready; + + idma_rsp_t out_rsp; + logic out_rsp_valid; + logic out_rsp_ready; + + // Bypass side: driven by this TB. + idma_nd_req_t byp_req; + logic byp_req_valid; + logic byp_req_ready; + + idma_rsp_t byp_rsp; + logic byp_rsp_valid; + logic byp_rsp_ready; + + // Counters to detect routing mismatch. + int unsigned bypass_req_issued; + int unsigned bypass_rsp_seen; + int unsigned internal_rsp_seen; + clk_rst_gen #( .ClkPeriod ( 1ns ), .RstClkCycles ( 1 ) @@ -41,7 +66,7 @@ module tb_idma_rt_midend; idma_rt_midend #( .NumEvents ( NumEvents ), .EventCntWidth ( 32'd32 ), - .NumOutstanding ( 32'd2 ), + .NumOutstanding ( 32'd4 ), .addr_t ( axi_addr_t ), .idma_nd_req_t ( idma_nd_req_t ), .idma_rsp_t ( idma_rsp_t ) @@ -59,26 +84,104 @@ module tb_idma_rt_midend; .dst_2d_stride_i ( '0 ), .num_2d_reps_i ( '0 ), .event_ena_i ( event_ena ), - .event_counts_o (), - .nd_req_o (), - .nd_req_valid_o (), - .nd_req_ready_i ( 1'b1 ), - .burst_rsp_i ( '1 ), - .burst_rsp_valid_i ( 1'b1 ), - .burst_rsp_ready_o (), - .nd_req_i ( '1 ), - .nd_req_ready_o (), - .nd_req_valid_i ( 1'b0 ), - .burst_rsp_o ( ), - .burst_rsp_valid_o ( ), - .burst_rsp_ready_i ( 1'b1 ) + .event_counts_o ( ), + .nd_req_o ( out_req ), + .nd_req_valid_o ( out_req_valid ), + .nd_req_ready_i ( out_req_ready ), + .burst_rsp_i ( out_rsp ), + .burst_rsp_valid_i ( out_rsp_valid ), + .burst_rsp_ready_o ( out_rsp_ready ), + .nd_req_i ( byp_req ), + .nd_req_valid_i ( byp_req_valid ), + .nd_req_ready_o ( byp_req_ready ), + .burst_rsp_o ( byp_rsp ), + .burst_rsp_valid_o ( byp_rsp_valid ), + .burst_rsp_ready_i ( byp_rsp_ready ) ); + // Always accept the downstream request and acknowledge any response stream. + assign out_req_ready = 1'b1; + assign byp_rsp_ready = 1'b1; + + // Drive a response one cycle after every accepted request (1:1 order). + logic out_req_handshake; + assign out_req_handshake = out_req_valid & out_req_ready; + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + out_rsp_valid <= 1'b0; + out_rsp <= '0; + end else begin + // Hold an unconsumed response. + if (out_rsp_valid && !out_rsp_ready) begin + out_rsp_valid <= 1'b1; + end else begin + out_rsp_valid <= out_req_handshake; + out_rsp <= '1; + end + end + end + + // -- Counters ------------------------------------------------------ + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + bypass_req_issued <= '0; + bypass_rsp_seen <= '0; + internal_rsp_seen <= '0; + end else begin + if (byp_req_valid && byp_req_ready) + bypass_req_issued <= bypass_req_issued + 1; + if (byp_rsp_valid && byp_rsp_ready) + bypass_rsp_seen <= bypass_rsp_seen + 1; + // Responses not routed to bypass are internal. + if (out_rsp_valid && out_rsp_ready && !byp_rsp_valid) + internal_rsp_seen <= internal_rsp_seen + 1; + end + end + + // -- Bypass stimulus ----------------------------------------------- + initial begin : drive_bypass + byp_req_valid = 1'b0; + byp_req = '0; + byp_req.burst_req.length = 32'h0000_1000; + byp_req.burst_req.src_addr = 32'hC000_0000; + byp_req.burst_req.dst_addr = 32'hD000_0000; + + wait (rst_n === 1'b1); + @(posedge clk); + + // Issue 8 bypass requests interleaved with the counter traffic. + for (int i = 0; i < 8; i++) begin + repeat (3 + (i % 4)) @(posedge clk); + byp_req_valid = 1'b1; + byp_req.burst_req.length = 32'h0000_1000 + i; + @(posedge clk); + while (!byp_req_ready) @(posedge clk); + byp_req_valid = 1'b0; + end + end + + // -- Main stimulus ------------------------------------------------- initial begin event_counts = {32'd17, 32'd300, 32'd800, 32'd1000, 32'd2000}; #10ns; event_ena = {1'd1, 1'd1, 1'd1, 1'd1, 1'd1}; #5000ns; + + // Bounded drain for outstanding bypass responses. + for (int i = 0; i < 1000; i++) begin + if (bypass_rsp_seen >= bypass_req_issued) break; + @(posedge clk); + end + + // Every bypass request must produce exactly one bypass response. + if (bypass_rsp_seen != bypass_req_issued) begin + $fatal(1, "[tb_idma_rt_midend] routing mismatch: issued=%0d seen=%0d", + bypass_req_issued, bypass_rsp_seen); + end + + $display("[tb_idma_rt_midend] bypass req: %0d, bypass rsp: %0d, internal rsp: %0d", + bypass_req_issued, bypass_rsp_seen, internal_rsp_seen); $finish(); end