FPGA: Replay block version 1.1

- Add registers to read current record and  play positions.
- Add register to read current space in play command FIFO to allow
  software to avoid overflowing the FIFO.
- Cache base address and size with play command in command FIFO.
- Fix timestamp logic.  Timestamp is only for the first packet of a
  burst.  The increment of 1 for each sample is not accurate because
  it assumed the Replay block was playing at the same rate as the
  Radio, which cannot be assumed.

Maintained backwards compatibility with older API.

Signed-off-by: michael-west <michael.west@ettus.com>


Original-commit: 1545d3ff05f14b8dd175736b326fe6cae7dc830d
This commit is contained in:
michael-west
2022-04-01 13:33:08 -07:00
committed by Aaron Rossetto
parent 16c1235b30
commit 1c5b744a4d
3 changed files with 266 additions and 50 deletions
+114 -50
View File
@@ -32,7 +32,8 @@
// writing a command to the REG_PLAY_CMD register. The play command indicates
// if it should play a fixed number of words then stop (PLAY_CMD_FINITE),
// playback forever (PLAY_CMD_CONTINUOUS), or stop playback (PLAY_CMD_STOP).
// The number of words to play back with PLAY_CMD_FINITE is set by first
// The beginning of the playback is set by first writing to
// REG_PLAY_BASE_ADDR. The number of words to play back is set by first
// writing to REG_PLAY_CMD_NUM_WORDS.
//
// The length of the packets generated during playback is configured by the
@@ -40,9 +41,7 @@
//
// A timestamp for playback can also be specified by setting
// REG_PLAY_CMD_TIME and setting the REG_PLAY_TIMED_POS bit as part of the
// command write. The timestamp will then be included in all output packets,
// starting with the provided timestamp value and auto-incrementing by one
// for every REG_ITEM_SIZE bytes of data in each packet.
// command write. The timestamp will be included in the first output packet.
//
// When playback reaches the end of the configured playback buffer, if more
// words were requested, it will loop back to the beginning of the buffer to
@@ -150,7 +149,7 @@ module axis_replay #(
//---------------------------------------------------------------------------
localparam [REG_MAJOR_LEN-1:0] COMPAT_MAJOR = 1;
localparam [REG_MINOR_LEN-1:0] COMPAT_MINOR = 0;
localparam [REG_MINOR_LEN-1:0] COMPAT_MINOR = 1;
localparam [REG_ITEM_SIZE_LEN-1:0] DEFAULT_ITEM_SIZE = 4; // 4 bytes for sc16
@@ -240,20 +239,26 @@ module axis_replay #(
reg [MEM_ADDR_W-1:0] reg_rec_base_addr;
reg [MEM_SIZE_W-1:0] reg_rec_buffer_size;
reg [31:0] reg_rec_fullness_hi;
reg [31:0] reg_rec_pos_hi;
reg rec_restart;
reg [MEM_ADDR_W-1:0] reg_play_base_addr;
reg [MEM_SIZE_W-1:0] reg_play_buffer_size;
reg [31:0] reg_play_pos_hi;
reg [NUM_WORDS_W-1:0] reg_play_cmd_num_words;
reg [TIME_W-1:0] reg_play_cmd_time;
reg [CMD_W-1:0] reg_play_cmd;
reg reg_play_cmd_timed;
reg reg_play_cmd_valid;
wire reg_play_cmd_ready;
reg play_cmd_stop;
reg clear_cmd_fifo;
reg [WPP_W-1:0] reg_play_words_per_pkt = REG_PLAY_WORDS_PER_PKT_INIT;
reg [REG_ITEM_SIZE_LEN-1:0] reg_item_size = DEFAULT_ITEM_SIZE;
wire [5:0] reg_cmd_fifo_space;
wire [63:0] reg_rec_fullness;
wire [63:0] reg_rec_pos;
wire [63:0] reg_play_pos;
reg rec_restart_clear;
reg play_cmd_stop_ack;
@@ -275,8 +280,10 @@ module axis_replay #(
reg_rec_base_addr <= 0;
reg_rec_buffer_size <= 0;
reg_rec_fullness_hi <= 'bX;
reg_rec_pos_hi <= 'bX;
reg_play_base_addr <= 0;
reg_play_buffer_size <= 0;
reg_play_pos_hi <= 'bX;
reg_play_cmd_num_words <= 0;
reg_play_cmd_time <= 0;
reg_play_words_per_pkt <= REG_PLAY_WORDS_PER_PKT_INIT;
@@ -379,13 +386,46 @@ module axis_replay #(
REG_PLAY_ITEM_SIZE :
s_ctrlport_resp_data[REG_ITEM_SIZE_POS+:REG_ITEM_SIZE_LEN]
<= reg_item_size;
REG_REC_POS_LO : begin
s_ctrlport_resp_data <= reg_rec_pos[31:0];
if (MEM_SIZE_W > 32) begin
// The LO register must be read first. Save HI part now to
// guarantee coherence when HI register is read.
reg_rec_pos_hi <= 0;
reg_rec_pos_hi[0 +: max(MEM_SIZE_W-32, 1)]
<= reg_rec_pos[32 +: max(MEM_SIZE_W-32, 1)];
end
end
REG_REC_POS_HI :
if (MEM_SIZE_W > 32) begin
// Return the saved value to guarantee coherence
s_ctrlport_resp_data <= reg_rec_pos_hi;
end
REG_PLAY_POS_LO : begin
s_ctrlport_resp_data <= reg_play_pos[31:0];
if (MEM_SIZE_W > 32) begin
// The LO register must be read first. Save HI part now to
// guarantee coherence when HI register is read.
reg_play_pos_hi <= 0;
reg_play_pos_hi[0 +: max(MEM_SIZE_W-32, 1)]
<= reg_play_pos[32 +: max(MEM_SIZE_W-32, 1)];
end
end
REG_PLAY_POS_HI :
if (MEM_SIZE_W > 32) begin
// Return the saved value to guarantee coherence
s_ctrlport_resp_data <= reg_play_pos_hi;
end
REG_PLAY_CMD_FIFO_SPACE :
s_ctrlport_resp_data[5:0] <= reg_cmd_fifo_space;
endcase
end
//-----------------------------------------
// Register Writes
//-----------------------------------------
end else if (s_ctrlport_req_wr) begin
if (s_ctrlport_req_wr) begin
s_ctrlport_resp_ack <= 1;
case (s_ctrlport_req_addr)
REG_REC_BASE_ADDR_LO :
@@ -473,23 +513,25 @@ module axis_replay #(
wire cmd_timed_cf;
wire [NUM_WORDS_W-1:0] cmd_num_words_cf;
wire [TIME_W-1:0] cmd_time_cf;
wire [MEM_ADDR_W-1:0] cmd_base_addr_cf;
wire [MEM_SIZE_W-1:0] cmd_buffer_size_cf;
wire cmd_fifo_valid;
reg cmd_fifo_ready;
axi_fifo_short #(
.WIDTH (1 + CMD_W + NUM_WORDS_W + TIME_W)
.WIDTH (MEM_ADDR_W + MEM_SIZE_W + 1 + CMD_W + NUM_WORDS_W + TIME_W)
) command_fifo (
.clk (clk),
.reset (rst),
.clear (clear_cmd_fifo),
.i_tdata ({reg_play_cmd_timed, reg_play_cmd, reg_play_cmd_num_words, reg_play_cmd_time}),
.i_tdata ({play_base_addr_sr, play_buffer_size_sr, reg_play_cmd_timed, reg_play_cmd, reg_play_cmd_num_words, reg_play_cmd_time}),
.i_tvalid (reg_play_cmd_valid),
.i_tready (),
.o_tdata ({cmd_timed_cf, cmd_cf, cmd_num_words_cf, cmd_time_cf}),
.i_tready (reg_play_cmd_ready),
.o_tdata ({cmd_base_addr_cf, cmd_buffer_size_cf, cmd_timed_cf, cmd_cf, cmd_num_words_cf, cmd_time_cf}),
.o_tvalid (cmd_fifo_valid),
.o_tready (cmd_fifo_ready),
.occupied (),
.space ()
.space (reg_cmd_fifo_space)
);
@@ -553,6 +595,7 @@ module axis_replay #(
reg rec_wait_timeout;
assign reg_rec_fullness = rec_buffer_used * BYTES_PER_WORD;
assign reg_rec_pos = rec_addr;
always @(posedge clk) begin
if (rst) begin
@@ -564,7 +607,7 @@ module axis_replay #(
rec_buffer_used <= 0;
// Don't care:
rec_addr <= {MEM_ADDR_W{1'bX}};
rec_addr <= {MEM_ADDR_W{1'b0}};
rec_size_0 <= {MEM_ADDR_W{1'bX}};
rec_size <= {MEM_ADDR_W{1'bX}};
write_count <= {MEM_COUNT_W{1'bX}};
@@ -706,16 +749,17 @@ module axis_replay #(
// FSM States
localparam PLAY_IDLE = 0;
localparam PLAY_WAIT_DATA_READY = 1;
localparam PLAY_CHECK_ALIGN = 2;
localparam PLAY_SIZE_CALC = 3;
localparam PLAY_MEM_REQ = 4;
localparam PLAY_WAIT_MEM_START = 5;
localparam PLAY_WAIT_MEM_COMMIT = 6;
localparam PLAY_DONE_CHECK = 7;
localparam PLAY_CHECK_SIZES = 1;
localparam PLAY_WAIT_DATA_READY = 2;
localparam PLAY_CHECK_ALIGN = 3;
localparam PLAY_SIZE_CALC = 4;
localparam PLAY_MEM_REQ = 5;
localparam PLAY_WAIT_MEM_START = 6;
localparam PLAY_WAIT_MEM_COMMIT = 7;
localparam PLAY_DONE_CHECK = 8;
// State Signals
reg [2:0] play_state;
reg [3:0] play_state;
// Registers
reg [MEM_ADDR_W-1:0] play_addr; // Current byte offset into record buffer
@@ -729,13 +773,14 @@ module axis_replay #(
//
reg [NUM_WORDS_W-1:0] play_words_remaining; // Number of words left for playback command
reg [CMD_W-1:0] cmd; // Copy of cmd_cf from last command
reg cmd_timed; // Copy of cmd_timed_cf from last command
reg [TIME_W-1:0] cmd_time; // Copy of cmd_time_cf from last command
reg [MEM_ADDR_W-1:0] cmd_base_addr; // Copy of cmd_base_addr_cf from last command
reg [MEM_SIZE_W-1:0] cmd_buffer_size; // Copy of cmd_buffer_size_cf from last command
reg last_trans; // Is this the last read transaction for the command?
reg play_full_burst_avail; // True if we there's a full burst to read
reg play_buffer_avail_nonzero; // True if play_buffer_avail > 0
reg next_read_size_ok; // True if it's OK to read next_read_size
reg play_buffer_zero; // True if play buffer size is zero
reg num_words_zero; // True if number of words to play is zero
reg [MEM_ADDR_W-1:0] next_read_size_m1; // next_read_size - 1
reg [MEM_ADDR_W-1:0] play_words_remaining_m1; // play_words_remaining - 1
@@ -745,21 +790,23 @@ module axis_replay #(
reg pause_data_transfer;
assign reg_play_pos = play_addr;
always @(posedge clk)
begin
if (rst) begin
play_state <= PLAY_IDLE;
cmd_fifo_ready <= 1'b0;
play_addr <= {MEM_ADDR_W{1'b0}};
last_trans <= 1'b0;
// Don't care:
play_full_burst_avail <= 1'bX;
play_buffer_avail_nonzero <= 1'bX;
play_buffer_end <= {MEM_SIZE_W{1'bX}};
read_ctrl_valid <= 1'bX;
play_addr <= {MEM_ADDR_W{1'bX}};
cmd <= {CMD_W{1'bX}};
cmd_time <= {TIME_W{1'bX}};
cmd_timed <= 1'bX;
cmd_base_addr <= {MEM_ADDR_W{1'bX}};
cmd_buffer_size <= {MEM_SIZE_W{1'bX}};
play_buffer_avail <= {MEM_SIZE_W{1'bX}};
play_size_aligned <= {MEM_SIZE_W{1'bX}};
play_words_remaining <= {NUM_WORDS_W{1'bX}};
@@ -773,14 +820,13 @@ module axis_replay #(
play_addr_0 <= {MEM_ADDR_W+1{1'bX}};
play_buffer_avail_0 <= {MEM_SIZE_W{1'bX}};
play_addr_1 <= {MEM_ADDR_W{1'bX}};
last_trans <= 1'b0;
play_buffer_zero <= 1'bX;
num_words_zero <= 1'bX;
end else begin
// Calculate how many words are left to read from the record buffer
play_full_burst_avail <= (play_buffer_avail >= MEM_BURST_LEN);
play_buffer_avail_nonzero <= (play_buffer_avail > 0);
play_buffer_end <= play_base_addr_sr + play_buffer_size_sr;
play_size_aligned <= AXI_ALIGNMENT - ((play_addr/BYTES_PER_WORD) & (AXI_ALIGNMENT-1));
@@ -794,28 +840,43 @@ module axis_replay #(
//
case (play_state)
PLAY_IDLE : begin
// Always start reading at the start of the record buffer
play_addr <= play_base_addr_sr;
// Save needed command info
cmd <= cmd_cf;
cmd_base_addr <= cmd_base_addr_cf;
cmd_buffer_size <= cmd_buffer_size_cf / BYTES_PER_WORD;
// Save off command info
if (cmd_cf == PLAY_CMD_CONTINUOUS)
// Initialize the play variables
if (cmd_cf == PLAY_CMD_CONTINUOUS) begin
play_words_remaining <= MEM_BURST_LEN;
else
num_words_zero <= 0;
end else begin
play_words_remaining <= cmd_num_words_cf;
cmd_timed <= cmd_timed_cf;
cmd_time <= cmd_time_cf;
cmd <= cmd_cf;
num_words_zero <= (cmd_num_words_cf == 0);
end
play_buffer_avail <= cmd_buffer_size_cf / BYTES_PER_WORD;
play_buffer_end <= {1'b0, cmd_base_addr_cf} + cmd_buffer_size_cf;
play_buffer_zero <= (cmd_buffer_size_cf == 0);
// Save the buffer info so it doesn't update during playback
play_buffer_avail <= play_buffer_size_sr / BYTES_PER_WORD;
// Wait until we receive a command and we have enough data recorded
// to honor it.
// Wait until we receive a command
if (play_cmd_stop) begin
play_cmd_stop_ack <= 1'b1;
end else if (cmd_fifo_valid && play_buffer_avail_nonzero) begin
end else if (cmd_fifo_valid) begin
// Only update the play address when valid so readback is accurate
play_addr <= cmd_base_addr_cf;
// Dequeue the command from the FIFO
cmd_fifo_ready <= 1'b1;
play_state <= PLAY_CHECK_SIZES;
end
end
PLAY_CHECK_SIZES : begin
// Check buffer and num_word sizes and allow propagation of
// play_full_burst_avail.
if (play_buffer_zero | num_words_zero) begin
play_state <= PLAY_IDLE;
end else begin
play_state <= PLAY_WAIT_DATA_READY;
end
end
@@ -902,18 +963,21 @@ module axis_replay #(
// signals that the interface has received a response for the whole
// read transaction.
if (read_ctrl_ready) begin
// Check if this is the last transaction.
if (last_trans) begin
play_addr_1 <= play_addr_0[MEM_ADDR_W-1:0];
play_buffer_avail <= 0;
// 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 / BYTES_PER_WORD;
end else if (play_addr_0 >= play_buffer_end) begin
play_addr_1 <= cmd_base_addr;
play_buffer_avail <= cmd_buffer_size;
end else begin
play_addr_1 <= play_addr_0[MEM_ADDR_W-1:0];
play_buffer_avail <= play_buffer_avail_0;
end
// Update the time for the first word of the next transaction
cmd_time <= cmd_time + (read_count + 1) * (MEM_DATA_W/32);
play_state <= PLAY_DONE_CHECK;
end
end