diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc')
8 files changed, 1454 insertions, 0 deletions
| diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/Makefile new file mode 100644 index 000000000..f05034702 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/Makefile @@ -0,0 +1,45 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir. Note: +# UHD_FPGA_DIR must be passed into this Makefile. +BASE_DIR = ../../../../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_window_tb glbl +SIM_SRCS = \ +$(abspath rfnoc_block_window_tb.sv) \ +$(VIVADO_PATH)/data/verilog/src/glbl.v \ + +#------------------------------------------------- +# 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_window/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/Makefile.srcs new file mode 100644 index 000000000..6c71a7562 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/Makefile.srcs @@ -0,0 +1,25 @@ +# +# 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)))), \ +noc_shell_window.v \ +window.v \ +rfnoc_block_window_regs.vh \ +rfnoc_window_core.v \ +rfnoc_block_window.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/noc_shell_window.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/noc_shell_window.v new file mode 100644 index 000000000..24d37020d --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/noc_shell_window.v @@ -0,0 +1,309 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_window +// +// Description: +// +//   This is a tool-generated NoC-shell for the window 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 +//   NUM_PORTS   : Number of window module instances to connect +// + +`default_nettype none + + +module noc_shell_window #( +  parameter [9:0] THIS_PORTID = 10'd0, +  parameter       CHDR_W      = 64, +  parameter [5:0] MTU         = 10, +  parameter       NUM_PORTS   = 1 +) ( +  //--------------------- +  // 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 Payload Context Clock and Reset +  output wire               axis_data_clk, +  output wire               axis_data_rst, +  // Payload Stream to User Logic: in +  output wire [NUM_PORTS*32*1-1:0]   m_in_payload_tdata, +  output wire [NUM_PORTS*1-1:0]      m_in_payload_tkeep, +  output wire [NUM_PORTS-1:0]        m_in_payload_tlast, +  output wire [NUM_PORTS-1:0]        m_in_payload_tvalid, +  input  wire [NUM_PORTS-1:0]        m_in_payload_tready, +  // Context Stream to User Logic: in +  output wire [NUM_PORTS*CHDR_W-1:0] m_in_context_tdata, +  output wire [NUM_PORTS*4-1:0]      m_in_context_tuser, +  output wire [NUM_PORTS-1:0]        m_in_context_tlast, +  output wire [NUM_PORTS-1:0]        m_in_context_tvalid, +  input  wire [NUM_PORTS-1:0]        m_in_context_tready, +  // Payload Stream to User Logic: out +  input  wire [NUM_PORTS*32*1-1:0]   s_out_payload_tdata, +  input  wire [NUM_PORTS*1-1:0]      s_out_payload_tkeep, +  input  wire [NUM_PORTS-1:0]        s_out_payload_tlast, +  input  wire [NUM_PORTS-1:0]        s_out_payload_tvalid, +  output wire [NUM_PORTS-1:0]        s_out_payload_tready, +  // Context Stream to User Logic: out +  input  wire [NUM_PORTS*CHDR_W-1:0] s_out_context_tdata, +  input  wire [NUM_PORTS*4-1:0]      s_out_context_tuser, +  input  wire [NUM_PORTS-1:0]        s_out_context_tlast, +  input  wire [NUM_PORTS-1:0]        s_out_context_tvalid, +  output wire [NUM_PORTS-1:0]        s_out_context_tready +); + +  //--------------------------------------------------------------------------- +  //  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'hD0530000), +    .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_pyld_ctxt #( +      .CHDR_W              (CHDR_W), +      .ITEM_W              (32), +      .NIPC                (1), +      .SYNC_CLKS           (0), +      .CONTEXT_FIFO_SIZE   ($clog2(2)), +      .PAYLOAD_FIFO_SIZE   ($clog2(32)), +      .CONTEXT_PREFETCH_EN (1) +    ) chdr_to_axis_pyld_ctxt_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_payload_tdata  (m_in_payload_tdata[(32*1)*i+:(32*1)]), +      .m_axis_payload_tkeep  (m_in_payload_tkeep[1*i+:1]), +      .m_axis_payload_tlast  (m_in_payload_tlast[i]), +      .m_axis_payload_tvalid (m_in_payload_tvalid[i]), +      .m_axis_payload_tready (m_in_payload_tready[i]), +      .m_axis_context_tdata  (m_in_context_tdata[CHDR_W*i+:CHDR_W]), +      .m_axis_context_tuser  (m_in_context_tuser[4*i+:4]), +      .m_axis_context_tlast  (m_in_context_tlast[i]), +      .m_axis_context_tvalid (m_in_context_tvalid[i]), +      .m_axis_context_tready (m_in_context_tready[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_pyld_ctxt_to_chdr #( +      .CHDR_W              (CHDR_W), +      .ITEM_W              (32), +      .NIPC                (1), +      .SYNC_CLKS           (0), +      .CONTEXT_FIFO_SIZE   ($clog2(2)), +      .PAYLOAD_FIFO_SIZE   ($clog2(32)), +      .MTU                 (MTU), +      .CONTEXT_PREFETCH_EN (1) +    ) axis_pyld_ctxt_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_payload_tdata  (s_out_payload_tdata[(32*1)*i+:(32*1)]), +      .s_axis_payload_tkeep  (s_out_payload_tkeep[1*i+:1]), +      .s_axis_payload_tlast  (s_out_payload_tlast[i]), +      .s_axis_payload_tvalid (s_out_payload_tvalid[i]), +      .s_axis_payload_tready (s_out_payload_tready[i]), +      .s_axis_context_tdata  (s_out_context_tdata[CHDR_W*i+:CHDR_W]), +      .s_axis_context_tuser  (s_out_context_tuser[4*i+:4]), +      .s_axis_context_tlast  (s_out_context_tlast[i]), +      .s_axis_context_tvalid (s_out_context_tvalid[i]), +      .s_axis_context_tready (s_out_context_tready[i]), +      .framer_errors         (), +      .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_window + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window.v new file mode 100644 index 000000000..c25145450 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window.v @@ -0,0 +1,285 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_window +// +// Description: +// +//   Windowing module for use with FFT block. This block can be loaded with up +//   to MAX_WINDOW_SIZE coefficients. Each input packet is then multiplied by +//   the preloaded coefficients before being output. +// +//   The first sample of the first packet is multiplied by the first +//   coefficient, then the second sample is multiplied by the second +//   coefficient, and so on. After "window size" coefficients, processing +//   continues with the first coefficient again, and the process repeats. +// +//   See rfnoc_block_window_regs.vh for register descriptions. +// +// 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 +//                     CHDR words is 2**MTU). +//   NUM_PORTS       : Number of window instances to include. +//   MAX_WINDOW_SIZE : Maximum window size to support, in number of samples. +//                     This is ideally a power of 2. +// + +`default_nettype none + + +module rfnoc_block_window #( +  parameter [9:0] THIS_PORTID     = 10'd0, +  parameter       CHDR_W          = 64, +  parameter [5:0] MTU             = 10, +  parameter       NUM_PORTS       = 1, +  parameter       MAX_WINDOW_SIZE = 4096 +) ( +  // 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 [NUM_PORTS*CHDR_W-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 [NUM_PORTS*CHDR_W-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 +); + +  `include "rfnoc_block_window_regs.vh" + +  //--------------------------------------------------------------------------- +  // Signal Declarations +  //--------------------------------------------------------------------------- + +  // 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; +  // Payload Stream to User Logic: in +  wire [NUM_PORTS*32*1-1:0]   m_in_payload_tdata; +  wire [NUM_PORTS-1:0]        m_in_payload_tlast; +  wire [NUM_PORTS-1:0]        m_in_payload_tvalid; +  wire [NUM_PORTS-1:0]        m_in_payload_tready; +  // Context Stream to User Logic: in +  wire [NUM_PORTS*CHDR_W-1:0] m_in_context_tdata; +  wire [NUM_PORTS*4-1:0]      m_in_context_tuser; +  wire [NUM_PORTS-1:0]        m_in_context_tlast; +  wire [NUM_PORTS-1:0]        m_in_context_tvalid; +  wire [NUM_PORTS-1:0]        m_in_context_tready; +  // Payload Stream to User Logic: out +  wire [NUM_PORTS*32*1-1:0]   s_out_payload_tdata; +  wire [NUM_PORTS-1:0]        s_out_payload_tlast; +  wire [NUM_PORTS-1:0]        s_out_payload_tvalid; +  wire [NUM_PORTS-1:0]        s_out_payload_tready; +  // Context Stream to User Logic: out +  wire [NUM_PORTS*CHDR_W-1:0] s_out_context_tdata; +  wire [NUM_PORTS*4-1:0]      s_out_context_tuser; +  wire [NUM_PORTS-1:0]        s_out_context_tlast; +  wire [NUM_PORTS-1:0]        s_out_context_tvalid; +  wire [NUM_PORTS-1:0]        s_out_context_tready; + +  //--------------------------------------------------------------------------- +  // NoC Shell +  //--------------------------------------------------------------------------- + +  wire ce_rst; + +  noc_shell_window #( +    .CHDR_W      (CHDR_W), +    .THIS_PORTID (THIS_PORTID), +    .MTU         (MTU), +    .NUM_PORTS   (NUM_PORTS) +  ) noc_shell_window_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_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        (), +    // Payload Stream to User Logic: in +    .m_in_payload_tdata   (m_in_payload_tdata), +    .m_in_payload_tkeep   (), +    .m_in_payload_tlast   (m_in_payload_tlast), +    .m_in_payload_tvalid  (m_in_payload_tvalid), +    .m_in_payload_tready  (m_in_payload_tready), +    // Context Stream to User Logic: in +    .m_in_context_tdata   (m_in_context_tdata), +    .m_in_context_tuser   (m_in_context_tuser), +    .m_in_context_tlast   (m_in_context_tlast), +    .m_in_context_tvalid  (m_in_context_tvalid), +    .m_in_context_tready  (m_in_context_tready), +    // Payload Stream from User Logic: out +    .s_out_payload_tdata  (s_out_payload_tdata), +    .s_out_payload_tkeep  ({NUM_PORTS{1'b1}}), +    .s_out_payload_tlast  (s_out_payload_tlast), +    .s_out_payload_tvalid (s_out_payload_tvalid), +    .s_out_payload_tready (s_out_payload_tready), +    // Context Stream from User Logic: out +    .s_out_context_tdata  (s_out_context_tdata), +    .s_out_context_tuser  (s_out_context_tuser), +    .s_out_context_tlast  (s_out_context_tlast), +    .s_out_context_tvalid (s_out_context_tvalid), +    .s_out_context_tready (s_out_context_tready) +  ); + +  // Input packets have the same properties as output packets, so pass through +  // the header information unchanged. +  assign s_out_context_tdata  = m_in_context_tdata; +  assign s_out_context_tuser  = m_in_context_tuser; +  assign s_out_context_tlast  = m_in_context_tlast; +  assign s_out_context_tvalid = m_in_context_tvalid; +  assign m_in_context_tready  = s_out_context_tready; + + +  //--------------------------------------------------------------------------- +  // CtrlPort Splitter +  //--------------------------------------------------------------------------- + +  // Create a CtrlPort bus for each port instance + +  wire [ 1*NUM_PORTS-1:0] ctrlport_req_wr; +  wire [ 1*NUM_PORTS-1:0] ctrlport_req_rd; +  wire [20*NUM_PORTS-1:0] ctrlport_req_addr; +  wire [32*NUM_PORTS-1:0] ctrlport_req_data; +  wire [ 1*NUM_PORTS-1:0] ctrlport_resp_ack; +  wire [32*NUM_PORTS-1:0] ctrlport_resp_data; + +  ctrlport_decoder #( +    .NUM_SLAVES   (NUM_PORTS), +    .BASE_ADDR    (0), +    .SLAVE_ADDR_W (WINDOW_ADDR_W) +  ) ctrlport_decoder_i ( +    .ctrlport_clk            (ce_clk), +    .ctrlport_rst            (ce_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_byte_en  (4'hF), +    .s_ctrlport_req_has_time (1'b0), +    .s_ctrlport_req_time     (64'b0), +    .s_ctrlport_resp_ack     (m_ctrlport_resp_ack), +    .s_ctrlport_resp_status  (), +    .s_ctrlport_resp_data    (m_ctrlport_resp_data), +    .m_ctrlport_req_wr       (ctrlport_req_wr), +    .m_ctrlport_req_rd       (ctrlport_req_rd), +    .m_ctrlport_req_addr     (ctrlport_req_addr), +    .m_ctrlport_req_data     (ctrlport_req_data), +    .m_ctrlport_req_byte_en  (), +    .m_ctrlport_req_has_time (), +    .m_ctrlport_req_time     (), +    .m_ctrlport_resp_ack     (ctrlport_resp_ack), +    .m_ctrlport_resp_status  ({NUM_PORTS{2'b0}}), +    .m_ctrlport_resp_data    (ctrlport_resp_data) +  ); + +  //--------------------------------------------------------------------------- +  // Port Instances +  //--------------------------------------------------------------------------- + +  genvar port; +  generate +    for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_ports + +      rfnoc_window_core #( +        .MAX_WINDOW_SIZE (MAX_WINDOW_SIZE), +        .COEFF_WIDTH     (REG_LOAD_COEFF_LEN) +      ) rfnoc_window_core_i ( +        .clk                  (ce_clk), +        .rst                  (ce_rst), +        .s_ctrlport_req_wr    (ctrlport_req_wr      [port* 1 +:  1]), +        .s_ctrlport_req_rd    (ctrlport_req_rd      [port* 1 +:  1]), +        .s_ctrlport_req_addr  (ctrlport_req_addr    [port*20 +: 20]), +        .s_ctrlport_req_data  (ctrlport_req_data    [port*32 +: 32]), +        .s_ctrlport_resp_ack  (ctrlport_resp_ack    [port* 1 +:  1]), +        .s_ctrlport_resp_data (ctrlport_resp_data   [port*32 +: 32]), +        .s_tdata              (m_in_payload_tdata   [port*32 +: 32]), +        .s_tlast              (m_in_payload_tlast   [port* 1 +:  1]), +        .s_tvalid             (m_in_payload_tvalid  [port* 1 +:  1]), +        .s_tready             (m_in_payload_tready  [port* 1 +:  1]), +        .m_tdata              (s_out_payload_tdata  [port*32 +: 32]), +        .m_tlast              (s_out_payload_tlast  [port* 1 +:  1]), +        .m_tvalid             (s_out_payload_tvalid [port* 1 +:  1]), +        .m_tready             (s_out_payload_tready [port* 1 +:  1]) +      ); + +    end +  endgenerate + +endmodule + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_regs.vh new file mode 100644 index 000000000..f847a4b25 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_regs.vh @@ -0,0 +1,59 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module:  rfnoc_block_window_regs (Header) +// +// Description: RFNoC Window block register descriptions +// + +// Address space size, per window module. That is, each window module's +// address space is separated in the CtrlPort address space by 2^WINDOW_ADDR_W +// bytes. +localparam WINDOW_ADDR_W = 4; + + + +// REG_WINDOW_SIZE (R/W) +// +// Controls the current window size, in number of samples. +// +localparam REG_WINDOW_SIZE = 'h0; + + +// REG_WINDOW_MAX_SIZE (R) +// +// Reports the maximum supported window size, in number of samples. +// +localparam REG_WINDOW_MAX_SIZE = 'h4; + + +// REG_LOAD_COEFF (W) +// +// Register for inputting the next coefficient to be loaded into the window +// module. To load a new set of coefficients, write REG_WINDOW_SIZE-1 +// coefficients to this register, then write the last coefficient to +// REG_LOAD_COEFF_LAST. +// +// [31:16] : Reserved +// [15: 0] : The next coefficient to be loaded +// +localparam REG_LOAD_COEFF = 'h8; +// +localparam REG_LOAD_COEFF_LEN = 16; + + +// REG_LOAD_COEFF_LAST (W) +// +// Register for inputting the last coefficient to be loaded into the window +// module. To load a new set of filter coefficients, write REG_WINDOW_SIZE-1 +// coefficients to REG_LOAD_COEFF, then write the last coefficient to this +// register. +// +// [31:16] : Reserved +// [15: 0] : The last coefficient to be loaded +// +localparam REG_LOAD_COEFF_LAST = 'hC; +// +// The length of the last coefficient is the same as REG_LOAD_COEFF_LEN. diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_tb.sv new file mode 100644 index 000000000..bc6427962 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_block_window_tb.sv @@ -0,0 +1,424 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_window_tb +// +// Description: Testbench for the Window RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_window_tb; + +  `include "test_exec.svh" + +  import PkgTestExec::*; +  import PkgChdrUtils::*; +  import PkgRfnocBlockCtrlBfm::*; +  import PkgRfnocItemUtils::*; + +  `include "rfnoc_block_window_regs.vh" + +  //--------------------------------------------------------------------------- +  // Testbench Configuration +  //--------------------------------------------------------------------------- + +  localparam [31:0] NOC_ID          = 32'hD0530000; +  localparam [ 9:0] THIS_PORTID     = 10'h123; +  localparam int    CHDR_W          = 64;    // CHDR size in bits +  localparam int    MTU             = 10;    // Log2 of max transmission unit in CHDR words +  localparam int    NUM_PORTS       = 2; +  localparam int    NUM_PORTS_I     = NUM_PORTS; +  localparam int    NUM_PORTS_O     = NUM_PORTS; +  localparam int    ITEM_W          = 32;    // Sample size in bits +  localparam int    SPP             = 256;   // Samples per packet +  localparam int    PKT_SIZE_BYTES  = SPP * (ITEM_W/8); +  localparam int    STALL_PROB      = 25;    // Default BFM stall probability +  localparam real   CHDR_CLK_PER    = 5.0;   // 200 MHz +  localparam real   CTRL_CLK_PER    = 8.0;   // 125 MHz +  localparam real   CE_CLK_PER      = 4.0;   // 250 MHz +  localparam int    MAX_WINDOW_SIZE = 128; + +  //--------------------------------------------------------------------------- +  // 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_I] (rfnoc_chdr_clk, 1'b0); +  AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS_O] (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; +  typedef ChdrData #(CHDR_W, ITEM_W)::item_queue_t item_queue_t; + +  // Connect block controller to BFMs +  for (genvar i = 0; i < NUM_PORTS_I; 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_O; 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_I-1:0] s_rfnoc_chdr_tdata; +  logic [       NUM_PORTS_I-1:0] s_rfnoc_chdr_tlast; +  logic [       NUM_PORTS_I-1:0] s_rfnoc_chdr_tvalid; +  logic [       NUM_PORTS_I-1:0] s_rfnoc_chdr_tready; + +  // DUT Master (Output) Port Signals +  logic [CHDR_W*NUM_PORTS_O-1:0] m_rfnoc_chdr_tdata; +  logic [       NUM_PORTS_O-1:0] m_rfnoc_chdr_tlast; +  logic [       NUM_PORTS_O-1:0] m_rfnoc_chdr_tvalid; +  logic [       NUM_PORTS_O-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; 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_O; 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_window #( +    .THIS_PORTID         (THIS_PORTID), +    .CHDR_W              (CHDR_W), +    .MTU                 (MTU), +    .NUM_PORTS           (NUM_PORTS), +    .MAX_WINDOW_SIZE     (MAX_WINDOW_SIZE) +  ) 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) +  ); + + +  //--------------------------------------------------------------------------- +  // Register Access +  //--------------------------------------------------------------------------- + +  task automatic write_reg(int port, bit [19:0] addr, bit [31:0] value); +    blk_ctrl.reg_write(port * (2**WINDOW_ADDR_W) + addr, value); +  endtask : write_reg + +  task automatic read_reg(int port, bit [19:0] addr, output logic [31:0] value); +    blk_ctrl.reg_read(port * (2**WINDOW_ADDR_W) + addr, value); +  endtask : read_reg + + +  //--------------------------------------------------------------------------- +  // Test Procedures +  //--------------------------------------------------------------------------- + +  // Model the behavior of the mult_rc module. As configured for our DUT, it +  // treats each value as a 16-bit signed fixed point value with 15 fractional +  // bits. +  function bit [31:0] mult_rc(bit signed [15:0] real_data, bit signed [31:0] complex_data); +    bit [47:0] temp; +    bit [31:0] result; +    temp = (real_data * signed'(complex_data[31:16])) >> 15; +    result[31:16] = temp; +    temp = (real_data * signed'(complex_data[15: 0])) >> 15; +    result[15: 0] = temp; +    return result; +  endfunction : mult_rc + + +  // Test read-only and read/write registers +  task automatic test_registers(int port); +    int unsigned value, expected; + +    test.start_test($sformatf("Test registers, port %0d", port), 10us); + +    read_reg(port, REG_WINDOW_MAX_SIZE, value); +    `ASSERT_ERROR(value == MAX_WINDOW_SIZE, "REG_WINDOW_MAX_SIZE reports incorrect value"); + +    expected = 2**$clog2(MAX_WINDOW_SIZE+1)-1;  // Max value (all ones) +    write_reg(port, REG_WINDOW_SIZE, expected); +    read_reg(port, REG_WINDOW_SIZE, value); +    `ASSERT_ERROR(value == expected, "REG_WINDOW_SIZE did not update"); + +    expected = 0; +    write_reg(port, REG_WINDOW_SIZE, expected); +    read_reg(port, REG_WINDOW_SIZE, value); +    `ASSERT_ERROR(value == expected, "REG_WINDOW_SIZE did not update"); + +    test.end_test(); +  endtask : test_registers + + +  // Run a single test on the block using random coefficients and packet data. +  // +  //  port        : Port number of the block to test +  //  window_size : Size of the window to use (number of coefficients) +  //  packet_len  : Packet size to input and read out, in samples +  //  num_packets : Number of packets to input and check on the output +  // +  task automatic test_window(int port, int window_size, int packet_len, int num_packets); +    item_t coefficients[$]; +    int coeff_index; +    mailbox #(item_queue_t) packets = new(); + +    $display("Testing: window_size=%04d, packet_len=%04d, num_packets=%04d", +      window_size, packet_len, num_packets); + +    write_reg(port, REG_WINDOW_SIZE, window_size); + +    // Generate random coefficients  +    for (int count=0; count < window_size; count++) begin +      item_t coeff; + +      coeff = $urandom(); +      coefficients.push_back(coeff); +      // Load each coefficient, but write the last coefficient to the "last" +      // register. +      if (count == window_size-1) +        write_reg(port, REG_LOAD_COEFF_LAST, coeff); +      else +        write_reg(port, REG_LOAD_COEFF, coeff); +    end + +    // Updating the coefficients should restart the address counter +    coeff_index = 0; + +    // Generate and enqueue packets for transmission +    for (int packet_count = 0; packet_count < num_packets; packet_count++) begin +      item_t payload[$]; + +      payload = {}; +      for (int sample_count = 0; sample_count < packet_len; sample_count++) begin +        payload.push_back($urandom()); +      end +      packets.put(payload); +      blk_ctrl.send_items(port, payload); +    end + +    // Receive and check the results +    for (int packet_count = 0; packet_count < num_packets; packet_count++) begin +      item_t sent[$], received[$], expected[$]; +      packets.get(sent); + +      // Calculate the expected result +      foreach(sent[i]) begin +        expected[i] = mult_rc(coefficients[coeff_index], sent[i]); +        if (coeff_index >= window_size-1) coeff_index = 0; +        else coeff_index++; +      end + +      // Retrieve the resulting packet +      blk_ctrl.recv_items(port, received); + +      // Check that the packet length matches what was input +      `ASSERT_ERROR( +        expected.size() == received.size(),  +        $sformatf("For packet %0d, received length was incorrect", packet_count) +      ); + +      // Check that the payload is correct +      foreach(received[i]) begin +        `ASSERT_ERROR( +          received[i] == expected[i],  +          $sformatf("Unexpected result for packet %0d, sample %0d", packet_count, i) +        ); +      end +    end + +  endtask : test_window + + +  // Run random tests to look for anything unexpected +  // +  //   port            : Port number of the block to test +  //   num_iterations  : Number of times to repeat a random test +  //   max_window_size : Maximum window size to use (number of coefficients) +  //   max_packet_len  : Maximum packet length to use (in samples) +  // +  task automatic test_random( +    int port, +    int num_iterations  = 100, +    int max_window_size = MAX_WINDOW_SIZE, +    int max_packet_len  = SPP +  ); +    test.start_test("Test random", 10ms); + +    // Repeat the test num_iterations times +    for (int iteration = 0; iteration < num_iterations; iteration++) begin +      int window_size; +      int packet_len; +      int num_packets; +      int packets_per_window; + +      // Choose random attributes for this test +      window_size = $urandom_range(1, MAX_WINDOW_SIZE); +      packet_len  = $urandom_range(1, SPP); + +      // Send up to two windows worth of packets +      packets_per_window = $ceil(real'(window_size) / real'(packet_len)); +      num_packets = $urandom_range(1, 2*packets_per_window); + +      // Run the test +      test_window(port, window_size, packet_len, num_packets); +    end + +    test.end_test(); +  endtask : test_random + + +  // Run a few directed tests to check corner cases +  task automatic test_directed(int port); +    test.start_test("Test directed", 100us); +    test_window(port, 16,   1, 33);    // Min packet size +    test_window(port, 16,   8,  5); +    test_window(port, 16,  16,  3);    // Packet size equals window +    test_window(port, 16,  31,  3);    // Packet size larger than window +    test_window(port, 16, SPP,  3);    // Max packet size +    if (MAX_WINDOW_SIZE <= SPP) begin  // Max window size and packet size +      test_window(port, MAX_WINDOW_SIZE, MAX_WINDOW_SIZE, 2); +      test_window(port, MAX_WINDOW_SIZE, SPP, 2); +    end else begin +      test_window(port, MAX_WINDOW_SIZE, SPP, 2); +    end +    test.end_test(); +  endtask : test_directed + +  // Test with slow BFM slave to make sure back-pressure is working correctly. +  task automatic test_back_pressure(int port); +    test.start_test("Test back pressure", 1ms); +    blk_ctrl.set_slave_stall_prob(port, 90); +    test_window(port, 16, SPP, 20); +    blk_ctrl.set_slave_stall_prob(port, STALL_PROB); +    test.end_test(); +  endtask : test_back_pressure + + +  // Test with slow BFM master to make sure AXI-stream flow control is working +  // correctly. +  task automatic test_underflow(int port); +    test.start_test("Test underflow", 1ms); +    blk_ctrl.set_master_stall_prob(port, 90); +    test_window(port, 16, SPP, 20); +    blk_ctrl.set_master_stall_prob(port, STALL_PROB); +    test.end_test(); +  endtask : test_underflow + + +  //--------------------------------------------------------------------------- +  // Main Test Process +  //--------------------------------------------------------------------------- + +  initial begin : tb_main +    int port; + +    // Initialize the test exec object for this testbench +    test.start_tb("rfnoc_block_window_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_I, "Incorrect NUM_DATA_I Value"); +    `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS_O, "Incorrect NUM_DATA_O Value"); +    `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value"); +    test.end_test(); + +    //-------------------------------- +    // Test Sequences +    //-------------------------------- + +    // Run register and directed tests on all ports +    for (port = 0; port < NUM_PORTS; port++) begin +      test_registers(port); +      test_directed(port); +    end +    // Run remaining tests on just one port +    port = NUM_PORTS-1; +    test_random(port, 100); +    test_back_pressure(port); +    test_underflow(port); + +    //-------------------------------- +    // Finish Up +    //-------------------------------- + +    // Display final statistics and results +    test.end_tb(); +  end : tb_main + +endmodule : rfnoc_block_window_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_window_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_window_core.v new file mode 100644 index 000000000..e4591a277 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/rfnoc_window_core.v @@ -0,0 +1,141 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_window_core +// +// Description: +// +//   This module contains the registers and window module for a single RFNoC +//   window module instance. +// +// Parameters: +// +//   MAX_WINDOW_SIZE : Maximum window size to support, in number of samples. +//   COEFF_WIDTH     : Width of the coefficients to use. +// + +`default_nettype none + + +module rfnoc_window_core #( +  parameter MAX_WINDOW_SIZE = 4096, +  parameter COEFF_WIDTH     = 16 +) ( +  input wire clk, +  input wire rst, + +  // CtrlPort Slave +  input  wire        s_ctrlport_req_wr, +  input  wire        s_ctrlport_req_rd, +  input  wire [19:0] s_ctrlport_req_addr, +  input  wire [31:0] s_ctrlport_req_data, +  output reg         s_ctrlport_resp_ack, +  output reg  [31:0] s_ctrlport_resp_data, + +  // Input data stream +  input  wire [31:0] s_tdata, +  input  wire        s_tlast, +  input  wire        s_tvalid, +  output wire        s_tready, + +  // Output data stream +  output wire [31:0] m_tdata, +  output wire        m_tlast, +  output wire        m_tvalid, +  input  wire        m_tready +); + +  `include "rfnoc_block_window_regs.vh" + +  // The maximum window size is 2**WINDOW_SIZE +  localparam WINDOW_SIZE = $clog2(MAX_WINDOW_SIZE); + +  // WINDOW_SIZE_W is the number of bits needed to represent 2**WINDOW_SIZE. +  localparam WINDOW_SIZE_W = $clog2(MAX_WINDOW_SIZE+1); + + +  //----------------------------------------------------------------------- +  // Registers +  //----------------------------------------------------------------------- + +  reg [WINDOW_SIZE_W-1:0] window_size; +  reg [  COEFF_WIDTH-1:0] m_axis_coeff_tdata; +  reg                     m_axis_coeff_tlast; +  reg                     m_axis_coeff_tvalid; + +  always @(posedge clk) begin +    if (rst) begin +      window_size         <= 'bX; +      m_axis_coeff_tdata  <= 'bX; +      m_axis_coeff_tlast  <= 0; +      m_axis_coeff_tvalid <= 0; +    end else begin +      // Default assignments +      s_ctrlport_resp_ack  <= 0; +      s_ctrlport_resp_data <= 0; +      m_axis_coeff_tlast   <= 0; +      m_axis_coeff_tvalid  <= 0; + +      // Handle register writes +      if (s_ctrlport_req_wr) begin +        s_ctrlport_resp_ack <= 1; +        case (s_ctrlport_req_addr) +          REG_WINDOW_SIZE : +            window_size <= s_ctrlport_req_data[WINDOW_SIZE_W-1:0]; +          REG_LOAD_COEFF : begin +            m_axis_coeff_tdata  <= s_ctrlport_req_data[COEFF_WIDTH-1:0]; +            m_axis_coeff_tlast  <= 0; +            m_axis_coeff_tvalid <= 1; +          end +          REG_LOAD_COEFF_LAST : begin +            m_axis_coeff_tdata  <= s_ctrlport_req_data[COEFF_WIDTH-1:0]; +            m_axis_coeff_tlast  <= 1; +            m_axis_coeff_tvalid <= 1; +          end +        endcase +      end + +      // Handle register reads +      if (s_ctrlport_req_rd) begin +        s_ctrlport_resp_ack <= 1; +        case (s_ctrlport_req_addr) +          REG_WINDOW_SIZE : +            s_ctrlport_resp_data[WINDOW_SIZE_W-1:0] <= window_size; +          REG_WINDOW_MAX_SIZE : +            s_ctrlport_resp_data[WINDOW_SIZE_W-1:0] <= MAX_WINDOW_SIZE; +        endcase +      end +    end +  end + +  //----------------------------------------------------------------------- +  // Window Instance +  //----------------------------------------------------------------------- + +  window #( +    .WINDOW_SIZE (WINDOW_SIZE), +    .COEFF_WIDTH (COEFF_WIDTH) +  ) window_i ( +    .clk                 (clk), +    .rst                 (rst), +    .window_size         (window_size), +    .m_axis_coeff_tdata  (m_axis_coeff_tdata), +    .m_axis_coeff_tlast  (m_axis_coeff_tlast), +    .m_axis_coeff_tvalid (m_axis_coeff_tvalid), +    .m_axis_coeff_tready (),         // Window block is always ready +    .i_tdata             (s_tdata), +    .i_tlast             (s_tlast), +    .i_tvalid            (s_tvalid), +    .i_tready            (s_tready), +    .o_tdata             (m_tdata), +    .o_tlast             (m_tlast), +    .o_tvalid            (m_tvalid), +    .o_tready            (m_tready) +  ); + +endmodule + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/window.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/window.v new file mode 100644 index 000000000..d7791c45e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_window/window.v @@ -0,0 +1,166 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Description:  +// +//   Windowing module. Multiplies each input packet by the coefficients loaded +//   into an internal memory. +// +// Parameters: +// +//   WINDOW_SIZE : Sets the maximum window size to 2^WINDOW_SIZE samples. +//   COEFF_WIDTH : The bit width of coefficients. +// + +module window #( +  parameter WINDOW_SIZE = 10, +  parameter COEFF_WIDTH = 16 +) ( +  input wire clk, +  input wire rst, + +  // Window size to use +  input wire [WINDOW_SIZE:0] window_size, + +  // Coefficient input +  input  wire [COEFF_WIDTH-1:0] m_axis_coeff_tdata, +  input  wire                   m_axis_coeff_tlast, +  input  wire                   m_axis_coeff_tvalid, +  output wire                   m_axis_coeff_tready, + +  // Input data stream +  input  wire [31:0] i_tdata, +  input  wire        i_tlast, +  input  wire        i_tvalid, +  output wire        i_tready, + +  // Output data stream +  output wire [31:0] o_tdata, +  output wire        o_tlast, +  output wire        o_tvalid, +  input  wire        o_tready +); + +  // Sample data stream (from i_tdata) +  wire [           31:0] stream_tdata; +  wire                   stream_tlast; +  wire                   stream_tvalid; +  wire                   stream_tready; +  // RAM output data stream (saved coefficients) +  wire [           15:0] ram_tdata; +  wire                   ram_tvalid; +  wire                   ram_tready; +  // Counter data stream (RAM address for coefficient lookup) +  wire [WINDOW_SIZE-1:0] count_tdata; +  wire                   count_tvalid; +  wire                   count_tready; +  // Flow control stream. This keeps the counter stream in sync with the +  // sample data stream. +  wire                   flow_tvalid; +  wire                   flow_tready; + +  wire clear_counter; + +  // Restart the address counter whenever we load a new set of coefficients. +  assign clear_counter = m_axis_coeff_tlast &  +                         m_axis_coeff_tvalid & +                         m_axis_coeff_tready; + +  // Split the incoming data stream into two +  split_stream_fifo #( +    .WIDTH       (32), +    .ACTIVE_MASK (4'b0011) +  ) split_stream_fifo_i ( +    .clk       (clk), +    .reset     (rst), +    .clear     (1'b0), +    .i_tdata   (i_tdata), +    .i_tlast   (i_tlast), +    .i_tvalid  (i_tvalid), +    .i_tready  (i_tready), +    .o0_tdata  (stream_tdata), +    .o0_tlast  (stream_tlast), +    .o0_tvalid (stream_tvalid), +    .o0_tready (stream_tready), +    .o1_tdata  (), +    .o1_tlast  (), +    .o1_tvalid (flow_tvalid), +    .o1_tready (flow_tready), +    .o2_tdata  (), +    .o2_tlast  (), +    .o2_tvalid (), +    .o2_tready (1'b0), +    .o3_tdata  (), +    .o3_tlast  (), +    .o3_tvalid (), +    .o3_tready (1'b0) +  ); + +  // Address generation +  counter #( +    .WIDTH (WINDOW_SIZE) +  ) counter_i ( +    .clk      (clk), +    .reset    (rst), +    .clear    (clear_counter), +    .max      (window_size), +    .i_tlast  (1'b0), +    .i_tvalid (flow_tvalid), +    .i_tready (flow_tready), +    .o_tdata  (count_tdata), +    .o_tlast  (), +    .o_tvalid (count_tvalid), +    .o_tready (count_tready) +  ); + +  // RAM to store window coefficients +  ram_to_fifo #( +    .DWIDTH (COEFF_WIDTH), +    .AWIDTH (WINDOW_SIZE) +  ) ram_to_fifo_i ( +    .clk           (clk), +    .reset         (rst), +    .clear         (1'b0), +    .config_tdata  (m_axis_coeff_tdata), +    .config_tlast  (m_axis_coeff_tlast), +    .config_tvalid (m_axis_coeff_tvalid), +    .config_tready (m_axis_coeff_tready), +    .i_tdata       (count_tdata), +    .i_tlast       (1'b0), +    .i_tvalid      (count_tvalid), +    .i_tready      (count_tready), +    .o_tdata       (ram_tdata), +    .o_tlast       (), +    .o_tvalid      (ram_tvalid), +    .o_tready      (ram_tready) +  ); + +  // Real by complex multiplier. This multiplier is configured for signed +  // fixed point with 15 fractional bits. For N fractional bits (a right-shift +  // by N in the multiplication result), set: +  // DROP_TOP_P = (WIDTH_REAL + WIDTH_CPLX - WIDTH_P + 5) - N +  mult_rc #( +    .WIDTH_REAL (COEFF_WIDTH), +    .WIDTH_CPLX (16), +    .WIDTH_P    (16), +    .DROP_TOP_P (6) +  ) mult_rc_i ( +    .clk         (clk), +    .reset       (rst), +    .real_tdata  (ram_tdata), +    .real_tlast  (1'b0), +    .real_tvalid (ram_tvalid), +    .real_tready (ram_tready), +    .cplx_tdata  (stream_tdata), +    .cplx_tlast  (stream_tlast), +    .cplx_tvalid (stream_tvalid), +    .cplx_tready (stream_tready), +    .p_tdata     (o_tdata), +    .p_tlast     (o_tlast), +    .p_tvalid    (o_tvalid), +    .p_tready    (o_tready) +  ); + +endmodule | 
