diff options
Diffstat (limited to 'fpga/usrp3')
| -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" },  | 
