From dfccc012252149d17f56d998ed73e0d4ef16540f Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:47:27 +0300 Subject: [PATCH] tests: auto tb for out_axis_fifo --- rtl/accum/tests/out_axis_fifo_tb.sv | 184 +++++++++++++++++++++------- 1 file changed, 138 insertions(+), 46 deletions(-) diff --git a/rtl/accum/tests/out_axis_fifo_tb.sv b/rtl/accum/tests/out_axis_fifo_tb.sv index d785296..2d3943d 100644 --- a/rtl/accum/tests/out_axis_fifo_tb.sv +++ b/rtl/accum/tests/out_axis_fifo_tb.sv @@ -5,6 +5,8 @@ module tb_out_axis_fifo; localparam int ACCUM_WIDTH = 32; localparam int WINDOW_SIZE = 65; localparam int PACKET_SIZE = 1024; + localparam int BYTES_PER_WORD = ACCUM_WIDTH / 8; + localparam int WORDS_PER_BATCH = PACKET_SIZE / BYTES_PER_WORD; // 1024 / 4 = 256 слов logic eth_clk_in; logic acc_clk_in; @@ -20,6 +22,9 @@ module tb_out_axis_fifo; logic [ACCUM_WIDTH-1:0] acc_din; logic din_valid; + logic send_req; + logic req_ready; + logic readout_begin; logic batch_req; @@ -45,45 +50,98 @@ module tb_out_axis_fifo; .readout_begin (readout_begin), + .req_ready (req_ready), + .send_req (send_req), + .batch_req (batch_req), .finish (finish) ); - // ----------------------------- // clocks - // ----------------------------- initial begin eth_clk_in = 0; - forever #4 eth_clk_in = ~eth_clk_in; // 125 MHz + forever #6 eth_clk_in = ~eth_clk_in; // 125 end initial begin acc_clk_in = 0; - forever #3 acc_clk_in = ~acc_clk_in; // ~166.7 MHz + forever #7.692307692 acc_clk_in = ~acc_clk_in; // 65 end - // ----------------------------- - // simple AXIS sink - // ----------------------------- - initial begin - s_axis_tready = 1'b1; + // scoreboard + byte expected_bytes[$]; + int unsigned compared_bytes; + int unsigned mismatch_count; + int unsigned total_pushed_words; + + task automatic scoreboard_reset(); + begin + expected_bytes.delete(); + compared_bytes = 0; + mismatch_count = 0; + total_pushed_words = 0; + end + endtask + + task automatic push_expected_word(input logic [ACCUM_WIDTH-1:0] word); + begin + // queue push + expected_bytes.push_back(word[7:0]); + expected_bytes.push_back(word[15:8]); + expected_bytes.push_back(word[23:16]); + expected_bytes.push_back(word[31:24]); + total_pushed_words++; + end + endtask + + task automatic check_expected_empty(string case_name); + begin + if (expected_bytes.size() != 0) begin + $error("[%0t] %s: expected_bytes is not empty, remaining=%0d", + $time, case_name, expected_bytes.size()); + end else begin + $display("[%0t] %s: scoreboard queue empty, all expected bytes were transmitted", + $time, case_name); + end + end + endtask + + // axis check + always_ff @(posedge eth_clk_in or posedge rst) begin + byte exp_byte; + if (rst) begin + compared_bytes <= 0; + mismatch_count <= 0; + end else begin + if (s_axis_tvalid && s_axis_tready) begin + if (expected_bytes.size() == 0) begin + $error("[%0t] AXIS produced unexpected byte 0x%02x: expected queue is empty", + $time, s_axis_tdata); + mismatch_count <= mismatch_count + 1; + end else begin + exp_byte = expected_bytes.pop_front(); + compared_bytes <= compared_bytes + 1; + + if (s_axis_tdata !== exp_byte) begin + $error("[%0t] AXIS mismatch at byte #%0d: got=0x%02x expected=0x%02x", + $time, compared_bytes, s_axis_tdata, exp_byte); + mismatch_count <= mismatch_count + 1; + end + end + end + end end - // У DUT нет своей логики rd_en, поэтому для теста подадим её force-ом. - initial begin - force dut.rd_en = dut.s_axis_tvalid && s_axis_tready; - end - - // ----------------------------- // helpers - // ----------------------------- task automatic do_reset(); begin - rst = 1'b1; + rst = 1'b1; readout_begin = 1'b0; - din_valid = 1'b0; - acc_din = '0; - smp_num = '0; + din_valid = 1'b0; + acc_din = '0; + smp_num = '0; + + scoreboard_reset(); repeat (10) @(posedge acc_clk_in); rst = 1'b0; @@ -104,11 +162,17 @@ module tb_out_axis_fifo; task automatic send_random_words(input int unsigned n_words); int unsigned i; + logic [ACCUM_WIDTH-1:0] rand_word; begin for (i = 0; i < n_words; i++) begin + rand_word = $urandom; + @(posedge acc_clk_in); din_valid <= 1'b1; - acc_din <= $urandom; + acc_din <= rand_word; + + // expected result + push_expected_word(rand_word); end @(posedge acc_clk_in); @@ -117,17 +181,20 @@ module tb_out_axis_fifo; end endtask - // Один запуск: - // 1) задаём smp_num - // 2) даём readout_begin - // 3) каждый раз, когда DUT просит batch_req, отправляем PACKET_SIZE слов - // 4) ждём finish + + // 1. set smp_num + // 2. pulse readout_begon + // 3. send 1KB (PACKET_SIZE) after each batch_req pulse + // 4. wait for finish + // 5. compare axis result task automatic run_case(input logic [31:0] smp_num_i); int batch_count; + string case_name; begin batch_count = 0; + case_name = $sformatf("run_case(smp_num=%0d)", smp_num_i); - $display("[%0t] run_case start, smp_num=%0d", $time, smp_num_i); + $display("[%0t] %s start", $time, case_name); pulse_readout_begin(smp_num_i); @@ -136,39 +203,57 @@ module tb_out_axis_fifo; if (batch_req) begin batch_count++; - $display("[%0t] batch_req #%0d -> send %0d words", - $time, batch_count, PACKET_SIZE / ACCUM_WIDTH * 8); - - // send packets to accomplish 1kb packet. - send_random_words(PACKET_SIZE / ACCUM_WIDTH * 8); + $display("[%0t] %s: batch_req #%0d -> send %0d words", + $time, case_name, batch_count, WORDS_PER_BATCH); + + send_random_words(WORDS_PER_BATCH); end end - $display("[%0t] run_case done, smp_num=%0d, batches=%0d, wr_cnt=%0d, wr_total=%0d", - $time, smp_num_i, batch_count, dut.wr_cnt, dut.wr_total); + + repeat (200) @(posedge eth_clk_in); + + $display("[%0t] %s done: batches=%0d, pushed_words=%0d, compared_bytes=%0d, mismatches=%0d, wr_cnt=%0d, wr_total=%0d", + $time, case_name, batch_count, total_pushed_words, compared_bytes, mismatch_count, + dut.wr_cnt, dut.wr_total); + + check_expected_empty(case_name); + + if (mismatch_count != 0) begin + $fatal(1, "[%0t] %s FAILED: mismatches=%0d", $time, case_name, mismatch_count); + end else begin + $display("[%0t] %s PASSED", $time, case_name); + end @(posedge acc_clk_in); end endtask - // ----------------------------- - // monitor - // ----------------------------- + // eth beh simulator int axis_byte_count; always_ff @(posedge eth_clk_in or posedge rst) begin if (rst) begin axis_byte_count <= 0; + req_ready <= 0; + s_axis_tready <= 1'b0; end else begin + req_ready <= 1; + + // request send + if (send_req) begin + s_axis_tready <= 1'b1; + req_ready <= 0; + end + if (s_axis_tvalid && s_axis_tready) begin axis_byte_count <= axis_byte_count + 1; end end end - // ----------------------------- + // main - // ----------------------------- initial begin // init rst = 1'b0; @@ -177,20 +262,27 @@ module tb_out_axis_fifo; acc_din = '0; smp_num = '0; - // 1-й запуск - do_reset(); - run_case(32'd17); + repeat (500) @(posedge acc_clk_in); - // 2-й запуск + // 1 + do_reset(); + repeat (500) @(posedge acc_clk_in); + run_case(32'd17); + repeat (20) @(posedge acc_clk_in); + + // 2 do_reset(); run_case(32'd1024); + repeat (20) @(posedge acc_clk_in); - // 3-й запуск + // 3 do_reset(); run_case(32'd77777); - + repeat (20) @(posedge acc_clk_in); + do_reset(); - repeat (50) @(posedge acc_clk_in); + repeat (20) @(posedge acc_clk_in); + $display("[%0t] ALL TESTS DONE", $time); $finish; end