// // Copyright 2017 Ettus Research, A National Instruments Company // // SPDX-License-Identifier: LGPL-3.0 // // Module: axi_replay.v // Description: // // This block implements the state machine and control logic for recording and // playback of AXI-Stream data, using a DMA-accessible memory as a buffer. module axi_replay #( parameter DATA_WIDTH = 64, parameter ADDR_WIDTH = 32, // Byte address width used by DMA master parameter COUNT_WIDTH = 8 // Length of counters used to connect to the DMA // master's read and write interfaces. ) ( input wire clk, input wire rst, // Synchronous to clk //--------------------------------------------------------------------------- // Settings Bus //--------------------------------------------------------------------------- input wire set_stb, input wire [ 7:0] set_addr, input wire [31:0] set_data, output reg [31:0] rb_data, input wire [ 7:0] rb_addr, //--------------------------------------------------------------------------- // AXI Stream Interface //--------------------------------------------------------------------------- // Input input wire [DATA_WIDTH-1:0] i_tdata, input wire i_tvalid, input wire i_tlast, output wire i_tready, // Output output wire [DATA_WIDTH-1:0] o_tdata, output wire o_tvalid, output wire o_tlast, input wire o_tready, //--------------------------------------------------------------------------- // DMA Interface //--------------------------------------------------------------------------- // Write interface output reg [ ADDR_WIDTH-1:0] write_addr, // Byte address for start of write // transaction (64-bit aligned). output reg [COUNT_WIDTH-1:0] write_count, // Count of 64-bit words to write, minus 1. output reg write_ctrl_valid, input wire write_ctrl_ready, output wire [ DATA_WIDTH-1:0] write_data, output wire write_data_valid, input wire write_data_ready, // Read interface output reg [ ADDR_WIDTH-1:0] read_addr, // Byte address for start of read // transaction (64-bit aligned). output reg [COUNT_WIDTH-1:0] read_count, // Count of 64-bit words to read, minus 1. output reg read_ctrl_valid, input wire read_ctrl_ready, input wire [ DATA_WIDTH-1:0] read_data, input wire read_data_valid, output wire read_data_ready ); //--------------------------------------------------------------------------- // Constants //--------------------------------------------------------------------------- // Size constants localparam CMD_WIDTH = 32; // Command width localparam LINES_WIDTH = 28; // Width of cmd_num_lines localparam WORD_SIZE = DATA_WIDTH/8; // Size of DATA_WIDTH in bytes // Register offsets localparam [7:0] SR_REC_BASE_ADDR = 128; localparam [7:0] SR_REC_BUFFER_SIZE = 129; localparam [7:0] SR_REC_RESTART = 130; localparam [7:0] SR_REC_FULLNESS = 131; localparam [7:0] SR_PLAY_BASE_ADDR = 132; localparam [7:0] SR_PLAY_BUFFER_SIZE = 133; localparam [7:0] SR_RX_CTRL_COMMAND = 152; // Same offset as radio localparam [7:0] SR_RX_CTRL_HALT = 155; // Same offset as radio localparam [7:0] SR_RX_CTRL_MAXLEN = 156; // Same offset as radio // Memory buffering parameters: // // Log base 2 of the depth of the input and output FIFOs to use. The FIFOs // should be large enough to store more than a complete burst // (MEM_BURST_SIZE). A size of 9 (512 64-bit words) is one 36-kbit BRAM. localparam REC_FIFO_ADDR_WIDTH = 9; // Log2 of input/record FIFO size localparam PLAY_FIFO_ADDR_WIDTH = 9; // Log2 of output/playback FIFO size // // Amount of data to buffer before writing to RAM. This should be a power of // two so that it evenly divides the AXI_ALIGNMENT requirement. It also must // not exceed 2**COUNT_WIDTH (the maximum count allowed by DMA master). localparam MEM_BURST_SIZE = 2**COUNT_WIDTH; // Size in DATA_WIDTH-sized words // // AXI alignment requirement (4096 bytes) in DATA_WIDTH-bit words localparam AXI_ALIGNMENT = 4096 / WORD_SIZE; // // Clock cycles to wait before writing something less than MEM_BURST_SIZE // to memory. localparam DATA_WAIT_TIMEOUT = 31; //--------------------------------------------------------------------------- // Signals //--------------------------------------------------------------------------- // Command wires wire cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf; wire [LINES_WIDTH-1:0] cmd_num_lines_cf; // Settings registers signals wire [ ADDR_WIDTH-1:0] rec_base_addr_sr; // Byte address wire [ ADDR_WIDTH-1:0] rec_buffer_size_sr; // Size in bytes wire [ ADDR_WIDTH-1:0] play_base_addr_sr; // Byte address wire [ ADDR_WIDTH-1:0] play_buffer_size_sr; // Size in bytes reg rec_restart; reg rec_restart_clear; wire [ CMD_WIDTH-1:0] command; wire command_valid; reg play_halt; reg play_halt_clear; wire [COUNT_WIDTH:0] play_max_len_sr; // Command FIFO wire cmd_fifo_valid; reg cmd_fifo_ready; // Record Data FIFO (Input) wire [DATA_WIDTH-1:0] rec_fifo_o_tdata; wire rec_fifo_o_tvalid; wire rec_fifo_o_tready; wire [ 15:0] rec_fifo_occupied; // Playback Data FIFO (Output) wire [DATA_WIDTH-1:0] play_fifo_i_tdata; wire play_fifo_i_tvalid; wire play_fifo_i_tready; wire [ 15:0] play_fifo_space; // Free space in play_axi_fifo // Buffer usage registers reg [ADDR_WIDTH-1:0] rec_buffer_avail; // Amount of free buffer space in words reg [ADDR_WIDTH-1:0] rec_buffer_used; // Amount of occupied buffer space in words //--------------------------------------------------------------------------- // Registers //--------------------------------------------------------------------------- // Record Base Address Register. Address is a byte address. This must be a // multiple of 8 bytes. setting_reg #( .my_addr (SR_REC_BASE_ADDR), .width (ADDR_WIDTH) ) sr_rec_base_addr ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (rec_base_addr_sr), .changed () ); // Record Buffer Size Register. This indicates the portion of the RAM // allocated to the record buffer, in bytes. This should be a multiple of 8 // bytes. setting_reg #( .my_addr (SR_REC_BUFFER_SIZE), .width (ADDR_WIDTH) ) sr_rec_buffer_size ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (rec_buffer_size_sr), .changed () ); // Playback Base Address Register. Address is a byte address. This must be a // multiple of the 8 bytes. setting_reg #( .my_addr (SR_PLAY_BASE_ADDR), .width (ADDR_WIDTH) ) sr_play_base_addr ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (play_base_addr_sr), .changed () ); // Playback Buffer Size Register. This indicates the portion of the RAM // allocated to the record buffer, in bytes. This should be a multiple of 8 // bytes. setting_reg #( .my_addr (SR_PLAY_BUFFER_SIZE), .width (ADDR_WIDTH) ) sr_play_buffer_size ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (play_buffer_size_sr), .changed () ); // Record Buffer Restart Register. Software must write to this register after // updating the base address or buffer size. A write to this register means // we need to stop any recording in progress and reset the record buffers // according to the current buffer base address and size registers. always @(posedge clk) begin : sr_restart if(rst) begin rec_restart <= 1'b0; end else begin if(set_stb & (set_addr == SR_REC_RESTART)) begin rec_restart <= 1'b1; end else if (rec_restart_clear) begin rec_restart <= 1'b0; end end end // Halt Register. A write to this register stops any replay operation as soon // as the current DRAM transaction completes. always @(posedge clk) begin : sr_halt if(rst) begin play_halt <= 1'b0; end else begin if(set_stb & (set_addr == SR_RX_CTRL_HALT)) begin play_halt <= 1'b1; end else if (play_halt_clear) begin play_halt <= 1'b0; end end end // Play Command Register // // This register mirrors the behavior of the RFNoC RX radio block. All // commands are queued up in the replay command FIFO. The fields are as // follows. // // send_imm [31] Send command immediately (don't use time). // // chain [30] When done with num_lines, immediately run next command. // // reload [29] When done with num_lines, rerun the same command if // cmd_chain is set and no new command is available. // // stop [28] When done with num_lines, stop transferring if // cmd_chain is set. // // num_lines [27:0] Number of samples to transfer to/from block. // setting_reg #( .my_addr (SR_RX_CTRL_COMMAND), .width (CMD_WIDTH) ) sr_command ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (command), .changed (command_valid) ); // Max Length Register. This register sets the number of words for the // maximum packet size. setting_reg #( .my_addr (SR_RX_CTRL_MAXLEN), .width (COUNT_WIDTH+1), .at_reset({1'b1, {COUNT_WIDTH{1'b0}}}) ) sr_max_len ( .clk (clk), .rst (rst), .strobe (set_stb), .addr (set_addr), .in (set_data), .out (play_max_len_sr), .changed () ); // Implement register read always @(*) begin case (rb_addr) SR_REC_BASE_ADDR : rb_data = rec_base_addr_sr; SR_REC_BUFFER_SIZE : rb_data = rec_buffer_size_sr; SR_REC_FULLNESS : rb_data = rec_buffer_used * WORD_SIZE; SR_PLAY_BASE_ADDR : rb_data = play_base_addr_sr; SR_PLAY_BUFFER_SIZE : rb_data = play_buffer_size_sr; SR_RX_CTRL_MAXLEN : rb_data = play_max_len_sr; default : rb_data = 32'h0; endcase end //--------------------------------------------------------------------------- // Playback Command FIFO //--------------------------------------------------------------------------- // // This block queues up commands for playback control. // //--------------------------------------------------------------------------- axi_fifo_short #( .WIDTH (CMD_WIDTH) ) command_fifo ( .clk (clk), .reset (rst), .clear (play_halt_clear), .i_tdata (command), .i_tvalid (command_valid), .i_tready (), .o_tdata ({cmd_send_imm_cf, cmd_chain_cf, cmd_reload_cf, cmd_stop_cf, cmd_num_lines_cf}), .o_tvalid (cmd_fifo_valid), .o_tready (cmd_fifo_ready), .occupied (), .space () ); //--------------------------------------------------------------------------- // Record Input Data FIFO //--------------------------------------------------------------------------- // // This FIFO stores data to be recording into the RAM buffer. // //--------------------------------------------------------------------------- axi_fifo #( .WIDTH (DATA_WIDTH), .SIZE (REC_FIFO_ADDR_WIDTH) ) rec_axi_fifo ( .clk (clk), .reset (rst), .clear (1'b0), // .i_tdata (i_tdata), .i_tvalid (i_tvalid), .i_tready (i_tready), // .o_tdata (rec_fifo_o_tdata), .o_tvalid (rec_fifo_o_tvalid), .o_tready (rec_fifo_o_tready), // .space (), .occupied (rec_fifo_occupied) ); //--------------------------------------------------------------------------- // Record State Machine //--------------------------------------------------------------------------- // FSM States localparam REC_WAIT_FIFO = 0; localparam REC_CHECK_ALIGN = 1; localparam REC_DMA_REQ = 2; localparam REC_WAIT_DMA_START = 3; localparam REC_WAIT_DMA_COMMIT = 4; // State Signals reg [2:0] rec_state; // Registers reg [ADDR_WIDTH-1:0] rec_base_addr; // Last base address pulled from settings register reg [ADDR_WIDTH-1:0] rec_buffer_size; // Last buffer size pulled from settings register reg [ADDR_WIDTH-1:0] rec_addr; // Current offset into record buffer reg [ADDR_WIDTH-1:0] rec_size; // Number of words to transfer next reg [ADDR_WIDTH-1:0] rec_size_0; // Pipeline stage for computation of rec_size reg signed [ADDR_WIDTH:0] rec_size_aligned; // rec_size reduced to not cross 4k boundary // Timer to count how many cycles we've been waiting for new data reg [$clog2(DATA_WAIT_TIMEOUT+1)-1:0] rec_wait_timer; reg rec_wait_timeout; always @(posedge clk) begin if (rst) begin rec_state <= REC_WAIT_FIFO; rec_addr <= 0; write_ctrl_valid <= 1'b0; rec_buffer_avail <= 0; rec_buffer_used <= 0; rec_wait_timer <= 0; rec_wait_timeout <= 0; end else begin // Default assignments rec_restart_clear <= 1'b0; // Update wait timer if (i_tvalid || !rec_fifo_occupied) begin // If a new word is presented to the input FIFO, or the FIFO is empty, // then reset the timer. rec_wait_timer <= 0; rec_wait_timeout <= 1'b0; end else if (rec_fifo_occupied) begin // If no new word is written, but there's data in the FIFO, update the // timer. Latch timeout condition when we reach out limit. rec_wait_timer <= rec_wait_timer + 1; if (rec_wait_timer == DATA_WAIT_TIMEOUT) begin rec_wait_timeout <= 1'b1; end end // Pre-calculate the aligned size rec_size_aligned <= $signed(AXI_ALIGNMENT) - $signed(rec_addr & (AXI_ALIGNMENT-1)); // // State logic // case (rec_state) REC_WAIT_FIFO : begin // Wait until there's enough data to initiate a transfer from the // FIFO to the RAM. // Check if a restart was requested on the record interface if (rec_restart) begin rec_restart_clear <= 1'b1; // Latch the new register values. We don't want them to change // while we're running. rec_base_addr <= rec_base_addr_sr; rec_buffer_size <= rec_buffer_size_sr / WORD_SIZE; // Store size in words // Reset counters and address any time we update the buffer size or // base address. rec_buffer_avail <= rec_buffer_size_sr / WORD_SIZE; // Store size in words rec_buffer_used <= 0; rec_addr <= rec_base_addr_sr; // Check if there's room left in the record RAM buffer end else if (rec_buffer_used < rec_buffer_size) begin // See if we can transfer a full burst if (rec_fifo_occupied >= MEM_BURST_SIZE && rec_buffer_avail >= MEM_BURST_SIZE) begin rec_size_0 <= MEM_BURST_SIZE; rec_state <= REC_CHECK_ALIGN; // Otherwise, if we've been waiting a long time, see if we can // transfer less than a burst. end else if (rec_fifo_occupied > 0 && rec_wait_timeout) begin rec_size_0 <= (rec_fifo_occupied <= rec_buffer_avail) ? rec_fifo_occupied : rec_buffer_avail; rec_state <= REC_CHECK_ALIGN; end end end REC_CHECK_ALIGN : begin // Check the address alignment, since AXI requires that an access not // cross 4k boundaries (boo), and the axi_dma_master doesn't handle // this automatically (boo again). rec_size <= ($signed({1'b0,rec_size_0}) > rec_size_aligned) ? rec_size_aligned : rec_size_0; // DMA interface is ready, so transaction will begin rec_state <= REC_DMA_REQ; end REC_DMA_REQ : begin // The write count written to the DMA engine should be 1 less than // the number of words you want to write (not the number of bytes). write_count <= rec_size - 1; // Create the physical RAM byte address by combining the address and // base address. write_addr <= rec_addr; // Once the interface is ready, make the DMA request if (write_ctrl_ready) begin // Request the write transaction write_ctrl_valid <= 1'b1; rec_state <= REC_WAIT_DMA_START; end end REC_WAIT_DMA_START : begin // Wait until DMA interface deasserts ready, indicating it has // started on the request. write_ctrl_valid <= 1'b0; if (!write_ctrl_ready) begin rec_state <= REC_WAIT_DMA_COMMIT; end end REC_WAIT_DMA_COMMIT : begin // Wait for the DMA interface to reassert write_ctrl_ready, which // signals that the DMA engine has received a response for the whole // write transaction and (we assume) it has been committed to RAM. // After this, we can update the write address and start the next // transaction. if (write_ctrl_ready) begin rec_addr <= rec_addr + (rec_size * WORD_SIZE); rec_buffer_used <= rec_buffer_used + rec_size; rec_buffer_avail <= rec_buffer_avail - rec_size; rec_state <= REC_WAIT_FIFO; end end default : begin rec_state <= REC_WAIT_FIFO; end endcase end end // Connect output of record FIFO to input of DMA write interface assign write_data = rec_fifo_o_tdata; assign write_data_valid = rec_fifo_o_tvalid; assign rec_fifo_o_tready = write_data_ready; //--------------------------------------------------------------------------- // Playback State Machine //--------------------------------------------------------------------------- // FSM States localparam PLAY_IDLE = 0; localparam PLAY_WAIT_DATA_READY = 1; localparam PLAY_SIZE_CALC = 2; localparam PLAY_DMA_REQ = 3; localparam PLAY_WAIT_DMA_START = 4; localparam PLAY_WAIT_DMA_COMMIT = 5; localparam PLAY_DONE_CHECK = 6; // State Signals reg [2:0] play_state; // Registers reg [ADDR_WIDTH-1:0] play_base_addr; // Last base address pulled from settings register reg [ADDR_WIDTH-1:0] play_buffer_size; // Last buffer size pulled from settings register reg [ADDR_WIDTH-1:0] play_addr; // Current byte offset into record buffer reg [ADDR_WIDTH-1:0] play_addr_0; // Pipeline stage for computing play_addr reg [ADDR_WIDTH-1:0] play_addr_1; // Pipeline stage for computing play_addr reg [ADDR_WIDTH-1:0] play_buffer_end; // Address of location after end of buffer reg [ADDR_WIDTH-1:0] max_dma_size; // Maximum size of next transfer, in words // reg [LINES_WIDTH-1:0] cmd_num_lines; // Copy of cmd_num_lines from last command reg [LINES_WIDTH-1:0] play_words_remaining; // Number of lines left to read for command reg cmd_chain; // Copy of cmd_chain from last command reg cmd_reload; // Copy of cmd_reload from last command reg play_full_burst_avail; // True if we there's a full burst to read reg play_buffer_avail_nonzero; // True if > 0 reg cmd_num_lines_cf_nonzero; // True if > 0 reg max_dma_size_ok; // True if it's OK to read max_dma_size reg [ADDR_WIDTH-1:0] max_dma_size_m1; // max_dma_size - 1 reg [ADDR_WIDTH-1:0] play_words_remaining_m1; // play_words_remaining - 1 reg [ADDR_WIDTH-1:0] play_buffer_avail; // Number of words left to read in record buffer reg [ADDR_WIDTH-1:0] play_buffer_avail_0; // Pipeline stage for computing play_buffer_avail always @(posedge clk) begin if (rst) begin play_state <= PLAY_IDLE; cmd_fifo_ready <= 1'b0; end else begin // Calculate how many words are left to read from the record buffer play_full_burst_avail <= (play_buffer_avail >= MEM_BURST_SIZE); play_buffer_avail_nonzero <= (play_buffer_avail > 0); cmd_num_lines_cf_nonzero <= (cmd_num_lines_cf > 0); play_buffer_end <= play_base_addr_sr + play_buffer_size_sr; // Default values cmd_fifo_ready <= 1'b0; read_ctrl_valid <= 1'b0; play_halt_clear <= 1'b0; // // State logic // case (play_state) PLAY_IDLE : begin // Always start reading at the start of the record buffer play_addr <= play_base_addr_sr; // Save off command info, in case we need to repeat the command cmd_num_lines <= cmd_num_lines_cf; cmd_reload <= cmd_reload_cf; cmd_chain <= cmd_chain_cf; // Save the buffer info so it doesn't update during playback play_base_addr <= play_base_addr_sr; play_buffer_size <= play_buffer_size_sr; play_buffer_avail <= play_buffer_size_sr / WORD_SIZE; // Wait until we receive a command and we have enough data recorded // to honor it. if (cmd_fifo_valid && ~play_halt_clear) begin // Load the number of word remaining to complete this command play_words_remaining <= cmd_num_lines_cf; // We don't support time yet, so we require send_imm to do // anything. Also, we can't do anything until we have data recorded. if (cmd_stop_cf) begin // Do nothing, except clear command from the FIFO cmd_fifo_ready <= 1'b1; end else if (cmd_send_imm_cf && play_buffer_avail_nonzero && cmd_num_lines_cf_nonzero) begin // Dequeue the command from the FIFO cmd_fifo_ready <= 1'b1; play_state <= PLAY_WAIT_DATA_READY; end end else if (play_halt) begin // In case we get a HALT after a command has finished play_halt_clear <= 1'b1; end end PLAY_WAIT_DATA_READY : begin // Save the maximum size we can read from RAM max_dma_size <= play_full_burst_avail ? MEM_BURST_SIZE : play_buffer_avail; // Check if we got a halt command while waiting if (play_halt) begin play_halt_clear <= 1'b1; play_state <= PLAY_IDLE; // Wait for output FIFO to empty sufficiently so we can read an // entire burst at once. This may be more space than needed, but we // won't know the exact size until the next state. end else if (play_fifo_space >= MEM_BURST_SIZE) begin play_state <= PLAY_SIZE_CALC; end end PLAY_SIZE_CALC : begin // Do some intermediate calculations to determine what the read_count // should be. play_words_remaining_m1 <= play_words_remaining-1; max_dma_size_m1 <= max_dma_size-1; max_dma_size_ok <= play_words_remaining >= max_dma_size; play_state <= PLAY_DMA_REQ; end PLAY_DMA_REQ : begin // Load the size of the next read into a register. We try to read the // max amount available (up to the burst size) or however many words // are needed to reach the end of the RAM buffer. // // The read count written to the DMA engine should be 1 less than the // number of words you want to read (not the number of bytes). read_count <= max_dma_size_ok ? max_dma_size_m1 : play_words_remaining_m1; // Load the address to read. Note that we don't do an alignment check // since we assume that multiples of MEM_BURST_SIZE meet the // AXI_ALIGNMENT requirement. read_addr <= play_addr; // Request the read transaction as soon as DMA interface is ready if (read_ctrl_ready) begin read_ctrl_valid <= 1'b1; play_state <= PLAY_WAIT_DMA_START; end end PLAY_WAIT_DMA_START : begin // Wait until DMA interface deasserts ready, indicating it has // started on the request. read_ctrl_valid <= 1'b0; if (!read_ctrl_ready) begin // Update values for next transaction play_addr_0 <= play_addr + ({{(ADDR_WIDTH-COUNT_WIDTH){1'b0}}, read_count} + 1) * WORD_SIZE; play_words_remaining <= play_words_remaining - ({1'b0, read_count} + 1); play_buffer_avail_0 <= play_buffer_avail - ({1'b0, read_count} + 1); play_state <= PLAY_WAIT_DMA_COMMIT; end end PLAY_WAIT_DMA_COMMIT : begin // Wait for the DMA interface to reassert read_ctrl_ready, which // signals that the DMA engine has received a response for the whole // read transaction. if (read_ctrl_ready) begin // Check if we need to wrap the address for the next transaction if (play_addr_0 >= play_buffer_end) begin play_addr_1 <= play_base_addr_sr; play_buffer_avail <= play_buffer_size_sr / WORD_SIZE; end else begin play_addr_1 <= play_addr_0; play_buffer_avail <= play_buffer_avail_0; end play_state <= PLAY_DONE_CHECK; end end PLAY_DONE_CHECK : begin play_addr <= play_addr_1; // Check if we have more data to transfer for this command if (play_words_remaining) begin play_state <= PLAY_WAIT_DATA_READY; // Check if we're chaining end else if (cmd_chain) begin // Check if there's a new command waiting if (cmd_fifo_valid) begin // Load the next command. Note that we don't reset the playback // address when commands are chained together. play_words_remaining <= cmd_num_lines_cf; cmd_num_lines <= cmd_num_lines_cf; cmd_reload <= cmd_reload_cf; cmd_chain <= cmd_chain_cf; // Dequeue the command from the FIFO cmd_fifo_ready <= 1'b1; // Stop if it's a stop command, otherwise restart if (cmd_stop_cf) begin play_state <= PLAY_IDLE; end else begin play_state <= PLAY_WAIT_DATA_READY; end // Check if we need to restart the previous command end else if (cmd_reload) begin play_words_remaining <= cmd_num_lines; play_state <= PLAY_WAIT_DATA_READY; end // Nothing left to do end else begin play_state <= PLAY_IDLE; end end endcase end end // Connect output of DMA master to playback data FIFO assign play_fifo_i_tdata = read_data; assign play_fifo_i_tvalid = read_data_valid; assign read_data_ready = play_fifo_i_tready; //--------------------------------------------------------------------------- // TLAST Generation //--------------------------------------------------------------------------- // // This block monitors the signals to/from the DMA master and generates the // TLAST signal. We assert TLAST at the end of every read transaction and // after every play_max_len_sr words, so that no packets are longer than the // length indicated by the max_len register. // // The timing of this block relies on the fact that read_ctrl_ready is not // reasserted by the DMA master until after TLAST gets asserted. // //--------------------------------------------------------------------------- reg [COUNT_WIDTH-1:0] read_counter; reg [COUNT_WIDTH-1:0] length_counter; reg play_fifo_i_tlast; always @(posedge clk) begin if (rst) begin play_fifo_i_tlast <= 1'b0; end else begin // Check if we're requesting a read transaction if (read_ctrl_valid && read_ctrl_ready) begin // Initialize read_counter for new transaction read_counter <= read_count; length_counter <= play_max_len_sr; // If read_count is 0, then the first word is also the last word if (read_count == 0) begin play_fifo_i_tlast <= 1'b1; end // Track the number of words read out by DMA master end else if (read_data_valid && read_data_ready) begin read_counter <= read_counter - 1; length_counter <= length_counter - 1; // Check if the word currently being output is the last word of a // packet, which means we need to clear tlast. if (play_fifo_i_tlast) begin // But make sure that the next word isn't also the last of a DMA // burst, for which we will need to keep tlast asserted. if (read_counter != 1) begin play_fifo_i_tlast <= 1'b0; end // Restart length counter length_counter <= play_max_len_sr; // Check if the next word to be output should be the last of a packet. end else if (read_counter == 1 || length_counter == 2) begin play_fifo_i_tlast <= 1'b1; end end end end //--------------------------------------------------------------------------- // Playback Output Data FIFO //--------------------------------------------------------------------------- // // This FIFO buffers data that has been read out of RAM as part of a playback // operation. // //--------------------------------------------------------------------------- axi_fifo #( .WIDTH (DATA_WIDTH+1), .SIZE (PLAY_FIFO_ADDR_WIDTH) ) play_axi_fifo ( .clk (clk), .reset (rst), .clear (1'b0), // .i_tdata ({play_fifo_i_tlast, play_fifo_i_tdata}), .i_tvalid (play_fifo_i_tvalid), .i_tready (play_fifo_i_tready), // .o_tdata ({o_tlast, o_tdata}), .o_tvalid (o_tvalid), .o_tready (o_tready), // .space (play_fifo_space), .occupied () ); endmodule