diff options
8 files changed, 1488 insertions, 0 deletions
| diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile new file mode 100644 index 000000000..17de7c3ae --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile @@ -0,0 +1,42 @@ +# +# Copyright 2020 Ettus Research, A National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +BASE_DIR = $(abspath ../../../../top) +# Include viv_sim_preample after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Include makefiles and sources for the DUT and its  +# dependencies. +include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs +include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs +include Makefile.srcs + +DESIGN_SRCS += $(abspath \ +$(RFNOC_CORE_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_OOT_SRCS)  \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +SIM_TOP = rfnoc_block_keep_one_in_n_tb +SIM_SRCS = \ +$(abspath rfnoc_block_keep_one_in_n_tb.sv) + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile.srcs new file mode 100644 index 000000000..11eb5cd6d --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/Makefile.srcs @@ -0,0 +1,24 @@ +# +# Copyright 2020 Ettus Research, A National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +################################################## +# RFNoC Block Sources +################################################## +# Here, list all the files that are necessary to synthesize this block. Don't +# include testbenches! +# Make sure that the source files are nicely detectable by a regex. Best to put +# one on each line. +# The first argument to addprefix is the current path to this Makefile, so the +# path list is always absolute, regardless of from where we're including or +# calling this file. RFNOC_OOT_SRCS needs to be a simply expanded variable +# (not a recursively expanded variable), and we take care of that in the build +# infrastructure. +RFNOC_OOT_SRCS += $(addprefix $(dir $(abspath $(lastword $(MAKEFILE_LIST)))), \ +rfnoc_block_keep_one_in_n.v \ +rfnoc_keep_one_in_n.v \ +rfnoc_keep_one_in_n_regs.vh \ +noc_shell_keep_one_in_n.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/noc_shell_keep_one_in_n.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/noc_shell_keep_one_in_n.v new file mode 100644 index 000000000..78572597e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/noc_shell_keep_one_in_n.v @@ -0,0 +1,304 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_keep_one_in_n +// +// Description: +// +//   This is a tool-generated NoC-shell for the keep_one_in_n block. +//   See the RFNoC specification for more information about NoC shells. +// +// Parameters: +// +//   THIS_PORTID : Control crossbar port to which this block is connected +//   CHDR_W      : AXIS-CHDR data bus width +//   MTU         : Maximum transmission unit (i.e., maximum packet size in +// + +`default_nettype none + + +module noc_shell_keep_one_in_n #( +  parameter [9:0] THIS_PORTID     = 10'd0, +  parameter       CHDR_W          = 64, +  parameter       NUM_PORTS       = 1, +  parameter [5:0] MTU             = 10 +) ( +  //--------------------- +  // Framework Interface +  //--------------------- + +  // RFNoC Framework Clocks +  input  wire rfnoc_chdr_clk, +  input  wire rfnoc_ctrl_clk, +  input  wire ce_clk, + +  // NoC Shell Generated Resets +  output wire rfnoc_chdr_rst, +  output wire rfnoc_ctrl_rst, +  output wire ce_rst, + +  // RFNoC Backend Interface +  input  wire [511:0]          rfnoc_core_config, +  output wire [511:0]          rfnoc_core_status, + +  // AXIS-CHDR Input Ports (from framework) +  input  wire [(0+NUM_PORTS)*CHDR_W-1:0] s_rfnoc_chdr_tdata, +  input  wire [(0+NUM_PORTS)-1:0]        s_rfnoc_chdr_tlast, +  input  wire [(0+NUM_PORTS)-1:0]        s_rfnoc_chdr_tvalid, +  output wire [(0+NUM_PORTS)-1:0]        s_rfnoc_chdr_tready, +  // AXIS-CHDR Output Ports (to framework) +  output wire [(0+NUM_PORTS)*CHDR_W-1:0] m_rfnoc_chdr_tdata, +  output wire [(0+NUM_PORTS)-1:0]        m_rfnoc_chdr_tlast, +  output wire [(0+NUM_PORTS)-1:0]        m_rfnoc_chdr_tvalid, +  input  wire [(0+NUM_PORTS)-1:0]        m_rfnoc_chdr_tready, + +  // AXIS-Ctrl Control Input Port (from framework) +  input  wire [31:0]           s_rfnoc_ctrl_tdata, +  input  wire                  s_rfnoc_ctrl_tlast, +  input  wire                  s_rfnoc_ctrl_tvalid, +  output wire                  s_rfnoc_ctrl_tready, +  // AXIS-Ctrl Control Output Port (to framework) +  output wire [31:0]           m_rfnoc_ctrl_tdata, +  output wire                  m_rfnoc_ctrl_tlast, +  output wire                  m_rfnoc_ctrl_tvalid, +  input  wire                  m_rfnoc_ctrl_tready, + +  //--------------------- +  // Client Interface +  //--------------------- + +  // CtrlPort Clock and Reset +  output wire               ctrlport_clk, +  output wire               ctrlport_rst, +  // CtrlPort Master +  output wire               m_ctrlport_req_wr, +  output wire               m_ctrlport_req_rd, +  output wire [19:0]        m_ctrlport_req_addr, +  output wire [31:0]        m_ctrlport_req_data, +  input  wire               m_ctrlport_resp_ack, +  input  wire [31:0]        m_ctrlport_resp_data, + +  // AXI-Stream Data Clock and Reset +  output wire               axis_data_clk, +  output wire               axis_data_rst, +  // Data Stream to User Logic: in +  output wire [NUM_PORTS*32*1-1:0]   m_in_axis_tdata, +  output wire [NUM_PORTS*1-1:0]      m_in_axis_tkeep, +  output wire [NUM_PORTS-1:0]        m_in_axis_tlast, +  output wire [NUM_PORTS-1:0]        m_in_axis_tvalid, +  input  wire [NUM_PORTS-1:0]        m_in_axis_tready, +  output wire [NUM_PORTS*64-1:0]     m_in_axis_ttimestamp, +  output wire [NUM_PORTS-1:0]        m_in_axis_thas_time, +  output wire [NUM_PORTS*16-1:0]     m_in_axis_tlength, +  output wire [NUM_PORTS-1:0]        m_in_axis_teov, +  output wire [NUM_PORTS-1:0]        m_in_axis_teob, +  // Data Stream to User Logic: out +  input  wire [NUM_PORTS*32*1-1:0]   s_out_axis_tdata, +  input  wire [NUM_PORTS*1-1:0]      s_out_axis_tkeep, +  input  wire [NUM_PORTS-1:0]        s_out_axis_tlast, +  input  wire [NUM_PORTS-1:0]        s_out_axis_tvalid, +  output wire [NUM_PORTS-1:0]        s_out_axis_tready, +  input  wire [NUM_PORTS*64-1:0]     s_out_axis_ttimestamp, +  input  wire [NUM_PORTS-1:0]        s_out_axis_thas_time, +  input  wire [NUM_PORTS*16-1:0]     s_out_axis_tlength, +  input  wire [NUM_PORTS-1:0]        s_out_axis_teov, +  input  wire [NUM_PORTS-1:0]        s_out_axis_teob +); + +  //--------------------------------------------------------------------------- +  //  Backend Interface +  //--------------------------------------------------------------------------- + +  wire         data_i_flush_en; +  wire [31:0]  data_i_flush_timeout; +  wire [63:0]  data_i_flush_active; +  wire [63:0]  data_i_flush_done; +  wire         data_o_flush_en; +  wire [31:0]  data_o_flush_timeout; +  wire [63:0]  data_o_flush_active; +  wire [63:0]  data_o_flush_done; + +  backend_iface #( +    .NOC_ID        (32'h02460000), +    .NUM_DATA_I    (0+NUM_PORTS), +    .NUM_DATA_O    (0+NUM_PORTS), +    .CTRL_FIFOSIZE ($clog2(32)), +    .MTU           (MTU) +  ) backend_iface_i ( +    .rfnoc_chdr_clk       (rfnoc_chdr_clk), +    .rfnoc_chdr_rst       (rfnoc_chdr_rst), +    .rfnoc_ctrl_clk       (rfnoc_ctrl_clk), +    .rfnoc_ctrl_rst       (rfnoc_ctrl_rst), +    .rfnoc_core_config    (rfnoc_core_config), +    .rfnoc_core_status    (rfnoc_core_status), +    .data_i_flush_en      (data_i_flush_en), +    .data_i_flush_timeout (data_i_flush_timeout), +    .data_i_flush_active  (data_i_flush_active), +    .data_i_flush_done    (data_i_flush_done), +    .data_o_flush_en      (data_o_flush_en), +    .data_o_flush_timeout (data_o_flush_timeout), +    .data_o_flush_active  (data_o_flush_active), +    .data_o_flush_done    (data_o_flush_done) +  ); + +  //--------------------------------------------------------------------------- +  //  Reset Generation +  //--------------------------------------------------------------------------- + +  wire ce_rst_pulse; + +  pulse_synchronizer #(.MODE ("POSEDGE")) pulse_synchronizer_ce ( +    .clk_a(rfnoc_chdr_clk), .rst_a(1'b0), .pulse_a (rfnoc_chdr_rst), .busy_a (), +    .clk_b(ce_clk), .pulse_b (ce_rst_pulse) +  ); + +  pulse_stretch_min #(.LENGTH(32)) pulse_stretch_min_ce ( +    .clk(ce_clk), .rst(1'b0), +    .pulse_in(ce_rst_pulse), .pulse_out(ce_rst) +  ); + +  //--------------------------------------------------------------------------- +  //  Control Path +  //--------------------------------------------------------------------------- + +  assign ctrlport_clk = ce_clk; +  assign ctrlport_rst = ce_rst; + +  ctrlport_endpoint #( +    .THIS_PORTID      (THIS_PORTID), +    .SYNC_CLKS        (0), +    .AXIS_CTRL_MST_EN (0), +    .AXIS_CTRL_SLV_EN (1), +    .SLAVE_FIFO_SIZE  ($clog2(32)) +  ) ctrlport_endpoint_i ( +    .rfnoc_ctrl_clk            (rfnoc_ctrl_clk), +    .rfnoc_ctrl_rst            (rfnoc_ctrl_rst), +    .ctrlport_clk              (ctrlport_clk), +    .ctrlport_rst              (ctrlport_rst), +    .s_rfnoc_ctrl_tdata        (s_rfnoc_ctrl_tdata), +    .s_rfnoc_ctrl_tlast        (s_rfnoc_ctrl_tlast), +    .s_rfnoc_ctrl_tvalid       (s_rfnoc_ctrl_tvalid), +    .s_rfnoc_ctrl_tready       (s_rfnoc_ctrl_tready), +    .m_rfnoc_ctrl_tdata        (m_rfnoc_ctrl_tdata), +    .m_rfnoc_ctrl_tlast        (m_rfnoc_ctrl_tlast), +    .m_rfnoc_ctrl_tvalid       (m_rfnoc_ctrl_tvalid), +    .m_rfnoc_ctrl_tready       (m_rfnoc_ctrl_tready), +    .m_ctrlport_req_wr         (m_ctrlport_req_wr), +    .m_ctrlport_req_rd         (m_ctrlport_req_rd), +    .m_ctrlport_req_addr       (m_ctrlport_req_addr), +    .m_ctrlport_req_data       (m_ctrlport_req_data), +    .m_ctrlport_req_byte_en    (), +    .m_ctrlport_req_has_time   (), +    .m_ctrlport_req_time       (), +    .m_ctrlport_resp_ack       (m_ctrlport_resp_ack), +    .m_ctrlport_resp_status    (2'b0), +    .m_ctrlport_resp_data      (m_ctrlport_resp_data), +    .s_ctrlport_req_wr         (1'b0), +    .s_ctrlport_req_rd         (1'b0), +    .s_ctrlport_req_addr       (20'b0), +    .s_ctrlport_req_portid     (10'b0), +    .s_ctrlport_req_rem_epid   (16'b0), +    .s_ctrlport_req_rem_portid (10'b0), +    .s_ctrlport_req_data       (32'b0), +    .s_ctrlport_req_byte_en    (4'hF), +    .s_ctrlport_req_has_time   (1'b0), +    .s_ctrlport_req_time       (64'b0), +    .s_ctrlport_resp_ack       (), +    .s_ctrlport_resp_status    (), +    .s_ctrlport_resp_data      () +  ); + +  //--------------------------------------------------------------------------- +  //  Data Path +  //--------------------------------------------------------------------------- + +  genvar i; + +  assign axis_data_clk = ce_clk; +  assign axis_data_rst = ce_rst; + +  //--------------------- +  // Input Data Paths +  //--------------------- + +  for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_input_in +    chdr_to_axis_data #( +      .CHDR_W         (CHDR_W), +      .ITEM_W         (32), +      .NIPC           (1), +      .SYNC_CLKS      (0), +      .INFO_FIFO_SIZE ($clog2(32)), +      .PYLD_FIFO_SIZE ($clog2(32)) +    ) chdr_to_axis_data_in_in ( +      .axis_chdr_clk      (rfnoc_chdr_clk), +      .axis_chdr_rst      (rfnoc_chdr_rst), +      .axis_data_clk      (axis_data_clk), +      .axis_data_rst      (axis_data_rst), +      .s_axis_chdr_tdata  (s_rfnoc_chdr_tdata[((0+i)*CHDR_W)+:CHDR_W]), +      .s_axis_chdr_tlast  (s_rfnoc_chdr_tlast[0+i]), +      .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[0+i]), +      .s_axis_chdr_tready (s_rfnoc_chdr_tready[0+i]), +      .m_axis_tdata       (m_in_axis_tdata[(32*1)*i+:(32*1)]), +      .m_axis_tkeep       (m_in_axis_tkeep[1*i+:1]), +      .m_axis_tlast       (m_in_axis_tlast[i]), +      .m_axis_tvalid      (m_in_axis_tvalid[i]), +      .m_axis_tready      (m_in_axis_tready[i]), +      .m_axis_ttimestamp  (m_in_axis_ttimestamp[64*i+:64]), +      .m_axis_thas_time   (m_in_axis_thas_time[i]), +      .m_axis_tlength     (m_in_axis_tlength[16*i+:16]), +      .m_axis_teov        (m_in_axis_teov[i]), +      .m_axis_teob        (m_in_axis_teob[i]), +      .flush_en           (data_i_flush_en), +      .flush_timeout      (data_i_flush_timeout), +      .flush_active       (data_i_flush_active[0+i]), +      .flush_done         (data_i_flush_done[0+i]) +    ); +  end + +  //--------------------- +  // Output Data Paths +  //--------------------- + +  for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_output_out +    axis_data_to_chdr #( +      .CHDR_W          (CHDR_W), +      .ITEM_W          (32), +      .NIPC            (1), +      .SYNC_CLKS       (0), +      .INFO_FIFO_SIZE  ($clog2(32)), +      .PYLD_FIFO_SIZE  ($clog2(MTU)), +      .MTU             (MTU), +      .SIDEBAND_AT_END (1) +    ) axis_data_to_chdr_out_out ( +      .axis_chdr_clk      (rfnoc_chdr_clk), +      .axis_chdr_rst      (rfnoc_chdr_rst), +      .axis_data_clk      (axis_data_clk), +      .axis_data_rst      (axis_data_rst), +      .m_axis_chdr_tdata  (m_rfnoc_chdr_tdata[(0+i)*CHDR_W+:CHDR_W]), +      .m_axis_chdr_tlast  (m_rfnoc_chdr_tlast[0+i]), +      .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0+i]), +      .m_axis_chdr_tready (m_rfnoc_chdr_tready[0+i]), +      .s_axis_tdata       (s_out_axis_tdata[(32*1)*i+:(32*1)]), +      .s_axis_tkeep       (s_out_axis_tkeep[1*i+:1]), +      .s_axis_tlast       (s_out_axis_tlast[i]), +      .s_axis_tvalid      (s_out_axis_tvalid[i]), +      .s_axis_tready      (s_out_axis_tready[i]), +      .s_axis_ttimestamp  (s_out_axis_ttimestamp[64*i+:64]), +      .s_axis_thas_time   (s_out_axis_thas_time[i]), +      .s_axis_tlength     (s_out_axis_tlength[16*i+:16]), +      .s_axis_teov        (s_out_axis_teov[i]), +      .s_axis_teob        (s_out_axis_teob[i]), +      .flush_en           (data_o_flush_en), +      .flush_timeout      (data_o_flush_timeout), +      .flush_active       (data_o_flush_active[0+i]), +      .flush_done         (data_o_flush_done[0+i]) +    ); +  end + +endmodule // noc_shell_keep_one_in_n + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n.v new file mode 100644 index 000000000..27cb21d5b --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n.v @@ -0,0 +1,284 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_keep_one_in_n +// +// Description: +//  The Keep One in N block has two modes: sample mode and packet mode. +//  In sample mode, the first sample is kept and then N-1 samples are dropped. +//  Packet mode is similar to sample mode, except the first packet of samples +//  is kept and then N-1 packets are dropped. The packet size is determined +//  automatically from tlast. +// +// Parameters: +// +//   WIDTH_N     : Bit width of N parameter, must be 31 bits or less +//   THIS_PORTID : Control crossbar port to which this block is connected +//   CHDR_W      : AXIS-CHDR data bus width +//   NUM_PORTS   : Number of block instances +//   MTU         : Maximum transmission unit (i.e., maximum packet size in +//                 CHDR words is 2**MTU). +// +`default_nettype none + +module rfnoc_block_keep_one_in_n #( +  parameter       WIDTH_N     = 24, // Must be 31 bits or less +  parameter [9:0] THIS_PORTID = 10'd0, +  parameter       CHDR_W      = 64, +  parameter       NUM_PORTS   = 1, +  parameter [5:0] MTU         = 10 +)( +  // RFNoC Framework Clocks and Resets +  input  wire                         rfnoc_chdr_clk, +  input  wire                         rfnoc_ctrl_clk, +  input  wire                         ce_clk, +  // RFNoC Backend Interface +  input  wire [511:0]                 rfnoc_core_config, +  output wire [511:0]                 rfnoc_core_status, +  // AXIS-CHDR Input Ports (from framework) +  input  wire [CHDR_W*NUM_PORTS-1:0]  s_rfnoc_chdr_tdata, +  input  wire [       NUM_PORTS-1:0]  s_rfnoc_chdr_tlast, +  input  wire [       NUM_PORTS-1:0]  s_rfnoc_chdr_tvalid, +  output wire [       NUM_PORTS-1:0]  s_rfnoc_chdr_tready, +  // AXIS-CHDR Output Ports (to framework) +  output wire [CHDR_W*NUM_PORTS-1:0]  m_rfnoc_chdr_tdata, +  output wire [       NUM_PORTS-1:0]  m_rfnoc_chdr_tlast, +  output wire [       NUM_PORTS-1:0]  m_rfnoc_chdr_tvalid, +  input  wire [       NUM_PORTS-1:0]  m_rfnoc_chdr_tready, +  // AXIS-Ctrl Input Port (from framework) +  input  wire [31:0]                  s_rfnoc_ctrl_tdata, +  input  wire                         s_rfnoc_ctrl_tlast, +  input  wire                         s_rfnoc_ctrl_tvalid, +  output wire                         s_rfnoc_ctrl_tready, +  // AXIS-Ctrl Output Port (to framework) +  output wire [31:0]                  m_rfnoc_ctrl_tdata, +  output wire                         m_rfnoc_ctrl_tlast, +  output wire                         m_rfnoc_ctrl_tvalid, +  input  wire                         m_rfnoc_ctrl_tready +); + +  //--------------------------------------------------------------------------- +  // Signal Declarations +  //--------------------------------------------------------------------------- + +  // Clocks and Resets +  wire                     ctrlport_clk; +  wire                     ctrlport_rst; +  // CtrlPort Master +  wire                     m_ctrlport_req_wr; +  wire                     m_ctrlport_req_rd; +  wire [19:0]              m_ctrlport_req_addr; +  wire [31:0]              m_ctrlport_req_data; +  wire                     m_ctrlport_resp_ack; +  wire [31:0]              m_ctrlport_resp_data; +  // Data Stream to User Logic: in +  wire [32*NUM_PORTS-1:0]  m_in_axis_tdata; +  wire [   NUM_PORTS-1:0]  m_in_axis_tlast; +  wire [   NUM_PORTS-1:0]  m_in_axis_tvalid; +  wire [   NUM_PORTS-1:0]  m_in_axis_tready; +  wire [64*NUM_PORTS-1:0]  m_in_axis_ttimestamp; +  wire [   NUM_PORTS-1:0]  m_in_axis_thas_time; +  wire [16*NUM_PORTS-1:0]  m_in_axis_tlength; +  wire [   NUM_PORTS-1:0]  m_in_axis_teov; +  wire [   NUM_PORTS-1:0]  m_in_axis_teob; +  // Data Stream from User Logic: out +  wire [32*NUM_PORTS-1:0]  s_out_axis_tdata; +  wire [   NUM_PORTS-1:0]  s_out_axis_tlast; +  wire [   NUM_PORTS-1:0]  s_out_axis_tvalid; +  wire [   NUM_PORTS-1:0]  s_out_axis_tready; +  wire [64*NUM_PORTS-1:0]  s_out_axis_ttimestamp; +  wire [   NUM_PORTS-1:0]  s_out_axis_thas_time; +  wire [   NUM_PORTS-1:0]  s_out_axis_teov; +  wire [   NUM_PORTS-1:0]  s_out_axis_teob; + +  wire                     ce_rst; + +  //--------------------------------------------------------------------------- +  // NoC Shell +  //--------------------------------------------------------------------------- + +  noc_shell_keep_one_in_n #( +    .CHDR_W      (CHDR_W), +    .THIS_PORTID (THIS_PORTID), +    .NUM_PORTS   (NUM_PORTS), +    .MTU         (MTU) +  ) noc_shell_keep_one_in_n_i ( +    //// Framework Interface +    // Clock Inputs +    .rfnoc_chdr_clk        (rfnoc_chdr_clk), +    .rfnoc_ctrl_clk        (rfnoc_ctrl_clk), +    .ce_clk                (ce_clk), +    // Reset Outputs +    .rfnoc_chdr_rst        (), +    .rfnoc_ctrl_rst        (), +    .ce_rst                (ce_rst), +    // RFNoC Backend Interface +    .rfnoc_core_config     (rfnoc_core_config), +    .rfnoc_core_status     (rfnoc_core_status), +    // CHDR Input Ports  (from framework) +    .s_rfnoc_chdr_tdata    (s_rfnoc_chdr_tdata), +    .s_rfnoc_chdr_tlast    (s_rfnoc_chdr_tlast), +    .s_rfnoc_chdr_tvalid   (s_rfnoc_chdr_tvalid), +    .s_rfnoc_chdr_tready   (s_rfnoc_chdr_tready), +    // CHDR Output Ports (to framework) +    .m_rfnoc_chdr_tdata    (m_rfnoc_chdr_tdata), +    .m_rfnoc_chdr_tlast    (m_rfnoc_chdr_tlast), +    .m_rfnoc_chdr_tvalid   (m_rfnoc_chdr_tvalid), +    .m_rfnoc_chdr_tready   (m_rfnoc_chdr_tready), +    // AXIS-Ctrl Input Port (from framework) +    .s_rfnoc_ctrl_tdata    (s_rfnoc_ctrl_tdata), +    .s_rfnoc_ctrl_tlast    (s_rfnoc_ctrl_tlast), +    .s_rfnoc_ctrl_tvalid   (s_rfnoc_ctrl_tvalid), +    .s_rfnoc_ctrl_tready   (s_rfnoc_ctrl_tready), +    // AXIS-Ctrl Output Port (to framework) +    .m_rfnoc_ctrl_tdata    (m_rfnoc_ctrl_tdata), +    .m_rfnoc_ctrl_tlast    (m_rfnoc_ctrl_tlast), +    .m_rfnoc_ctrl_tvalid   (m_rfnoc_ctrl_tvalid), +    .m_rfnoc_ctrl_tready   (m_rfnoc_ctrl_tready), +    //// Client Interface +    // CtrlPort Clock and Reset +    .ctrlport_clk          (ctrlport_clk), +    .ctrlport_rst          (ctrlport_rst), +    // CtrlPort Master +    .m_ctrlport_req_wr     (m_ctrlport_req_wr), +    .m_ctrlport_req_rd     (m_ctrlport_req_rd), +    .m_ctrlport_req_addr   (m_ctrlport_req_addr), +    .m_ctrlport_req_data   (m_ctrlport_req_data), +    .m_ctrlport_resp_ack   (m_ctrlport_resp_ack), +    .m_ctrlport_resp_data  (m_ctrlport_resp_data), +    // AXI-Stream Payload Context Clock and Reset +    .axis_data_clk         (), +    .axis_data_rst         (), +    // Data Stream to User Logic: in +    .m_in_axis_tdata       (m_in_axis_tdata), +    .m_in_axis_tkeep       (), +    .m_in_axis_tlast       (m_in_axis_tlast), +    .m_in_axis_tvalid      (m_in_axis_tvalid), +    .m_in_axis_tready      (m_in_axis_tready), +    .m_in_axis_ttimestamp  (m_in_axis_ttimestamp), +    .m_in_axis_thas_time   (m_in_axis_thas_time), +    .m_in_axis_tlength     (m_in_axis_tlength), +    .m_in_axis_teov        (m_in_axis_teov), +    .m_in_axis_teob        (m_in_axis_teob), +    // Data Stream from User Logic: out +    .s_out_axis_tdata      (s_out_axis_tdata), +    .s_out_axis_tkeep      ({(NUM_PORTS){1'b1}}), +    .s_out_axis_tlast      (s_out_axis_tlast), +    .s_out_axis_tvalid     (s_out_axis_tvalid), +    .s_out_axis_tready     (s_out_axis_tready), +    .s_out_axis_ttimestamp (s_out_axis_ttimestamp), +    .s_out_axis_thas_time  (s_out_axis_thas_time), +    .s_out_axis_teov       (s_out_axis_teov), +    .s_out_axis_teob       (s_out_axis_teob) +  ); + +  wire [ 8*NUM_PORTS-1:0] set_addr; +  wire [32*NUM_PORTS-1:0] set_data; +  wire [   NUM_PORTS-1:0] set_stb; +  wire [ 8*NUM_PORTS-1:0] rb_addr; +  reg  [64*NUM_PORTS-1:0] rb_data; +  wire [   NUM_PORTS-1:0] rb_stb; + +  ctrlport_to_settings_bus # ( +    .NUM_PORTS (NUM_PORTS) +  ) ctrlport_to_settings_bus_i ( +    .ctrlport_clk             (ctrlport_clk), +    .ctrlport_rst             (ctrlport_rst), +    .s_ctrlport_req_wr        (m_ctrlport_req_wr), +    .s_ctrlport_req_rd        (m_ctrlport_req_rd), +    .s_ctrlport_req_addr      (m_ctrlport_req_addr), +    .s_ctrlport_req_data      (m_ctrlport_req_data), +    .s_ctrlport_req_has_time  (1'b0), +    .s_ctrlport_req_time      (64'd0), +    .s_ctrlport_resp_ack      (m_ctrlport_resp_ack), +    .s_ctrlport_resp_data     (m_ctrlport_resp_data), +    .set_data                 (set_data), +    .set_addr                 (set_addr), +    .set_stb                  (set_stb), +    .set_time                 (), +    .set_has_time             (), +    .rb_stb                   (rb_stb), +    .rb_addr                  (rb_addr), +    .rb_data                  (rb_data), +    .timestamp                ()); + +  //--------------------------------------------------------------------------- +  // User Logic +  //--------------------------------------------------------------------------- + +  `include "rfnoc_keep_one_in_n_regs.vh" + +  wire [REG_N_LEN*NUM_PORTS-1:0] n; +  wire [REG_MODE_LEN*NUM_PORTS-1:0] mode; + +  genvar i; +  for (i = 0; i < NUM_PORTS; i = i+1) begin + +    setting_reg #( +      .my_addr  (REG_N), +      .awidth   (8), +      .width    (REG_N_LEN)) +    inst_setting_reg_n ( +      .clk      (ce_clk), +      .rst      (ce_rst), +      .strobe   (set_stb[i]), +      .addr     (set_addr[8*(i+1)-1:8*i]), +      .in       (set_data[32*(i+1)-1:32*i]), +      .out      (n[WIDTH_N*(i+1)-1:WIDTH_N*i]), +      .changed  ()); + +    setting_reg #( +      .my_addr  (REG_MODE), +      .awidth   (8), +      .width    (REG_MODE_LEN)) +    inst_setting_reg_mode ( +      .clk      (ce_clk), +      .rst      (ce_rst), +      .strobe   (set_stb[i]), +      .addr     (set_addr[8*(i+1)-1:8*i]), +      .in       (set_data[32*(i+1)-1:32*i]), +      .out      (mode[i]), +      .changed  ()); + +    // Readback +    assign rb_stb[i] = 1'b1; +    always @* +      case (rb_addr[8*(i+1)-1:8*i]) +        REG_N           : rb_data[64*(i+1)-1:64*i] <= {{(64-REG_N_LEN){1'b0}}, n[WIDTH_N*(i+1)-1:WIDTH_N*i]}; +        REG_MODE        : rb_data[64*(i+1)-1:64*i] <= {{(64-REG_MODE_LEN){1'b0}}, mode[i]}; +        REG_WIDTH_N     : rb_data[64*(i+1)-1:64*i] <= {{(64-REG_WIDTH_N){1'b0}}, WIDTH_N}; +        default         : rb_data[64*(i+1)-1:64*i] <= 64'h0BADC0DE0BADC0DE; +    endcase + +    rfnoc_keep_one_in_n #( +      .WIDTH      (32), +      .WIDTH_N    (WIDTH_N)) +    inst_rfnoc_keep_one_in_n ( +      .clk               (ce_clk), +      .reset             (ce_rst), +      .mode              (mode[i]), +      .n                 (n[WIDTH_N*(i+1)-1:WIDTH_N*i]), +      .s_axis_tdata      (m_in_axis_tdata[32*(i+1)-1:32*i]), +      .s_axis_tlast      (m_in_axis_tlast[i]), +      .s_axis_tvalid     (m_in_axis_tvalid[i]), +      .s_axis_tready     (m_in_axis_tready[i]), +      .s_axis_ttimestamp (m_in_axis_ttimestamp[64*(i+1)-1:64*i]), +      .s_axis_thas_time  (m_in_axis_thas_time[i]), +      .s_axis_tlength    (m_in_axis_tlength[16*(i+1)-1:16*i]), +      .s_axis_teov       (m_in_axis_teov[i]), +      .s_axis_teob       (m_in_axis_teob[i]), +      .m_axis_tdata      (s_out_axis_tdata[32*(i+1)-1:32*i]), +      .m_axis_tlast      (s_out_axis_tlast[i]), +      .m_axis_tvalid     (s_out_axis_tvalid[i]), +      .m_axis_tready     (s_out_axis_tready[i]), +      .m_axis_ttimestamp (s_out_axis_ttimestamp[64*(i+1)-1:64*i]), +      .m_axis_thas_time  (s_out_axis_thas_time[i]), +      .m_axis_teov       (s_out_axis_teov[i]), +      .m_axis_teob       (s_out_axis_teob[i])); +  end + +endmodule // rfnoc_block_keep_one_in_n + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n_tb.sv new file mode 100644 index 000000000..969ff6e69 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_block_keep_one_in_n_tb.sv @@ -0,0 +1,484 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_keep_one_in_n_tb +// +// Description: Testbench for the keep_one_in_n RFNoC block. +// + +`default_nettype none + +module rfnoc_block_keep_one_in_n_tb; + +  `include "test_exec.svh" + +  import PkgTestExec::*; +  import PkgChdrUtils::*; +  import PkgRfnocBlockCtrlBfm::*; +  import PkgRfnocItemUtils::*; + +  //--------------------------------------------------------------------------- +  // Testbench Configuration +  //--------------------------------------------------------------------------- + +  localparam [ 9:0] THIS_PORTID     = 10'h123; +  localparam [31:0] NOC_ID          = 32'h02460000; +  localparam int    CHDR_W          = 64; +  localparam int    ITEM_W          = 32; +  localparam int    NUM_PORTS       = 2; +  localparam int    MTU             = 13; +  localparam int    SPP             = 64; +  localparam int    PKT_SIZE_BYTES  = SPP * (ITEM_W/8); +  localparam int    STALL_PROB      = 50;      // Default BFM stall probability +  localparam real   CHDR_CLK_PER    = 5.0;     // 200 MHz +  localparam real   CTRL_CLK_PER    = 25.0;    // 40 MHz +  localparam real   CE_CLK_PER      = 5.0;  // 200 MHz + +  localparam        WIDTH_N     = 8; + +  localparam bit    SAMPLE_MODE = 0; +  localparam bit    PACKET_MODE = 1; + +  //--------------------------------------------------------------------------- +  // Clocks and Resets +  //--------------------------------------------------------------------------- + +  bit rfnoc_chdr_clk; +  bit rfnoc_ctrl_clk; +  bit ce_clk; + +  sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); +  sim_clock_gen #(CTRL_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); +  sim_clock_gen #(CE_CLK_PER) ce_clk_gen (.clk(ce_clk), .rst()); + +  //--------------------------------------------------------------------------- +  // Bus Functional Models +  //--------------------------------------------------------------------------- + +  // Backend Interface +  RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); + +  // AXIS-Ctrl Interface +  AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); +  AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); + +  // AXIS-CHDR Interfaces +  AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); +  AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); + +  // Block Controller BFM +  RfnocBlockCtrlBfm #(CHDR_W, ITEM_W) blk_ctrl = new(backend, m_ctrl, s_ctrl); + +  // CHDR word and item/sample data types +  typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; +  typedef ChdrData #(CHDR_W, ITEM_W)::item_t      item_t; + +  // Connect block controller to BFMs +  for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_input_connections +    initial begin +      blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES); +      blk_ctrl.set_master_stall_prob(i, STALL_PROB); +    end +  end +  for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_output_connections +    initial begin +      blk_ctrl.connect_slave_data_port(i, s_chdr[i]); +      blk_ctrl.set_slave_stall_prob(i, STALL_PROB); +    end +  end + +  //--------------------------------------------------------------------------- +  // Device Under Test (DUT) +  //--------------------------------------------------------------------------- + +  // DUT Slave (Input) Port Signals +  logic [CHDR_W*NUM_PORTS-1:0] s_rfnoc_chdr_tdata; +  logic [       NUM_PORTS-1:0] s_rfnoc_chdr_tlast; +  logic [       NUM_PORTS-1:0] s_rfnoc_chdr_tvalid; +  logic [       NUM_PORTS-1:0] s_rfnoc_chdr_tready; + +  // DUT Master (Output) Port Signals +  logic [CHDR_W*NUM_PORTS-1:0] m_rfnoc_chdr_tdata; +  logic [       NUM_PORTS-1:0] m_rfnoc_chdr_tlast; +  logic [       NUM_PORTS-1:0] m_rfnoc_chdr_tvalid; +  logic [       NUM_PORTS-1:0] m_rfnoc_chdr_tready; + +  // Map the array of BFMs to a flat vector for the DUT connections +  for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_dut_input_connections +    // Connect BFM master to DUT slave port +    assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata; +    assign s_rfnoc_chdr_tlast[i]                = m_chdr[i].tlast; +    assign s_rfnoc_chdr_tvalid[i]               = m_chdr[i].tvalid; +    assign m_chdr[i].tready                     = s_rfnoc_chdr_tready[i]; +  end +  for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_dut_output_connections +    // Connect BFM slave to DUT master port +    assign s_chdr[i].tdata        = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W]; +    assign s_chdr[i].tlast        = m_rfnoc_chdr_tlast[i]; +    assign s_chdr[i].tvalid       = m_rfnoc_chdr_tvalid[i]; +    assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready; +  end + +  rfnoc_block_keep_one_in_n #( +    .WIDTH_N             (WIDTH_N), +    .THIS_PORTID         (THIS_PORTID), +    .CHDR_W              (CHDR_W), +    .MTU                 (MTU), +    .NUM_PORTS           (NUM_PORTS) +  ) dut ( +    .rfnoc_chdr_clk      (rfnoc_chdr_clk), +    .rfnoc_ctrl_clk      (rfnoc_ctrl_clk), +    .ce_clk              (ce_clk), +    .rfnoc_core_config   (backend.cfg), +    .rfnoc_core_status   (backend.sts), +    .s_rfnoc_chdr_tdata  (s_rfnoc_chdr_tdata), +    .s_rfnoc_chdr_tlast  (s_rfnoc_chdr_tlast), +    .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid), +    .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready), +    .m_rfnoc_chdr_tdata  (m_rfnoc_chdr_tdata), +    .m_rfnoc_chdr_tlast  (m_rfnoc_chdr_tlast), +    .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid), +    .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready), +    .s_rfnoc_ctrl_tdata  (m_ctrl.tdata), +    .s_rfnoc_ctrl_tlast  (m_ctrl.tlast), +    .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid), +    .s_rfnoc_ctrl_tready (m_ctrl.tready), +    .m_rfnoc_ctrl_tdata  (s_ctrl.tdata), +    .m_rfnoc_ctrl_tlast  (s_ctrl.tlast), +    .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid), +    .m_rfnoc_ctrl_tready (s_ctrl.tready) +  ); + +  //--------------------------------------------------------------------------- +  // Helper Logic +  //--------------------------------------------------------------------------- + +  // Translate the desired register access to a ctrlport write request. +  task automatic write_reg(input logic [$clog2(NUM_PORTS)-1:0] port, input ctrl_address_t addr, input logic [31:0] value); +    blk_ctrl.reg_write(256*8*port + addr*8, value); +  endtask : write_reg + +  // Translate the desired register access to a ctrlport read request. +  task automatic read_user_reg(input logic [$clog2(NUM_PORTS)-1:0] port, input ctrl_address_t addr, output logic [63:0] value); +    blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]); +    blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]); +  endtask : read_user_reg + +  typedef struct { +    item_t        samples[$]; +    chdr_word_t   mdata[$]; +    packet_info_t pkt_info; +  } test_packet_t; + +  // Check if input test packets are identical +  function automatic void compare_test_packets(const ref test_packet_t a, b); +    string str; + +    // Packet payload +    $sformat(str, +      "Packet payload size incorrect! Expected: %4d, Received: %4d", +      a.samples.size(), b.samples.size()); +    `ASSERT_ERROR(a.samples.size() == b.samples.size(), str); + +    for (int i = 0; i < a.samples.size(); i++) begin +      $sformat(str, +        "Packet payload word %4d incorrect! Expected: 0x%8X, Received: 0x%8X", +        i, a.samples[i], b.samples[i]); +      `ASSERT_ERROR(a.samples[i] == b.samples[i], str); +    end + +    // Packet metadata +    $sformat(str, +      "Packet metadata size incorrect! Expected: %4d, Received: %4d", +      a.mdata.size(), b.mdata.size()); +    `ASSERT_ERROR(a.mdata.size() == b.mdata.size(), str); + +    for (int i = 0; i < a.mdata.size(); i++) begin +      $sformat(str, +        "Packet metadata word %04d incorrect! Expected: 0x%8X, Received: 0x%8X", +        i, a.mdata[i], b.mdata[i]); +      `ASSERT_ERROR(a.mdata[i] == b.mdata[i], str); +    end + +    // Packet info +    $sformat(str, +      "Packet info field 'vc' incorrect! Expected: %2d, Received: %2d", +      a.pkt_info.vc, b.pkt_info.vc); +    `ASSERT_ERROR(a.pkt_info.vc == b.pkt_info.vc, str); + +    $sformat(str, +      "Packet info field 'eob' incorrect! Expected: %1d, Received: %1d", +      a.pkt_info.eob, b.pkt_info.eob); +    `ASSERT_ERROR(a.pkt_info.eob == b.pkt_info.eob, str); + +    $sformat(str, +      "Packet info field 'eov' incorrect! Expected: %1d, Received: %1d", +      a.pkt_info.eov, b.pkt_info.eov); +    `ASSERT_ERROR(a.pkt_info.eov == b.pkt_info.eov, str); + +    $sformat(str, +      "Packet info field 'has_time' incorrect! Expected: %1d, Received: %1d", +      a.pkt_info.has_time, b.pkt_info.has_time); +    `ASSERT_ERROR(a.pkt_info.has_time == b.pkt_info.has_time, str); + +    $sformat(str, +      "Packet info field 'timestamp' incorrect! Expected: 0x%16X, Received: 0x%16X", +      a.pkt_info.timestamp, b.pkt_info.timestamp); +    `ASSERT_ERROR(a.pkt_info.timestamp == b.pkt_info.timestamp, str); + +  endfunction + +  //--------------------------------------------------------------------------- +  // Test Tasks +  //--------------------------------------------------------------------------- + +  task automatic test_keep_one_in_n ( +    input bit mode, +    input int n, +    input int num_packets, +    input int port        = 0, +    input int spp         = SPP, +    input int stall_prob  = STALL_PROB +  ); + +    mailbox #(test_packet_t) tb_send_packets = new(); + +    blk_ctrl.set_master_stall_prob(port, stall_prob); +    blk_ctrl.set_slave_stall_prob(port, stall_prob); + +    $display("N = %3d, Number of Packets = %3d, Port Number = %1d", n, num_packets, port); + +    begin +      logic [63:0] readback; +      string str; + +      write_reg(port, dut.REG_MODE, mode); +      read_user_reg(port, dut.REG_MODE, readback); +      $sformat(str, +        "Mode incorrect! Expected: %1d, Received: %1d", +        mode, readback[0]); +      `ASSERT_ERROR(readback[0] == mode, str); + +      write_reg(port, dut.REG_N, n); +      read_user_reg(port, dut.REG_N, readback); +      $sformat(str, +        "N incorrect! Expected: %5d, Received: %5d", +        n, readback); +      `ASSERT_ERROR(readback == n, str); +    end + +    fork +      // TX +      begin +        for (int i = 0; i < num_packets; i++) begin +          test_packet_t tb_send_pkt; + +          for (int k = 0; k < spp; k++) begin +            tb_send_pkt.samples.push_back($urandom()); +          end +          tb_send_pkt.mdata = {}; +          tb_send_pkt.pkt_info = '{ +            vc:        0, +            eob:       (i == num_packets-1), +            eov:       bit'($urandom()), +            has_time:  1'b1, +            timestamp: {$urandom(),$urandom()}}; + +          blk_ctrl.send_items(port, tb_send_pkt.samples, tb_send_pkt.mdata, tb_send_pkt.pkt_info); + +          tb_send_packets.put(tb_send_pkt); +        end +      end +      // RX +      begin +        int l = 0; +        mailbox #(test_packet_t) tb_recv_packets = new(); +        int num_packets_expected = int'($ceil(real'(num_packets)/real'(n))); + +        for (int i = 0; i < num_packets_expected; i++) begin +          test_packet_t tb_recv_pkt; + +          blk_ctrl.recv_items_adv(port, tb_recv_pkt.samples, tb_recv_pkt.mdata, tb_recv_pkt.pkt_info); + +          tb_recv_packets.put(tb_recv_pkt); +        end + +        for (int i = 0; i < num_packets; i = i + n) begin +          test_packet_t tb_recv_pkt, tb_send_pkt, tb_dropped_pkt; + +          // Packet mode keeps first packet, drops n-1 +          if (mode) begin +            tb_send_packets.get(tb_send_pkt); +            for (int k = 0; k < n-1; k++) begin +              if (!tb_send_packets.try_get(tb_dropped_pkt)) break; +              tb_send_pkt.pkt_info.eob = tb_send_pkt.pkt_info.eob | tb_dropped_pkt.pkt_info.eob; +              tb_send_pkt.pkt_info.eov = tb_send_pkt.pkt_info.eov | tb_dropped_pkt.pkt_info.eov; +            end +          // Sample mode loops through n packets, keeps 1 in n samples +          // from all the packets +          end else begin +            item_t samples_pruned[$]; + +            // Peek first packet to grab mdata and packet info +            tb_send_packets.peek(tb_send_pkt); + +            // Loop through n packets, grabbing 1 in n samples. +            for (int k = 0; k < n; k++) begin + +              if (!tb_send_packets.try_get(tb_dropped_pkt)) break; +              tb_send_pkt.pkt_info.eob = tb_send_pkt.pkt_info.eob | tb_dropped_pkt.pkt_info.eob; +              tb_send_pkt.pkt_info.eov = tb_send_pkt.pkt_info.eov | tb_dropped_pkt.pkt_info.eov; +              while (l < tb_dropped_pkt.samples.size()) begin +                samples_pruned.push_back(tb_dropped_pkt.samples[l]); +                l = l + n; +              end +              // Account for wrap around when dropping samples between packet boundaries +              l = l - tb_dropped_pkt.samples.size(); +            end + +            // Replace packet samples with 1 in n samples of n packets +            tb_send_pkt.samples = samples_pruned; +          end + +          tb_recv_packets.get(tb_recv_pkt); +          compare_test_packets(tb_send_pkt, tb_recv_pkt); +        end + +        begin +          string str; +          $sformat(str, +            "Sent packets queue not empty! Number of extra items: %2d", +            tb_send_packets.num); +          `ASSERT_ERROR(tb_send_packets.num == 0, str); +          $sformat(str, +            "Receive packets queue not empty! Number of extra items: %2d", +            tb_recv_packets.num); +          `ASSERT_ERROR(tb_recv_packets.num == 0, str); +        end +      end +    join + +  endtask; + +  //--------------------------------------------------------------------------- +  // Main Test Process +  //--------------------------------------------------------------------------- + +  initial begin : tb_main + +    // Initialize the test exec object for this testbench +    test.start_tb("rfnoc_block_keep_one_in_n_tb"); + +    // Start the BFMs running +    blk_ctrl.run(); + +    //-------------------------------- +    // Reset +    //-------------------------------- + +    test.start_test("Flush block then reset it", 10us); +    blk_ctrl.flush_and_reset(); +    test.end_test(); + +    //-------------------------------- +    // Verify Block Info +    //-------------------------------- + +    test.start_test("Verify Block Info", 2us); +    `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect NOC_ID Value"); +    `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I Value"); +    `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O Value"); +    `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value"); +    test.end_test(); + +    //-------------------------------- +    // Test Sequences +    //-------------------------------- + +    // Packet mode +    test.start_test("Test packet mode", NUM_PORTS*1100us); +    for (int port = 0; port < NUM_PORTS; port++) begin +      test_keep_one_in_n(PACKET_MODE,  1,  1, port); // mode, n, num_packets, port +      test_keep_one_in_n(PACKET_MODE,  1,  2, port); +      test_keep_one_in_n(PACKET_MODE,  1, 10, port); +      test_keep_one_in_n(PACKET_MODE,  2,  1, port); +      test_keep_one_in_n(PACKET_MODE,  2,  2, port); +      test_keep_one_in_n(PACKET_MODE,  2,  3, port); +      test_keep_one_in_n(PACKET_MODE,  2, 10, port); +      test_keep_one_in_n(PACKET_MODE,  2, 51, port); +      test_keep_one_in_n(PACKET_MODE,  3,  1, port); +      test_keep_one_in_n(PACKET_MODE,  3,  2, port); +      test_keep_one_in_n(PACKET_MODE,  3,  3, port); +      test_keep_one_in_n(PACKET_MODE,  3,  4, port); +      test_keep_one_in_n(PACKET_MODE,  3, 10, port); +      test_keep_one_in_n(PACKET_MODE,  3, 53, port); +      test_keep_one_in_n(PACKET_MODE, 11,  1, port); +      test_keep_one_in_n(PACKET_MODE, 11,  7, port); +      test_keep_one_in_n(PACKET_MODE, 11, 10, port); +      test_keep_one_in_n(PACKET_MODE, 11, 11, port); +      test_keep_one_in_n(PACKET_MODE, 11, 12, port); +      test_keep_one_in_n(PACKET_MODE, 11, 13, port); +      test_keep_one_in_n(PACKET_MODE, 11, 20, port); +      test_keep_one_in_n(PACKET_MODE, 11, 21, port); +      test_keep_one_in_n(PACKET_MODE, 11, 32, port); +      test_keep_one_in_n(PACKET_MODE, 11, 33, port); +      test_keep_one_in_n(PACKET_MODE, 11, 34, port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2**WIDTH_N-2, port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2**WIDTH_N-1, port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2**WIDTH_N  , port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-3), port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-2), port); +      test_keep_one_in_n(PACKET_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-1), port); +    end +    test.end_test(); + +    // Packet mode +    test.start_test("Test sample mode", NUM_PORTS*1100us); +    for (int port = 0; port < NUM_PORTS; port++) begin +      test_keep_one_in_n(SAMPLE_MODE,  1,  1, port); +      test_keep_one_in_n(SAMPLE_MODE,  1,  2, port); +      test_keep_one_in_n(SAMPLE_MODE,  1, 10, port); +      test_keep_one_in_n(SAMPLE_MODE,  2,  1, port); +      test_keep_one_in_n(SAMPLE_MODE,  2,  2, port); +      test_keep_one_in_n(SAMPLE_MODE,  2,  3, port); +      test_keep_one_in_n(SAMPLE_MODE,  2, 10, port); +      test_keep_one_in_n(SAMPLE_MODE,  2, 51, port); +      test_keep_one_in_n(SAMPLE_MODE,  3,  1, port); +      test_keep_one_in_n(SAMPLE_MODE,  3,  2, port); +      test_keep_one_in_n(SAMPLE_MODE,  3,  3, port); +      test_keep_one_in_n(SAMPLE_MODE,  3,  4, port); +      test_keep_one_in_n(SAMPLE_MODE,  3, 10, port); +      test_keep_one_in_n(SAMPLE_MODE,  3, 53, port); +      test_keep_one_in_n(SAMPLE_MODE, 11,  1, port); +      test_keep_one_in_n(SAMPLE_MODE, 11,  7, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 10, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 11, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 12, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 13, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 20, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 21, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 32, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 33, port); +      test_keep_one_in_n(SAMPLE_MODE, 11, 34, port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2**WIDTH_N-2, port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2**WIDTH_N-1, port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2**WIDTH_N  , port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-3), port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-2), port); +      test_keep_one_in_n(SAMPLE_MODE, 2**WIDTH_N-1, 2*(2**WIDTH_N-1), port); +    end +    test.end_test(); + +    //-------------------------------- +    // Finish Up +    //-------------------------------- + +    // Display final statistics and results +    test.end_tb(); +  end : tb_main + +endmodule : rfnoc_block_keep_one_in_n_tb + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n.v new file mode 100644 index 000000000..c41cddda1 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n.v @@ -0,0 +1,269 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module:  rfnoc_keep_one_in_n +// +// Keep one sample and drop N-1 samples. Also, handles timestamp and setting EOB / EOV. +// +// n:    Drop n-1 samples or packets +// mode: 0 = operate on samples, 1 = operate on packets that are delineated by tlast +// +`default_nettype none + +module rfnoc_keep_one_in_n #( +  parameter WIDTH      = 32, +  parameter WIDTH_N    = 16 +)( +  input  wire                       clk, +  input  wire                       reset, +  input  wire                       mode, +  input  wire [WIDTH_N-1:0]         n, +  input  wire [WIDTH-1:0]           s_axis_tdata, +  input  wire                       s_axis_tlast, +  input  wire                       s_axis_tvalid, +  output wire                       s_axis_tready, +  input  wire [63:0]                s_axis_ttimestamp, +  input  wire                       s_axis_thas_time, +  input  wire [15:0]                s_axis_tlength, +  input  wire                       s_axis_teov, +  input  wire                       s_axis_teob, +  output wire [WIDTH-1:0]           m_axis_tdata, +  output wire                       m_axis_tlast, +  output wire                       m_axis_tvalid, +  input  wire                       m_axis_tready, +  output wire [63:0]                m_axis_ttimestamp, +  output wire                       m_axis_thas_time, +  output wire                       m_axis_teov, +  output wire                       m_axis_teob +); + +  reg first_samp = 1'b1; +  always @(posedge clk) begin +    if (reset) begin +      first_samp <= 1'b1; +    end else begin +      if (s_axis_tvalid & s_axis_tready) begin +        first_samp <= s_axis_tlast; +      end +    end +  end + +  // Packet mode state machine +  // - Keeps first packet which is delinated by tlast +  //   and drops the following n-1 packets. +  // - EOB signifies the end of a stream and causes the +  //   state machine to stay in or immediately return to the +  //   "keep" state. +  reg state_pkt; +  localparam S_PKT_KEEP = 1'd0; +  localparam S_PKT_DROP = 1'd1; + +  reg [WIDTH_N-1:0] cnt_pkt_n = 2; + +  always @(posedge clk) begin +    if (reset) begin +      cnt_pkt_n <= 2; +      state_pkt <= S_PKT_KEEP; +    end else begin +      case (state_pkt) +        S_PKT_KEEP : begin +          cnt_pkt_n <= 2; +          if (s_axis_tvalid & s_axis_tready & s_axis_tlast) begin +            // If EOB or n == 1, stay in this state +            if (~s_axis_teob & n != 1) begin +              state_pkt <= S_PKT_DROP; +            end +          end +        end +        S_PKT_DROP : begin +          if (s_axis_tvalid & s_axis_tready & s_axis_tlast) begin +            if (s_axis_teob) begin +              state_pkt <= S_PKT_KEEP; +            end else begin +              cnt_pkt_n <= cnt_pkt_n + 1; +              if (cnt_pkt_n >= n) begin +                cnt_pkt_n <= 2; +                state_pkt <= S_PKT_KEEP; +              end +            end +          end +        end +        default : state_pkt <= S_PKT_KEEP; +      endcase +    end +  end + +  // Sample mode state machine +  // - Keeps first sample and drops n-1 samples. +  // - EOB also causes this state machine stay in or return +  //   to the "keep" state. +  reg [WIDTH_N-1:0] cnt_samp_n; + +  reg state_samp; +  localparam S_SAMP_KEEP = 1'd0; +  localparam S_SAMP_DROP = 1'd1; + +  always @(posedge clk) begin +    if (reset) begin +      cnt_samp_n <= 2; +      state_samp <= S_SAMP_KEEP; +    end else begin +      case (state_samp) +        S_SAMP_KEEP : begin +          cnt_samp_n <= 2; +          if (s_axis_tvalid & s_axis_tready) begin +            // If EOB or n == 1, stay in this state +            if (~(s_axis_tlast & s_axis_teob) & n != 1) begin +              state_samp <= S_SAMP_DROP; +            end +          end +        end +        S_SAMP_DROP : begin +          if (s_axis_tvalid & s_axis_tready) begin +            if (s_axis_tlast & s_axis_teob) begin +              state_samp <= S_SAMP_KEEP; +            end else begin +              cnt_samp_n <= cnt_samp_n + 1; +              if (cnt_samp_n >= n) begin +                cnt_samp_n <= 2; +                state_samp <= S_SAMP_KEEP; +              end +            end +          end +        end +        default : state_samp <= S_SAMP_KEEP; +      endcase +    end +  end + +  wire keep_sample = mode ? (state_pkt == S_PKT_KEEP) : (state_samp == S_SAMP_KEEP); + +  // Output state machine +  reg [1:0] state_o; +  localparam S_O_FIRST_SAMP    = 2'd0; +  localparam S_O_OUTPUT        = 2'd1; +  localparam S_O_LAST_SAMP     = 2'd2; +  localparam S_O_LAST_SAMP_EOB = 2'd3; + +  reg [WIDTH-1:0]      sample_reg; +  reg [63:0]           timestamp_reg; +  reg                  has_time_reg; +  reg                  eov_reg; +  reg [15-WIDTH/8:0]   length_reg; +  reg [15-WIDTH/8:0]   length_cnt; + +  always @(posedge clk) begin +    if (reset) begin +      length_cnt    <= 2; +      sample_reg    <=  'd0; +      timestamp_reg <=  'd0; +      has_time_reg  <= 1'b0; +      length_reg    <=  'd0; +      eov_reg       <= 1'b0; +      state_o       <= S_O_FIRST_SAMP; +    end else begin +      case (state_o) +        // Preload the output register sample_reg. This is necessary +        // so the state machine can have a sample to output if +        // an EOB arrives while dropping samples / packets. If the +        // state machine did not have that sample to output when +        // an EOB arrives, then the EOB would be dropped because +        // you cannot send packets without a payload with RFNoC. +        S_O_FIRST_SAMP : begin +          length_cnt <= 2; +          if (keep_sample & s_axis_tvalid & s_axis_tready) begin +            sample_reg    <= s_axis_tdata; +            timestamp_reg <= s_axis_ttimestamp; +            // If this sample isn't the first sample in the input packet, +            // then the vita time does not correspond to this sample +            // and should be ignored. This situation can happen if the +            // packet length is not consistent. +            has_time_reg  <= s_axis_thas_time & first_samp; +            length_reg    <= s_axis_tlength[15:$clog2(WIDTH/8)]; +            eov_reg       <= s_axis_teov; +            // First sample is also EOB, so it will be immediately released +            if (s_axis_tlast & s_axis_teob) begin +              state_o <= S_O_LAST_SAMP_EOB; +            // Packet size is 1 sample +            end else if (s_axis_tlength[15:$clog2(WIDTH/8)] == 1) begin +              state_o <= S_O_LAST_SAMP; +            end else begin +              state_o <= S_O_OUTPUT; +            end +          end +        end +        // Output samples until either we need to either +        // set tlast or encounter an EOB +        S_O_OUTPUT : begin +          if (s_axis_tvalid & s_axis_tready) begin +            // Make EOV bit sticky +            eov_reg <= eov_reg | s_axis_teov; +            if (keep_sample) begin +              sample_reg <= s_axis_tdata; +              length_cnt <= length_cnt + 1; +            end +            if (s_axis_tlast & s_axis_teob) begin +              state_o <= S_O_LAST_SAMP_EOB; +            end else if (keep_sample) begin +              // Use length from input packet to set tlast +              if (length_cnt >= length_reg) begin +                state_o  <= S_O_LAST_SAMP; +              end +            end +          end +        end +        S_O_LAST_SAMP : begin +          length_cnt <= 2; +          if (s_axis_tvalid & s_axis_tready) begin +            if (keep_sample) begin +              sample_reg    <= s_axis_tdata; +              timestamp_reg <= s_axis_ttimestamp; +              has_time_reg  <= s_axis_thas_time & first_samp; +              length_reg    <= s_axis_tlength[15:$clog2(WIDTH/8)]; +              eov_reg       <= s_axis_teov; +            end else begin +              eov_reg       <= eov_reg | s_axis_teov; +            end +            if (s_axis_tlast & s_axis_teob) begin +              state_o <= S_O_LAST_SAMP_EOB; +            end else if (keep_sample) begin +              if (s_axis_tlength[15:$clog2(WIDTH/8)] > 1) begin +                state_o <= S_O_OUTPUT; +              end +            end +          end +        end +        S_O_LAST_SAMP_EOB : begin +          if (s_axis_tready) begin +            state_o <= S_O_FIRST_SAMP; +          end +        end +        default : state_o <= S_O_FIRST_SAMP; +      endcase +    end +  end + +  assign m_axis_tdata      = sample_reg; +  assign m_axis_tlast      = (state_o == S_O_LAST_SAMP) || (state_o == S_O_LAST_SAMP_EOB); +  assign m_axis_ttimestamp = timestamp_reg; +  assign m_axis_thas_time  = has_time_reg; +  assign m_axis_teov       = eov_reg; +  assign m_axis_teob       = (state_o == S_O_LAST_SAMP_EOB); + +  assign m_axis_tvalid = (state_o == S_O_FIRST_SAMP)      ? 1'b0 : +                         (state_o == S_O_OUTPUT)          ? keep_sample & s_axis_tvalid : +                         (state_o == S_O_LAST_SAMP)       ? keep_sample & s_axis_tvalid : +                         (state_o == S_O_LAST_SAMP_EOB)   ? 1'b1 : +                         1'b0; + +  assign s_axis_tready = (state_o == S_O_FIRST_SAMP)      ? 1'b1 : +                         (state_o == S_O_OUTPUT)          ? m_axis_tready : +                         (state_o == S_O_LAST_SAMP)       ? m_axis_tready : +                         (state_o == S_O_LAST_SAMP_EOB)   ? m_axis_tready : +                         1'b0; + +endmodule + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n_regs.vh new file mode 100644 index 000000000..e3de9a680 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_keep_one_in_n/rfnoc_keep_one_in_n_regs.vh @@ -0,0 +1,25 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module:  rfnoc_keep_one_in_n_regs +// +// Description:  Header file for rfnoc_block_keep_one_in_n_regs. +// + +// Offset in bytes between each ports's bank of registers. This is a hardcoded +// value in ctrlport_to_settings_bus.v put here for documentation purposes only. +localparam REG_BANK_OFFSET = 2**11; // 2048 + +// [WIDTH_N-1:0] : N, drop N-1 samples or packets +localparam REG_N     = 0; +localparam REG_N_LEN = WIDTH_N; + +// [0:0] : 0 = Sample Mode, 1 = Packet Mode +localparam REG_MODE     = 1; +localparam REG_MODE_LEN = 1; + +// [31:0] : Bit width of N, Read Only +localparam REG_WIDTH_N     = 2; +localparam REG_WIDTH_N_LEN = 32; diff --git a/host/include/uhd/rfnoc/blocks/keep_one_in_n.yml b/host/include/uhd/rfnoc/blocks/keep_one_in_n.yml new file mode 100644 index 000000000..1cef0d233 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/keep_one_in_n.yml @@ -0,0 +1,56 @@ +schema: rfnoc_modtool_args +module_name: keep_one_in_n +version: 1.0 +rfnoc_version: 1.0 +chdr_width: 64 +noc_id: 0x02460000 +makefile_srcs: "${fpga_lib_dir}/blocks/rfnoc_block_keep_one_in_n/Makefile.srcs" + +parameters: +  NUM_PORTS: 1 +  WIDTH_N: 24 + +clocks: +  - name: rfnoc_chdr +    freq: "[]" +  - name: rfnoc_ctrl +    freq: "[]" +  - name: ce +    freq: "[]" + +control: +  sw_iface: nocscript +  fpga_iface: ctrlport +  interface_direction: slave +  fifo_depth: 32 +  clk_domain: ce +  ctrlport: +    byte_mode: False +    timed: False +    has_status: False + +data: +  fpga_iface: axis_data +  clk_domain: ce +  inputs: +    in: +      num_ports: NUM_PORTS +      item_width: 32 +      nipc: 1 +      info_fifo_depth: 32 +      payload_fifo_depth: 32 +      format: int32 +      mdata_sig: ~ +  outputs: +    out: +      num_ports: NUM_PORTS +      item_width: 32 +      nipc: 1 +      info_fifo_depth: 32 +      payload_fifo_depth: MTU +      format: int32 +      mdata_sig: ~ + +registers: + +properties: | 
