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