`timescale 1ns/1ps 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; logic rst; logic [31:0] smp_num; logic [7:0] s_axis_tdata; logic s_axis_tvalid; logic s_axis_tready; logic s_axis_tlast; logic [ACCUM_WIDTH-1:0] acc_din; logic din_valid; logic send_req; logic req_ready; logic readout_begin; logic batch_req; logic finish; out_axis_fifo #( .ACCUM_WIDTH(ACCUM_WIDTH), .WINDOW_SIZE(WINDOW_SIZE), .PACKET_SIZE(PACKET_SIZE) ) dut ( .eth_clk_in (eth_clk_in), .acc_clk_in (acc_clk_in), .rst (rst), .smp_num (smp_num), .s_axis_tdata (s_axis_tdata), .s_axis_tvalid (s_axis_tvalid), .s_axis_tready (s_axis_tready), .s_axis_tlast (s_axis_tlast), .acc_din (acc_din), .din_valid (din_valid), .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 #6 eth_clk_in = ~eth_clk_in; // 125 end initial begin acc_clk_in = 0; forever #7.692307692 acc_clk_in = ~acc_clk_in; // 65 end // 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 // helpers task automatic do_reset(); begin rst = 1'b1; readout_begin = 1'b0; din_valid = 1'b0; acc_din = '0; smp_num = '0; scoreboard_reset(); repeat (10) @(posedge acc_clk_in); rst = 1'b0; repeat (10) @(posedge acc_clk_in); end endtask task automatic pulse_readout_begin(input logic [31:0] smp_num_i); begin smp_num = smp_num_i; @(posedge acc_clk_in); readout_begin <= 1'b1; @(posedge acc_clk_in); readout_begin <= 1'b0; end endtask 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 <= rand_word; // expected result push_expected_word(rand_word); end @(posedge acc_clk_in); din_valid <= 1'b0; acc_din <= '0; end endtask // 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] %s start", $time, case_name); pulse_readout_begin(smp_num_i); while (finish !== 1'b1) begin @(posedge acc_clk_in); if (batch_req) begin batch_count++; $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 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 // 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; readout_begin = 1'b0; din_valid = 1'b0; acc_din = '0; smp_num = '0; repeat (500) @(posedge acc_clk_in); // 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 do_reset(); run_case(32'd77777); repeat (20) @(posedge acc_clk_in); do_reset(); repeat (20) @(posedge acc_clk_in); $display("[%0t] ALL TESTS DONE", $time); $finish; end endmodule