Files
reflectometer_fpga_project/rtl/accum/src/out_axis_fifo.sv
T
2026-07-29 14:03:18 +03:00

335 lines
12 KiB
Systemverilog

module out_axis_fifo #(
parameter ACCUM_WIDTH = 32,
parameter RW_WIDTH = 32,
parameter PACKET_SIZE = 256
) (
input logic dma_clk_in,
input logic acc_clk_in,
input logic rst,
input logic [31:0] smp_num,
input logic [31:0] window_size,
// AXI stream master for output, dma_clk_in domain
output logic [RW_WIDTH-1: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,
output logic [15:0] udp_data_length,
// 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
);
// 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
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 / RW_WIDTH;
localparam int RDEPTH_BITS = $clog2(FIFO_RDEPTH) + 1;
localparam int WR_WORD_BYTES = ACCUM_WIDTH/8;
localparam int RD_WORD_BYTES = RW_WIDTH/8;
localparam int PACKET_WR_WORDS = PACKET_SIZE / WR_WORD_BYTES;
localparam int PACKET_RD_WORDS = PACKET_SIZE / RD_WORD_BYTES;
wire wr_unavail;
wire wr_rst_busy;
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!
logic [31:0] window_size_reg;
wire [31:0] window_size_safe = (window_size == 32'd0) ? 32'd1 : window_size;
wire empty;
wire [WDEPTH_BITS:0] wr_data_count;
// NOTE:
// each written "acc_din" ACCUM_WIDTH word
// is counted as window_size samples actually
// because hw division for counters is painful
// so we just increased the counter sizes
always_ff @(posedge acc_clk_in) begin
if (rst_acc) begin
wr_state <= WR_IDLE;
wr_cnt <= 32'b0;
wr_batch_tgt <= 32'b0;
wr_total <= 32'b0;
window_size_reg <= 32'd1;
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;
window_size_reg <= window_size_safe;
batch_req <= 0;
finish <= 0;
end
end
// wait until we can request a word
// depends on prog_full signal
WR_CHECK: begin
if ((wr_data_count < (FIFO_WDEPTH - PACKET_WR_WORDS)) && ~wr_rst_busy) 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 window_size samples !!!
wr_batch_tgt <= wr_batch_tgt + (8 * window_size_reg * 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 (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
if (din_valid) begin
// data supplied
// count as we got window_size samples
wr_cnt <= wr_cnt + ACCUM_WIDTH * window_size_reg;
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
// wait until all data is sent
if (empty) begin
finish <= 1;
wr_state <= WR_IDLE;
end
end else begin
// next word
wr_state <= WR_CHECK;
end
end
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 dma_clk_in) begin
if (rst_eth) begin
rd_state <= RD_IDLE;
send_req <= 1'b0;
sent_cnt <= 16'd0;
m_axis_tlast <= 1'b0;
m_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_RD_WORDS) begin
// enough data to send packet, begin
rd_state <= RD_CHECK;
end
send_req <= 1'b0;
sent_cnt <= 16'd0;
rd_en <= 1'b0;
m_axis_tlast <= 1'b0;
m_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 (m_axis_tready && rd_valid) begin
rd_en <= 1'b1;
m_axis_tvalid <= 1'b1;
sent_cnt <= sent_cnt + 1;
// final packet of the batch
if (sent_cnt == PACKET_RD_WORDS-1) begin
rd_state <= RD_IDLE;
m_axis_tlast <= 1'b1;
end
end else begin
rd_en <= 1'b0;
m_axis_tvalid <= 1'b0;
end
end
endcase
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
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_RD_WORDS),
.PROG_FULL_THRESH(PACKET_WR_WORDS),
.RD_DATA_COUNT_WIDTH(RDEPTH_BITS),
.READ_DATA_WIDTH(RW_WIDTH), // 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(m_axis_tdata),
.empty(empty),
.full( ),
.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), // 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), // WR_DATA_COUNT_WIDTH-bit output: Write Data Count: This bus indicates the number of words written into the
// FIFO.
.rd_clk(dma_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(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(fifo_wr_en_r),
.wr_rst_busy(wr_rst_busy)
);
endmodule