diff options
7 files changed, 1062 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/Makefile new file mode 100644 index 000000000..cd8e2e45b --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/Makefile @@ -0,0 +1,46 @@ +# +# 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_logpwr_all_tb glbl +SIM_SRCS = \ +$(abspath rfnoc_block_logpwr_tb.sv) \ +$(abspath rfnoc_block_logpwr_all_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_logpwr/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/Makefile.srcs new file mode 100644 index 000000000..c050d7f03 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/Makefile.srcs @@ -0,0 +1,22 @@ +# +# 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_logpwr.v \ +noc_shell_logpwr.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/noc_shell_logpwr.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/noc_shell_logpwr.v new file mode 100644 index 000000000..e68f4e680 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/noc_shell_logpwr.v @@ -0,0 +1,256 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_logpwr +// +// Description: +// +//   This is a tool-generated NoC-shell for the logpwr 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_logpwr #( +  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 +  //--------------------- + +  // 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*16*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'h4C500000), +    .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 +  //--------------------------------------------------------------------------- + +  // No control path for this block +  assign s_rfnoc_ctrl_tready =  1'b1; +  assign m_rfnoc_ctrl_tdata  = 32'b0; +  assign m_rfnoc_ctrl_tlast  =  1'b0; +  assign m_rfnoc_ctrl_tvalid =  1'b0; + +  //--------------------------------------------------------------------------- +  //  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              (16), +      .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[(16*1)*i+:(16*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_logpwr + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr.v new file mode 100644 index 000000000..5486a6fef --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr.v @@ -0,0 +1,255 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_logpwr +// +// Description: +// +//   This block takes in signed 16-bit complex samples and computes an +//   estimate of 1024 * log2(i^2+q^2), and puts the result in the upper +//   16-bits of each 32-bit output item. The log is estimated using a lookup +//   table and random noise is added to reduce quantization effects. +// +// 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 Log-Power module instances to include. +//   RANDOM_MODE : Configures the random_mode for the logpwr block. +//                 [0] = Enable random LSBs on each input +//                 [1] = Enable random noise addition +// + +`default_nettype none + + +module rfnoc_block_logpwr #( +  parameter [9:0] THIS_PORTID = 10'd0, +  parameter       CHDR_W      = 64, +  parameter [5:0] MTU         = 10, +  parameter       NUM_PORTS   = 1, +  parameter       RANDOM_MODE = 2'b11 +) ( +  // 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 "../../core/rfnoc_chdr_utils.vh" + +  //--------------------------------------------------------------------------- +  // Signal Declarations +  //--------------------------------------------------------------------------- + +  // 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; +  reg  [NUM_PORTS-1:0]        m_in_context_tready; +  // Payload Stream to User Logic: out +  wire [NUM_PORTS*16*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 +  reg  [NUM_PORTS*CHDR_W-1:0] s_out_context_tdata; +  reg  [NUM_PORTS*4-1:0]      s_out_context_tuser; +  reg  [NUM_PORTS-1:0]        s_out_context_tlast; +  reg  [NUM_PORTS-1:0]        s_out_context_tvalid; +  wire [NUM_PORTS-1:0]        s_out_context_tready; + +  //--------------------------------------------------------------------------- +  // NoC Shell +  //--------------------------------------------------------------------------- + +  wire ce_rst; + +  noc_shell_logpwr #( +    .CHDR_W      (CHDR_W), +    .THIS_PORTID (THIS_PORTID), +    .MTU         (MTU), +    .NUM_PORTS   (NUM_PORTS) +  ) noc_shell_logpwr_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 +    //--------------------- + +    // 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) +  ); + + +  //--------------------------------------------------------------------------- +  // Context Handling +  //--------------------------------------------------------------------------- +  // +  // Output packets have half the payload size of input packets, so we need to +  // update the header length field as it passes through. +  // +  //--------------------------------------------------------------------------- + +  genvar port; + +  for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_context_ports + +    always @(*) begin : update_packet_length +      reg [CHDR_W-1:0] old_tdata; +      reg [CHDR_W-1:0] new_tdata; + +      old_tdata = m_in_context_tdata[CHDR_W*port +: CHDR_W]; + +      // Check if this context word contains the header +      if (m_in_context_tuser[4*port +: 4] == CONTEXT_FIELD_HDR ||  +          m_in_context_tuser[4*port +: 4] == CONTEXT_FIELD_HDR_TS +      ) begin : change_header +        // Update the lower 64-bits (the header word) with the new length +        reg [15:0] pyld_length; +        pyld_length     = chdr_calc_payload_length(CHDR_W, old_tdata) / 2; +        new_tdata       = old_tdata; +        new_tdata[63:0] = chdr_update_length(CHDR_W, old_tdata, pyld_length); +      end else begin : pass_through_header +        // Not a header word, so pass through unchanged +        new_tdata = old_tdata; +      end + +      s_out_context_tdata  [CHDR_W*port +: CHDR_W] = new_tdata; +      s_out_context_tuser  [     4*port +:      4] = m_in_context_tuser   [4*port +: 4]; +      s_out_context_tlast  [     1*port +:      1] = m_in_context_tlast   [1*port +: 1]; +      s_out_context_tvalid [     1*port +:      1] = m_in_context_tvalid  [1*port +: 1]; +      m_in_context_tready  [     1*port +:      1] = s_out_context_tready [1*port +: 1]; +    end // update_packet_length + +  end // gen_context_ports + + +  //--------------------------------------------------------------------------- +  // Log-Power +  //--------------------------------------------------------------------------- + +  for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_logpwr_ports + +    wire [15:0] s_out_payload_tdata_temp; + +    axi_logpwr #( +      .RANDOM_MODE (RANDOM_MODE) +    ) inst_axi_logpwr ( +      .clk      (ce_clk), +      .reset    (ce_rst), +      .i_tdata  (m_in_payload_tdata   [port*32 +: 32]), +      .i_tlast  (m_in_payload_tlast   [port]), +      .i_tvalid (m_in_payload_tvalid  [port]), +      .i_tready (m_in_payload_tready  [port]), +      .o_tdata  (s_out_payload_tdata_temp), +      .o_tlast  (s_out_payload_tlast  [port]), +      .o_tvalid (s_out_payload_tvalid [port]), +      .o_tready (s_out_payload_tready [port]) +    ); + +    // Convert the 16-bit unsigned result to a signed 16-bit result. This +    // makes the output an estimate of 1024 * log2(i^2+q^2) instead of the +    // 2048 * log2(i^2+q^2) returned by the block. +    assign s_out_payload_tdata[port*16 +: 16] = { +      1'b0, s_out_payload_tdata_temp[15:1] +    }; + +  end // gen_logpwr_ports + +endmodule // rfnoc_block_logpwr + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_all_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_all_tb.sv new file mode 100644 index 000000000..22ea78246 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_all_tb.sv @@ -0,0 +1,25 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_logpwr_all_tb +// +// Description: Top-level testbench for the logpwr RFNoC block. This +// instantiates rfnoc_block_logpwr_tb with different parameters to test +// multiple configurations. +// + +`default_nettype none + + +module rfnoc_block_logpwr_all_tb; + +  // Test multiple CHDR widths +  rfnoc_block_logpwr_tb #(.CHDR_W(64))  test_chdr_64  (); +  rfnoc_block_logpwr_tb #(.CHDR_W(128)) test_chdr_128 (); + +endmodule : rfnoc_block_logpwr_all_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_tb.sv new file mode 100644 index 000000000..2f62a5e47 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_logpwr/rfnoc_block_logpwr_tb.sv @@ -0,0 +1,402 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_logpwr_tb +// +// Description: Testbench for the logpwr RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_logpwr_tb #( +  parameter int CHDR_W = 64    // CHDR size in bits +); + +  `include "test_exec.svh" + +  import PkgTestExec::*; +  import PkgChdrUtils::*; +  import PkgRfnocBlockCtrlBfm::*; +  import PkgRfnocItemUtils::*; + +  //--------------------------------------------------------------------------- +  // Testbench Configuration +  //--------------------------------------------------------------------------- + +  localparam [31:0] NOC_ID          = 32'h4C500000; +  localparam [ 9:0] THIS_PORTID     = 10'h123; +  localparam int    MTU             = 10;    // Log2 of max transmission unit in CHDR words +  localparam int    NUM_PORTS       = 2; +  localparam int    RANDOM_MODE     = 2'b11; +  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             = 64;    // 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 + +  //--------------------------------------------------------------------------- +  // 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; + +  typedef ChdrPacket #(CHDR_W) ChdrPacket_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_logpwr #( +    .THIS_PORTID         (THIS_PORTID), +    .CHDR_W              (CHDR_W), +    .MTU                 (MTU), +    .NUM_PORTS           (NUM_PORTS), +    .RANDOM_MODE         (2'b00) +  ) 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 Tasks +  //--------------------------------------------------------------------------- + +  // Rand#(WIDTH)::rand_logic() returns a WIDTH-bit random number. We avoid +  // std::randomize() due to license requirements and limited tool support. +  class Rand #(WIDTH = 32); +    static function logic [WIDTH-1:0] rand_logic(); +      logic [WIDTH-1:0] result; +      int num_rand32 = (WIDTH + 31) / 32; +      for (int i = 0; i < num_rand32; i++) begin +        result = {result, $urandom()}; +      end +      return result; +    endfunction : rand_logic +  endclass : Rand + + +  // Compute the 16-bit unsigned log-power, modeled after the HDL +  function automatic bit unsigned [15:0] log_pwr(bit [31:0] sample); +    real       i, q, logpwr; +    bit [16:0] temp; +    bit [31:0] result; + +    // Compute log power +    i = shortint'(sample[31:16]); +    q = shortint'(sample[15: 0]); +    logpwr = $ln(i*i + q*q) / $ln(2); + +    // Shift it the same way the IP does +    result = int'(logpwr) * 2048; + +    return result; +  endfunction : log_pwr + + +  // Generate a random CHDR packet with the given number of samples +  function automatic ChdrPacket_t gen_rand_chdr_pkt( +    int max_samps = SPP, +    int max_mdata = 31 +  ); +    ChdrPacket_t     packet = new(); +    chdr_header_t    header; +    chdr_word_t      data[$]; +    chdr_word_t      mdata[$]; +    chdr_timestamp_t timestamp; +    int              num_samps; + +    // Start with a random header (important fields will be overwritten) +    header = Rand#($bits(header))::rand_logic(); +    // Randomly choose if we use a timestamp +    header.pkt_type = ($urandom() & 1) ? CHDR_DATA_NO_TS : CHDR_DATA_WITH_TS; +    // Random timestamp +    timestamp = Rand#(64)::rand_logic(); +    // Random metadata +    repeat ($urandom_range(0, max_mdata)); +      mdata.push_back(Rand#(CHDR_W)::rand_logic()); +    // Random payload +    num_samps = $urandom_range(1, max_samps); +    repeat (num_samps * ITEM_W / CHDR_W) +      data.push_back(Rand#(CHDR_W)::rand_logic()); +    // Round up to right number of CHDR words +    if (num_samps * ITEM_W % CHDR_W != 0) +      data.push_back(Rand#(CHDR_W)::rand_logic()); + +    // Build packet +    packet.write_raw(header, data, mdata, timestamp, num_samps * (ITEM_W/8)); + +    return packet; +  endfunction : gen_rand_chdr_pkt + + +  task automatic test_random(int port, int num_packets); +    mailbox #(ChdrPacket_t) packets = new(); + +    // Generate and enqueue packets for transmission +    for (int packet_count = 0; packet_count < num_packets; packet_count++) begin +      ChdrPacket_t packet; + +      packet = gen_rand_chdr_pkt(.max_samps(SPP), .max_mdata(3)); +      packets.put(packet); +      blk_ctrl.put_chdr(port, packet); +    end + +    // Receive and check the results +    for (int packet_count = 0; packet_count < num_packets; packet_count++) begin +      ChdrPacket_t  sent_packet, recv_packet; +      item_t        sent[$]; +      logic [15:0]  received[$], expected[$]; +      chdr_header_t expected_header; + +      // Retrieve the next packet that was sent and unpack the payload +      packets.get(sent_packet); +      sent = ChdrData#(CHDR_W, ITEM_W)::chdr_to_item( +        sent_packet.data, +        sent_packet.data_bytes() +      ); + +      // Calculate the expected result based on what was sent +      foreach(sent[i]) begin +        // Right-shift by one, since the actual block converts the unsigned +        // value to a signed value. +        expected[i] = log_pwr(sent[i]) >> 1; +      end + +      // Retrieve the packet that was received and unpack the payload +      blk_ctrl.get_chdr(port, recv_packet); +      received = ChdrData#(CHDR_W, 16)::chdr_to_item( +        recv_packet.data, +        recv_packet.data_bytes() +      ); + +      // Check the header, except the length, which should be different +      expected_header = sent_packet.header; +      expected_header.length = recv_packet.header.length; +      `ASSERT_ERROR(expected_header == recv_packet.header, +        "Header mismatch on received packet"); + +      // Check the timestamp +      if (sent_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin +        `ASSERT_ERROR(sent_packet.timestamp == recv_packet.timestamp, +          "Timestamp mismatch on received packet"); +      end + +      // Check the metadata +      `ASSERT_ERROR(sent_packet.metadata.size() == recv_packet.metadata.size(), +        "Metadata length mismatch on received packet"); +      foreach(sent_packet.metadata[i]) begin +        `ASSERT_ERROR(sent_packet.metadata[i] == recv_packet.metadata[i], +          "Metadata mismatch on received packet"); +      end + +      // Check that the packet data 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. Because of the random number +      // generator within the logpwr block, a wide range is acceptable. +      foreach(received[i]) begin +        int error; +        error = int'(received[i]) - int'(expected[i]); +        if (error < 0) error = -error; +        `ASSERT_ERROR( +          error <= 16'h0200, +          $sformatf("Unexpected result for packet %0d, sample %0d; Expected %X, received %X for input %X", +            packet_count, i, expected[i], received[i], sent[i]) +        ); +      end +    end +  endtask : test_random + + +  //--------------------------------------------------------------------------- +  // Main Test Process +  //--------------------------------------------------------------------------- + +  initial begin : tb_main +    int port; +    int num_packets; + +    // Initialize the test exec object for this testbench +    test.start_tb($sformatf("rfnoc_block_logpwr_tb (CHDR_W = %0d)", CHDR_W)); + +    // Don't start the clocks until after start_tb() returns. This ensures that +    // the clocks aren't toggling while other instances of this testbench are +    // running, which speeds up simulation time. +    rfnoc_chdr_clk_gen.start(); +    rfnoc_ctrl_clk_gen.start(); +    ce_clk_gen.start(); + +    // 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 +    //-------------------------------- + +    num_packets = 200; + +    // Test all ports +    for (port = 0; port < NUM_PORTS; port++) begin +      test.start_test($sformatf("Test random packets (port %0d)", port), 1ms); +      test_random(port, num_packets); +      test.end_test(); +    end + +    // Run remaining tests on a single port +    port = 0; + +    // Test with slow BFM slave to make sure back-pressure is working correctly. +    test.start_test("Test back pressure", 1ms); +    blk_ctrl.set_slave_stall_prob(port, 90); +    test_random(port, num_packets); +    blk_ctrl.set_slave_stall_prob(port, STALL_PROB); +    test.end_test(); + +    // Test with slow BFM master to make sure AXI-stream flow control is +    // working correctly. +    test.start_test("Test underflow", 1ms); +    blk_ctrl.set_master_stall_prob(port, 90); +    test_random(port, num_packets); +    blk_ctrl.set_master_stall_prob(port, STALL_PROB); +    test.end_test(); + +    //-------------------------------- +    // Finish Up +    //-------------------------------- + +    // Display final statistics and results, but don't call $finish, since we +    // don't want to kill other instances of this testbench that may be +    // running. +    test.end_tb(0); + +    // Kill the clocks to end this instance of the testbench +    rfnoc_chdr_clk_gen.kill(); +    rfnoc_ctrl_clk_gen.kill(); +    ce_clk_gen.kill(); +  end : tb_main + +endmodule : rfnoc_block_logpwr_tb + + +`default_nettype wire diff --git a/host/include/uhd/rfnoc/blocks/logpwr.yml b/host/include/uhd/rfnoc/blocks/logpwr.yml new file mode 100644 index 000000000..c1350c627 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/logpwr.yml @@ -0,0 +1,56 @@ +schema: rfnoc_modtool_args +module_name: logpwr +version: 1.0 +rfnoc_version: 1.0 +chdr_width: 64 +noc_id: 0x4C500000 +makefile_srcs: "${fpga_lib_dir}/blocks/rfnoc_block_logpwr/Makefile.srcs" + +parameters: +  NUM_PORTS: 1 +  RANDOM_MODE: 2'b11 + +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_pyld_ctxt +  clk_domain: ce +  inputs: +    in: +      num_ports: NUM_PORTS +      item_width: 32 +      nipc: 1 +      context_fifo_depth: 2 +      payload_fifo_depth: 32 +      format: sc16 +      mdata_sig: ~ +  outputs: +    out: +      num_ports: NUM_PORTS +      item_width: 16 +      nipc: 1 +      context_fifo_depth: 2 +      payload_fifo_depth: 32 +      format: s16 +      mdata_sig: ~ + +registers: + +properties:  | 
