From 4786d2d7f614c96f976f263759780493629786ce Mon Sep 17 00:00:00 2001 From: Phil Date: Fri, 17 Apr 2026 21:50:30 +0300 Subject: [PATCH 01/25] rtl: wip accum output module, currently only with write part --- rtl/accum/src/out_axis_fifo.sv | 193 +++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 rtl/accum/src/out_axis_fifo.sv diff --git a/rtl/accum/src/out_axis_fifo.sv b/rtl/accum/src/out_axis_fifo.sv new file mode 100644 index 0000000..10d7faa --- /dev/null +++ b/rtl/accum/src/out_axis_fifo.sv @@ -0,0 +1,193 @@ +module out_axis_fifo #( + parameter ACCUM_WIDTH = 32, + parameter WINDOW_SIZE = 65, + parameter PACKET_SIZE = 1024 +) ( + input logic eth_clk_in, + input logic acc_clk_in, + input logic rst, + input logic [31:0] smp_num, + + // AXI stream master for output, eth_clk_in domain + output logic [7:0] s_axis_tdata, + output logic s_axis_tvalid, + input logic s_axis_tready, + output logic s_axis_tlast, + + // data from acc + input logic [ACCUM_WIDTH-1:0] acc_din, + input logic din_valid, + + // input pulse + input logic readout_begin, + + // output pulses + output logic batch_req, + output logic finish +); + + // fifo params calc + // round up to be enough for 2xPACKET_SIZE storage + localparam int MIN_BYTES = 2 * PACKET_SIZE; + localparam int MIN_BITS = MIN_BYTES * 8; + localparam int MIN_WR_WORDS = (MIN_BITS + ACCUM_WIDTH - 1) / ACCUM_WIDTH; // ceil div + localparam int WDEPTH_BITS = $clog2(MIN_WR_WORDS); + localparam int FIFO_WDEPTH = 1 << WDEPTH_BITS; + + localparam int FIFO_RDEPTH = FIFO_WDEPTH * ACCUM_WIDTH / 8; + localparam int RDEPTH_BITS = $clog2(FIFO_RDEPTH) + 1; + + + wire rd_unavail; + wire wr_unavail; + reg rd_en; + + + typedef enum logic [2:0] { + WR_IDLE = 3'd0, + WR_CHECK = 3'd1, + WR_RUN = 3'd2, + WR_END = 3'd3 + } wr_state_t; + + (* MARK_DEBUG="true" *) wr_state_t wr_state; + + // Write FSM + reg [31:0] wr_cnt; // current BIT mem ptr + reg [31:0] wr_batch_tgt; // next 'target' that should be written from batch + reg [31:0] wr_total; // total BITS to be sent! + + // NOTE: + // each written "acc_din" ACCUM_WIDTH word + // is counted as WINDOWS_SIZE samples actually + // because hw division for counters is painful + // so we just increased the counter sizes + + always_ff @(posedge acc_clk_in or posedge rst) begin + if (rst) begin + wr_state <= WR_IDLE; + wr_cnt <= 32'b0; + wr_batch_tgt <= 32'b0; + wr_total <= 32'b0; + batch_req <= 0; + finish <= 0; + + end else begin + + case (wr_state) + // wait until readout is requested + WR_IDLE: begin + if (readout_begin) begin + wr_cnt <= 32'b0; + wr_state <= WR_CHECK; + wr_total <= smp_num * ACCUM_WIDTH; + wr_batch_tgt <= 32'b0; + end + end + + // wait until we can request a word + // depends on prog_full signal + WR_CHECK: begin + if (~wr_unavail) begin + batch_req <= 1; + // should give us exactly PACKET_SIZE * 8 bits + // multiplied by WINDOW_SIZE, because we count + // each given ACCUM_WIDTH word as WINDOWS_SIZE samples !!! + wr_batch_tgt <= wr_batch_tgt + (8 * WINDOW_SIZE * PACKET_SIZE); + wr_state <= WR_RUN; + end else begin + batch_req <= 0; + end + end + + // wait until all requested packet is written + WR_RUN: begin + batch_req <= 0; + if (din_valid) begin + // data supplied + // count as we got WINDOW_SIZE samples + wr_cnt <= wr_cnt + ACCUM_WIDTH * WINDOW_SIZE; + end else if (wr_cnt == wr_batch_tgt) begin + // got enough words + wr_state <= WR_END; + end else if (wr_cnt > wr_batch_tgt) begin + // weird case when accum gave us too much words + // block resets + wr_cnt <= 32'hffffffff; // sort of signal for sim/ila + wr_state <= WR_END; + end + end + + // check if this was last data batch + WR_END: begin + // here we check that we sent enough data + // wr_cnt should be by design PACKET_SIZE-aligned + if (wr_cnt >= wr_total) begin + finish <= 1; + wr_state <= WR_IDLE; + end else begin + // next word + wr_state <= WR_CHECK; + end + end + endcase + end + end + + // xpm_fifo_async: Asynchronous FIFO + // Xilinx Parameterized Macro, version 2025.1 + + xpm_fifo_async #( + .DOUT_RESET_VALUE("0"), // String + .FIFO_READ_LATENCY(1), // DECIMAL + .FIFO_WRITE_DEPTH(FIFO_WDEPTH), + .FULL_RESET_VALUE(0), + .PROG_EMPTY_THRESH(PACKET_SIZE), + .PROG_FULL_THRESH(PACKET_SIZE), + .RD_DATA_COUNT_WIDTH(RDEPTH_BITS), + .READ_DATA_WIDTH(8), // always 8 bit for eth + .READ_MODE("fwft"), + .SIM_ASSERT_CHK(1), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages + .USE_ADV_FEATURES("1606"), // String + .WRITE_DATA_WIDTH(ACCUM_WIDTH), + .WR_DATA_COUNT_WIDTH(WDEPTH_BITS) + ) + xpm_fifo_async_inst ( + + .data_valid(s_axis_tvalid), // 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the + // output bus (dout). + + .dout(s_axis_tdata), + .empty( ), + + .full( ), + + .prog_empty(rd_unavail), // 1-bit output: Programmable Empty: This signal is asserted when the number of words in the FIFO is less than + // or equal to the programmable empty threshold value. It is de-asserted when the number of words in the FIFO + // exceeds the programmable empty threshold value. + + .prog_full(wr_unavail), // 1-bit output: Programmable Full: This signal is asserted when the number of words in the FIFO is greater than + // or equal to the programmable full threshold value. It is de-asserted when the number of words in the FIFO is + // less than the programmable full threshold value. + + .rd_data_count( ), // RD_DATA_COUNT_WIDTH-bit output: Read Data Count: This bus indicates the number of words read from the FIFO. + + .wr_data_count( ), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the + // FIFO. + + + + + .rd_clk(eth_clk_in), // 1-bit input: Read clock: Used for read operation. rd_clk must be a free running clock. + .rd_en(rd_en), // 1-bit input: Read Enable: If the FIFO is not empty, asserting this signal causes data (on dout) to be read + // from the FIFO. Must be held active-low when rd_rst_busy is active high. + + .rst(rst), + + .din(acc_din), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO. + .wr_clk(acc_clk_in), // 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock. + .wr_en(din_valid) + + ); + +endmodule \ No newline at end of file -- 2.49.0 From 7f9ad95e68943cd1fbfe2365ff9884e04650f206 Mon Sep 17 00:00:00 2001 From: Phil Date: Fri, 17 Apr 2026 21:51:00 +0300 Subject: [PATCH 02/25] 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 -- 2.49.0 From 8e46f965df9362e40325e9339402579357d83c0b Mon Sep 17 00:00:00 2001 From: Phil Date: Fri, 17 Apr 2026 21:58:20 +0300 Subject: [PATCH 03/25] fix: incorrect fifo threshold value --- rtl/accum/src/out_axis_fifo.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/accum/src/out_axis_fifo.sv b/rtl/accum/src/out_axis_fifo.sv index 10d7faa..c73fa59 100644 --- a/rtl/accum/src/out_axis_fifo.sv +++ b/rtl/accum/src/out_axis_fifo.sv @@ -143,7 +143,7 @@ module out_axis_fifo #( .FIFO_WRITE_DEPTH(FIFO_WDEPTH), .FULL_RESET_VALUE(0), .PROG_EMPTY_THRESH(PACKET_SIZE), - .PROG_FULL_THRESH(PACKET_SIZE), + .PROG_FULL_THRESH(PACKET_SIZE / (ACCUM_WIDTH / 8)), .RD_DATA_COUNT_WIDTH(RDEPTH_BITS), .READ_DATA_WIDTH(8), // always 8 bit for eth .READ_MODE("fwft"), -- 2.49.0 From 21785aaac713615bb67cb01e116216ae1fd1829d Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 17:26:02 +0300 Subject: [PATCH 04/25] rtl: send part of out_axis_fifo --- rtl/accum/src/out_axis_fifo.sv | 224 ++++++++++++++++++++++++--------- 1 file changed, 166 insertions(+), 58 deletions(-) diff --git a/rtl/accum/src/out_axis_fifo.sv b/rtl/accum/src/out_axis_fifo.sv index c73fa59..1bfc079 100644 --- a/rtl/accum/src/out_axis_fifo.sv +++ b/rtl/accum/src/out_axis_fifo.sv @@ -13,6 +13,10 @@ module out_axis_fifo #( output logic s_axis_tvalid, input logic s_axis_tready, output logic s_axis_tlast, + // eth handshake + input logic req_ready, + output logic send_req, + output logic [15:0] udp_data_length, // data from acc input logic [ACCUM_WIDTH-1:0] acc_din, @@ -25,6 +29,32 @@ module out_axis_fifo #( output logic batch_req, output logic finish ); + // sync reset + reg [1:0] rst_sync_ff; + reg rst_eth; + + always @(posedge acc_clk_in or posedge rst) begin + if (rst) begin + rst_sync_ff <= 2'b11; + end else begin + rst_sync_ff <= {rst_sync_ff[0], 1'b0}; + end + end + + assign rst_eth = rst_sync_ff[1]; + + logic [1:0] rst_acc_ff; + logic rst_acc; + + always_ff @(posedge acc_clk_in or posedge rst) begin + if (rst) + rst_acc_ff <= 2'b11; + else + rst_acc_ff <= {rst_acc_ff[0], 1'b0}; + end + + assign rst_acc = rst_acc_ff[1]; + // fifo params calc // round up to be enough for 2xPACKET_SIZE storage @@ -37,9 +67,8 @@ module out_axis_fifo #( localparam int FIFO_RDEPTH = FIFO_WDEPTH * ACCUM_WIDTH / 8; localparam int RDEPTH_BITS = $clog2(FIFO_RDEPTH) + 1; - - wire rd_unavail; wire wr_unavail; + wire wr_rst_busy; reg rd_en; @@ -57,14 +86,16 @@ module out_axis_fifo #( reg [31:0] wr_batch_tgt; // next 'target' that should be written from batch reg [31:0] wr_total; // total BITS to be sent! + wire [WDEPTH_BITS:0] wr_data_count; + // NOTE: // each written "acc_din" ACCUM_WIDTH word // is counted as WINDOWS_SIZE samples actually // because hw division for counters is painful // so we just increased the counter sizes - always_ff @(posedge acc_clk_in or posedge rst) begin - if (rst) begin + always_ff @(posedge acc_clk_in) begin + if (rst_acc) begin wr_state <= WR_IDLE; wr_cnt <= 32'b0; wr_batch_tgt <= 32'b0; @@ -82,13 +113,15 @@ module out_axis_fifo #( wr_state <= WR_CHECK; wr_total <= smp_num * ACCUM_WIDTH; wr_batch_tgt <= 32'b0; + batch_req <= 0; + finish <= 0; end end // wait until we can request a word // depends on prog_full signal WR_CHECK: begin - if (~wr_unavail) begin + if (~wr_unavail && ~wr_rst_busy) begin batch_req <= 1; // should give us exactly PACKET_SIZE * 8 bits // multiplied by WINDOW_SIZE, because we count @@ -103,11 +136,7 @@ module out_axis_fifo #( // wait until all requested packet is written WR_RUN: begin batch_req <= 0; - if (din_valid) begin - // data supplied - // count as we got WINDOW_SIZE samples - wr_cnt <= wr_cnt + ACCUM_WIDTH * WINDOW_SIZE; - end else if (wr_cnt == wr_batch_tgt) begin + if (wr_cnt == wr_batch_tgt) begin // got enough words wr_state <= WR_END; end else if (wr_cnt > wr_batch_tgt) begin @@ -116,6 +145,12 @@ module out_axis_fifo #( wr_cnt <= 32'hffffffff; // sort of signal for sim/ila wr_state <= WR_END; end + + if (din_valid) begin + // data supplied + // count as we got WINDOW_SIZE samples + wr_cnt <= wr_cnt + ACCUM_WIDTH * WINDOW_SIZE; + end end // check if this was last data batch @@ -123,8 +158,11 @@ module out_axis_fifo #( // here we check that we sent enough data // wr_cnt should be by design PACKET_SIZE-aligned if (wr_cnt >= wr_total) begin - finish <= 1; - wr_state <= WR_IDLE; + // wait until all data is sent + if (wr_data_count == 0) begin + finish <= 1; + wr_state <= WR_IDLE; + end end else begin // next word wr_state <= WR_CHECK; @@ -133,61 +171,131 @@ module out_axis_fifo #( endcase end end + + // Readout FSM with ethernet request + + assign udp_data_length = PACKET_SIZE; // fixed packet size + reg [15:0] sent_cnt; + + typedef enum logic [2:0] { + RD_IDLE = 3'd0, + RD_CHECK = 3'd1, + RD_SEND = 3'd2 + } rd_state_t; + + (* MARK_DEBUG="true" *) rd_state_t rd_state; + + wire rd_valid; + wire [RDEPTH_BITS-1:0] rd_data_count; + + always_ff @(posedge eth_clk_in) begin + if (rst_eth) begin + rd_state <= RD_IDLE; + send_req <= 1'b0; + sent_cnt <= 16'd0; + s_axis_tlast <= 1'b0; + s_axis_tvalid <= 1'b0; + rd_en <= 1'b0; + + end else begin + + case (rd_state) + // wait until fifo has enough data to send + RD_IDLE: begin + if (rd_data_count == PACKET_SIZE) begin + // enough data to send packet, begin + rd_state <= RD_CHECK; + end + send_req <= 1'b0; + sent_cnt <= 16'd0; + rd_en <= 1'b0; + s_axis_tlast <= 1'b0; + s_axis_tvalid <= 1'b0; + end + + // await udp ready + RD_CHECK: begin + if (req_ready) begin + send_req <= 1'b1; + rd_state <= RD_SEND; + end + end + + // send data + RD_SEND: begin + // udp is ready and fifo is ready = sent + send_req <= 1'b0; + if (s_axis_tready && rd_valid) begin + rd_en <= 1'b1; + s_axis_tvalid <= 1'b1; + sent_cnt <= sent_cnt + 1; + // final packet of the batch + if (sent_cnt == PACKET_SIZE - 1) begin + rd_state <= RD_IDLE; + s_axis_tlast <= 1'b1; + end + end else begin + rd_en <= 1'b0; + s_axis_tvalid <= 1'b0; + end + end + endcase + end + end + + + // xpm_fifo_async: Asynchronous FIFO + // Xilinx Parameterized Macro, version 2025.1 + + xpm_fifo_async #( + .DOUT_RESET_VALUE("0"), // String + .FIFO_READ_LATENCY(1), // DECIMAL + .FIFO_WRITE_DEPTH(FIFO_WDEPTH), + .FULL_RESET_VALUE(0), + .PROG_EMPTY_THRESH(PACKET_SIZE), + .PROG_FULL_THRESH(PACKET_SIZE / (ACCUM_WIDTH / 8)), + .RD_DATA_COUNT_WIDTH(RDEPTH_BITS), + .READ_DATA_WIDTH(8), // always 8 bit for eth + .READ_MODE("fwft"), + .SIM_ASSERT_CHK(1), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages + .USE_ADV_FEATURES("1616"), // String + .WRITE_DATA_WIDTH(ACCUM_WIDTH), + .WR_DATA_COUNT_WIDTH(WDEPTH_BITS+1) + ) + xpm_fifo_async_inst ( + + .data_valid(rd_valid), // 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the + // output bus (dout). + + .dout(s_axis_tdata), + .empty( ), - // xpm_fifo_async: Asynchronous FIFO - // Xilinx Parameterized Macro, version 2025.1 + .full( ), - xpm_fifo_async #( - .DOUT_RESET_VALUE("0"), // String - .FIFO_READ_LATENCY(1), // DECIMAL - .FIFO_WRITE_DEPTH(FIFO_WDEPTH), - .FULL_RESET_VALUE(0), - .PROG_EMPTY_THRESH(PACKET_SIZE), - .PROG_FULL_THRESH(PACKET_SIZE / (ACCUM_WIDTH / 8)), - .RD_DATA_COUNT_WIDTH(RDEPTH_BITS), - .READ_DATA_WIDTH(8), // always 8 bit for eth - .READ_MODE("fwft"), - .SIM_ASSERT_CHK(1), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages - .USE_ADV_FEATURES("1606"), // String - .WRITE_DATA_WIDTH(ACCUM_WIDTH), - .WR_DATA_COUNT_WIDTH(WDEPTH_BITS) - ) - xpm_fifo_async_inst ( + .prog_full(wr_unavail), // 1-bit output: Programmable Full: This signal is asserted when the number of words in the FIFO is greater than + // or equal to the programmable full threshold value. It is de-asserted when the number of words in the FIFO is + // less than the programmable full threshold value. - .data_valid(s_axis_tvalid), // 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the - // output bus (dout). + .rd_data_count(rd_data_count), // RD_DATA_COUNT_WIDTH-bit output: Read Data Count: This bus indicates the number of words read from the FIFO. - .dout(s_axis_tdata), - .empty( ), - - .full( ), - - .prog_empty(rd_unavail), // 1-bit output: Programmable Empty: This signal is asserted when the number of words in the FIFO is less than - // or equal to the programmable empty threshold value. It is de-asserted when the number of words in the FIFO - // exceeds the programmable empty threshold value. - - .prog_full(wr_unavail), // 1-bit output: Programmable Full: This signal is asserted when the number of words in the FIFO is greater than - // or equal to the programmable full threshold value. It is de-asserted when the number of words in the FIFO is - // less than the programmable full threshold value. - - .rd_data_count( ), // RD_DATA_COUNT_WIDTH-bit output: Read Data Count: This bus indicates the number of words read from the FIFO. - - .wr_data_count( ), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the - // FIFO. + .wr_data_count(wr_data_count), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the + // FIFO. - .rd_clk(eth_clk_in), // 1-bit input: Read clock: Used for read operation. rd_clk must be a free running clock. - .rd_en(rd_en), // 1-bit input: Read Enable: If the FIFO is not empty, asserting this signal causes data (on dout) to be read - // from the FIFO. Must be held active-low when rd_rst_busy is active high. + .rd_clk(eth_clk_in), // 1-bit input: Read clock: Used for read operation. rd_clk must be a free running clock. + .rd_en(rd_en), // 1-bit input: Read Enable: If the FIFO is not empty, asserting this signal causes data (on dout) to be read + // from the FIFO. Must be held active-low when rd_rst_busy is active high. - .rst(rst), - - .din(acc_din), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO. - .wr_clk(acc_clk_in), // 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock. - .wr_en(din_valid) + .rst(rst), + + .din(acc_din), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO. + .wr_clk(acc_clk_in), // 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock. + .wr_en(din_valid), - ); + .wr_rst_busy(wr_rst_busy) + + ); endmodule \ No newline at end of file -- 2.49.0 From 4eb937e13f1c6005d36ebe3f9ddacf4c69afddf9 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:46:51 +0300 Subject: [PATCH 05/25] infra: make default sim longer --- scripts/vivado.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/vivado.mk b/scripts/vivado.mk index 358973e..80131ca 100644 --- a/scripts/vivado.mk +++ b/scripts/vivado.mk @@ -180,6 +180,8 @@ sim: $(PROJECT).xpr gen_ip echo "update_compile_order -fileset sources_1" >> run_sim.tcl echo "update_compile_order -fileset sim_1" >> run_sim.tcl echo "launch_simulation" >> run_sim.tcl + echo "run 1000 us" >> run_sim.tcl + echo "quit" >> run_sim.tcl vivado -mode batch -source run_sim.tcl simclean: -- 2.49.0 From dfccc012252149d17f56d998ed73e0d4ef16540f Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:47:27 +0300 Subject: [PATCH 06/25] 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 -- 2.49.0 From 3dcaaf8ea5582c334220bc3d16e9c2132e182f7f Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:47:46 +0300 Subject: [PATCH 07/25] fix: better sync for accum fifo --- rtl/accum/src/out_axis_fifo.sv | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/rtl/accum/src/out_axis_fifo.sv b/rtl/accum/src/out_axis_fifo.sv index 1bfc079..7749d93 100644 --- a/rtl/accum/src/out_axis_fifo.sv +++ b/rtl/accum/src/out_axis_fifo.sv @@ -243,6 +243,27 @@ module out_axis_fifo #( end end + logic [ACCUM_WIDTH-1:0] fifo_din_r, acc_din_reg, din_valid_reg; + logic fifo_wr_en_r; + + always_ff @(posedge acc_clk_in) begin + if (rst_acc) begin + fifo_din_r <= '0; + fifo_wr_en_r <= 1'b0; + + din_valid_reg <= 1'b0; + end else begin + fifo_wr_en_r <= 1'b0; + acc_din_reg <= acc_din; + + if (!wr_rst_busy && din_valid_reg) begin + fifo_din_r <= acc_din_reg; + fifo_wr_en_r <= 1'b1; + end + + din_valid_reg <= din_valid; + end + end // xpm_fifo_async: Asynchronous FIFO // Xilinx Parameterized Macro, version 2025.1 @@ -290,9 +311,9 @@ module out_axis_fifo #( .rst(rst), - .din(acc_din), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO. + .din(fifo_din_r), // WRITE_DATA_WIDTH-bit input: Write Data: The input data bus used when writing the FIFO. .wr_clk(acc_clk_in), // 1-bit input: Write clock: Used for write operation. wr_clk must be a free running clock. - .wr_en(din_valid), + .wr_en(fifo_wr_en_r), .wr_rst_busy(wr_rst_busy) -- 2.49.0 From 7be26d9d1a873dfb69b702668cd3d191f796bbfb Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:47:56 +0300 Subject: [PATCH 08/25] chore: update tb files --- rtl/accum/tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtl/accum/tests/Makefile b/rtl/accum/tests/Makefile index 1fed42f..b45b6bc 100644 --- a/rtl/accum/tests/Makefile +++ b/rtl/accum/tests/Makefile @@ -8,7 +8,7 @@ # FPGA settings FPGA_PART = xc7a35tfgg484-1 -FPGA_TOP = control +FPGA_TOP = accum FPGA_ARCH = artix7 RTL_DIR = ../src @@ -21,10 +21,10 @@ 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 +XDC_FILES += test_timing.xdc -# SYN_FILES += controller_tb.sv -# SIM_TOP = control_tb +SYN_FILES += out_axis_fifo_tb.sv +SIM_TOP = control_tb program: $(PROJECT).bit -- 2.49.0 From b54e69dec0905e72ff4cdb7b5a437dd5f7f6b861 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 21 Apr 2026 19:48:20 +0300 Subject: [PATCH 09/25] chore: add clocks for accum_fifo impl test --- rtl/accum/tests/test_timing.xdc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 rtl/accum/tests/test_timing.xdc diff --git a/rtl/accum/tests/test_timing.xdc b/rtl/accum/tests/test_timing.xdc new file mode 100644 index 0000000..56cb725 --- /dev/null +++ b/rtl/accum/tests/test_timing.xdc @@ -0,0 +1,10 @@ +# Primary clocks +create_clock -name eth_clk -period 8.000 [get_ports eth_clk_in] +create_clock -name acc_clk -period 15.385 [get_ports acc_clk_in] + + +# Asynchronous clock groups + +set_clock_groups -name ASYNC_ETH_ACC -asynchronous \ + -group [get_clocks eth_clk] \ + -group [get_clocks acc_clk] -- 2.49.0 From a8a3aff498b1585a4a936f7279fb7d692f372103 Mon Sep 17 00:00:00 2001 From: Phil Date: Wed, 22 Apr 2026 16:38:22 +0300 Subject: [PATCH 10/25] rtl: first impl of adder+accum --- rtl/accum/src/accum.sv | 272 +++++++++++++++++++++++++++++++++++++++++ rtl/accum/src/adder.sv | 52 ++++++++ 2 files changed, 324 insertions(+) create mode 100644 rtl/accum/src/accum.sv create mode 100644 rtl/accum/src/adder.sv diff --git a/rtl/accum/src/accum.sv b/rtl/accum/src/accum.sv new file mode 100644 index 0000000..51e2285 --- /dev/null +++ b/rtl/accum/src/accum.sv @@ -0,0 +1,272 @@ +`timescale 1ns / 1ps + +module accumulator +#( + parameter DATA_WIDTH = 12, + parameter ACCUM_WIDTH = 32, + parameter N_MAX = 4096, + parameter WINDOW_SIZE = 4, + parameter PACKET_SIZE = 8, + parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) + ) +( + input clk_in, + input rst, + input [DATA_WIDTH-1:0] s_axis_tdata, + input s_axis_tvalid, + input start, + input [31:0] smp_num, + input [15:0] seq_num, + + output [ACCUM_WIDTH-1:0] out_data, + output out_valid, + output readout_begin, + input batch_req, + input finish + ); + + logic [31:0] smp_num_reg, cnt_smp_num; + logic [15:0] seq_num_reg, cnt_seq_num; + logic [15:0] cnt_addr, addra, addrb; + + logic [ACCUM_WIDTH-1:0] data; + logic valid_data; + logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; + logic wea, enb; + + logic readout_begin_reg; + logic [ACCUM_WIDTH-1:0] out_data_reg; + logic out_valid_reg; + logic finish_reg, finish_buf; + + // registers for port b data request + reg req_data_b; + reg [15:0] req_addr_b; + + typedef enum logic [3:0] { + IDLE, + INIT_MEM, + BEGIN_SEQ, + REQ_WORD_B, + ACCUM, + READOUT_START, + READOUT_AWAIT, + READOUT_DELAY, + READOUT_PUT, + READOUT_LAST, + FINISH + } wr_state_t; + (* MARK_DEBUG="true" *) wr_state_t wr_state; + + always @(posedge clk_in) begin + if (rst) begin + smp_num_reg <= '0; + cnt_smp_num <= '0; + seq_num_reg <= '0; + cnt_seq_num <= '0; + cnt_addr <= '0; + wea <= 0; + enb <= 0; + wr_state <= IDLE; + finish_reg <= 0; + out_valid_reg <= 0; + end else begin + finish_buf <= finish; + + // FSM + case(wr_state) + + IDLE: begin + // wait for start signal + wea <= 0; + enb <= 0; + readout_begin_reg <= 0; + finish_reg <= 0; + out_valid_reg <= 0; + if (start) begin + smp_num_reg <= smp_num; + seq_num_reg <= seq_num; + wr_state <= INIT_MEM; + end + + end + INIT_MEM: begin + // first run to initialize memory with first batch of values + wea <= 0; + if (valid_data) begin + data_bram_in <= data; + addra <= cnt_addr; + wea <= 1; + cnt_addr <= cnt_addr + 1; + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + + end + if (cnt_smp_num >= smp_num_reg) begin + wr_state <= BEGIN_SEQ; + end + + end + BEGIN_SEQ: begin + // start new acc seq + wea <= 0; + enb <= 0; + if (cnt_seq_num == seq_num_reg - 1) begin + cnt_seq_num <= '0; + cnt_smp_num <= '0; + cnt_addr <= '0; + wr_state <= READOUT_START; + addrb <= '0; + enb <= 0; + end else begin + // beginning of new data sequence + cnt_seq_num <= cnt_seq_num + 1; + cnt_smp_num <= '0; + cnt_addr <= '0; + wea <= 0; + addrb <= 0; + wr_state <= REQ_WORD_B; + end + end + + REQ_WORD_B: begin + // pre-request data for port b + wea <= 0; + enb <= 1; + addrb <= cnt_addr; + wr_state <= ACCUM; + end + + ACCUM: begin + // sum mem+input + enb <= 0; + if (valid_data) begin + addra <= cnt_addr; + wea <= 1; + data_bram_in <= data + data_bram_out; + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin + wr_state <= BEGIN_SEQ; + end else begin + cnt_addr <= cnt_addr + 1; + wr_state <= REQ_WORD_B; + end + end + end + READOUT_START: begin + readout_begin_reg <= 1'b1; + wr_state <= READOUT_AWAIT; + addrb <= 0; + enb <= 0; + end + + READOUT_AWAIT: begin + // req await + delay for every-clock readout. + if (batch_req) begin + enb <= 1; + wr_state <= READOUT_DELAY; + end else if (finish_buf) begin + wr_state <= FINISH; + end else begin + enb <= 0; + out_valid_reg <= 0; + end + end + + READOUT_DELAY: begin + // wait for mem latency + addrb <= addrb + 1; + wr_state <= READOUT_PUT; + end + + READOUT_PUT: begin + // main data output + if (addrb == READ_BATCH_SIZE) begin + wr_state <= READOUT_LAST; + enb <= 0; + end + addrb <= addrb + 1; + out_valid_reg <= 1; + out_data_reg <= data_bram_out; + end + + READOUT_LAST: begin + // last word of packet + out_valid_reg <= 0; + out_data_reg <= data_bram_out; + wr_state <= FINISH; + end + + FINISH: begin + out_valid_reg <= 0; + enb <= 0; + wr_state <= IDLE; + end + + default: wr_state <= IDLE; + endcase + end + end + + + adder + #( + .DATA_WIDTH(DATA_WIDTH), + .WINDOW_SIZE(WINDOW_SIZE), + .ACCUM_WIDTH(ACCUM_WIDTH) + ) adder_dut + ( + .clk_in(clk_in), + .rst(rst), + .s_axis_tdata(s_axis_tdata), + .s_axis_tvalid(s_axis_tvalid), + .sum_data(data), + .sum_valid(valid_data) + ); + + xpm_memory_sdpram #( + .ADDR_WIDTH_A(16), // DECIMAL + .ADDR_WIDTH_B(16), // DECIMAL + .AUTO_SLEEP_TIME(0), // DECIMAL + .BYTE_WRITE_WIDTH_A(ACCUM_WIDTH), // DECIMAL + .CASCADE_HEIGHT(0), // DECIMAL + .CLOCKING_MODE("common_clock"), // String + .ECC_MODE("no_ecc"), // String + .MEMORY_INIT_FILE("none"), // String + .MEMORY_INIT_PARAM("0"), // String + .MEMORY_OPTIMIZATION("true"), // String + .MEMORY_PRIMITIVE("auto"), // String + .MEMORY_SIZE(N_MAX*ACCUM_WIDTH), // DECIMAL + .MESSAGE_CONTROL(0), // DECIMAL + .READ_DATA_WIDTH_B(ACCUM_WIDTH), // DECIMAL + .READ_LATENCY_B(1), // DECIMAL + .READ_RESET_VALUE_B("0"), // String + .RST_MODE_A("SYNC"), // String + .RST_MODE_B("SYNC"), // String + .SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages + .USE_EMBEDDED_CONSTRAINT(0), // DECIMAL + .USE_MEM_INIT(1), // DECIMAL + .USE_MEM_INIT_MMI(0), // DECIMAL + .WAKEUP_TIME("disable_sleep"), // String + .WRITE_DATA_WIDTH_A(ACCUM_WIDTH), // DECIMAL + .WRITE_MODE_B("no_change"), // String + .WRITE_PROTECT(1) // DECIMAL + ) + xpm_memory_sdpram_inst ( + + .doutb(data_bram_out), + + .addra(addra), + .addrb(addrb), + .clka(clk_in), + .clkb(clk_in), + .dina(data_bram_in), + .ena(1'b1), + .enb(enb), + .wea(wea) + ); + + assign readout_begin = readout_begin_reg; + assign out_data = out_data_reg; + assign out_valid = out_valid_reg; + +endmodule diff --git a/rtl/accum/src/adder.sv b/rtl/accum/src/adder.sv new file mode 100644 index 0000000..dd1c3c9 --- /dev/null +++ b/rtl/accum/src/adder.sv @@ -0,0 +1,52 @@ +`timescale 1ns / 1ps + + +module adder +#( + parameter DATA_WIDTH = 12, + parameter WINDOW_SIZE = 4, + parameter ACCUM_WIDTH = 32 + ) +( + input clk_in, + input rst, + input [DATA_WIDTH-1:0] s_axis_tdata, + input s_axis_tvalid, + + output [ACCUM_WIDTH-1:0] sum_data, + output sum_valid + ); + + logic [ACCUM_WIDTH-1:0] accum, res; + logic [DATA_WIDTH-1:0] axis_data; + logic res_valid, axis_valid; + (* MARK_DEBUG = "TRUE" *) logic [15:0] cnt; + + always @(posedge clk_in) begin + if (rst) begin + accum <= '0; + cnt <= '0; + res <= '0; + res_valid <= 0; + end else begin + res_valid <= 0; + axis_data <= s_axis_tdata; + axis_valid <= s_axis_tvalid; + if ( axis_valid) begin + if (cnt == WINDOW_SIZE-1) begin + res <= accum + axis_data; + res_valid <= 1; + accum <= '0; + cnt <= '0; + end else begin + accum <= accum + axis_data; + cnt <= cnt + 1; + end + end + end + end + + assign sum_valid = res_valid; + assign sum_data = res; + +endmodule -- 2.49.0 From 9b189f931f4bf81cee15067783eaefad050c7791 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 11:55:46 +0300 Subject: [PATCH 11/25] rtl: update accum design --- rtl/accum/src/accum.sv | 350 +++++++++++++++++++++-------------------- 1 file changed, 177 insertions(+), 173 deletions(-) diff --git a/rtl/accum/src/accum.sv b/rtl/accum/src/accum.sv index 51e2285..0522a7d 100644 --- a/rtl/accum/src/accum.sv +++ b/rtl/accum/src/accum.sv @@ -5,8 +5,8 @@ module accumulator parameter DATA_WIDTH = 12, parameter ACCUM_WIDTH = 32, parameter N_MAX = 4096, - parameter WINDOW_SIZE = 4, - parameter PACKET_SIZE = 8, + parameter WINDOW_SIZE = 4, //65 + parameter PACKET_SIZE = 8, //1024 parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) ) ( @@ -17,46 +17,38 @@ module accumulator input start, input [31:0] smp_num, input [15:0] seq_num, - + output [ACCUM_WIDTH-1:0] out_data, output out_valid, output readout_begin, input batch_req, input finish ); - - logic [31:0] smp_num_reg, cnt_smp_num; - logic [15:0] seq_num_reg, cnt_seq_num; - logic [15:0] cnt_addr, addra, addrb; - logic [ACCUM_WIDTH-1:0] data; - logic valid_data; - logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; - logic wea, enb; - - logic readout_begin_reg; - logic [ACCUM_WIDTH-1:0] out_data_reg; - logic out_valid_reg; - logic finish_reg, finish_buf; + logic [31:0] smp_num_reg, cnt_smp_num; + logic [15:0] seq_num_reg, cnt_seq_num; + logic [15:0] cnt_addr, addra, addrb; + logic [1:0] delay_cnt; + logic enable; + logic [ACCUM_WIDTH-1:0] data; + logic valid_data; +logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; + logic wea, enb, start_reg; - // registers for port b data request - reg req_data_b; - reg [15:0] req_addr_b; - - typedef enum logic [3:0] { + logic readout_begin_reg; + logic [ACCUM_WIDTH-1:0] out_data_reg; + logic out_valid_reg; + logic read_batch, finish_reg, batch_req_reg, finish_buf; + logic [31:0] cnt_readout; + + typedef enum logic [2:0] { IDLE, - INIT_MEM, - BEGIN_SEQ, - REQ_WORD_B, + INITIALIZE, + DELAY, ACCUM, - READOUT_START, - READOUT_AWAIT, - READOUT_DELAY, - READOUT_PUT, - READOUT_LAST, - FINISH - } wr_state_t; - (* MARK_DEBUG="true" *) wr_state_t wr_state; + READOUT + } state_t; + state_t state; always @(posedge clk_in) begin if (rst) begin @@ -65,150 +57,162 @@ module accumulator seq_num_reg <= '0; cnt_seq_num <= '0; cnt_addr <= '0; + enable <= 0; wea <= 0; enb <= 0; - wr_state <= IDLE; + delay_cnt <= '0; + state <= IDLE; + read_batch <= 0; + cnt_readout <= '0; finish_reg <= 0; out_valid_reg <= 0; end else begin + batch_req_reg <= batch_req; finish_buf <= finish; - - // FSM - case(wr_state) - + start_reg <= start; + case(state) IDLE: begin - // wait for start signal - wea <= 0; - enb <= 0; - readout_begin_reg <= 0; - finish_reg <= 0; - out_valid_reg <= 0; - if (start) begin - smp_num_reg <= smp_num; - seq_num_reg <= seq_num; - wr_state <= INIT_MEM; - end - - end - INIT_MEM: begin - // first run to initialize memory with first batch of values - wea <= 0; - if (valid_data) begin - data_bram_in <= data; - addra <= cnt_addr; - wea <= 1; - cnt_addr <= cnt_addr + 1; - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - - end - if (cnt_smp_num >= smp_num_reg) begin - wr_state <= BEGIN_SEQ; - end - - end - BEGIN_SEQ: begin - // start new acc seq - wea <= 0; - enb <= 0; - if (cnt_seq_num == seq_num_reg - 1) begin - cnt_seq_num <= '0; - cnt_smp_num <= '0; - cnt_addr <= '0; - wr_state <= READOUT_START; - addrb <= '0; - enb <= 0; - end else begin - // beginning of new data sequence - cnt_seq_num <= cnt_seq_num + 1; - cnt_smp_num <= '0; - cnt_addr <= '0; wea <= 0; - addrb <= 0; - wr_state <= REQ_WORD_B; - end - end - - REQ_WORD_B: begin - // pre-request data for port b - wea <= 0; - enb <= 1; - addrb <= cnt_addr; - wr_state <= ACCUM; - end - - ACCUM: begin - // sum mem+input - enb <= 0; - if (valid_data) begin - addra <= cnt_addr; - wea <= 1; - data_bram_in <= data + data_bram_out; - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin - wr_state <= BEGIN_SEQ; - end else begin - cnt_addr <= cnt_addr + 1; - wr_state <= REQ_WORD_B; - end - end - end - READOUT_START: begin - readout_begin_reg <= 1'b1; - wr_state <= READOUT_AWAIT; - addrb <= 0; - enb <= 0; - end - - READOUT_AWAIT: begin - // req await + delay for every-clock readout. - if (batch_req) begin - enb <= 1; - wr_state <= READOUT_DELAY; - end else if (finish_buf) begin - wr_state <= FINISH; - end else begin enb <= 0; + read_batch <= 0; + cnt_readout <= '0; + readout_begin_reg <= 0; + finish_reg <= 0; out_valid_reg <= 0; - end - end + if (start_reg) begin + enable <= 1; + end + if (enable) begin + smp_num_reg <= smp_num; + seq_num_reg <= seq_num; + state <= INITIALIZE; + end - READOUT_DELAY: begin - // wait for mem latency - addrb <= addrb + 1; - wr_state <= READOUT_PUT; - end + end + INITIALIZE: begin + if (enable) begin + if ( cnt_seq_num == 0) begin + wea <= 0; + if (valid_data) begin + data_bram_in <= data; + addra <= cnt_addr; + wea <= 1; + if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + end else begin + cnt_addr <= cnt_addr+1; + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + end + end + if (cnt_smp_num >= smp_num_reg) begin + cnt_seq_num <= cnt_seq_num + 1; + cnt_smp_num <= '0; + cnt_addr <= '0; + wea <= 0; - READOUT_PUT: begin - // main data output - if (addrb == READ_BATCH_SIZE) begin - wr_state <= READOUT_LAST; + addrb <= 0; + enb <= 1; + state <= DELAY; + end + end + else begin + state <= DELAY; + addrb <= cnt_addr; + enb <= 1; + wea <= 0; + end + end else begin + state <= IDLE; + end + end + DELAY: begin enb <= 0; - end - addrb <= addrb + 1; - out_valid_reg <= 1; - out_data_reg <= data_bram_out; - end + if (delay_cnt == 0) begin + delay_cnt <= 1; + end else if (delay_cnt == 1) begin + delay_cnt <= 0; + if (readout_begin_reg) begin + state <= READOUT; + end else begin + state <= ACCUM; + end + end + end + ACCUM: begin + if (enable) begin + if (valid_data) begin + addra <= cnt_addr; + wea <= 1; + data_bram_in <= data + data_bram_out; + if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + end else begin + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + cnt_addr <= cnt_addr + 1; + end + end - READOUT_LAST: begin - // last word of packet - out_valid_reg <= 0; - out_data_reg <= data_bram_out; - wr_state <= FINISH; - end - - FINISH: begin - out_valid_reg <= 0; - enb <= 0; - wr_state <= IDLE; - end - - default: wr_state <= IDLE; + if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin + if (cnt_seq_num == seq_num_reg - 1) begin + readout_begin_reg <= 1; + cnt_seq_num <= '0; + cnt_smp_num <= '0; + cnt_addr <= '0; + state <= DELAY; + addrb <= '0; + enb <= 0; + end else begin + cnt_seq_num <= cnt_seq_num + 1; + cnt_smp_num <= 0; + addrb <= 0; + cnt_addr <= 0; + state <= INITIALIZE; + end + end else if (valid_data) begin + state <= INITIALIZE; + end + end else begin + state <= IDLE; + end + end + READOUT: begin + enable <= 0; + wea <= 0; + if (finish_buf) begin + read_batch <= 0; + enb <= 0; + out_valid_reg <= 0; + cnt_readout <= 0; + addrb <= 0; + state <= IDLE; + readout_begin_reg <= 0; + end else if ( batch_req_reg ) begin + read_batch <= 1; + enb <= 1; + // addrb <= 0; + cnt_readout <= 0; + out_valid_reg <= 0; + end else if ( read_batch) begin + out_valid_reg <= 1; +// out_data_reg <= data_bram_out; + cnt_readout <= cnt_readout + 1; + if (cnt_readout < READ_BATCH_SIZE) begin + addrb <= addrb + 1; + end + if (cnt_readout == READ_BATCH_SIZE) begin + read_batch <= 0; + enb <= 0; + out_valid_reg <= 0; + cnt_readout <= 0; + end + end + end + default: state <= IDLE; endcase end end - - adder + adder #( .DATA_WIDTH(DATA_WIDTH), .WINDOW_SIZE(WINDOW_SIZE), @@ -222,8 +226,8 @@ module accumulator .sum_data(data), .sum_valid(valid_data) ); - - xpm_memory_sdpram #( + + xpm_memory_sdpram #( .ADDR_WIDTH_A(16), // DECIMAL .ADDR_WIDTH_B(16), // DECIMAL .AUTO_SLEEP_TIME(0), // DECIMAL @@ -248,25 +252,25 @@ module accumulator .USE_MEM_INIT_MMI(0), // DECIMAL .WAKEUP_TIME("disable_sleep"), // String .WRITE_DATA_WIDTH_A(ACCUM_WIDTH), // DECIMAL - .WRITE_MODE_B("no_change"), // String + .WRITE_MODE_B("read_first"), // String no_change .WRITE_PROTECT(1) // DECIMAL ) xpm_memory_sdpram_inst ( - .doutb(data_bram_out), - - .addra(addra), - .addrb(addrb), - .clka(clk_in), - .clkb(clk_in), + .doutb(data_bram_out), + + .addra(addra), + .addrb(addrb), + .clka(clk_in), + .clkb(clk_in), .dina(data_bram_in), - .ena(1'b1), - .enb(enb), - .wea(wea) + .ena(1'b1), + .enb(enb), + .wea(wea) ); - + assign readout_begin = readout_begin_reg; - assign out_data = out_data_reg; + assign out_data = data_bram_out; assign out_valid = out_valid_reg; - + endmodule -- 2.49.0 From 91eaf6c4f89f44c29c96ae4f4bf13392a871450e Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 11:56:19 +0300 Subject: [PATCH 12/25] fix: out_axis_fifo states --- rtl/accum/src/out_axis_fifo.sv | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/rtl/accum/src/out_axis_fifo.sv b/rtl/accum/src/out_axis_fifo.sv index 7749d93..0deb4f5 100644 --- a/rtl/accum/src/out_axis_fifo.sv +++ b/rtl/accum/src/out_axis_fifo.sv @@ -9,10 +9,10 @@ module out_axis_fifo #( input logic [31:0] smp_num, // AXI stream master for output, eth_clk_in domain - output logic [7:0] s_axis_tdata, - output logic s_axis_tvalid, - input logic s_axis_tready, - output logic s_axis_tlast, + output logic [7:0] m_axis_tdata, + output logic m_axis_tvalid, + input logic m_axis_tready, + output logic m_axis_tlast, // eth handshake input logic req_ready, output logic send_req, @@ -86,6 +86,8 @@ module out_axis_fifo #( reg [31:0] wr_batch_tgt; // next 'target' that should be written from batch reg [31:0] wr_total; // total BITS to be sent! + wire empty; + wire [WDEPTH_BITS:0] wr_data_count; // NOTE: @@ -121,7 +123,7 @@ module out_axis_fifo #( // wait until we can request a word // depends on prog_full signal WR_CHECK: begin - if (~wr_unavail && ~wr_rst_busy) begin + if ((wr_data_count < (FIFO_WDEPTH - (PACKET_SIZE / (ACCUM_WIDTH / 8)))) && ~wr_rst_busy) begin batch_req <= 1; // should give us exactly PACKET_SIZE * 8 bits // multiplied by WINDOW_SIZE, because we count @@ -159,7 +161,7 @@ module out_axis_fifo #( // wr_cnt should be by design PACKET_SIZE-aligned if (wr_cnt >= wr_total) begin // wait until all data is sent - if (wr_data_count == 0) begin + if (empty) begin finish <= 1; wr_state <= WR_IDLE; end @@ -193,8 +195,8 @@ module out_axis_fifo #( rd_state <= RD_IDLE; send_req <= 1'b0; sent_cnt <= 16'd0; - s_axis_tlast <= 1'b0; - s_axis_tvalid <= 1'b0; + m_axis_tlast <= 1'b0; + m_axis_tvalid <= 1'b0; rd_en <= 1'b0; end else begin @@ -209,8 +211,8 @@ module out_axis_fifo #( send_req <= 1'b0; sent_cnt <= 16'd0; rd_en <= 1'b0; - s_axis_tlast <= 1'b0; - s_axis_tvalid <= 1'b0; + m_axis_tlast <= 1'b0; + m_axis_tvalid <= 1'b0; end // await udp ready @@ -225,18 +227,18 @@ module out_axis_fifo #( RD_SEND: begin // udp is ready and fifo is ready = sent send_req <= 1'b0; - if (s_axis_tready && rd_valid) begin + if (m_axis_tready && rd_valid) begin rd_en <= 1'b1; - s_axis_tvalid <= 1'b1; + m_axis_tvalid <= 1'b1; sent_cnt <= sent_cnt + 1; // final packet of the batch if (sent_cnt == PACKET_SIZE - 1) begin rd_state <= RD_IDLE; - s_axis_tlast <= 1'b1; + m_axis_tlast <= 1'b1; end end else begin rd_en <= 1'b0; - s_axis_tvalid <= 1'b0; + m_axis_tvalid <= 1'b0; end end endcase @@ -288,8 +290,8 @@ module out_axis_fifo #( .data_valid(rd_valid), // 1-bit output: Read Data Valid: When asserted, this signal indicates that valid data is available on the // output bus (dout). - .dout(s_axis_tdata), - .empty( ), + .dout(m_axis_tdata), + .empty(empty), .full( ), -- 2.49.0 From 002f0cace525a13ffb5b7648e1d58fad0127e870 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 11:57:13 +0300 Subject: [PATCH 13/25] test: add full testbenches for accum --- rtl/accum/src/accum_top.sv | 92 +++++++++ rtl/accum/tests/Makefile | 5 +- rtl/accum/tests/accum_full_tb.sv | 344 +++++++++++++++++++++++++++++++ rtl/accum/tests/accum_tb.sv | 183 ++++++++++++++++ 4 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 rtl/accum/src/accum_top.sv create mode 100644 rtl/accum/tests/accum_full_tb.sv create mode 100644 rtl/accum/tests/accum_tb.sv diff --git a/rtl/accum/src/accum_top.sv b/rtl/accum/src/accum_top.sv new file mode 100644 index 0000000..bf1616b --- /dev/null +++ b/rtl/accum/src/accum_top.sv @@ -0,0 +1,92 @@ +`timescale 1ns / 1ps + +module accumulator_top +#( + parameter DATA_WIDTH = 12, + parameter ACCUM_WIDTH = 32, + parameter N_MAX = 4096, + parameter WINDOW_SIZE = 4, + parameter PACKET_SIZE = 8, + parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) + ) +( + // main clk + input clk_in, + input rst, + + // input data + input [DATA_WIDTH-1:0] s_axis_tdata, + input s_axis_tvalid, + + // parameters + input start, + input [31:0] smp_num, + input [15:0] seq_num, + + // eth signals + input eth_clk_in, + input req_ready, + output send_req, + + // output axis + output logic [7:0] m_axis_tdata, + output logic m_axis_tvalid, + input logic m_axis_tready, + output logic m_axis_tlast, + + output logic finish + ); + + wire [ACCUM_WIDTH-1:0] out_data; + wire out_valid; + wire readout_begin; + wire batch_req; + + accumulator #( + .DATA_WIDTH(DATA_WIDTH), + .ACCUM_WIDTH(ACCUM_WIDTH), + .N_MAX(N_MAX), + .WINDOW_SIZE(WINDOW_SIZE), + .PACKET_SIZE(PACKET_SIZE) + ) accum_main ( + .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), + .out_data(out_data), + .out_valid(out_valid), + .readout_begin(readout_begin), + .batch_req(batch_req), + .finish(finish) + ); + + out_axis_fifo #( + .ACCUM_WIDTH(ACCUM_WIDTH), + .WINDOW_SIZE(WINDOW_SIZE), + .PACKET_SIZE(PACKET_SIZE) + ) output_async_fifo ( + .eth_clk_in (eth_clk_in), + .acc_clk_in (clk_in), + .rst (rst), + .smp_num (smp_num), + + .m_axis_tdata (m_axis_tdata), + .m_axis_tvalid (m_axis_tvalid), + .m_axis_tready (m_axis_tready), + .m_axis_tlast (m_axis_tlast), + + .acc_din (out_data), + .din_valid (out_valid), + + .readout_begin (readout_begin), + + .req_ready (req_ready), + .send_req (send_req), + + .batch_req (batch_req), + .finish (finish) + ); +endmodule \ No newline at end of file diff --git a/rtl/accum/tests/Makefile b/rtl/accum/tests/Makefile index b45b6bc..056aad0 100644 --- a/rtl/accum/tests/Makefile +++ b/rtl/accum/tests/Makefile @@ -8,7 +8,7 @@ # FPGA settings FPGA_PART = xc7a35tfgg484-1 -FPGA_TOP = accum +FPGA_TOP = accumulator_top FPGA_ARCH = artix7 RTL_DIR = ../src @@ -24,7 +24,8 @@ XDC_FILES += ../../../constraints/ax7a035b.xdc XDC_FILES += test_timing.xdc SYN_FILES += out_axis_fifo_tb.sv -SIM_TOP = control_tb +SYN_FILE += accum_full_tb.sv +SIM_TOP = tb_accumulator_top program: $(PROJECT).bit diff --git a/rtl/accum/tests/accum_full_tb.sv b/rtl/accum/tests/accum_full_tb.sv new file mode 100644 index 0000000..1b6d1d5 --- /dev/null +++ b/rtl/accum/tests/accum_full_tb.sv @@ -0,0 +1,344 @@ +`timescale 1ns / 1ps + +module tb_accumulator_top; + + localparam DATA_WIDTH = 12; + localparam ACCUM_WIDTH = 32; + localparam N_MAX = 256; + localparam WINDOW_SIZE = 4; + localparam PACKET_SIZE = 128; + localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH; + localparam MAX_WORDS = N_MAX / WINDOW_SIZE; + 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-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 < 30000) 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, 2, 8, 1'b0, 1, "deterministic_small"); + run_test(2, 3, 8, 1'b1, 0, "random_seq3_smp8"); + run_test(3, 5, 16, 1'b1, 0, "random_seq5_smp16_multi_packet"); + // run_test(4, 1, 4, 1'b1, 0, "random_single_window"); + run_test(5, 7, 12, 1'b1, 0, "random_seq7_smp12"); + run_test(6, 4, 256, 1'b1, 0, "random_max_smpnum"); + + $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 diff --git a/rtl/accum/tests/accum_tb.sv b/rtl/accum/tests/accum_tb.sv new file mode 100644 index 0000000..d8d6385 --- /dev/null +++ b/rtl/accum/tests/accum_tb.sv @@ -0,0 +1,183 @@ +`timescale 1ns / 1ps + +module tb_accumulator; + + localparam DATA_WIDTH = 12; + localparam ACCUM_WIDTH = 32; + localparam N_MAX = 64; + localparam WINDOW_SIZE = 4; + localparam PACKET_SIZE = 8; // bytes + localparam READ_BATCH_SIZE = (PACKET_SIZE*8)/ACCUM_WIDTH; // = 2 + + reg clk_in; + reg rst; + reg [DATA_WIDTH-1:0] s_axis_tdata; + reg s_axis_tvalid; + reg start; + reg [31:0] smp_num; + reg [15:0] seq_num; + wire [ACCUM_WIDTH-1:0] out_data; + wire out_valid; + wire readout_begin; + reg batch_req; + reg finish; + + integer i; + integer out_count; + + reg [ACCUM_WIDTH-1:0] expected [0:READ_BATCH_SIZE-1]; + reg [ACCUM_WIDTH-1:0] got [0:READ_BATCH_SIZE-1]; + + accumulator #( + .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), + .out_data(out_data), + .out_valid(out_valid), + .readout_begin(readout_begin), + .batch_req(batch_req), + .finish(finish) + ); + + // clock 100 MHz + initial begin + clk_in = 0; + forever #5 clk_in = ~clk_in; + end + + // send one sample + task send_sample(input [DATA_WIDTH-1:0] val); + begin + @(posedge clk_in); + s_axis_tdata <= val; + s_axis_tvalid <= 1'b1; + end + endtask + + // one idle cycle after valid stream + task end_stream; + begin + @(posedge clk_in); + s_axis_tvalid <= 1'b0; + s_axis_tdata <= '0; + end + endtask + + // pulse start + task pulse_start; + begin + @(posedge clk_in); + start <= 1'b1; + @(posedge clk_in); + start <= 1'b0; + end + endtask + + // pulse batch request + task pulse_batch_req; + begin + @(posedge clk_in); + batch_req <= 1'b1; + @(posedge clk_in); + batch_req <= 1'b0; + end + endtask + + initial begin + repeat(100) @(posedge clk_in); + // init + rst = 1'b1; + s_axis_tdata = '0; + s_axis_tvalid= 1'b0; + start = 1'b0; + smp_num = 32'd8; + seq_num = 16'd2; + batch_req = 1'b0; + finish = 1'b0; + + expected[0] = 32'd60; + expected[1] = 32'd92; + + repeat(50) @(posedge clk_in); + rst = 1'b0; + repeat(50) @(posedge clk_in); + + $display("=== TEST START ==="); + + pulse_start(); + + // seq 0: [1..8] + send_sample(12'd1); + send_sample(12'd2); + send_sample(12'd3); + send_sample(12'd4); + send_sample(12'd5); + send_sample(12'd6); + send_sample(12'd7); + send_sample(12'd8); + end_stream(); + + // небольшой зазор + repeat(5) @(posedge clk_in); + + // seq 1: [11..18] + send_sample(12'd11); + send_sample(12'd12); + send_sample(12'd13); + send_sample(12'd14); + send_sample(12'd15); + send_sample(12'd16); + send_sample(12'd17); + send_sample(12'd18); + end_stream(); + + $display("[%0t] all input data sent, waiting readout_begin...", $time); + + wait(readout_begin == 1'b1); + $display("[%0t] readout_begin asserted", $time); + repeat(22) @(posedge clk_in); + pulse_batch_req(); + + out_count = 0; + + // ждём два слова + while (out_count < READ_BATCH_SIZE) begin + @(posedge clk_in); + if (out_valid) begin + got[out_count] = out_data; + $display("[%0t] out_valid: got[%0d] = %0d", $time, out_count, out_data); + out_count = out_count + 1; + end + end + + // проверка + for (i = 0; i < READ_BATCH_SIZE; i = i + 1) begin + if (got[i] !== expected[i]) begin + $error("Mismatch at index %0d: got=%0d expected=%0d", i, got[i], expected[i]); + end else begin + $display("OK index %0d: %0d", i, got[i]); + end + end + + // завершаем readout + @(posedge clk_in); + finish <= 1'b1; + + + repeat(10) @(posedge clk_in); + + $display("=== TEST PASSED ==="); + $finish; + end + +endmodule \ No newline at end of file -- 2.49.0 From 275055291ee514c0d2d116e70470711f93cd1ff9 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 11:57:53 +0300 Subject: [PATCH 14/25] fix: update names in out_axis_fifo_tb --- rtl/accum/tests/out_axis_fifo_tb.sv | 32 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/rtl/accum/tests/out_axis_fifo_tb.sv b/rtl/accum/tests/out_axis_fifo_tb.sv index 2d3943d..56135ff 100644 --- a/rtl/accum/tests/out_axis_fifo_tb.sv +++ b/rtl/accum/tests/out_axis_fifo_tb.sv @@ -4,7 +4,7 @@ module tb_out_axis_fifo; localparam int ACCUM_WIDTH = 32; localparam int WINDOW_SIZE = 65; - localparam int PACKET_SIZE = 1024; + localparam int PACKET_SIZE = 8; localparam int BYTES_PER_WORD = ACCUM_WIDTH / 8; localparam int WORDS_PER_BATCH = PACKET_SIZE / BYTES_PER_WORD; // 1024 / 4 = 256 слов @@ -14,10 +14,10 @@ module tb_out_axis_fifo; logic [31:0] smp_num; - logic [7:0] s_axis_tdata; - logic s_axis_tvalid; - logic s_axis_tready; - logic s_axis_tlast; + logic [7:0] m_axis_tdata; + logic m_axis_tvalid; + logic m_axis_tready; + logic m_axis_tlast; logic [ACCUM_WIDTH-1:0] acc_din; logic din_valid; @@ -40,10 +40,10 @@ module tb_out_axis_fifo; .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), + .m_axis_tdata (m_axis_tdata), + .m_axis_tvalid (m_axis_tvalid), + .m_axis_tready (m_axis_tready), + .m_axis_tlast (m_axis_tlast), .acc_din (acc_din), .din_valid (din_valid), @@ -113,18 +113,18 @@ module tb_out_axis_fifo; compared_bytes <= 0; mismatch_count <= 0; end else begin - if (s_axis_tvalid && s_axis_tready) begin + if (m_axis_tvalid && m_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); + $time, m_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 + if (m_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); + $time, compared_bytes, m_axis_tdata, exp_byte); mismatch_count <= mismatch_count + 1; end end @@ -236,17 +236,17 @@ module tb_out_axis_fifo; if (rst) begin axis_byte_count <= 0; req_ready <= 0; - s_axis_tready <= 1'b0; + m_axis_tready <= 1'b0; end else begin req_ready <= 1; // request send if (send_req) begin - s_axis_tready <= 1'b1; + m_axis_tready <= 1'b1; req_ready <= 0; end - if (s_axis_tvalid && s_axis_tready) begin + if (m_axis_tvalid && m_axis_tready) begin axis_byte_count <= axis_byte_count + 1; end end -- 2.49.0 From fc0e710b3e2bc43526634835471335ea068b54b6 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 12:11:41 +0300 Subject: [PATCH 15/25] tests: add waveconfigs --- rtl/accum/tests/tb_accumulator_top_behav.wcfg | 179 ++++++++++++++++ rtl/accum/tests/tb_out_axis_fifo_behav.wcfg | 196 ++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 rtl/accum/tests/tb_accumulator_top_behav.wcfg create mode 100644 rtl/accum/tests/tb_out_axis_fifo_behav.wcfg diff --git a/rtl/accum/tests/tb_accumulator_top_behav.wcfg b/rtl/accum/tests/tb_accumulator_top_behav.wcfg new file mode 100644 index 0000000..931e9f1 --- /dev/null +++ b/rtl/accum/tests/tb_accumulator_top_behav.wcfg @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + clk_in + clk_in + + + eth_clk_in + eth_clk_in + + + rst + rst + + + s_axis_tdata[11:0] + s_axis_tdata[11:0] + + + s_axis_tvalid + s_axis_tvalid + + + start + start + + + smp_num[31:0] + smp_num[31:0] + + + seq_num[15:0] + seq_num[15:0] + + + req_ready + req_ready + #E0FFFF + true + + + send_req + send_req + #E0FFFF + true + + + m_axis_tdata[7:0] + m_axis_tdata[7:0] + #008080 + true + + + m_axis_tready + m_axis_tready + #00FFFF + true + + + m_axis_tlast + m_axis_tlast + #008080 + true + + + finish + finish + #FAAFBE + true + + + batch_req + batch_req + #00FFFF + true + + + readout_begin + readout_begin + #00FFFF + true + + + acc + label + + + PACKET_SIZE[31:0] + PACKET_SIZE[31:0] + + + READ_BATCH_SIZE[31:0] + READ_BATCH_SIZE[31:0] + + + addrb[15:0] + addrb[15:0] + + + + fifo + label + + + acc_din[31:0] + acc_din[31:0] + #FF0080 + true + + + din_valid + din_valid + #FF0080 + true + + + batch_req + batch_req + + + wr_state[2:0] + wr_state[2:0] + + + rd_state[2:0] + rd_state[2:0] + + + wr_unavail + wr_unavail + #FFFF00 + true + + + wr_rst_busy + wr_rst_busy + #FFFF00 + true + + + empty + empty + + + PROG_FULL_THRESH[31:0] + PROG_FULL_THRESH[31:0] + + + wr_data_count[4:0] + wr_data_count[4:0] + UNSIGNEDDECRADIX + + + rd_data_count[6:0] + rd_data_count[6:0] + UNSIGNEDDECRADIX + + + diff --git a/rtl/accum/tests/tb_out_axis_fifo_behav.wcfg b/rtl/accum/tests/tb_out_axis_fifo_behav.wcfg new file mode 100644 index 0000000..aaed3f4 --- /dev/null +++ b/rtl/accum/tests/tb_out_axis_fifo_behav.wcfg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + eth_clk_in + eth_clk_in + + + acc_clk_in + acc_clk_in + + + rst + rst + + + smp_num[31:0] + smp_num[31:0] + UNSIGNEDDECRADIX + + + acc_din[31:0] + acc_din[31:0] + #FF0080 + true + + + din_valid + din_valid + #FF0080 + true + + + fifo_din_r[31:0] + fifo_din_r[31:0] + #FFA500 + true + + + fifo_wr_en_r + fifo_wr_en_r + #FFA500 + true + + + readout_begin + readout_begin + #FFFF00 + true + + + batch_req + batch_req + + + finish + finish + #00FFFF + true + + + m_axis_tdata[7:0] + m_axis_tdata[7:0] + + + m_axis_tvalid + m_axis_tvalid + + + m_axis_tready + m_axis_tready + + + m_axis_tlast + m_axis_tlast + + + axis_byte_count[31:0] + axis_byte_count[31:0] + UNSIGNEDDECRADIX + #F0E68C + true + + + ACCUM_WIDTH[31:0] + ACCUM_WIDTH[31:0] + UNSIGNEDDECRADIX + + + WINDOW_SIZE[31:0] + WINDOW_SIZE[31:0] + UNSIGNEDDECRADIX + + + PACKET_SIZE[31:0] + PACKET_SIZE[31:0] + UNSIGNEDDECRADIX + + + wr_state[2:0] + wr_state[2:0] + + + wr_cnt[31:0] + wr_cnt[31:0] + UNSIGNEDDECRADIX + + + wr_batch_tgt[31:0] + wr_batch_tgt[31:0] + UNSIGNEDDECRADIX + + + wr_total[31:0] + wr_total[31:0] + UNSIGNEDDECRADIX + + + prog_empty + prog_empty + + + prog_full + prog_full + + + wr_ack + wr_ack + + + wr_data_count[2:0] + wr_data_count[2:0] + UNSIGNEDDECRADIX + + + wr_data_count[2:0] + wr_data_count[2:0] + UNSIGNEDDECRADIX + + + rd_data_count[4:0] + rd_data_count[4:0] + UNSIGNEDDECRADIX + + + rst_sync_ff[1:0] + rst_sync_ff[1:0] + + + rd_state[2:0] + rd_state[2:0] + + + rd_en + rd_en + + + rd_valid + rd_valid + + + overflow + overflow + + + wr_rst_busy + wr_rst_busy + + + send_req + send_req + + + req_ready + req_ready + + -- 2.49.0 From 264c9ecb8e8c7101038a2fb09c0ba9d5553e28e5 Mon Sep 17 00:00:00 2001 From: otroubi Date: Tue, 28 Apr 2026 13:10:22 +0300 Subject: [PATCH 16/25] rtl: update sampler --- rtl/sampler/src/sampler.sv | 119 ++++++++++++++++++++++--------- rtl/sampler/tests/sampler_tb.sv | 121 ++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 34 deletions(-) create mode 100644 rtl/sampler/tests/sampler_tb.sv diff --git a/rtl/sampler/src/sampler.sv b/rtl/sampler/src/sampler.sv index d12d24e..18a533c 100644 --- a/rtl/sampler/src/sampler.sv +++ b/rtl/sampler/src/sampler.sv @@ -5,8 +5,8 @@ module sampler #( parameter DATA_WIDTH = 12, - parameter PACK_FACTOR = 3, - parameter PROCESS_MODE = 1 + parameter PACK_FACTOR = 1, + parameter PROCESS_MODE = 0 ) ( input clk_in, @@ -19,52 +19,103 @@ module sampler ); logic [DATA_WIDTH-1:0] data_converted; logic out_of_range_reg; + + generate + if (PROCESS_MODE) begin - always @( posedge clk_in) begin - if (rst) begin - data_converted <= '0; - end else begin - out_of_range_reg <= out_of_range; - if (PROCESS_MODE) begin - if (data_in == 12'b100000000000) begin + always @(posedge clk_in) begin + if (rst) begin + data_converted <= '0; + out_of_range_reg <= 0; + end + else begin + out_of_range_reg <= out_of_range; + if (data_in == {1'b1, {(DATA_WIDTH-1){1'b0}}}) data_converted <= data_in; - end else begin - data_converted <= data_in[DATA_WIDTH-1] ? {1'b1, (~data_in[DATA_WIDTH-2:0] + 1'b1)} : data_in; - - end - end else begin - data_converted <= data_in; + else + data_converted <= data_in[DATA_WIDTH-1] ?{1'b1, (~data_in[DATA_WIDTH-2:0] + 1'b1)}:data_in; end end - end - + end else begin + always @(posedge clk_in) begin + if (rst) begin + data_converted <= '0; + out_of_range_reg <= 0; + end + else begin + out_of_range_reg <= out_of_range; + data_converted <= data_in; + end + end + end + endgenerate logic [DATA_WIDTH*PACK_FACTOR-1:0] buffer; logic buffer_ready; logic [$clog2(PACK_FACTOR):0] cnt; - always @(posedge clk_in) begin - if (rst) begin - buffer <= '0; - cnt <= -1; // - buffer_ready <= 0; - end - else begin - buffer_ready <= 0; - if (!out_of_range_reg) begin - buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; - if (cnt == PACK_FACTOR-1) begin - cnt <= 0; + generate + if (PACK_FACTOR == 1) begin + always @(posedge clk_in) begin + if (rst) begin + buffer <= '0; + buffer_ready <= 0; + end + else begin + buffer_ready <= 0; + if (!out_of_range_reg) begin + buffer <= data_converted; buffer_ready <= 1; - buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; - end - else begin - cnt <= cnt + 1; end end end - end + end else begin + always @(posedge clk_in) begin + if (rst) begin + buffer <= '0; + cnt <= '0; // + buffer_ready <= 0; + end + else begin + buffer_ready <= 0; + if (!out_of_range_reg) begin + buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; + if (cnt == PACK_FACTOR-1) begin + cnt <= 0; + buffer_ready <= 1; + buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; + end + else begin + cnt <= cnt + 1; + end + end + end + end + end + endgenerate + +// always @(posedge clk_in) begin +// if (rst) begin +// buffer <= '0; +// cnt <= '0; // +// buffer_ready <= 0; +// end +// else begin +// buffer_ready <= 0; +// if (!out_of_range_reg) begin +// buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; +// if (cnt == PACK_FACTOR-1) begin +// cnt <= 0; +// buffer_ready <= 1; +// buffer <= {buffer[DATA_WIDTH*(PACK_FACTOR-1)-1:0], data_converted}; +// end +// else begin +// cnt <= cnt + 1; +// end +// end +// end +// end assign m_axis_tdata = buffer; assign m_axis_tvalid = buffer_ready; diff --git a/rtl/sampler/tests/sampler_tb.sv b/rtl/sampler/tests/sampler_tb.sv new file mode 100644 index 0000000..67b28dd --- /dev/null +++ b/rtl/sampler/tests/sampler_tb.sv @@ -0,0 +1,121 @@ + +`timescale 1ns / 1ps + +module sampler_tb; + + parameter DATA_WIDTH = 12; + parameter PACK_FACTOR = 1; + parameter PROCESS_MODE = 0; + parameter CLK_PERIOD = 15.3846; // 65 MHz + + logic clk; + logic rst; + logic [DATA_WIDTH-1:0] data_in; + logic out_of_range; + logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; + logic m_axis_tvalid; + + sampler #( + .DATA_WIDTH(DATA_WIDTH), + .PACK_FACTOR(PACK_FACTOR), + .PROCESS_MODE(PROCESS_MODE) + ) dut ( + .clk_in(clk), + .rst(rst), + .data_in(data_in), + .out_of_range(out_of_range), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid) + ); + + initial begin + clk = 0; + forever #(CLK_PERIOD/2) clk = ~clk; + end + + task send(input [DATA_WIDTH-1:0] word, input bit oor); + @(posedge clk); + data_in <= word; + out_of_range <= oor; + $display("Send: %h (%0d) OOR=%b", word, word, oor); + endtask + + initial begin + $display("\n=== SAMPLER TEST (MODE=%0d) ===\n", PROCESS_MODE); + + // Reset + rst = 1; + out_of_range = 0; + data_in = 0; +// send(12'h001, 0); + repeat(5) @(posedge clk); + rst = 0; + send(12'h001, 0); + repeat(1) @(posedge clk); + + // 1. Positive + $display("\n--- Positive numbers ---"); +// send(12'h001, 0); + send(12'h002, 0); + send(12'h003, 0); + + send(12'h004, 0); + send(12'h005, 0); + send(12'h806, 0); + + // 2. Negative + $display("\n--- Negative numbers ---"); + send(12'hFFF, 0); // -1 + send(12'hFFE, 0); // -2 + send(12'hFFD, 0); // -3 + + send(12'h800, 0); // -2048 + send(12'h801, 0); // -2047 + send(12'h802, 0); // -2046 + + // 3. Boundary + $display("\n--- Boundary values ---"); + send(12'h000, 0); // 0 + send(12'h001, 0); // 1 + send(12'h7FF, 0); // 2047 (max positive) + + send(12'h7FE, 0); // 2046 + send(12'h800, 0); // -2048 (min negative) + send(12'hFFF, 0); // -1 + + // 4. Out of range tests + $display("\n--- Out of range tests ---"); + + + send(12'h00A, 0); + send(12'h00B, 1); // + send(12'h00C, 0); + send(12'h00D, 0); + send(12'h00E, 0); + send(12'h00F, 0); + + send(12'h010, 0); + send(12'h011, 0); + send(12'h012, 1); // + + send(12'h013, 0); + send(12'h014, 0); + send(12'h015, 0); + + repeat(10) @(posedge clk); + $display("\n=== TEST FINISHED ==="); + $finish; + end + + // Results +// always @(posedge clk) begin +// if (m_axis_tvalid) begin +// $display("\n>>> PACKET RECEIVED at %0t ns:", $time); +// $display(" Full: %h", m_axis_tdata); +// $display(" Word0: %h", m_axis_tdata[11:0]); +// $display(" Word1: %h", m_axis_tdata[23:12]); +// $display(" Word2: %h\n", m_axis_tdata[35:24]); +// end +// end + +endmodule \ No newline at end of file -- 2.49.0 From e083cd5c2e26547b897287c70af58167a11cf231 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 13:13:27 +0300 Subject: [PATCH 17/25] rtl: update accum to support real cases --- rtl/accum/src/accum.sv | 321 +++++++++++++++---------------- rtl/accum/src/accum_top.sv | 4 +- rtl/accum/tests/accum_full_tb.sv | 24 +-- 3 files changed, 173 insertions(+), 176 deletions(-) diff --git a/rtl/accum/src/accum.sv b/rtl/accum/src/accum.sv index 0522a7d..121acdc 100644 --- a/rtl/accum/src/accum.sv +++ b/rtl/accum/src/accum.sv @@ -5,8 +5,8 @@ module accumulator parameter DATA_WIDTH = 12, parameter ACCUM_WIDTH = 32, parameter N_MAX = 4096, - parameter WINDOW_SIZE = 4, //65 - parameter PACKET_SIZE = 8, //1024 + parameter WINDOW_SIZE = 4, + parameter PACKET_SIZE = 8, parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) ) ( @@ -25,30 +25,38 @@ module accumulator input finish ); - logic [31:0] smp_num_reg, cnt_smp_num; - logic [15:0] seq_num_reg, cnt_seq_num; - logic [15:0] cnt_addr, addra, addrb; - logic [1:0] delay_cnt; - logic enable; - logic [ACCUM_WIDTH-1:0] data; - logic valid_data; -logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; - logic wea, enb, start_reg; + logic [31:0] smp_num_reg, cnt_smp_num; + logic [15:0] seq_num_reg, cnt_seq_num; + logic [15:0] cnt_addr, addra, addrb; - logic readout_begin_reg; - logic [ACCUM_WIDTH-1:0] out_data_reg; - logic out_valid_reg; - logic read_batch, finish_reg, batch_req_reg, finish_buf; - logic [31:0] cnt_readout; + logic [ACCUM_WIDTH-1:0] data; + logic valid_data; + logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; + logic wea, enb; - typedef enum logic [2:0] { + logic readout_begin_reg; + logic [ACCUM_WIDTH-1:0] out_data_reg; + logic out_valid_reg; + logic finish_reg, finish_buf; + + // registers for port b data request + reg req_data_b; + reg [15:0] req_addr_b; + + typedef enum logic [3:0] { IDLE, - INITIALIZE, - DELAY, + INIT_MEM, + BEGIN_SEQ, + REQ_WORD_B, ACCUM, - READOUT - } state_t; - state_t state; + READOUT_START, + READOUT_AWAIT, + READOUT_DELAY, + READOUT_PUT, + READOUT_LAST, + FINISH + } wr_state_t; + (* MARK_DEBUG="true" *) wr_state_t wr_state; always @(posedge clk_in) begin if (rst) begin @@ -57,161 +65,148 @@ logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; seq_num_reg <= '0; cnt_seq_num <= '0; cnt_addr <= '0; - enable <= 0; wea <= 0; enb <= 0; - delay_cnt <= '0; - state <= IDLE; - read_batch <= 0; - cnt_readout <= '0; + wr_state <= IDLE; finish_reg <= 0; out_valid_reg <= 0; end else begin - batch_req_reg <= batch_req; finish_buf <= finish; - start_reg <= start; - case(state) + + // FSM + case(wr_state) + IDLE: begin + // wait for start signal + wea <= 0; + enb <= 0; + readout_begin_reg <= 0; + finish_reg <= 0; + out_valid_reg <= 0; + if (start) begin + smp_num_reg <= smp_num; + seq_num_reg <= seq_num; + wr_state <= INIT_MEM; + end + + end + INIT_MEM: begin + // first run to initialize memory with first batch of values + wea <= 0; + if (valid_data) begin + data_bram_in <= data; + addra <= cnt_addr; + wea <= 1; + cnt_addr <= cnt_addr + 1; + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + + end + if (cnt_smp_num >= smp_num_reg) begin + wr_state <= BEGIN_SEQ; + end + + end + BEGIN_SEQ: begin + // start new acc seq + wea <= 0; + enb <= 0; + if (cnt_seq_num == seq_num_reg - 1) begin + cnt_seq_num <= '0; + cnt_smp_num <= '0; + cnt_addr <= '0; + wr_state <= READOUT_START; + addrb <= '0; + enb <= 0; + end else begin + // beginning of new data sequence + cnt_seq_num <= cnt_seq_num + 1; + cnt_smp_num <= '0; + cnt_addr <= '0; wea <= 0; - enb <= 0; - read_batch <= 0; - cnt_readout <= '0; - readout_begin_reg <= 0; - finish_reg <= 0; - out_valid_reg <= 0; - if (start_reg) begin - enable <= 1; - end - if (enable) begin - smp_num_reg <= smp_num; - seq_num_reg <= seq_num; - state <= INITIALIZE; - end + addrb <= 0; + wr_state <= REQ_WORD_B; + end + end - end - INITIALIZE: begin - if (enable) begin - if ( cnt_seq_num == 0) begin - wea <= 0; - if (valid_data) begin - data_bram_in <= data; - addra <= cnt_addr; - wea <= 1; - if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - end else begin - cnt_addr <= cnt_addr+1; - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - end - end - if (cnt_smp_num >= smp_num_reg) begin - cnt_seq_num <= cnt_seq_num + 1; - cnt_smp_num <= '0; - cnt_addr <= '0; - wea <= 0; + REQ_WORD_B: begin + // pre-request data for port b + wea <= 0; + enb <= 1; + addrb <= cnt_addr; + wr_state <= ACCUM; + end - addrb <= 0; - enb <= 1; - state <= DELAY; - end - end - else begin - state <= DELAY; - addrb <= cnt_addr; - enb <= 1; - wea <= 0; - end - end else begin - state <= IDLE; - end - end - DELAY: begin - enb <= 0; - if (delay_cnt == 0) begin - delay_cnt <= 1; - end else if (delay_cnt == 1) begin - delay_cnt <= 0; - if (readout_begin_reg) begin - state <= READOUT; - end else begin - state <= ACCUM; - end - end - end ACCUM: begin - if (enable) begin - if (valid_data) begin - addra <= cnt_addr; - wea <= 1; - data_bram_in <= data + data_bram_out; - if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - end else begin - cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; - cnt_addr <= cnt_addr + 1; - end - end - - if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin - if (cnt_seq_num == seq_num_reg - 1) begin - readout_begin_reg <= 1; - cnt_seq_num <= '0; - cnt_smp_num <= '0; - cnt_addr <= '0; - state <= DELAY; - addrb <= '0; - enb <= 0; - end else begin - cnt_seq_num <= cnt_seq_num + 1; - cnt_smp_num <= 0; - addrb <= 0; - cnt_addr <= 0; - state <= INITIALIZE; - end - end else if (valid_data) begin - state <= INITIALIZE; - end + // sum mem+input + enb <= 0; + if (valid_data) begin + addra <= cnt_addr; + wea <= 1; + data_bram_in <= data + data_bram_out; + cnt_smp_num <= cnt_smp_num + WINDOW_SIZE; + if (cnt_smp_num + WINDOW_SIZE >= smp_num_reg) begin + wr_state <= BEGIN_SEQ; end else begin - state <= IDLE; + cnt_addr <= cnt_addr + 1; + wr_state <= REQ_WORD_B; end - end - READOUT: begin - enable <= 0; - wea <= 0; - if (finish_buf) begin - read_batch <= 0; - enb <= 0; - out_valid_reg <= 0; - cnt_readout <= 0; - addrb <= 0; - state <= IDLE; - readout_begin_reg <= 0; - end else if ( batch_req_reg ) begin - read_batch <= 1; - enb <= 1; - // addrb <= 0; - cnt_readout <= 0; - out_valid_reg <= 0; - end else if ( read_batch) begin - out_valid_reg <= 1; -// out_data_reg <= data_bram_out; - cnt_readout <= cnt_readout + 1; - if (cnt_readout < READ_BATCH_SIZE) begin - addrb <= addrb + 1; - end - if (cnt_readout == READ_BATCH_SIZE) begin - read_batch <= 0; - enb <= 0; - out_valid_reg <= 0; - cnt_readout <= 0; - end - end - end - default: state <= IDLE; + end + end + READOUT_START: begin + readout_begin_reg <= 1'b1; + wr_state <= READOUT_AWAIT; + enb <= 0; + end + + READOUT_AWAIT: begin + // req await + delay for every-clock readout. + if (batch_req) begin + enb <= 1; + wr_state <= READOUT_DELAY; + end else if (finish_buf) begin + wr_state <= FINISH; + end else begin + enb <= 0; + out_valid_reg <= 0; + end + end + + READOUT_DELAY: begin + // wait for mem latency + addrb <= addrb + 1; + wr_state <= READOUT_PUT; + end + + READOUT_PUT: begin + // main data output + if ((addrb % READ_BATCH_SIZE) == 0) begin + wr_state <= READOUT_LAST; + enb <= 0; + end else addrb <= addrb + 1; + + out_valid_reg <= 1; + out_data_reg <= data_bram_out; + end + + READOUT_LAST: begin + // last word of packet + out_valid_reg <= 0; + out_data_reg <= data_bram_out; + wr_state <= READOUT_START; + end + + FINISH: begin + out_valid_reg <= 0; + enb <= 0; + wr_state <= IDLE; + end + + default: wr_state <= IDLE; endcase end end + adder #( .DATA_WIDTH(DATA_WIDTH), @@ -227,7 +222,7 @@ logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; .sum_valid(valid_data) ); - xpm_memory_sdpram #( + xpm_memory_sdpram #( .ADDR_WIDTH_A(16), // DECIMAL .ADDR_WIDTH_B(16), // DECIMAL .AUTO_SLEEP_TIME(0), // DECIMAL @@ -252,7 +247,7 @@ logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; .USE_MEM_INIT_MMI(0), // DECIMAL .WAKEUP_TIME("disable_sleep"), // String .WRITE_DATA_WIDTH_A(ACCUM_WIDTH), // DECIMAL - .WRITE_MODE_B("read_first"), // String no_change + .WRITE_MODE_B("no_change"), // String .WRITE_PROTECT(1) // DECIMAL ) xpm_memory_sdpram_inst ( @@ -270,7 +265,7 @@ logic [ACCUM_WIDTH-1:0] data_bram_in, data_bram_out; ); assign readout_begin = readout_begin_reg; - assign out_data = data_bram_out; + assign out_data = out_data_reg; assign out_valid = out_valid_reg; endmodule diff --git a/rtl/accum/src/accum_top.sv b/rtl/accum/src/accum_top.sv index bf1616b..3ae2f62 100644 --- a/rtl/accum/src/accum_top.sv +++ b/rtl/accum/src/accum_top.sv @@ -5,8 +5,8 @@ module accumulator_top parameter DATA_WIDTH = 12, parameter ACCUM_WIDTH = 32, parameter N_MAX = 4096, - parameter WINDOW_SIZE = 4, - parameter PACKET_SIZE = 8, + parameter WINDOW_SIZE = 65, + parameter PACKET_SIZE = 1024, parameter READ_BATCH_SIZE =(PACKET_SIZE*8)/(ACCUM_WIDTH) ) ( diff --git a/rtl/accum/tests/accum_full_tb.sv b/rtl/accum/tests/accum_full_tb.sv index 1b6d1d5..a7196c5 100644 --- a/rtl/accum/tests/accum_full_tb.sv +++ b/rtl/accum/tests/accum_full_tb.sv @@ -4,11 +4,11 @@ module tb_accumulator_top; localparam DATA_WIDTH = 12; localparam ACCUM_WIDTH = 32; - localparam N_MAX = 256; - localparam WINDOW_SIZE = 4; - localparam PACKET_SIZE = 128; + 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 / WINDOW_SIZE; + localparam MAX_WORDS = N_MAX; localparam MAX_SEQ_NUM = 64; logic clk_in; @@ -147,7 +147,7 @@ module tb_accumulator_top; input integer base_value, input string test_name ); - logic [DATA_WIDTH-1:0] sample_mem [0:MAX_SEQ_NUM-1][0:N_MAX-1]; + 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; @@ -213,7 +213,7 @@ module tb_accumulator_top; end timeout_cnt = 0; - while (packets_seen < exp_packet_count && timeout_cnt < 30000) begin + while (packets_seen < exp_packet_count && timeout_cnt < 50 * PACKET_SIZE) begin @(posedge eth_clk_in); timeout_cnt = timeout_cnt + 1; end @@ -318,12 +318,14 @@ module tb_accumulator_top; reset_dut(); - run_test(1, 2, 8, 1'b0, 1, "deterministic_small"); - run_test(2, 3, 8, 1'b1, 0, "random_seq3_smp8"); - run_test(3, 5, 16, 1'b1, 0, "random_seq5_smp16_multi_packet"); + 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, 7, 12, 1'b1, 0, "random_seq7_smp12"); - run_test(6, 4, 256, 1'b1, 0, "random_max_smpnum"); + 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"); -- 2.49.0 From c4a7c21bea92aacb358404172f1e81a95cc83ac4 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 13:13:40 +0300 Subject: [PATCH 18/25] fix: clock name un xdc --- rtl/accum/tests/test_timing.xdc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/accum/tests/test_timing.xdc b/rtl/accum/tests/test_timing.xdc index 56cb725..29312a3 100644 --- a/rtl/accum/tests/test_timing.xdc +++ b/rtl/accum/tests/test_timing.xdc @@ -1,6 +1,6 @@ # Primary clocks create_clock -name eth_clk -period 8.000 [get_ports eth_clk_in] -create_clock -name acc_clk -period 15.385 [get_ports acc_clk_in] +create_clock -name acc_clk -period 15.385 [get_ports clk_in] # Asynchronous clock groups -- 2.49.0 From 312bc0c798f5a779c3778227ab65b36c772780b8 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 13:28:49 +0300 Subject: [PATCH 19/25] fix: automate tb --- rtl/accum/tests/Makefile | 2 +- rtl/accum/tests/accum_full_tb.sv | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rtl/accum/tests/Makefile b/rtl/accum/tests/Makefile index 056aad0..45f5626 100644 --- a/rtl/accum/tests/Makefile +++ b/rtl/accum/tests/Makefile @@ -24,7 +24,7 @@ XDC_FILES += ../../../constraints/ax7a035b.xdc XDC_FILES += test_timing.xdc SYN_FILES += out_axis_fifo_tb.sv -SYN_FILE += accum_full_tb.sv +SYN_FILES += accum_full_tb.sv SIM_TOP = tb_accumulator_top diff --git a/rtl/accum/tests/accum_full_tb.sv b/rtl/accum/tests/accum_full_tb.sv index a7196c5..66d0071 100644 --- a/rtl/accum/tests/accum_full_tb.sv +++ b/rtl/accum/tests/accum_full_tb.sv @@ -165,7 +165,7 @@ module tb_accumulator_top; 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) + 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); @@ -322,10 +322,9 @@ module tb_accumulator_top; // $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"); + 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"); -- 2.49.0 From 4efc2f02a9b9bf2af368f05a383199ae4c9dba02 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 13:29:05 +0300 Subject: [PATCH 20/25] infra: increase sim time for larger tests --- scripts/vivado.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vivado.mk b/scripts/vivado.mk index 80131ca..ec67ca6 100644 --- a/scripts/vivado.mk +++ b/scripts/vivado.mk @@ -180,7 +180,7 @@ sim: $(PROJECT).xpr gen_ip echo "update_compile_order -fileset sources_1" >> run_sim.tcl echo "update_compile_order -fileset sim_1" >> run_sim.tcl echo "launch_simulation" >> run_sim.tcl - echo "run 1000 us" >> run_sim.tcl + echo "run 10000 us" >> run_sim.tcl echo "quit" >> run_sim.tcl vivado -mode batch -source run_sim.tcl -- 2.49.0 From 5f1f4e5a169a39aaf5d8f440d8ece616794278dd Mon Sep 17 00:00:00 2001 From: otroubi Date: Tue, 28 Apr 2026 13:57:28 +0300 Subject: [PATCH 21/25] rtl: new tb --- rtl/sampler/tests/sampler_main_tb.sv | 132 +++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 rtl/sampler/tests/sampler_main_tb.sv diff --git a/rtl/sampler/tests/sampler_main_tb.sv b/rtl/sampler/tests/sampler_main_tb.sv new file mode 100644 index 0000000..6ee619a --- /dev/null +++ b/rtl/sampler/tests/sampler_main_tb.sv @@ -0,0 +1,132 @@ +`timescale 1ns / 1ps + +module sampler_tb; + + parameter DATA_WIDTH = 12; + parameter PROCESS_MODE = 0; + parameter CLK_PERIOD = 15.3846; + parameter TEST_NUM = 1000; + + logic clk; + logic rst; + + logic [DATA_WIDTH-1:0] data_in; + logic out_of_range; + + logic [DATA_WIDTH-1:0] m_axis_tdata; + logic m_axis_tvalid; + + integer errors = 0; + + sampler #( + .DATA_WIDTH(DATA_WIDTH), + .PROCESS_MODE(PROCESS_MODE) + ) dut ( + .clk_in(clk), + .rst(rst), + .data_in(data_in), + .out_of_range(out_of_range), + .m_axis_tdata(m_axis_tdata), + .m_axis_tvalid(m_axis_tvalid) + ); + + initial begin + clk = 0; + forever #(CLK_PERIOD/2) clk = ~clk; + end + + function automatic [DATA_WIDTH-1:0] ref_convert(input [DATA_WIDTH-1:0] din); + if (PROCESS_MODE == 0) + return din; + else if (din == {1'b1, {(DATA_WIDTH-1){1'b0}}}) + return din; + else + return din[DATA_WIDTH-1] ? + {1'b1, (~din[DATA_WIDTH-2:0] + 1'b1)} : + din; + endfunction + + task send(input [DATA_WIDTH-1:0] word, input bit oor); + @(posedge clk); + data_in <= word; + out_of_range <= oor; + endtask + + logic [DATA_WIDTH-1:0] exp_d0, exp_d1, exp_d2; + logic oor_d0, oor_d1, oor_d2; + + initial begin + $display("\n=== RANDOM SAMPLER TEST===\n"); + + rst = 1; + data_in = 0; + out_of_range = 0; + + exp_d0 = 0; + exp_d1 = 0; + exp_d2 = 0; + + oor_d0 = 1; + oor_d1 = 1; + oor_d2 = 1; + + repeat(5) @(posedge clk); + rst = 0; + repeat(2) @(posedge clk); + + repeat (TEST_NUM) begin + logic [DATA_WIDTH-1:0] rand_data; + bit rand_oor; + + rand_data = $urandom_range(0, (1 << DATA_WIDTH) - 1); + rand_oor = ($urandom_range(0, 99) < 20); + + @(negedge clk); + + if (!oor_d2) begin + if (m_axis_tvalid !== 1) begin + $display("ERROR: valid=0"); + errors++; + end + + if (m_axis_tdata !== exp_d2) begin + $display("ERROR: data mismatch"); + $display(" expected = %h", exp_d2); + $display(" got = %h", m_axis_tdata); + errors++; + end + end + + send(rand_data, rand_oor); + + exp_d2 = exp_d1; + exp_d1 = exp_d0; + exp_d0 = ref_convert(rand_data); + + oor_d2 = oor_d1; + oor_d1 = oor_d0; + oor_d0 = rand_oor; + end + + @(posedge clk); + + + if (!oor_d2) begin + + if (m_axis_tdata !== exp_d2) begin + $display("ERROR: final mismatch"); + $display(" expected = %h", exp_d2); + $display(" got = %h", m_axis_tdata); + errors++; + end + end + + if (errors == 0) + $display("\n========== ALL PASSED ==========\n"); + else + $display("\n========== FAILED: %0d errors ==========\n", errors); + + $finish; + end + +endmodule \ No newline at end of file -- 2.49.0 From d5c3ff873f10eb78597af6fbf4f400544d65d911 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 14:41:56 +0300 Subject: [PATCH 22/25] infra: add sample makefile --- rtl/sampler/tests/Makefile | 51 ++++++++++ rtl/sampler/tests/sampler_tb.sv | 121 ----------------------- rtl/sampler/tests/sampler_tb_advanced.sv | 120 ---------------------- rtl/sampler/tests/sampler_tb_basic.sv | 67 ------------- 4 files changed, 51 insertions(+), 308 deletions(-) create mode 100644 rtl/sampler/tests/Makefile delete mode 100644 rtl/sampler/tests/sampler_tb.sv delete mode 100644 rtl/sampler/tests/sampler_tb_advanced.sv delete mode 100644 rtl/sampler/tests/sampler_tb_basic.sv diff --git a/rtl/sampler/tests/Makefile b/rtl/sampler/tests/Makefile new file mode 100644 index 0000000..2226f93 --- /dev/null +++ b/rtl/sampler/tests/Makefile @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2025 FPGA Ninja, LLC +# +# Authors: +# - Alex Forencich +# + +# FPGA settings +FPGA_PART = xc7a35tfgg484-1 +FPGA_TOP = sampler +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 + +SYN_FILES += sampler_main_tb.sv +SIM_TOP = sampler_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/sampler/tests/sampler_tb.sv b/rtl/sampler/tests/sampler_tb.sv deleted file mode 100644 index 67b28dd..0000000 --- a/rtl/sampler/tests/sampler_tb.sv +++ /dev/null @@ -1,121 +0,0 @@ - -`timescale 1ns / 1ps - -module sampler_tb; - - parameter DATA_WIDTH = 12; - parameter PACK_FACTOR = 1; - parameter PROCESS_MODE = 0; - parameter CLK_PERIOD = 15.3846; // 65 MHz - - logic clk; - logic rst; - logic [DATA_WIDTH-1:0] data_in; - logic out_of_range; - logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; - logic m_axis_tvalid; - - sampler #( - .DATA_WIDTH(DATA_WIDTH), - .PACK_FACTOR(PACK_FACTOR), - .PROCESS_MODE(PROCESS_MODE) - ) dut ( - .clk_in(clk), - .rst(rst), - .data_in(data_in), - .out_of_range(out_of_range), - .m_axis_tdata(m_axis_tdata), - .m_axis_tvalid(m_axis_tvalid) - ); - - initial begin - clk = 0; - forever #(CLK_PERIOD/2) clk = ~clk; - end - - task send(input [DATA_WIDTH-1:0] word, input bit oor); - @(posedge clk); - data_in <= word; - out_of_range <= oor; - $display("Send: %h (%0d) OOR=%b", word, word, oor); - endtask - - initial begin - $display("\n=== SAMPLER TEST (MODE=%0d) ===\n", PROCESS_MODE); - - // Reset - rst = 1; - out_of_range = 0; - data_in = 0; -// send(12'h001, 0); - repeat(5) @(posedge clk); - rst = 0; - send(12'h001, 0); - repeat(1) @(posedge clk); - - // 1. Positive - $display("\n--- Positive numbers ---"); -// send(12'h001, 0); - send(12'h002, 0); - send(12'h003, 0); - - send(12'h004, 0); - send(12'h005, 0); - send(12'h806, 0); - - // 2. Negative - $display("\n--- Negative numbers ---"); - send(12'hFFF, 0); // -1 - send(12'hFFE, 0); // -2 - send(12'hFFD, 0); // -3 - - send(12'h800, 0); // -2048 - send(12'h801, 0); // -2047 - send(12'h802, 0); // -2046 - - // 3. Boundary - $display("\n--- Boundary values ---"); - send(12'h000, 0); // 0 - send(12'h001, 0); // 1 - send(12'h7FF, 0); // 2047 (max positive) - - send(12'h7FE, 0); // 2046 - send(12'h800, 0); // -2048 (min negative) - send(12'hFFF, 0); // -1 - - // 4. Out of range tests - $display("\n--- Out of range tests ---"); - - - send(12'h00A, 0); - send(12'h00B, 1); // - send(12'h00C, 0); - send(12'h00D, 0); - send(12'h00E, 0); - send(12'h00F, 0); - - send(12'h010, 0); - send(12'h011, 0); - send(12'h012, 1); // - - send(12'h013, 0); - send(12'h014, 0); - send(12'h015, 0); - - repeat(10) @(posedge clk); - $display("\n=== TEST FINISHED ==="); - $finish; - end - - // Results -// always @(posedge clk) begin -// if (m_axis_tvalid) begin -// $display("\n>>> PACKET RECEIVED at %0t ns:", $time); -// $display(" Full: %h", m_axis_tdata); -// $display(" Word0: %h", m_axis_tdata[11:0]); -// $display(" Word1: %h", m_axis_tdata[23:12]); -// $display(" Word2: %h\n", m_axis_tdata[35:24]); -// end -// end - -endmodule \ No newline at end of file diff --git a/rtl/sampler/tests/sampler_tb_advanced.sv b/rtl/sampler/tests/sampler_tb_advanced.sv deleted file mode 100644 index 601f0fd..0000000 --- a/rtl/sampler/tests/sampler_tb_advanced.sv +++ /dev/null @@ -1,120 +0,0 @@ -`timescale 1ns / 1ps - -module sampler_tb; - - parameter DATA_WIDTH = 12; - parameter PACK_FACTOR = 3; - parameter PROCESS_MODE = 1; - parameter CLK_PERIOD = 15.3846; // 65 MHz - - logic clk; - logic rst; - logic [DATA_WIDTH-1:0] data_in; - logic out_of_range; - logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; - logic m_axis_tvalid; - - sampler #( - .DATA_WIDTH(DATA_WIDTH), - .PACK_FACTOR(PACK_FACTOR), - .PROCESS_MODE(PROCESS_MODE) - ) dut ( - .clk_in(clk), - .rst(rst), - .data_in(data_in), - .out_of_range(out_of_range), - .m_axis_tdata(m_axis_tdata), - .m_axis_tvalid(m_axis_tvalid) - ); - - initial begin - clk = 0; - forever #(CLK_PERIOD/2) clk = ~clk; - end - - task send(input [DATA_WIDTH-1:0] word, input bit oor); - @(posedge clk); - data_in <= word; - out_of_range <= oor; - $display("Send: %h (%0d) OOR=%b", word, word, oor); - endtask - - initial begin - $display("\n=== SAMPLER TEST (MODE=%0d) ===\n", PROCESS_MODE); - - // Reset - rst = 1; - out_of_range = 0; - data_in = 0; -// send(12'h001, 0); - repeat(5) @(posedge clk); - rst = 0; - send(12'h001, 0); - repeat(1) @(posedge clk); - - // 1. Positive - $display("\n--- Positive numbers ---"); -// send(12'h001, 0); - send(12'h002, 0); - send(12'h003, 0); - - send(12'h004, 0); - send(12'h005, 0); - send(12'h806, 0); - - // 2. Negative - $display("\n--- Negative numbers ---"); - send(12'hFFF, 0); // -1 - send(12'hFFE, 0); // -2 - send(12'hFFD, 0); // -3 - - send(12'h800, 0); // -2048 - send(12'h801, 0); // -2047 - send(12'h802, 0); // -2046 - - // 3. Boundary - $display("\n--- Boundary values ---"); - send(12'h000, 0); // 0 - send(12'h001, 0); // 1 - send(12'h7FF, 0); // 2047 (max positive) - - send(12'h7FE, 0); // 2046 - send(12'h800, 0); // -2048 (min negative) - send(12'hFFF, 0); // -1 - - // 4. Out of range tests - $display("\n--- Out of range tests ---"); - - - send(12'h00A, 0); - send(12'h00B, 1); // - send(12'h00C, 0); - send(12'h00D, 0); - send(12'h00E, 0); - send(12'h00F, 0); - - send(12'h010, 0); - send(12'h011, 0); - send(12'h012, 1); // - - send(12'h013, 0); - send(12'h014, 0); - send(12'h015, 0); - - repeat(10) @(posedge clk); - $display("\n=== TEST FINISHED ==="); - $finish; - end - - // Results - always @(posedge clk) begin - if (m_axis_tvalid) begin - $display("\n>>> PACKET RECEIVED at %0t ns:", $time); - $display(" Full: %h", m_axis_tdata); - $display(" Word0: %h", m_axis_tdata[11:0]); - $display(" Word1: %h", m_axis_tdata[23:12]); - $display(" Word2: %h\n", m_axis_tdata[35:24]); - end - end - -endmodule \ No newline at end of file diff --git a/rtl/sampler/tests/sampler_tb_basic.sv b/rtl/sampler/tests/sampler_tb_basic.sv deleted file mode 100644 index f574ce3..0000000 --- a/rtl/sampler/tests/sampler_tb_basic.sv +++ /dev/null @@ -1,67 +0,0 @@ -`timescale 1ns / 1ps - -module sampler_tb; - - parameter DATA_WIDTH = 12; - parameter PACK_FACTOR = 3; - parameter PROCESS_MODE = 0; - - parameter CLK_PERIOD = 15.3846; - - logic clk; - logic rst; - logic [DATA_WIDTH-1:0] data_in; - logic out_of_range; - - logic [DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata; - logic m_axis_tvalid; - - // DUT - sampler #( - .DATA_WIDTH(DATA_WIDTH), - .PACK_FACTOR(PACK_FACTOR), - .PROCESS_MODE(PROCESS_MODE) - ) dut ( - .clk_in(clk), - .rst(rst), - .data_in(data_in), - .out_of_range(out_of_range), - .m_axis_tdata(m_axis_tdata), - .m_axis_tvalid(m_axis_tvalid) - ); - - // clock - initial begin - clk = 0; - forever #(CLK_PERIOD/2) clk = ~clk; - end - - integer i; - - initial begin - clk = 0; - rst = 1; - data_in = 0; - out_of_range = 0; - - #20; - rst = 0; - repeat(5) @(posedge clk); - - for (i = 1; i < 20; i++) begin - @(posedge clk); - - data_in <= i; - end - - #50; - $finish; - end - - always @(posedge clk) begin - if (m_axis_tvalid) begin - $display("TIME=%0t PACKED DATA = %h", $time, m_axis_tdata); - end - end - -endmodule \ No newline at end of file -- 2.49.0 From 2e22eb68df98007c5336004b10bcf9d84101cba4 Mon Sep 17 00:00:00 2001 From: otroubi Date: Tue, 28 Apr 2026 15:21:58 +0300 Subject: [PATCH 23/25] docs: add sampler_readme --- rtl/sampler/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 rtl/sampler/README.md diff --git a/rtl/sampler/README.md b/rtl/sampler/README.md new file mode 100644 index 0000000..fda9f72 --- /dev/null +++ b/rtl/sampler/README.md @@ -0,0 +1,26 @@ +# Сэмплер +Модуль выполняет задачу сбора данных с выхода АЦП, их обработку, упаковку, и передачу дальше с помощью AXI Stream интерфейса. + +## Cписок параметров +DATA_WIDTH - ширина входных данных, получаемых с АЦП. +PACK_FACTOR - количество отсчетов, собираемых в один выходной пакет. +PROCESS_MODE - режим интерпретации входного кода. 0 - прямой код, 1 - дополнительный код. + +## Список входных портов +clk_in - сигнал тактирования выходного интерфейса. +rst - сброс модуля и остановка подачи импульсов. +[DATA_WIDTH-1:0] data_in - входной сигнал с АЦП. +out_of_range - флаг выхода значений данных за допустимый диапазон. 0 - валидны, 1 - не валидны. + +## Список выходных портов +[DATA_WIDTH*PACK_FACTOR-1:0] m_axis_tdata - урезанный axis формат, выходные данные. Ширина шины считается исходя из битности данных и фактора упаковки. +m_axis_tvalid - урезанный axis формат, валидность выходных данных. + +## Логика работы +На каждом такте принимаются data_in (значение АЦП) и out_of_range (флаг выхода значений данных за допустимый диапазон). Если out_of_range = 1, то данные игнорируются и не попадают во внутренний буффер. В противном случае, модуль накапливает данные во внутреннем буффере, идет его заполнение до количества данных, равное PACK_FACTOR. Когда буффер оказывается заполненным, он выдает пакет упакованных данных, сопровождая их импульсом m_axis_tvalid (готовность пакета). Если PROCESS_MODE = 1, данные выдаются в дополнительном коде, если PROCESS_MODE = 0 - в прямом. + +## Симуляция +Тесты запускаются автоматически через make. +```cd tests + make sim``` +При успешном завершении теста высвечивается "ALL PASSED". \ No newline at end of file -- 2.49.0 From bd8dc9d0d393aaa8bd9fd89a3f748c668df218f3 Mon Sep 17 00:00:00 2001 From: otroubi Date: Tue, 28 Apr 2026 15:25:51 +0300 Subject: [PATCH 24/25] chore: fix readme --- rtl/sampler/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rtl/sampler/README.md b/rtl/sampler/README.md index fda9f72..751865c 100644 --- a/rtl/sampler/README.md +++ b/rtl/sampler/README.md @@ -21,6 +21,8 @@ m_axis_tvalid - урезанный axis формат, валидность вы ## Симуляция Тесты запускаются автоматически через make. -```cd tests - make sim``` +``` +cd tests +make sim +``` При успешном завершении теста высвечивается "ALL PASSED". \ No newline at end of file -- 2.49.0 From 3ea03fd40cd89c106cd95378ed70b1c64a4c8627 Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 28 Apr 2026 15:36:59 +0300 Subject: [PATCH 25/25] docs: add README for accum --- rtl/accum/README.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 rtl/accum/README.md diff --git a/rtl/accum/README.md b/rtl/accum/README.md new file mode 100644 index 0000000..c3c518b --- /dev/null +++ b/rtl/accum/README.md @@ -0,0 +1,39 @@ +# Аккумулятор +Модуль аккумуляции данных для последующего усреднения. Принимает данные с входного потока АХI-Stream фиксированной ширины (задается параметрически), суммируя их сначала по окнам, а затем со значениями из предыдущей последовательности. + +## Список парамтеров: +- DATA_WIDTH - ширина входных данных, получаемых с АЦП +- ACCUM_WIDTH - размер данных для аккумуляции, должен быть степенью числа 2. По умолчанию - 32 +- N_MAX - максимальное число окон в последовательности. Должно быть степенью числа 2. Влияет на размер используемой памяти. +- WINDOW_SIZE - размер окна усреднения +- PACKET_SIZE - размер выходного пакета + +## Иерархия: +``` +├── accum_top - полная сборка аккумулятора +│   ├── accum - основная логика аккумуляции по окнам и последовательностям +│   │   ├── adder - модуль сложения по окнам +│   ├── out_axis_fifo - модуль для выдачи данных наружу в другом частотном домене +``` +## Список входных портов: +- clk_in - частота входных данных +- rst - сброс всего +- [DATA_WIDTH-1:0] s_axis_tdata - входные данные +- s_axis_tvalid - валидность входных данных +- start - начало аккумуляции +- [31:0] smp_num - число сэмплов (должно быть кратно WINDOW_SIZE) +- [15:0] seq_num - число последовательностей аккумуляции +- eth_clk_in - частота для выходных данных на ethernet +- req_ready - готовность отправителя начать принимать данные +- m_axis_tready - готовность выходного axis + + +## Список выходных портов: +- send_req - сигнал начала отправки данных +- [7:0] m_axis_tdata - данные выходного axis +- m_axis_tvalid - валидность выходного axis +- m_axis_tlast - последний пакет в axis +- finish - конец отправки всех данных, полный цикл работы завершен + +## Логика работы: +Модуль начинает работу при получении сигнала start. Сразу после начала работы можно подавать данные на входной axis, они будут суммироваться по WINDOW_SIZE штук и отправляться на хранение. Так будет сделано для последовательности длиной smp_num чисел, затем начинается новая последовательность - всего таких будет seq_num штук. Каждая последующая последовательность также суммируется по окнам, а затем полученные значения прибавляются к тем же значениям предыдущей последовательности. Таким образом, выполняется суммирование по двум осям, и из исходных данных seq_num по smp_num чисел остается вектор длиной 1 x (smp_num / WINDOW_SIZE). После накопления всех данных начинается выдача. Выдача осуществляется на выходной AXI stream, работающий в домене eth_clk, и имеющий ширину 8 бит - предполагается, что выдача пойдет на ethernet-udp. Когда поднят сигнал req_ready, модуль будет отправлять send_req (запрос отправки пакета), и по готовности m_axis_tready начнет выдавать пакет размер PACKET_SIZE байт. Если данные нельзя ровно разложить по пакетам, то в последнем пакете могут быть отправлены рандомные данные из памяти. После окончания отправки всех пакетов будет поднят сигнал finish. \ No newline at end of file -- 2.49.0