`timescale 1ns / 1ps module tb_accumulator_top; localparam DATA_WIDTH = 12; localparam ACCUM_WIDTH = 32; localparam N_MAX = 4096; localparam WINDOW_SIZE = 65; localparam PACKET_SIZE = 1024; localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH; localparam MAX_WORDS = N_MAX; localparam MAX_SEQ_NUM = 64; logic clk_in; logic eth_clk_in; logic rst; logic [DATA_WIDTH-1:0] s_axis_tdata; logic s_axis_tvalid; logic start; logic [31:0] smp_num; logic [15:0] seq_num; logic req_ready; wire send_req; wire [7:0] m_axis_tdata; wire m_axis_tvalid; logic m_axis_tready; wire m_axis_tlast; wire finish; integer seed; integer total_errors; integer tests_total; integer tests_failed; integer tests_passed; integer packets_seen; integer current_packet_byte_count; integer total_words_captured; byte packet_bytes [0:PACKET_SIZE-1]; logic [ACCUM_WIDTH-1:0] expected_words [0:MAX_WORDS-1]; logic [ACCUM_WIDTH-1:0] captured_words_le[0:MAX_WORDS-1]; logic [ACCUM_WIDTH-1:0] captured_words_be[0:MAX_WORDS-1]; accumulator_top #( .DATA_WIDTH(DATA_WIDTH), .ACCUM_WIDTH(ACCUM_WIDTH), .N_MAX(N_MAX), .WINDOW_SIZE(WINDOW_SIZE), .PACKET_SIZE(PACKET_SIZE) ) dut ( .clk_in(clk_in), .rst(rst), .s_axis_tdata(s_axis_tdata), .s_axis_tvalid(s_axis_tvalid), .start(start), .smp_num(smp_num), .seq_num(seq_num), .eth_clk_in(eth_clk_in), .req_ready(req_ready), .send_req(send_req), .m_axis_tdata(m_axis_tdata), .m_axis_tvalid(m_axis_tvalid), .m_axis_tready(m_axis_tready), .m_axis_tlast(m_axis_tlast), .finish(finish) ); initial begin clk_in = 1'b0; forever #5 clk_in = ~clk_in; end initial begin eth_clk_in = 1'b0; forever #4 eth_clk_in = ~eth_clk_in; end task automatic clear_scoreboard; integer i; begin packets_seen = 0; current_packet_byte_count = 0; total_words_captured = 0; for (i = 0; i < MAX_WORDS; i = i + 1) begin expected_words[i] = '0; captured_words_le[i] = '0; captured_words_be[i] = '0; end for (i = 0; i < PACKET_SIZE; i = i + 1) packet_bytes[i] = 8'h00; end endtask task automatic reset_dut; begin rst = 1'b1; start = 1'b0; s_axis_tdata = '0; s_axis_tvalid = 1'b0; smp_num = '0; seq_num = '0; req_ready = 1'b0; m_axis_tready = 1'b1; clear_scoreboard(); repeat(12) @(posedge clk_in); rst = 1'b0; repeat(8) @(posedge clk_in); end endtask task automatic pulse_start; begin @(posedge clk_in); start <= 1'b1; @(posedge clk_in); start <= 1'b0; end endtask task automatic send_one_sample(input logic [DATA_WIDTH-1:0] val); begin @(posedge clk_in); s_axis_tdata <= val; s_axis_tvalid <= 1'b1; end endtask task automatic stop_stream; begin @(posedge clk_in); s_axis_tdata <= '0; s_axis_tvalid <= 1'b0; end endtask task automatic run_test( input integer test_id, input integer seq_num_i, input integer smp_num_i, input bit randomize_data, input integer base_value, input string test_name ); logic [DATA_WIDTH-1:0] sample_mem [0:MAX_SEQ_NUM-1][0:(N_MAX*WINDOW_SIZE)-1]; integer seq_idx; integer sample_idx; integer word_idx; integer k; integer exp_word_count; integer exp_packet_count; integer sample_value; integer local_sum; integer timeout_cnt; bit le_ok; bit be_ok; integer errors_before; integer i; begin tests_total = tests_total + 1; errors_before = total_errors; if (smp_num_i <= 0 || smp_num_i > N_MAX * WINDOW_SIZE || (smp_num_i % WINDOW_SIZE) != 0) $fatal(1, "[%0s] invalid smp_num=%0d", test_name, smp_num_i); if (seq_num_i <= 0 || seq_num_i > MAX_SEQ_NUM) $fatal(1, "[%0s] invalid seq_num=%0d", test_name, seq_num_i); $display("\n========================================"); $display("TEST %0d: %0s", test_id, test_name); $display("seq_num=%0d smp_num=%0d randomize=%0d", seq_num_i, smp_num_i, randomize_data); $display("========================================"); reset_dut(); smp_num = smp_num_i; seq_num = seq_num_i; req_ready = 1'b1; // приемник готов заранее exp_word_count = smp_num_i / WINDOW_SIZE; exp_packet_count = (exp_word_count + READ_BATCH_SIZE - 1) / READ_BATCH_SIZE; for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin for (sample_idx = 0; sample_idx < smp_num_i; sample_idx = sample_idx + 1) begin if (randomize_data) sample_value = $unsigned($random(seed)) % (1 << DATA_WIDTH); else sample_value = (base_value + seq_idx * smp_num_i + sample_idx) % (1 << DATA_WIDTH); sample_mem[seq_idx][sample_idx] = sample_value[DATA_WIDTH-1:0]; end end for (word_idx = 0; word_idx < exp_word_count; word_idx = word_idx + 1) begin local_sum = 0; for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin for (k = 0; k < WINDOW_SIZE; k = k + 1) local_sum = local_sum + sample_mem[seq_idx][word_idx * WINDOW_SIZE + k]; end expected_words[word_idx] = local_sum[ACCUM_WIDTH-1:0]; $display(" expected[%0d] = %0d (0x%08x)", word_idx, expected_words[word_idx], expected_words[word_idx]); end pulse_start(); for (seq_idx = 0; seq_idx < seq_num_i; seq_idx = seq_idx + 1) begin for (sample_idx = 0; sample_idx < smp_num_i; sample_idx = sample_idx + 1) send_one_sample(sample_mem[seq_idx][sample_idx]); stop_stream(); repeat(2) @(posedge clk_in); end timeout_cnt = 0; while (packets_seen < exp_packet_count && timeout_cnt < 50 * PACKET_SIZE) begin @(posedge eth_clk_in); timeout_cnt = timeout_cnt + 1; end if (packets_seen < exp_packet_count) begin $display("[%0s] ERROR: timeout waiting packets, got=%0d exp=%0d", test_name, packets_seen, exp_packet_count); total_errors = total_errors + 1; end timeout_cnt = 0; while (finish !== 1'b1 && timeout_cnt < 30000) begin @(posedge clk_in); timeout_cnt = timeout_cnt + 1; end if (finish !== 1'b1) begin $display("[%0s] ERROR: timeout waiting finish", test_name); total_errors = total_errors + 1; end le_ok = 1'b1; be_ok = 1'b1; for (i = 0; i < exp_word_count; i = i + 1) begin if (captured_words_le[i] !== expected_words[i]) le_ok = 1'b0; if (captured_words_be[i] !== expected_words[i]) be_ok = 1'b0; end if (!le_ok && !be_ok) begin $display("[%0s] ERROR: payload mismatch", test_name); for (i = 0; i < exp_word_count; i = i + 1) $display(" idx=%0d exp=0x%08x le=0x%08x be=0x%08x", i, expected_words[i], captured_words_le[i], captured_words_be[i]); total_errors = total_errors + 1; end else if (le_ok) begin $display("[%0s] payload check passed in little-endian", test_name); end else begin $display("[%0s] payload check passed in big-endian", test_name); end if (total_errors == errors_before) begin tests_passed = tests_passed + 1; $display("TEST %0d PASSED: %0s", test_id, test_name); end else begin tests_failed = tests_failed + 1; $display("TEST %0d FAILED: %0s", test_id, test_name); end req_ready = 1'b0; repeat(10) @(posedge clk_in); end endtask always @(posedge eth_clk_in) begin : CAPTURE_AXIS integer idx; logic [31:0] tmp_le; logic [31:0] tmp_be; if (rst) begin current_packet_byte_count = 0; end else if (m_axis_tvalid && m_axis_tready) begin if (current_packet_byte_count < PACKET_SIZE) packet_bytes[current_packet_byte_count] = m_axis_tdata; current_packet_byte_count = current_packet_byte_count + 1; if (m_axis_tlast) begin packets_seen = packets_seen + 1; if (current_packet_byte_count != PACKET_SIZE) begin $display("[packet] ERROR: packet size=%0d expected=%0d", current_packet_byte_count, PACKET_SIZE); total_errors = total_errors + 1; end for (idx = 0; idx < READ_BATCH_SIZE; idx = idx + 1) begin tmp_le = { packet_bytes[idx*4 + 3], packet_bytes[idx*4 + 2], packet_bytes[idx*4 + 1], packet_bytes[idx*4 + 0] }; tmp_be = { packet_bytes[idx*4 + 0], packet_bytes[idx*4 + 1], packet_bytes[idx*4 + 2], packet_bytes[idx*4 + 3] }; if (total_words_captured + idx < MAX_WORDS) begin captured_words_le[total_words_captured + idx] = tmp_le; captured_words_be[total_words_captured + idx] = tmp_be; end end total_words_captured = total_words_captured + READ_BATCH_SIZE; current_packet_byte_count = 0; end end end initial begin seed = 32'h1badf00d; total_errors = 0; tests_total = 0; tests_failed = 0; tests_passed = 0; reset_dut(); run_test(1, 1, 1 * WINDOW_SIZE, 1'b0, 1, "deterministic_small"); // $finish; run_test(2, 2, 1 * WINDOW_SIZE, 1'b1, 0, "random_seq3_smp8"); run_test(3, 1, 16 * WINDOW_SIZE, 1'b1, 0, "random_seq5_smp16_multi_packet"); run_test(4, 2, 12 * WINDOW_SIZE, 1'b1, 0, "random_seq7_smp12"); run_test(5, 4, 256 * WINDOW_SIZE, 1'b1, 0, "random_max_smpnum"); run_test(6, 2, 1500 * WINDOW_SIZE, 1'b1, 0, "random_max_smpnum2"); $display("\n========================================"); $display("ALL TESTS COMPLETED"); $display("tests_total = %0d", tests_total); $display("tests_passed = %0d", tests_passed); $display("tests_failed = %0d", tests_failed); $display("total_errors = %0d", total_errors); $display("========================================"); if (total_errors != 0) $fatal(1, "TB FAILED with %0d error(s)", total_errors); else $display("TB PASSED"); $finish; end endmodule