347 lines
12 KiB
Systemverilog
347 lines
12 KiB
Systemverilog
`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 || (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, 1, 4, 1'b1, 0, "random_single_window");
|
|
run_test(5, 2, 12 * WINDOW_SIZE, 1'b1, 0, "random_seq7_smp12");
|
|
run_test(6, 4, 256 * WINDOW_SIZE, 1'b1, 0, "random_max_smpnum");
|
|
run_test(7, 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
|