aboutsummaryrefslogtreecommitdiffstats
path: root/fpga
diff options
context:
space:
mode:
authormichael-west <michael.west@ettus.com>2022-02-13 17:56:02 -0800
committerAaron Rossetto <aaron.rossetto@ni.com>2022-04-01 13:33:08 -0700
commit1545d3ff05f14b8dd175736b326fe6cae7dc830d (patch)
treeed324baf76d85f443f822b30f42152442a13154b /fpga
parent7a303df8e4a9c167691ffd242424be76559a712f (diff)
downloaduhd-1545d3ff05f14b8dd175736b326fe6cae7dc830d.tar.gz
uhd-1545d3ff05f14b8dd175736b326fe6cae7dc830d.tar.bz2
uhd-1545d3ff05f14b8dd175736b326fe6cae7dc830d.zip
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>
Diffstat (limited to 'fpga')
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v164
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_regs.vh20
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv132
3 files changed, 266 insertions, 50 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v
index e30ee81e2..194aed3e9 100644
--- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v
@@ -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;
-
- // Save the buffer info so it doesn't update during playback
- play_buffer_avail <= play_buffer_size_sr / BYTES_PER_WORD;
+ 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);
- // 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
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_regs.vh
index 4849da47f..a04c9f231 100644
--- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_regs.vh
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_regs.vh
@@ -196,6 +196,26 @@ localparam REG_PLAY_ITEM_SIZE = 'h50;
localparam REG_ITEM_SIZE_POS = 0;
localparam REG_ITEM_SIZE_LEN = 8;
+// REG_REC_POS (R)
+//
+// Returns the byte address of the record pointer.
+//
+localparam REG_REC_POS_LO = 'h54;
+localparam REG_REC_POS_HI = 'h58;
+
+// REG_PLAY_POS (R)
+//
+// Returns the byte address of the play pointer.
+//
+localparam REG_PLAY_POS_LO = 'h5C;
+localparam REG_PLAY_POS_HI = 'h60;
+
+// REG_PLAY_CMD_FIFO_SPACE (R)
+//
+// Returns remaining space in the command FIFO
+//
+localparam REG_PLAY_CMD_FIFO_SPACE = 'h64;
+
//-----------------------------------------------------------------------------
// Playback Commands
//-----------------------------------------------------------------------------
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv
index 5674a3cf6..d4cb071cf 100644
--- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv
@@ -476,6 +476,30 @@ module rfnoc_block_replay_tb#(
endtask : wait_record_fullness
+ // Validate record position
+ task automatic validate_record_position(
+ input int port,
+ input longint position
+ );
+ logic [63:0] value;
+
+ read_reg_64(port, REG_REC_POS_LO, value);
+ `ASSERT_ERROR(value == position, $sformatf("Record position expected at 0x%h, but at 0x%h", position, value));
+ endtask : validate_record_position
+
+
+ // Validate record position
+ task automatic validate_play_position(
+ input int port,
+ input longint position
+ );
+ logic [63:0] value;
+
+ read_reg_64(port, REG_PLAY_POS_LO, value);
+ `ASSERT_ERROR(value == position, $sformatf("Play position expected at 0x%h, but at 0x%h", position, value));
+ endtask : validate_play_position
+
+
// Make sure nothing is received until the timeout has elapsed
task automatic check_rx_idle(
input int port,
@@ -863,6 +887,10 @@ module rfnoc_block_replay_tb#(
(32'(MEM_ADDR_W) << REG_ADDR_SIZE_POS)
);
test_read_reg(port, REG_REC_FULLNESS_LO, 64);
+ test_read_reg(port, REG_PLAY_ITEM_SIZE, 32, 4);
+ test_read_reg(port, REG_REC_POS_LO, 64);
+ test_read_reg(port, REG_PLAY_POS_LO, 64);
+ test_read_reg(port, REG_PLAY_CMD_FIFO_SPACE, 32, 32);
// The following registers are write only and aren't tested here.
// REG_REC_RESTART - Tested during every record operation
@@ -1640,6 +1668,109 @@ module rfnoc_block_replay_tb#(
//---------------------------------------------------------------------------
+ // Test Record and Play position
+ //---------------------------------------------------------------------------
+ //
+ // Test record and play positions advance properly. Checks position after
+ // recording and playing each packet and checks proper position during wraps.
+ //
+ //---------------------------------------------------------------------------
+
+ task test_position(int port = 0);
+ item_t send_items[$];
+ item_t recv_items[$];
+ int num_items, num_packets;
+ longint unsigned mem_size;
+ logic [63:0] val64;
+
+ test.start_test("Test record and play positions", 2ms);
+
+ mem_size = 2**MEM_ADDR_W; // Memory size in bytes
+ num_items = 2**$clog2(SPP); // Pick a power of 2 near SPP
+ num_packets = 5;
+
+ // Set up the record buffer
+ write_reg_64(port, REG_REC_BASE_ADDR_LO, 0);
+ write_reg_64(port, REG_REC_BUFFER_SIZE_LO, num_packets*num_items*ITEM_SIZE);
+ write_reg (port, REG_REC_RESTART, 0);
+
+ // Fill the buffer and validate the record position after each packet.
+ for (int i = 0; i < num_packets; i++) begin
+ // Send a different random sequence for each packet
+ send_items = gen_test_data(num_items, i*num_items);
+ blk_ctrl.send_items(port, send_items);
+ wait_record_fullness(port, (i+1)*num_items*ITEM_SIZE);
+ validate_record_position(port, ((i+1)*num_items*ITEM_SIZE));
+ end
+
+ // Send one additional packet to ensure the position does not advance
+ send_items = gen_test_data(num_items, num_packets*num_items);
+ blk_ctrl.send_items(port, send_items);
+
+ // Give extra time for the last packet
+ #(CHDR_CLK_PER * num_items * 20);
+
+ // Make sure the position has not advanced and the fullness has not changed
+ validate_record_position(port, num_packets*num_items*ITEM_SIZE);
+ read_reg_64(port, REG_REC_FULLNESS_LO, val64);
+ `ASSERT_ERROR(val64 == num_packets*num_items*ITEM_SIZE, "Memory fullness is not correct");
+
+ // Play back the data one packet at a time and validate play position
+ write_reg(port, REG_PLAY_WORDS_PER_PKT, num_items*ITEM_SIZE/MEM_WORD_SIZE);
+ write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, num_items*ITEM_SIZE/MEM_WORD_SIZE);
+ write_reg_64(port, REG_PLAY_BUFFER_SIZE_LO, num_items*ITEM_SIZE);
+ for (int i = 0; i < num_packets; i++) begin
+ write_reg_64(port, REG_PLAY_BASE_ADDR_LO, i*num_items*ITEM_SIZE);
+ write_reg(port, REG_PLAY_CMD, PLAY_CMD_FINITE);
+ send_items = gen_test_data(num_items, i*num_items);
+ blk_ctrl.recv_items(port, recv_items);
+ `ASSERT_ERROR(
+ ChdrData#(CHDR_W, ITEM_W)::item_equal(send_items, recv_items),
+ "Playback data did not match"
+ );
+ validate_play_position(port, ((i+1)*num_items*ITEM_SIZE));
+ end
+
+ // Restart recording to get the extra packet we sent at the beginning
+ // to validate the record position wraps.
+ write_reg(port, REG_REC_RESTART, 0);
+ wait_record_fullness(port, num_items*ITEM_SIZE);
+ validate_record_position(port, num_items*ITEM_SIZE);
+
+ // Playback the new data, which should continue the values from the last
+ // record operation.
+ write_reg_64(port, REG_PLAY_BASE_ADDR_LO, 0);
+ write_reg_64(port, REG_PLAY_BUFFER_SIZE_LO, num_items*ITEM_SIZE);
+ write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, num_items*ITEM_SIZE/MEM_WORD_SIZE);
+ write_reg(port, REG_PLAY_CMD, PLAY_CMD_FINITE);
+ send_items = gen_test_data(num_items, num_packets*num_items);
+ blk_ctrl.recv_items(port, recv_items);
+ `ASSERT_ERROR(
+ ChdrData#(CHDR_W, ITEM_W)::item_equal(send_items, recv_items),
+ "Playback data did not match"
+ );
+ validate_play_position(port, num_items*ITEM_SIZE);
+
+ // Test the play wrap. Play num_packets plus one and validate position.
+ // This is done at the end of the memory space to test the wrap within the
+ // buffer space as well as the wrap at the end of the memory space.
+ write_reg_64(port, REG_PLAY_BASE_ADDR_LO, mem_size-(num_packets*num_items*ITEM_SIZE));
+ write_reg_64(port, REG_PLAY_BUFFER_SIZE_LO, num_packets*num_items*ITEM_SIZE);
+ write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, (num_packets+1)*num_items*ITEM_SIZE/MEM_WORD_SIZE);
+ write_reg(port, REG_PLAY_CMD, PLAY_CMD_FINITE);
+ for (int i = 0; i < num_packets+1; i++) begin
+ blk_ctrl.recv_items(port, recv_items);
+ end
+ validate_play_position(port, mem_size-((num_packets-1)*num_items*ITEM_SIZE));
+
+ // Make sure there are no more packets
+ check_rx_idle(port);
+
+ test.end_test();
+ endtask : test_position
+
+
+ //---------------------------------------------------------------------------
// Test Filling the memory
//---------------------------------------------------------------------------
//
@@ -1795,6 +1926,7 @@ module rfnoc_block_replay_tb#(
test_4k_boundary();
test_small_packet();
test_timed_playback();
+ test_position();
test_full_memory();
//--------------------------------