diff options
-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" }, |