diff options
author | Wade Fife <wade.fife@ettus.com> | 2022-03-04 18:50:21 -0600 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2022-03-09 07:57:21 -0600 |
commit | 082a733c326d6332d581d5b7bf9ee86a503ba502 (patch) | |
tree | 69f8520915be26974407fcbb9b9d052f1c0408bc /fpga | |
parent | 08d03f881e2767f45ab7fc18c8003c9739b0ffb2 (diff) | |
download | uhd-082a733c326d6332d581d5b7bf9ee86a503ba502.tar.gz uhd-082a733c326d6332d581d5b7bf9ee86a503ba502.tar.bz2 uhd-082a733c326d6332d581d5b7bf9ee86a503ba502.zip |
fpga: rfnoc: Make Replay packet length independent of burst size
Before this change, the packet size output by the Replay block during
playback was limited to length of a full memory burst transaction.
This led to relatively small packets during playback (typically
2 KiB) and had other side effects, such as simultaneous playback from
two different memory locations using different packet sizes because of
differences in memory alignment.
With this change, the configured packet size, as set by the register
REG_PLAY_WORDS_PER_PKT, is used for all packets except the last
packet of playback, which can of course be smaller.
Diffstat (limited to 'fpga')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/axis_replay.v | 157 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_replay/rfnoc_block_replay_tb.sv | 107 |
2 files changed, 158 insertions, 106 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 c83de1fc1..e30ee81e2 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 @@ -281,7 +281,7 @@ module axis_replay #( reg_play_cmd_time <= 0; reg_play_words_per_pkt <= REG_PLAY_WORDS_PER_PKT_INIT; reg_item_size <= DEFAULT_ITEM_SIZE; - items_per_word <= 'bX; + items_per_word <= 'bX; rec_restart <= 0; play_cmd_stop <= 0; clear_cmd_fifo <= 0; @@ -576,7 +576,7 @@ module axis_replay #( rec_restart_clear <= 1'b0; // Update wait timer - if (i_tvalid || !rec_fifo_occupied) begin + if ((i_tvalid && i_tready) || !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; @@ -943,78 +943,91 @@ module axis_replay #( // // This section monitors the signals to/from the memory interface and // generates the TLAST and sideband signals. We assert TLAST at the end of - // every read transaction and after every reg_play_words_per_pkt words, so + // every reg_play_words_per_pkt words and at the end of the last packet, so // that no packets are longer than the length indicated by the // REG_PLAY_WORDS_PER_PKT register. // - // The sideband signals consist of the timestamp, has_time flag, and eob - // flag. These are generated by the playback logic for each memory - // transaction. + // The sideband signals consist of the timestamp, has-time flag, and EOB (end + // of burst) flag. Timestamp and has_time are set for the first packet of + // each playback. EOB applies to each packet but is only set to 1 for the + // last packet of a playback. // - // The timing of this block relies on the fact that read_ctrl_ready is not - // reasserted by the memory interface until after TLAST gets asserted. + // The timing of this section relies on the fact axi_dma_master doesn't allow + // overlapping read transactions. This means that the next read_ctrl_ready + // won't be asserted until after previous memory transaction finishes being + // read out. // //--------------------------------------------------------------------------- - reg [MEM_COUNT_W-1:0] read_counter; - reg [ WPP_W-1:0] length_counter; - reg [ TIME_W-1:0] time_counter; - reg play_fifo_i_tlast; - reg has_time; - reg eob; + reg [MEM_COUNT_W-1:0] read_counter; // Track outstanding words to read + reg [ WPP_W-1:0] length_counter; // Track packet length + reg [ TIME_W-1:0] timestamp; // Timestamp for the current burst + reg has_time; // Is current burst timed? + reg eob; // End of burst + reg play_fifo_i_tlast; // End of packet always @(posedge clk) begin - if (rst) begin - play_fifo_i_tlast <= 1'b0; - // Don't care: - read_counter <= {MEM_COUNT_W{1'bX}}; - length_counter <= {MEM_COUNT_W+1{1'bX}}; - time_counter <= {TIME_W{1'bX}}; - has_time <= 1'bX; - eob <= 1'bX; - 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 <= reg_play_words_per_pkt; - time_counter <= cmd_time; - has_time <= cmd_timed; - eob <= last_trans && (read_count < reg_play_words_per_pkt); + // synthesis translate_off + // + // Check our assumption about non-overlapping read transactions. + if (read_ctrl_ready && play_fifo_i_tvalid) begin + $fatal(1, "New read transaction started before the previous one completed!"); + end + // synthesis translate_on - // 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 + if (read_ctrl_valid && read_ctrl_ready) begin + read_counter <= read_count; - // Track the number of words read out by memory interface - end else if (read_data_valid && read_data_ready) begin - read_counter <= read_counter - 1; - length_counter <= length_counter - 1; - time_counter <= time_counter + items_per_word; - - // 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 memory - // burst, for which we will need to keep tlast asserted. - if (read_counter != 1) begin - play_fifo_i_tlast <= 1'b0; - end + // If read_count is 0, then the next word is also the last word + if (read_count == 0) begin + play_fifo_i_tlast <= 1'b1; + eob <= last_trans; + end + end - // Restart length counter - length_counter <= reg_play_words_per_pkt; + if (play_fifo_i_tvalid && play_fifo_i_tready) begin + read_counter <= read_counter - 1; + length_counter <= length_counter - 1; - // Check if next packet is the end of the burst (EOB) - eob <= last_trans && (read_counter <= reg_play_words_per_pkt); + // Check if the current word is the last of the packet + if (play_fifo_i_tlast) begin + length_counter <= reg_play_words_per_pkt; - // 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; + // Clear tlast, unless the first word of the next packet is also the + // last word of the next packet. + if (!(last_trans && read_counter == 1)) begin + play_fifo_i_tlast <= 1'b0; end + + // The timestamp only applies to the first packet, so disable for + // subsequent packets. + has_time <= 1'b0; end + // Check if the next word will be the last of the packet. + // + // First, check if the next word is the last word of playback, in which + // case it's both the last word of the packet and the end of the burst. + if (last_trans && read_counter == 1) begin + play_fifo_i_tlast <= 1'b1; + eob <= 1; + + // Next, check if this is the last word of the packet according to packet + // length. But note that the next word won't be the last if we're already + // outputting the last word of a burst on the current cycle. + end else if (length_counter == 2 && !(eob && play_fifo_i_tlast)) begin + play_fifo_i_tlast <= 1'b1; + end + end + + if (play_state == PLAY_IDLE) begin + // Reset signals for the next playback + length_counter <= reg_play_words_per_pkt; + timestamp <= cmd_time_cf; + has_time <= cmd_timed_cf; + eob <= 0; + play_fifo_i_tlast <= 1'b0; end end @@ -1053,20 +1066,6 @@ module axis_replay #( .occupied () ); - reg play_fifo_i_sop = 1'b1; - - // Make play_fifo_i_sop true whenever the next play_fifo_i word is the start - // of a packet. - always @(posedge clk) begin - if (rst) begin - play_fifo_i_sop <= 1'b1; - end else begin - if (play_fifo_i_tvalid & play_fifo_i_tready) begin - play_fifo_i_sop <= play_fifo_i_tlast; - end - end - end - //--------------------------------------------------------------------------- // Header Info FIFO @@ -1080,6 +1079,7 @@ module axis_replay #( wire [(TIME_W+2)-1:0] hdr_fifo_i_tdata; wire hdr_fifo_i_tvalid; wire [(TIME_W+2)-1:0] hdr_fifo_o_tdata; + wire hdr_fifo_o_tvalid; wire hdr_fifo_o_tready; wire [15:0] hdr_fifo_space; @@ -1097,20 +1097,31 @@ module axis_replay #( .i_tready (), // .o_tdata (hdr_fifo_o_tdata), - .o_tvalid (), + .o_tvalid (hdr_fifo_o_tvalid), .o_tready (hdr_fifo_o_tready), // .space (hdr_fifo_space), .occupied () ); - assign hdr_fifo_i_tdata = {has_time, eob, time_counter}; + // synthesis translate_off + // + // The FIFO code above assumes the header info will always be available when + // the last word of the payload FIFO is read out. Check that assumption here. + always @(posedge clk) begin + if (hdr_fifo_o_tready && !hdr_fifo_o_tvalid) begin + $fatal(1, "Header FIFO read without valid data!"); + end + end + // synthesis translate_on + + assign hdr_fifo_i_tdata = {has_time, eob, timestamp }; // Pop the timestamp whenever we finish reading out a data packet assign hdr_fifo_o_tready = o_tvalid & o_tready & o_tlast; // Write the timestamp at the start of each packet - assign hdr_fifo_i_tvalid = play_fifo_i_tvalid & play_fifo_i_tready & play_fifo_i_sop; + assign hdr_fifo_i_tvalid = play_fifo_i_tvalid & play_fifo_i_tready & play_fifo_i_tlast; assign { o_thas_time, o_teob, o_ttimestamp } = hdr_fifo_o_tdata; 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 9a0d9c810..5674a3cf6 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 @@ -46,6 +46,8 @@ module rfnoc_block_replay_tb#( `include "rfnoc_block_replay_regs.vh" + `define MIN(X,Y) ((X)<(Y)?(X):(Y)) + //--------------------------------------------------------------------------- // Testbench Configuration @@ -535,6 +537,8 @@ module rfnoc_block_replay_tb#( "Buffer size extends beyond available memory"); `ASSERT_FATAL(spp * ITEM_SIZE % MEM_WORD_SIZE == 0, "Requested SPP must be a multiple of the memory word size"); + `ASSERT_FATAL(spp <= 2**MTU, + "Requested SPP exceeds MTU"); // Update record buffer settings write_reg_64(port, REG_REC_BASE_ADDR_LO, base_addr); @@ -583,6 +587,7 @@ module rfnoc_block_replay_tb#( // exp_items : Queue of the items you expect to receive // eob : Indicates if we expect EOB to be set for the last item (set // to 1'bX to skip this check) + // spp : Samples per packet to expect. Set to 0 to skip this check. // timestamp : The timestamp we expect to receive for the first item (set to // 'X to skip timestamp checking) // @@ -591,6 +596,7 @@ module rfnoc_block_replay_tb#( output string error_msg, input item_t exp_items[$], input logic eob = 1'b1, + input int spp = SPP, input logic [63:0] timestamp = 64'bX ); item_t recv_items[$]; @@ -614,9 +620,9 @@ module rfnoc_block_replay_tb#( // Check the packet length if (item_count + recv_items.size() > exp_items.size()) begin $sformat(error_msg, - "On packet %0d, size exceeds expected by %0d items", - packet_count, - (item_count + recv_items.size()) - exp_items.size()); + "On packet %0d, size is %0d items, which exceeds expected by %0d items.", + packet_count, recv_items.size(), + (item_count + recv_items.size()) - exp_items.size()); return; end @@ -645,8 +651,27 @@ module rfnoc_block_replay_tb#( end end - // Check the timestamp - if (timestamp !== 64'bX) begin + // Check the payload length against expected SPP + if (spp > 0) begin + // If this is NOT the last packet, then its length should match the SPP. + // If it is the last packet, then it can be shorter than SPP. + if (!pkt_info.eob) begin + if (recv_items.size() != spp) begin + $sformat(error_msg, + "On packet %0d, expected SPP of %0d but packet has %0d items", + packet_count, spp, recv_items.size()); + end + end else begin + if (recv_items.size() > spp) begin + $sformat(error_msg, + "On packet %0d (EOB), expected SPP of %0d or less but packet has %0d items", + packet_count, spp, recv_items.size()); + end + end + end + + // Check the timestamp (only first packet should have a timestamp) + if (timestamp !== 64'bX && packet_count == 0) begin if (!pkt_info.timestamp) begin $sformat(error_msg, "On packet %0d, timestamp is missing", @@ -925,37 +950,40 @@ module rfnoc_block_replay_tb#( // For each test below, we record two memory bursts and playback two memory // bursts. Each time we change the playback packet size to test boundary // conditions. + // + // Note that we can't let the SPP exceed the MTU, so if the burst size is + // too large, we won't be testing the full size. // Test packet size equals burst size - spp = MEM_BURST_LEN * MEM_WORD_SIZE / ITEM_SIZE; + spp = `MIN(MEM_BURST_LEN * MEM_WORD_SIZE / ITEM_SIZE, 2**MTU); start_replay(port, send_items, buffer_size, num_items, spp); - verify_rx_data(port, error_string, send_items, 1); + verify_rx_data(port, error_string, send_items, 1, spp); `ASSERT_ERROR(error_string == "", error_string); // Test packet is one less than burst size - spp = (MEM_BURST_LEN-1) * MEM_WORD_SIZE / ITEM_SIZE; + spp = `MIN((MEM_BURST_LEN-1) * MEM_WORD_SIZE / ITEM_SIZE, 2**MTU-(MEM_WORD_SIZE / ITEM_SIZE)); start_replay(port, send_items, buffer_size, num_items, spp); - verify_rx_data(port, error_string, send_items, 1); + verify_rx_data(port, error_string, send_items, 1, spp); `ASSERT_ERROR(error_string == "", error_string); // Test packet is one more than burst size - spp = (MEM_BURST_LEN+1) * MEM_WORD_SIZE / ITEM_SIZE; + spp = `MIN((MEM_BURST_LEN+1) * MEM_WORD_SIZE / ITEM_SIZE, 2**MTU); start_replay(port, send_items, buffer_size, num_items, spp); - verify_rx_data(port, error_string, send_items, 1); + verify_rx_data(port, error_string, send_items, 1, spp); `ASSERT_ERROR(error_string == "", error_string); // For each test below, we record two memory bursts and playback one memory // burst plus or minus one word, keeping the packet size the same. - spp = MEM_BURST_LEN * MEM_WORD_SIZE / ITEM_SIZE; + spp = `MIN(MEM_BURST_LEN * MEM_WORD_SIZE / ITEM_SIZE, 2**MTU); // Playback one less than burst/packet size start_replay(port, send_items, buffer_size, spp-MEM_WORD_SIZE/ITEM_SIZE, spp); - verify_rx_data(port, error_string, send_items[0:spp-1-MEM_WORD_SIZE/ITEM_SIZE], 1); + verify_rx_data(port, error_string, send_items[0:spp-1-MEM_WORD_SIZE/ITEM_SIZE], 1, spp); `ASSERT_ERROR(error_string == "", error_string); // Playback one more than burst/packet size start_replay(port, send_items, buffer_size, spp+MEM_WORD_SIZE/ITEM_SIZE, spp); - verify_rx_data(port, error_string, send_items[0:spp-1+MEM_WORD_SIZE/ITEM_SIZE], 1); + verify_rx_data(port, error_string, send_items[0:spp-1+MEM_WORD_SIZE/ITEM_SIZE], 1, spp); `ASSERT_ERROR(error_string == "", error_string); // Make sure there are no more packets @@ -1016,8 +1044,8 @@ module rfnoc_block_replay_tb#( // Set number of words to test buffer_size = (3 * MEM_BURST_LEN) / 2 * MEM_WORD_SIZE; // 1.5 memory bursts in size (in bytes) - num_items_rec = buffer_size / ITEM_SIZE; // 1.5 memory bursts in size (in CHDR words) - num_items_play = 2 * MEM_BURST_LEN * MEM_WORD_SIZE / // 2 memory bursts in size (in CHDR words) + num_items_rec = buffer_size / ITEM_SIZE; // 1.5 memory bursts in size (in samples) + num_items_play = 2 * MEM_BURST_LEN * MEM_WORD_SIZE / // 2 memory bursts in size (in samples) ITEM_SIZE; // Start playback of data @@ -1045,19 +1073,25 @@ module rfnoc_block_replay_tb#( item_t send_items[$]; string error_string; logic [31:0] cmd; - int num_items, mem_words; + int num_items, mem_words, spp; test.start_test("Test continuous mode", TEST_TIMEOUT); mem_words = 70; num_items = mem_words * MEM_WORD_SIZE / ITEM_SIZE; + // Make sure the spp evenly divides our test size, or else we'll end a + // check mid packet, which verify_rx_data doesn't support. + spp = num_items / 2; + `ASSERT_ERROR(num_items % spp == 0, + "num_items should be a multiple of spp for this test"); + // Update record buffer settings write_reg_64(port, REG_REC_BASE_ADDR_LO, 0); write_reg_64(port, REG_REC_BUFFER_SIZE_LO, num_items*ITEM_SIZE); 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 (port, REG_PLAY_WORDS_PER_PKT, SPP * ITEM_SIZE/MEM_WORD_SIZE); + write_reg (port, REG_PLAY_WORDS_PER_PKT, spp * ITEM_SIZE/MEM_WORD_SIZE); // Restart the record buffer write_reg(port, REG_REC_RESTART, 0); @@ -1071,16 +1105,15 @@ module rfnoc_block_replay_tb#( // Make sure the REG_PLAY_CMD_NUM_WORDS value is ignored by setting it to // something smaller than what we receive. - write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, mem_words); + write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, 0); // Send command for continuous playback cmd = PLAY_CMD_CONTINUOUS; - write_reg_64(port, REG_PLAY_CMD_NUM_WORDS_LO, mem_words); write_reg(port, REG_PLAY_CMD, cmd); // Check the output, looking for the full set of data, multiple times repeat (5) begin - verify_rx_data(port, error_string, send_items, 0); + verify_rx_data(port, error_string, send_items, 0, spp); `ASSERT_ERROR(error_string == "", error_string); end @@ -1153,6 +1186,7 @@ module rfnoc_block_replay_tb#( // Check the result verify_rx_data(port, error_string, send_items, 1); + `ASSERT_ERROR(error_string == "", error_string); // Check the fullness read_reg_64(port, REG_REC_FULLNESS_LO, val64); @@ -1264,15 +1298,21 @@ module rfnoc_block_replay_tb#( item_t send_items[$]; item_t exp_items[$]; string error_string; - int num_items, mem_words; + int num_items, spp; int num_items_buf; logic [63:0] val64; test.start_test("Test overfilled record buffer", TEST_TIMEOUT); // Choose the sizes we want to use for this test - num_items = 97*MEM_WORD_SIZE/ITEM_SIZE; // Number of items to record - num_items_buf = 43*MEM_WORD_SIZE/ITEM_SIZE; // Size of buffer to use in items + num_items = 98*MEM_WORD_SIZE/ITEM_SIZE; // Number of items to record + num_items_buf = 44*MEM_WORD_SIZE/ITEM_SIZE; // Size of buffer to use in items + + // Make sure the spp evenly divides our test size, or else we'll end a + // check mid packet, which verify_rx_data doesn't support. + spp = num_items_buf / 4; + `ASSERT_ERROR(num_items_buf % spp == 0, + "num_items_buf should be a multiple of spp for this test"); // Restart the record buffer write_reg(port, REG_REC_RESTART, 0); @@ -1281,17 +1321,17 @@ module rfnoc_block_replay_tb#( send_items = gen_test_data(num_items); // Start playback of the larger size - start_replay(port, send_items, num_items_buf*ITEM_SIZE, num_items); + start_replay(port, send_items, num_items_buf*ITEM_SIZE, num_items, spp); // We should get two frames of num_items_buf, then one smaller frame to // bring us up to num_items total. exp_items = send_items[0 : num_items_buf-1]; for (int i = 0; i < 2; i ++) begin - verify_rx_data(port, error_string, exp_items, 0); + verify_rx_data(port, error_string, exp_items, 0, spp); `ASSERT_ERROR(error_string == "", error_string); end exp_items = exp_items[0 : (num_items % num_items_buf)-1]; - verify_rx_data(port, error_string, exp_items, 1); + verify_rx_data(port, error_string, exp_items, 1, spp); `ASSERT_ERROR(error_string == "", error_string); // Make sure REG_REC_FULLNESS didn't keep increasing @@ -1447,12 +1487,11 @@ module rfnoc_block_replay_tb#( send_items = gen_test_data(num_items); start_replay(port, send_items, buffer_size, num_items, pkt_size_items); - // We should get 8 small packets instead of 2 large ones, with EOB set on - // the last packet. + // We should get 8 small packets with EOB set on the last packet. for (int k = 0; k < 8; k ++) begin verify_rx_data(port, error_string, send_items[pkt_size_items*k : pkt_size_items*(k+1)-1], - (k == 7 ? 1 : 0)); + (k == 7 ? 1 : 0), pkt_size_items); `ASSERT_ERROR(error_string == "", error_string); end @@ -1473,7 +1512,7 @@ module rfnoc_block_replay_tb#( // with EOB set on the last packet. for (int i=0; i < num_items; i += pkt_size_items) begin verify_rx_data(port, error_string, send_items[i:i+pkt_size_items-1], - i == num_items-pkt_size_items ? 1 : 0); + i == num_items-pkt_size_items ? 1 : 0, pkt_size_items); `ASSERT_FATAL(error_string == "", error_string); end @@ -1507,7 +1546,7 @@ module rfnoc_block_replay_tb#( send_items = gen_test_data(num_items); start_replay(port, send_items, buffer_size, num_items, spp, 0, 0, timestamp); - verify_rx_data(port, error_string, send_items, 1, timestamp); + verify_rx_data(port, error_string, send_items, 1, spp, timestamp); `ASSERT_ERROR(error_string == "", error_string); test.end_test(); @@ -1581,6 +1620,8 @@ module rfnoc_block_replay_tb#( blk_ctrl.send_items(port+1, port1_data); // Play back on each port + write_reg(port+0, REG_PLAY_WORDS_PER_PKT, SPP*ITEM_SIZE/MEM_WORD_SIZE); + write_reg(port+1, REG_PLAY_WORDS_PER_PKT, SPP*ITEM_SIZE/MEM_WORD_SIZE); write_reg_64(port+0, REG_PLAY_CMD_NUM_WORDS_LO, mem_words); write_reg_64(port+1, REG_PLAY_CMD_NUM_WORDS_LO, mem_words); write_reg(port+0, REG_PLAY_CMD, PLAY_CMD_FINITE); @@ -1704,7 +1745,7 @@ module rfnoc_block_replay_tb#( // Generate a string for the name of this instance of the testbench tb_name = $sformatf( { "rfnoc_block_replay_tb\n", - "CHDR_W = %03d, ITEM_W = %02d, NUM_PORTS = %d,\n", + "CHDR_W = %03d, ITEM_W = %02d, NUM_PORTS = %0d,\n", "MEM_DATA_W = %03d, MEM_ADDR_W = %02d,\n", "TEST_REGS = %03d, TEST_FULL = %02d,\n", "STALL_PROB = %03d" }, |