From 7f9ad95e68943cd1fbfe2365ff9884e04650f206 Mon Sep 17 00:00:00 2001 From: Phil Date: Fri, 17 Apr 2026 21:51:00 +0300 Subject: [PATCH] tests: add simple tb for accum output fifo --- rtl/accum/tests/Makefile | 52 ++++++++ rtl/accum/tests/out_axis_fifo_tb.sv | 198 ++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 rtl/accum/tests/Makefile create mode 100644 rtl/accum/tests/out_axis_fifo_tb.sv diff --git a/rtl/accum/tests/Makefile b/rtl/accum/tests/Makefile new file mode 100644 index 0000000..1fed42f --- /dev/null +++ b/rtl/accum/tests/Makefile @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich +# + +# FPGA settings +FPGA_PART = xc7a35tfgg484-1 +FPGA_TOP = control +FPGA_ARCH = artix7 + +RTL_DIR = ../src + + +include ../../../scripts/vivado.mk + +SYN_FILES += $(sort $(shell find ../src -type f \( -name '*.v' -o -name '*.sv' \))) + +XCI_FILES = $(sort $(shell find ../src -type f -name '*.xci')) + +XDC_FILES += ../../../constraints/ax7a035b.xdc +# XDC_FILES += test_timing.xdc + +# SYN_FILES += controller_tb.sv +# SIM_TOP = control_tb + + +program: $(PROJECT).bit + echo "open_hw_manager" > program.tcl + echo "connect_hw_server" >> program.tcl + echo "open_hw_target" >> program.tcl + echo "current_hw_device [lindex [get_hw_devices] 0]" >> program.tcl + echo "refresh_hw_device -update_hw_probes false [current_hw_device]" >> program.tcl + echo "set_property PROGRAM.FILE {$(PROJECT).bit} [current_hw_device]" >> program.tcl + echo "program_hw_devices [current_hw_device]" >> program.tcl + echo "exit" >> program.tcl + vivado -nojournal -nolog -mode batch -source program.tcl + +$(PROJECT).mcs $(PROJECT).prm: $(PROJECT).bit + echo "write_cfgmem -force -format mcs -size 16 -interface SPIx4 -loadbit {up 0x0000000 $*.bit} -checksum -file $*.mcs" > generate_mcs.tcl + echo "exit" >> generate_mcs.tcl + vivado -nojournal -nolog -mode batch -source generate_mcs.tcl + mkdir -p rev + COUNT=100; \ + while [ -e rev/$*_rev$$COUNT.bit ]; \ + do COUNT=$$((COUNT+1)); done; \ + COUNT=$$((COUNT-1)); \ + for x in .mcs .prm; \ + do cp $*$$x rev/$*_rev$$COUNT$$x; \ + echo "Output: rev/$*_rev$$COUNT$$x"; done; diff --git a/rtl/accum/tests/out_axis_fifo_tb.sv b/rtl/accum/tests/out_axis_fifo_tb.sv new file mode 100644 index 0000000..d785296 --- /dev/null +++ b/rtl/accum/tests/out_axis_fifo_tb.sv @@ -0,0 +1,198 @@ +`timescale 1ns/1ps + +module tb_out_axis_fifo; + + localparam int ACCUM_WIDTH = 32; + localparam int WINDOW_SIZE = 65; + localparam int PACKET_SIZE = 1024; + + 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 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), + + .batch_req (batch_req), + .finish (finish) + ); + + // ----------------------------- + // clocks + // ----------------------------- + initial begin + eth_clk_in = 0; + forever #4 eth_clk_in = ~eth_clk_in; // 125 MHz + end + + initial begin + acc_clk_in = 0; + forever #3 acc_clk_in = ~acc_clk_in; // ~166.7 MHz + end + + // ----------------------------- + // simple AXIS sink + // ----------------------------- + initial begin + s_axis_tready = 1'b1; + 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; + readout_begin = 1'b0; + din_valid = 1'b0; + acc_din = '0; + smp_num = '0; + + 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; + begin + for (i = 0; i < n_words; i++) begin + @(posedge acc_clk_in); + din_valid <= 1'b1; + acc_din <= $urandom; + end + + @(posedge acc_clk_in); + din_valid <= 1'b0; + acc_din <= '0; + end + endtask + + // Один запуск: + // 1) задаём smp_num + // 2) даём readout_begin + // 3) каждый раз, когда DUT просит batch_req, отправляем PACKET_SIZE слов + // 4) ждём finish + task automatic run_case(input logic [31:0] smp_num_i); + int batch_count; + begin + batch_count = 0; + + $display("[%0t] run_case start, smp_num=%0d", $time, smp_num_i); + + pulse_readout_begin(smp_num_i); + + while (finish !== 1'b1) begin + @(posedge acc_clk_in); + + 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); + 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); + + @(posedge acc_clk_in); + end + endtask + + // ----------------------------- + // monitor + // ----------------------------- + int axis_byte_count; + + always_ff @(posedge eth_clk_in or posedge rst) begin + if (rst) begin + axis_byte_count <= 0; + end else begin + 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; + + // 1-й запуск + do_reset(); + run_case(32'd17); + + // 2-й запуск + do_reset(); + run_case(32'd1024); + + // 3-й запуск + do_reset(); + run_case(32'd77777); + + do_reset(); + repeat (50) @(posedge acc_clk_in); + $display("[%0t] ALL TESTS DONE", $time); + $finish; + end + +endmodule \ No newline at end of file