diff options
| author | Wade Fife <wade.fife@ettus.com> | 2020-04-14 16:23:59 -0500 | 
|---|---|---|
| committer | Wade Fife <wade.fife@ettus.com> | 2020-04-14 16:37:43 -0500 | 
| commit | d386c750074f6da4ab86038e2c30a3fe6e0f9d47 (patch) | |
| tree | e4258b2744e24bf6e829910e66c93a74b8d7603a /fpga/usrp3/lib | |
| parent | a8c4f021277cf3b0a0897fa9da0252541512f3a6 (diff) | |
| download | uhd-d386c750074f6da4ab86038e2c30a3fe6e0f9d47.tar.gz uhd-d386c750074f6da4ab86038e2c30a3fe6e0f9d47.tar.bz2 uhd-d386c750074f6da4ab86038e2c30a3fe6e0f9d47.zip | |
rfnoc: Add RFNoC fosphor block
Diffstat (limited to 'fpga/usrp3/lib')
7 files changed, 1585 insertions, 1 deletions
| diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/Makefile new file mode 100644 index 000000000..58a2cde0e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/Makefile @@ -0,0 +1,44 @@ +# +# 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 +BASE_DIR = $(abspath ../../../../top) +# Include viv_sim_preamble 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_fosphor_tb glbl +SIM_SRCS = \ +$(abspath rfnoc_block_fosphor_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_fosphor/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/Makefile.srcs new file mode 100644 index 000000000..620e993e2 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/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_fosphor.v \ +noc_shell_fosphor.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/noc_shell_fosphor.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/noc_shell_fosphor.v new file mode 100644 index 000000000..dd1845b40 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/noc_shell_fosphor.v @@ -0,0 +1,344 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_fosphor +// +// Description:  +// +//   This is a tool-generated NoC-shell for the fosphor 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_fosphor #( +  parameter [9:0] THIS_PORTID     = 10'd0, +  parameter       CHDR_W          = 64, +  parameter [5:0] MTU             = 10 +) ( +  //--------------------- +  // Framework Interface +  //--------------------- + +  // RFNoC Framework Clocks +  input  wire rfnoc_chdr_clk, +  input  wire rfnoc_ctrl_clk, +  input  wire ce_clk, + +  // NoC Shell Generated Resets +  output wire rfnoc_chdr_rst, +  output wire rfnoc_ctrl_rst, +  output wire ce_rst, + +  // RFNoC Backend Interface +  input  wire [511:0]          rfnoc_core_config, +  output wire [511:0]          rfnoc_core_status, + +  // AXIS-CHDR Input Ports (from framework) +  input  wire [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata, +  input  wire [(1)-1:0]        s_rfnoc_chdr_tlast, +  input  wire [(1)-1:0]        s_rfnoc_chdr_tvalid, +  output wire [(1)-1:0]        s_rfnoc_chdr_tready, +  // AXIS-CHDR Output Ports (to framework) +  output wire [(2)*CHDR_W-1:0] m_rfnoc_chdr_tdata, +  output wire [(2)-1:0]        m_rfnoc_chdr_tlast, +  output wire [(2)-1:0]        m_rfnoc_chdr_tvalid, +  input  wire [(2)-1:0]        m_rfnoc_chdr_tready, + +  // AXIS-Ctrl Control Input Port (from framework) +  input  wire [31:0]           s_rfnoc_ctrl_tdata, +  input  wire                  s_rfnoc_ctrl_tlast, +  input  wire                  s_rfnoc_ctrl_tvalid, +  output wire                  s_rfnoc_ctrl_tready, +  // AXIS-Ctrl Control Output Port (to framework) +  output wire [31:0]           m_rfnoc_ctrl_tdata, +  output wire                  m_rfnoc_ctrl_tlast, +  output wire                  m_rfnoc_ctrl_tvalid, +  input  wire                  m_rfnoc_ctrl_tready, + +  //--------------------- +  // Client Interface +  //--------------------- + +  // CtrlPort Clock and Reset +  output wire               ctrlport_clk, +  output wire               ctrlport_rst, +  // CtrlPort Master +  output wire               m_ctrlport_req_wr, +  output wire               m_ctrlport_req_rd, +  output wire [19:0]        m_ctrlport_req_addr, +  output wire [31:0]        m_ctrlport_req_data, +  input  wire               m_ctrlport_resp_ack, +  input  wire [31:0]        m_ctrlport_resp_data, + +  // AXI-Stream Data Clock and Reset +  output wire               axis_data_clk, +  output wire               axis_data_rst, +  // Data Stream to User Logic: fft_in +  output wire [32*1-1:0]    m_fft_in_axis_tdata, +  output wire [1-1:0]       m_fft_in_axis_tkeep, +  output wire               m_fft_in_axis_tlast, +  output wire               m_fft_in_axis_tvalid, +  input  wire               m_fft_in_axis_tready, +  output wire [63:0]        m_fft_in_axis_ttimestamp, +  output wire               m_fft_in_axis_thas_time, +  output wire [15:0]        m_fft_in_axis_tlength, +  output wire               m_fft_in_axis_teov, +  output wire               m_fft_in_axis_teob, +  // Data Stream from User Logic: hist +  input  wire [8*4-1:0]    s_hist_axis_tdata, +  input  wire [3:0]         s_hist_axis_tkeep, +  input  wire               s_hist_axis_tlast, +  input  wire               s_hist_axis_tvalid, +  output wire               s_hist_axis_tready, +  input  wire [63:0]        s_hist_axis_ttimestamp, +  input  wire               s_hist_axis_thas_time, +  input  wire [15:0]        s_hist_axis_tlength, +  input  wire               s_hist_axis_teov, +  input  wire               s_hist_axis_teob, +  // Data Stream from User Logic: wf +  input  wire [8*4-1:0]    s_wf_axis_tdata, +  input  wire [3:0]         s_wf_axis_tkeep, +  input  wire               s_wf_axis_tlast, +  input  wire               s_wf_axis_tvalid, +  output wire               s_wf_axis_tready, +  input  wire [63:0]        s_wf_axis_ttimestamp, +  input  wire               s_wf_axis_thas_time, +  input  wire [15:0]        s_wf_axis_tlength, +  input  wire               s_wf_axis_teov, +  input  wire               s_wf_axis_teob +); + +  //--------------------------------------------------------------------------- +  //  Backend Interface +  //--------------------------------------------------------------------------- + +  wire         data_i_flush_en; +  wire [31:0]  data_i_flush_timeout; +  wire [63:0]  data_i_flush_active; +  wire [63:0]  data_i_flush_done; +  wire         data_o_flush_en; +  wire [31:0]  data_o_flush_timeout; +  wire [63:0]  data_o_flush_active; +  wire [63:0]  data_o_flush_done; + +  backend_iface #( +    .NOC_ID        (32'h666F0000), +    .NUM_DATA_I    (1), +    .NUM_DATA_O    (2), +    .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 +  //--------------------- + +  chdr_to_axis_data #( +    .CHDR_W         (CHDR_W), +    .ITEM_W         (32), +    .NIPC           (1), +    .SYNC_CLKS      (0), +    .INFO_FIFO_SIZE ($clog2(32)), +    .PYLD_FIFO_SIZE ($clog2(32)) +  ) chdr_to_axis_data_in_fft_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)*CHDR_W+:CHDR_W]), +    .s_axis_chdr_tlast  (s_rfnoc_chdr_tlast[0]), +    .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[0]), +    .s_axis_chdr_tready (s_rfnoc_chdr_tready[0]), +    .m_axis_tdata       (m_fft_in_axis_tdata), +    .m_axis_tkeep       (m_fft_in_axis_tkeep), +    .m_axis_tlast       (m_fft_in_axis_tlast), +    .m_axis_tvalid      (m_fft_in_axis_tvalid), +    .m_axis_tready      (m_fft_in_axis_tready), +    .m_axis_ttimestamp  (m_fft_in_axis_ttimestamp), +    .m_axis_thas_time   (m_fft_in_axis_thas_time), +    .m_axis_tlength     (m_fft_in_axis_tlength), +    .m_axis_teov        (m_fft_in_axis_teov), +    .m_axis_teob        (m_fft_in_axis_teob), +    .flush_en           (data_i_flush_en), +    .flush_timeout      (data_i_flush_timeout), +    .flush_active       (data_i_flush_active[0]), +    .flush_done         (data_i_flush_done[0]) +  ); + +  //--------------------- +  // Output Data Paths +  //--------------------- + +  axis_data_to_chdr #( +    .CHDR_W          (CHDR_W), +    .ITEM_W          (8), +    .NIPC            (4), +    .SYNC_CLKS       (0), +    .INFO_FIFO_SIZE  ($clog2(32)), +    .PYLD_FIFO_SIZE  ($clog2(32)), +    .MTU             (MTU), +    .SIDEBAND_AT_END (0) +  ) axis_data_to_chdr_out_hist ( +    .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)*CHDR_W+:CHDR_W]), +    .m_axis_chdr_tlast  (m_rfnoc_chdr_tlast[0]), +    .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0]), +    .m_axis_chdr_tready (m_rfnoc_chdr_tready[0]), +    .s_axis_tdata       (s_hist_axis_tdata), +    .s_axis_tkeep       (s_hist_axis_tkeep), +    .s_axis_tlast       (s_hist_axis_tlast), +    .s_axis_tvalid      (s_hist_axis_tvalid), +    .s_axis_tready      (s_hist_axis_tready), +    .s_axis_ttimestamp  (s_hist_axis_ttimestamp), +    .s_axis_thas_time   (s_hist_axis_thas_time), +    .s_axis_tlength     (s_hist_axis_tlength), +    .s_axis_teov        (s_hist_axis_teov), +    .s_axis_teob        (s_hist_axis_teob), +    .flush_en           (data_o_flush_en), +    .flush_timeout      (data_o_flush_timeout), +    .flush_active       (data_o_flush_active[0]), +    .flush_done         (data_o_flush_done[0]) +  ); + +  axis_data_to_chdr #( +    .CHDR_W          (CHDR_W), +    .ITEM_W          (8), +    .NIPC            (4), +    .SYNC_CLKS       (0), +    .INFO_FIFO_SIZE  ($clog2(32)), +    .PYLD_FIFO_SIZE  ($clog2(32)), +    .MTU             (MTU), +    .SIDEBAND_AT_END (0) +  ) axis_data_to_chdr_out_wf ( +    .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[(1)*CHDR_W+:CHDR_W]), +    .m_axis_chdr_tlast  (m_rfnoc_chdr_tlast[1]), +    .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[1]), +    .m_axis_chdr_tready (m_rfnoc_chdr_tready[1]), +    .s_axis_tdata       (s_wf_axis_tdata), +    .s_axis_tkeep       (s_wf_axis_tkeep), +    .s_axis_tlast       (s_wf_axis_tlast), +    .s_axis_tvalid      (s_wf_axis_tvalid), +    .s_axis_tready      (s_wf_axis_tready), +    .s_axis_ttimestamp  (s_wf_axis_ttimestamp), +    .s_axis_thas_time   (s_wf_axis_thas_time), +    .s_axis_tlength     (s_wf_axis_tlength), +    .s_axis_teov        (s_wf_axis_teov), +    .s_axis_teob        (s_wf_axis_teob), +    .flush_en           (data_o_flush_en), +    .flush_timeout      (data_o_flush_timeout), +    .flush_active       (data_o_flush_active[1]), +    .flush_done         (data_o_flush_done[1]) +  ); + +endmodule // noc_shell_fosphor + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor.v new file mode 100644 index 000000000..40112b05e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor.v @@ -0,0 +1,398 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_fosphor +// +// Description: +// +//   Fosphor RFNoC block. This block accepts packets containing FFT data (one +//   FFT output per packet) and generates two output data streams, one +//   containing histogram data and the other containing waterfall plot data. +// +//   Each set of waterfall data is output as a single packet. The frequency of +//   waterfall output depends on the waterfall decimation register setting. +// +//   Each set of histogram data is output as a burst of 64 packets, followed by +//   a single packet of max values and then a single packet of average values. +//   The frequency of waterfall output bursts depends on the waterfall +//   decimation register setting. +// +//   For all outputs, the packets contain byte values, and the number of bytes +//   in each packet matches the number of 4-byte sc16 samples in the input +//   packets (i.e., the FFT size). In other words, the output packet size is +//   1/4th the input packet size. +// +//   Many registers control the visual effects and behavior of the waterfall +//   and histogram. See the register descriptions in +//   rfnoc_block_fosphor_regs.vh for details. +// +// 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). +// + +`default_nettype none + + +module rfnoc_block_fosphor #( +  parameter [9:0] THIS_PORTID     = 10'd0, +  parameter       CHDR_W          = 64, +  parameter [5:0] MTU             = 10 +) ( +  // RFNoC Framework Clocks and Resets +  input  wire                rfnoc_chdr_clk, +  input  wire                rfnoc_ctrl_clk, +  input  wire                ce_clk, +  // RFNoC Backend Interface +  input  wire [       511:0] rfnoc_core_config, +  output wire [       511:0] rfnoc_core_status, +  // AXIS-CHDR Input Ports (from framework) +  input  wire [1*CHDR_W-1:0] s_rfnoc_chdr_tdata, +  input  wire [     (1)-1:0] s_rfnoc_chdr_tlast, +  input  wire [     (1)-1:0] s_rfnoc_chdr_tvalid, +  output wire [     (1)-1:0] s_rfnoc_chdr_tready, +  // AXIS-CHDR Output Ports (to framework) +  output wire [2*CHDR_W-1:0] m_rfnoc_chdr_tdata, +  output wire [     (2)-1:0] m_rfnoc_chdr_tlast, +  output wire [     (2)-1:0] m_rfnoc_chdr_tvalid, +  input  wire [     (2)-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 +  //--------------------------------------------------------------------------- + +  // 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; +  reg                m_ctrlport_resp_ack; +  reg  [31:0]        m_ctrlport_resp_data; +  // Data Stream to User Logic: in +  wire [32*1-1:0]    in_tdata; +  wire               in_tlast; +  wire               in_tvalid; +  wire               in_tready; +  wire [15:0]        in_tlength; +  // Data Stream from User Logic: hist +  wire [8*4-1:0]     hist_tdata; +  wire               hist_tlast; +  wire               hist_tvalid; +  wire               hist_tready; +  wire [15:0]        hist_tlength; +  wire               hist_teob; +  // Data Stream from User Logic: wf +  wire [8*4-1:0]     wf_tdata; +  wire               wf_tlast; +  wire               wf_tvalid; +  wire               wf_tready; +  wire [15:0]        wf_tlength; + +  //--------------------------------------------------------------------------- +  // NoC Shell +  //--------------------------------------------------------------------------- + +  wire ce_rst; + +  noc_shell_fosphor #( +    .CHDR_W      (CHDR_W), +    .THIS_PORTID (THIS_PORTID), +    .MTU         (MTU) +  ) noc_shell_fosphor_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 Clock and Reset +    .axis_data_clk            (), +    .axis_data_rst            (), +    // Data Stream to User Logic: in +    .m_fft_in_axis_tdata      (in_tdata), +    .m_fft_in_axis_tkeep      (), +    .m_fft_in_axis_tlast      (in_tlast), +    .m_fft_in_axis_tvalid     (in_tvalid), +    .m_fft_in_axis_tready     (in_tready), +    .m_fft_in_axis_ttimestamp (), +    .m_fft_in_axis_thas_time  (), +    .m_fft_in_axis_tlength    (in_tlength), +    .m_fft_in_axis_teov       (), +    .m_fft_in_axis_teob       (), +    // Data Stream from User Logic: hist +    .s_hist_axis_tdata        (hist_tdata), +    .s_hist_axis_tkeep        (4'hF), +    .s_hist_axis_tlast        (hist_tlast), +    .s_hist_axis_tvalid       (hist_tvalid), +    .s_hist_axis_tready       (hist_tready), +    .s_hist_axis_ttimestamp   (64'b0), +    .s_hist_axis_thas_time    (1'b0), +    .s_hist_axis_tlength      (hist_tlength), +    .s_hist_axis_teov         (1'b0), +    .s_hist_axis_teob         (hist_teob), +    // Data Stream from User Logic: wf +    .s_wf_axis_tdata          (wf_tdata), +    .s_wf_axis_tkeep          (4'hF), +    .s_wf_axis_tlast          (wf_tlast), +    .s_wf_axis_tvalid         (wf_tvalid), +    .s_wf_axis_tready         (wf_tready), +    .s_wf_axis_ttimestamp     (64'b0), +    .s_wf_axis_thas_time      (1'b0), +    .s_wf_axis_tlength        (wf_tlength), +    .s_wf_axis_teov           (1'b0), +    .s_wf_axis_teob           (1'b0) +  ); + + +  //--------------------------------------------------------------------------- +  // Registers +  //--------------------------------------------------------------------------- + +  `include "rfnoc_block_fosphor_regs.vh" + +  // Configuration registers +  reg [REG_ENABLE_LEN-1:0]     cfg_enable; +  reg                          clear_req; +  reg                          fosphor_rst = 1; +  reg [REG_RANDOM_LEN-1:0]     cfg_random; +  reg [REG_HIST_DECIM_LEN-1:0] cfg_hist_decim; +  reg [REG_OFFSET_LEN-1:0]     cfg_offset; +  reg [REG_SCALE_LEN-1:0]      cfg_scale; +  reg [REG_TRISE_LEN-1:0]      cfg_trise; +  reg [REG_TDECAY_LEN-1:0]     cfg_tdecay; +  reg [REG_ALPHA_LEN-1:0]      cfg_alpha; +  reg [REG_EPSILON_LEN-1:0]    cfg_epsilon; +  reg [REG_WF_DIV_LEN-1:0]     cfg_wf_div; +  reg                          cfg_wf_mode; +  reg [REG_WF_DECIM_LEN-1:0]   cfg_wf_decim; +  reg                          cfg_hist_decim_changed; +  reg                          cfg_wf_decim_changed; + +  always @(posedge ce_clk) begin +    if (ce_rst) begin +      m_ctrlport_resp_ack    <= 0; +      m_ctrlport_resp_data   <= 'bX; +      cfg_enable             <= 0; +      clear_req              <= 0; +      fosphor_rst            <= 1; +      cfg_random             <= 0; +      cfg_hist_decim         <= 0; +      cfg_hist_decim_changed <= 0; +      cfg_offset             <= 0; +      cfg_scale              <= 0; +      cfg_trise              <= 0; +      cfg_tdecay             <= 0; +      cfg_alpha              <= 0; +      cfg_epsilon            <= 0; +      cfg_wf_div             <= 0; +      cfg_wf_mode            <= 0; +      cfg_wf_decim           <= 0; +      cfg_wf_decim_changed   <= 0; +    end else begin +      // Default assignments +      m_ctrlport_resp_ack    <= 0; +      m_ctrlport_resp_data   <= 0; +      cfg_hist_decim_changed <= 0; +      cfg_wf_decim_changed   <= 0; +      clear_req              <= 0; +      fosphor_rst            <= 0; +      m_ctrlport_resp_data   <= 0; +      m_ctrlport_resp_ack    <= 0; + +      // Handle register writes +      if (m_ctrlport_req_wr) begin +        m_ctrlport_resp_ack <= 1; +        case (m_ctrlport_req_addr) +          REG_ENABLE     : cfg_enable  <= m_ctrlport_req_data[0+:REG_ENABLE_LEN]; +          REG_CLEAR      : begin +            fosphor_rst                <= m_ctrlport_req_data[REG_RESET_POS]; +            clear_req                  <= m_ctrlport_req_data[REG_CLEAR_POS]; +          end +          REG_RANDOM     : cfg_random  <= m_ctrlport_req_data[0+:REG_RANDOM_LEN]; +          REG_HIST_DECIM : begin +            cfg_hist_decim             <= m_ctrlport_req_data[0+:REG_HIST_DECIM_LEN]; +            cfg_hist_decim_changed     <= 1'b1; +          end +          REG_OFFSET     : cfg_offset  <= m_ctrlport_req_data[0+:REG_OFFSET_LEN]; +          REG_SCALE      : cfg_scale   <= m_ctrlport_req_data[0+:REG_SCALE_LEN]; +          REG_TRISE      : cfg_trise   <= m_ctrlport_req_data[0+:REG_TRISE_LEN]; +          REG_TDECAY     : cfg_tdecay  <= m_ctrlport_req_data[0+:REG_TDECAY_LEN]; +          REG_ALPHA      : cfg_alpha   <= m_ctrlport_req_data[0+:REG_ALPHA_LEN]; +          REG_EPSILON    : cfg_epsilon <= m_ctrlport_req_data[0+:REG_EPSILON_LEN]; +          REG_WF_CTRL    : begin +            cfg_wf_mode                <= m_ctrlport_req_data[REG_WF_MODE_POS]; +            cfg_wf_div                 <= m_ctrlport_req_data[REG_WF_DIV_POS+:REG_WF_DIV_LEN]; +          end +          REG_WF_DECIM   : begin +            cfg_wf_decim               <= m_ctrlport_req_data[0+:REG_WF_DECIM_LEN]; +            cfg_wf_decim_changed       <= 1'b1; +          end +        endcase + +      // Handle register reads +      end else if (m_ctrlport_req_rd) begin +        m_ctrlport_resp_ack <= 1; +        case (m_ctrlport_req_addr) +          REG_ENABLE     : m_ctrlport_resp_data[0+:REG_ENABLE_LEN]     <= cfg_enable; +          REG_RANDOM     : m_ctrlport_resp_data[0+:REG_RANDOM_LEN]     <= cfg_random; +          REG_HIST_DECIM : m_ctrlport_resp_data[0+:REG_HIST_DECIM_LEN] <= cfg_hist_decim; +          REG_OFFSET     : m_ctrlport_resp_data[0+:REG_OFFSET_LEN]     <= cfg_offset; +          REG_SCALE      : m_ctrlport_resp_data[0+:REG_SCALE_LEN]      <= cfg_scale; +          REG_TRISE      : m_ctrlport_resp_data[0+:REG_TRISE_LEN]      <= cfg_trise; +          REG_TDECAY     : m_ctrlport_resp_data[0+:REG_TDECAY_LEN]     <= cfg_tdecay; +          REG_ALPHA      : m_ctrlport_resp_data[0+:REG_ALPHA_LEN]      <= cfg_alpha; +          REG_EPSILON    : m_ctrlport_resp_data[0+:REG_EPSILON_LEN]    <= cfg_epsilon; +          REG_WF_CTRL    : begin +            m_ctrlport_resp_data[REG_WF_MODE_POS]                      <= cfg_wf_mode; +            m_ctrlport_resp_data[REG_WF_DIV_POS+:REG_WF_DIV_LEN]       <= cfg_wf_div; +          end +          REG_WF_DECIM   : m_ctrlport_resp_data[0+:REG_WF_DECIM_LEN]   <= cfg_wf_decim; +        endcase +      end +    end +  end + + +  //--------------------------------------------------------------------------- +  // Output Packet Length Register +  //--------------------------------------------------------------------------- + +  // The output length is always 1/4th the input length, since +  // we output one byte for each sc16 input. +  reg [15:0] out_packet_length; +  reg        start_of_packet = 1'b1; + +  assign wf_tlength   = out_packet_length; +  assign hist_tlength = out_packet_length; + +  always @(posedge ce_clk) begin +    if (ce_rst) begin +      start_of_packet   <= 1'b1; +      out_packet_length <=  'bX; +    end else begin +      if (in_tvalid && in_tready) begin +        start_of_packet <= in_tlast; +        if (start_of_packet) begin +          out_packet_length <= in_tlength / 4; +        end +      end +    end +  end + + +  //--------------------------------------------------------------------------- +  // Fosphor Core +  //--------------------------------------------------------------------------- + +  wire hist_tvalid_tmp; +  wire hist_tready_tmp; +  wire wf_tvalid_tmp; +  wire wf_tready_tmp; + +  f15_core f15_core_inst ( +    .clk                  (ce_clk), +    .reset                (fosphor_rst), +    .clear_req            (clear_req), +    .cfg_random           (cfg_random), +    .cfg_offset           (cfg_offset), +    .cfg_scale            (cfg_scale), +    .cfg_trise            (cfg_trise), +    .cfg_tdecay           (cfg_tdecay), +    .cfg_alpha            (cfg_alpha), +    .cfg_epsilon          (cfg_epsilon), +    .cfg_decim            (cfg_hist_decim), +    .cfg_decim_changed    (cfg_hist_decim_changed), +    .cfg_wf_div           (cfg_wf_div), +    .cfg_wf_mode          (cfg_wf_mode), +    .cfg_wf_decim         (cfg_wf_decim), +    .cfg_wf_decim_changed (cfg_wf_decim_changed), +    .i_tdata              (in_tdata), +    .i_tlast              (in_tlast), +    .i_tvalid             (in_tvalid), +    .i_tready             (in_tready), +    .o_hist_tdata         (hist_tdata), +    .o_hist_tlast         (hist_tlast), +    .o_hist_tvalid        (hist_tvalid_tmp), +    .o_hist_tready        (hist_tready_tmp), +    .o_hist_teob          (hist_teob), +    .o_wf_tdata           (wf_tdata), +    .o_wf_tlast           (wf_tlast), +    .o_wf_tvalid          (wf_tvalid_tmp), +    .o_wf_tready          (wf_tready_tmp) +  ); + +  // Enable/disable logic. All we're doing here is discarding the output for +  // the "disabled" output. It is still generated internally. +  assign hist_tready_tmp = hist_tready     | ~cfg_enable[0]; +  assign hist_tvalid     = hist_tvalid_tmp &  cfg_enable[0]; +  assign wf_tready_tmp   = wf_tready       | ~cfg_enable[1]; +  assign wf_tvalid       = wf_tvalid_tmp   &  cfg_enable[1]; + +endmodule // rfnoc_block_fosphor + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_regs.vh new file mode 100644 index 000000000..eca372169 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_regs.vh @@ -0,0 +1,186 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_fosphor_vh (Header) +// +// Description: +// +//   Fosphor RFNoC block register descriptions. See the block controller +//   (fosphor_block_control.hpp) for additional documentation. +// + + +// REG_ENABLE (R/W) +// +// This register enables or disables the stream of histogram data from the +// block. The streams are always generated internally and this register causes +// them to be discarded or not. This register should only be updated when the +// Fosphor block is idle to avoid enabling it while packets are in flight. +//  +// [1] : Enable waterfall output stream +// [0] : Enable histogram output stream +// +localparam REG_ENABLE = 'h00; +// +localparam REG_ENABLE_LEN = 2; + +// REG_CLEAR (W) +// +// Controls reset of the Fosphor IP and clearing of the accumulated history +// (average and max hold values). Note that reset is not a superset of clear, +// and both reset and clear should not be asserted in the same write. To reset +// and clear, set only the reset bit in the first write then set only the clear +// bit in a second write. +//  +// [1] : Reset (strobe). This is a self-clearing strobe bit to reset the +//       internal Fosphor core. +// [0] : Clear (strobe). This is a self-clearing strobe bit to clear the +//       history of the fosphor core. +// +localparam REG_CLEAR = 'h04; +// +localparam REG_CLEAR_LEN = 2; +// +localparam REG_RESET_POS = 1; +localparam REG_CLEAR_POS = 0; + +// REG_RANDOM (R/W) +// +// Enables or disables the addition of random noise and/or dithering to the +// incoming signal. +//  +// [1] : Noise enable. Adds random numbers to the signal. +// [0] : Dither enable. Randomizes the least-significant bits of the signal. +// +localparam REG_RANDOM = 'h08; +// +localparam REG_RANDOM_LEN = 2; + +// REG_HIST_DECIM (R/W) +//  +// [11:0] : Histogram decimation. This determines the amount of histogram data +//          that is output relative to the amount of input FFT data. The actual +//          decimation is N:1 where N=VALUE+2. That is, you'll get 1 histogram +//          output packet for ever N FFT packets received, on average. However, +//          histogram data is always output as a burst of 66 packets (64 +//          histogram, 1 maximum value, 1 average value). +// +localparam REG_HIST_DECIM = 'h0C; +// +localparam REG_HIST_DECIM_LEN = 12; + +// REG_OFFSET (R/W) +// +// Histogram offset to apply to the FFT power levels before determining the +// appropriate histogram bin. +//  +// [15:0] : Offset +// +localparam REG_OFFSET = 'h10; +// +localparam REG_OFFSET_LEN = 16; + +// REG_SCALE (R/W) +// +// Histogram scaling factor. Controls the scaling factor to apply to FFT power +// levels before determining the appropriate histogram bin. The scaling factor +// is scale / 256. +//  +// [15:0] : Scale +// +localparam REG_SCALE = 'h14; +// +localparam REG_SCALE_LEN = 16; + +// REG_TRISE (R/W) +// +// Histogram rise rate. Controls the rate at which the hit count in each +// frequency bin increases when there are hits in the particular bin. The +// higher the value, the more quickly the values increase. +//  +// [15:0] : Rise time +// +localparam REG_TRISE = 'h18; +// +localparam REG_TRISE_LEN = 16; + +// REG_TDECAY (R/W) +// +// Histogram decay rate. Controls the rate at which the hit count in each +// frequency and power bin decreases when there are no hits in a particular +// bin. The higher the value, the more quickly the values decrease. +//  +// [15:0] : Decay time +// +localparam REG_TDECAY = 'h1C; +// +localparam REG_TDECAY_LEN = 16; + +// REG_ALPHA (R/W) +// +// Controls the weighting to be applied to the average power level value for +// each FFT frequency bin. The higher the value, the higher the weight that is +// given to older samples and the more slowly the average values change over +// time in each bin. +// +// [15:0] : Alpha +// +localparam REG_ALPHA = 'h20; +// +localparam REG_ALPHA_LEN = 16; + +// REG_EPSILON (R/W) +// +// Controls the rate at which the maximum value for each FFT frequency bin +// decays. The higher the value, the faster the decay rate. A value of 0 +// retains the maximum values indefinitely. +//  +// [15:0] : Epsilon +// +localparam REG_EPSILON = 'h24; +// +localparam REG_EPSILON_LEN = 16; + +// REG_WF_CTRL (R/W) +//  +// Waterfall Control register +// +//   [7] : Waterfall mode. Controls the source of the waterfall history data. +//         When set to "Max Hold", the waterfall data is comprised of the max +//         power values from each frequency bin. When set to "Average", the +//         waterfall data is comprised of the accumulated average value from +//         each frequency bin between waterfall output packets. Can take on the +//         following values: +// +//           0 = Max Hold +//           1 = Average +// +// [1:0] : Waterfall pre-division. Controls the scaling factor applied to +//         waterfall values. Can take on the following values: +// +//           0 = 1:1 +//           1 = 1:8 +//           2 = 1:64 +//           3 = 1:256 +// +localparam REG_WF_CTRL = 'h28; +// +localparam REG_WF_CTRL_LEN = 8; +// +localparam REG_WF_MODE_POS = 7; +// +localparam REG_WF_DIV_POS  = 0; +localparam REG_WF_DIV_LEN  = 2; + +// REG_WF_DECIM (R/W) +//  +// [7:0] : Waterfall decimation. This controls the amount of waterfall data +//         that is output relative to the amount of input FFT data. The actual +//         decimation is N:1 where N=VALUE+2. That is, you'll get 1 waterfall +//         output packet for ever N FFT packets received. +// +localparam REG_WF_DECIM = 'h2C; +// +localparam REG_WF_DECIM_LEN = 8; diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_tb.sv new file mode 100644 index 000000000..3f46e4383 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fosphor/rfnoc_block_fosphor_tb.sv @@ -0,0 +1,590 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_fosphor_tb +// +// Description: Testbench for the fosphor RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_fosphor_tb; + +  `include "test_exec.svh" + +  import PkgTestExec::*; +  import PkgChdrUtils::*; +  import PkgRfnocBlockCtrlBfm::*; +  import PkgRfnocItemUtils::*; + +  `include "rfnoc_block_fosphor_regs.vh" + +  //--------------------------------------------------------------------------- +  // Testbench Configuration +  //--------------------------------------------------------------------------- + +  localparam [ 9:0] THIS_PORTID     = 10'h123; +  localparam [31:0] NOC_ID          = 32'h666F0000; +  localparam int    CHDR_W          = 64; +  localparam int    ITEM_W          = 32; +  localparam int    NUM_PORTS_I     = 1; +  localparam int    NUM_PORTS_O     = 2; +  localparam int    MTU             = 13; +  localparam int    SPP             = 128; +  localparam int    PKT_SIZE_BYTES  = SPP * (ITEM_W/8); +  localparam int    STALL_PROB      = 60;      // Default BFM stall probability +  localparam real   CHDR_CLK_PER    = 5.0;     // 200 MHz +  localparam real   CTRL_CLK_PER    = 25.0;    // 40 MHz +  localparam real   CE_CLK_PER      = 5.0;     // 200 MHz + +  localparam int HIST_PKT_PER_BURST = 66;   // Always 64 hist + 1 max + 1 avg +  localparam int HIST_PORT          = 0; +  localparam int WF_PORT            = 1; + +  //--------------------------------------------------------------------------- +  // Clocks and Resets +  //--------------------------------------------------------------------------- + +  bit rfnoc_chdr_clk; +  bit rfnoc_ctrl_clk; +  bit ce_clk; + +  sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); +  sim_clock_gen #(CTRL_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); +  sim_clock_gen #(CE_CLK_PER)   ce_clk_gen         (.clk(ce_clk), .rst()); + +  //--------------------------------------------------------------------------- +  // Bus Functional Models +  //--------------------------------------------------------------------------- + +  // Backend Interface +  RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); + +  // AXIS-Ctrl Interface +  AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); +  AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); + +  // AXIS-CHDR Interfaces +  AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS_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; + +  // 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_fosphor #( +    .THIS_PORTID         (THIS_PORTID), +    .CHDR_W              (CHDR_W), +    .MTU                 (MTU) +  ) 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 Functions +  //--------------------------------------------------------------------------- + +  typedef enum bit       { WF_MAX_HOLD, WF_AVERAGE }           wf_mode_t; +  typedef enum bit [1:0] { WF_1_1, WF_1_8, WF_1_64, WF_1_256 } wf_div_t; + +  // Data structure to hold the Fosphor configuration state +  typedef struct packed { +    bit              en_wf; +    bit              en_hist; +    bit              en_noise; +    bit              en_dither; +    bit       [11:0] hist_decim; +    bit       [15:0] offset; +    bit       [15:0] scale; +    bit       [15:0] trise; +    bit       [15:0] tdecay; +    bit       [15:0] alpha; +    bit       [15:0] epsilon; +    wf_mode_t        wf_mode; +    wf_div_t         wf_div; +    bit       [ 7:0] wf_decim; +  } fosphor_config_t; + +  // Default configuration copied from GNURadio +  const fosphor_config_t DEFAULT_CONFG = '{ +    en_wf      : 1, +    en_hist    : 1, +    en_noise   : 0, +    en_dither  : 0, +    hist_decim : 2, +    offset     : 0, +    scale      : 256, +    trise      : 4096, +    tdecay     : 16384, +    alpha      : 65280, +    epsilon    : 2, +    wf_mode    : WF_MAX_HOLD, +    wf_div     : WF_1_8, +    wf_decim   : 2 +  }; + + +  // 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_bits(); +      bit [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_bits + +  endclass : Rand + + +  // Set all Fosphor registers based off the cfg data structure +  task automatic set_registers(fosphor_config_t cfg); +    blk_ctrl.reg_write(REG_ENABLE,     (int'(cfg.en_wf)     << 1) | +                                       (int'(cfg.en_hist)   << 0)); +    blk_ctrl.reg_write(REG_RANDOM,     (int'(cfg.en_noise)  << 1) | +                                       (int'(cfg.en_dither) << 0)); +    blk_ctrl.reg_write(REG_HIST_DECIM, cfg.hist_decim); +    blk_ctrl.reg_write(REG_OFFSET,     cfg.offset); +    blk_ctrl.reg_write(REG_SCALE,      cfg.scale); +    blk_ctrl.reg_write(REG_TRISE,      cfg.trise); +    blk_ctrl.reg_write(REG_TDECAY,     cfg.tdecay); +    blk_ctrl.reg_write(REG_ALPHA,      cfg.alpha); +    blk_ctrl.reg_write(REG_EPSILON,    cfg.epsilon); +    blk_ctrl.reg_write(REG_WF_CTRL,    (int'(cfg.wf_mode) << 7) | int'(cfg.wf_div)); +    blk_ctrl.reg_write(REG_WF_DECIM,   cfg.wf_decim); +  endtask : set_registers; + + +  // Verify that all the Fosphor registers match the cfg data structure +  task automatic verify_registers(fosphor_config_t cfg); +    bit [31:0] value; + +    blk_ctrl.reg_read(REG_ENABLE, value); +    `ASSERT_ERROR(value[1] == cfg.en_wf, "REG_ENABLE[1] didn't have expected value"); +    `ASSERT_ERROR(value[0] == cfg.en_hist, "REG_ENABLE[0] didn't have expected value"); +     +    blk_ctrl.reg_read(REG_CLEAR, value); +    `ASSERT_ERROR(value == 0, "REG_CLEAR didn't have expected value"); +     +    blk_ctrl.reg_read(REG_RANDOM, value); +    `ASSERT_ERROR(value[1] == cfg.en_noise, "REG_RANDOM[1] didn't have expected value"); +    `ASSERT_ERROR(value[0] == cfg.en_dither, "REG_RANDOM[0] didn't have expected value"); +     +    blk_ctrl.reg_read(REG_HIST_DECIM, value); +    `ASSERT_ERROR(value == cfg.hist_decim, "REG_HIST_DECIM didn't have expected value"); +     +    blk_ctrl.reg_read(REG_OFFSET, value); +    `ASSERT_ERROR(value == cfg.offset, "REG_OFFSET didn't have expected value"); +     +    blk_ctrl.reg_read(REG_SCALE, value); +    `ASSERT_ERROR(value == cfg.scale, "REG_SCALE didn't have expected value"); +     +    blk_ctrl.reg_read(REG_TRISE, value); +    `ASSERT_ERROR(value == cfg.trise, "REG_TRISE didn't have expected value"); +     +    blk_ctrl.reg_read(REG_TDECAY, value); +    `ASSERT_ERROR(value == cfg.tdecay, "REG_TDECAY didn't have expected value"); +     +    blk_ctrl.reg_read(REG_ALPHA, value); +    `ASSERT_ERROR(value == cfg.alpha, "REG_ALPHA didn't have expected value"); +     +    blk_ctrl.reg_read(REG_EPSILON, value); +    `ASSERT_ERROR(value == cfg.epsilon, "REG_EPSILON didn't have expected value"); +     +    blk_ctrl.reg_read(REG_WF_CTRL, value); +    `ASSERT_ERROR(value[7] == cfg.wf_mode, "REG_WF_CTRL didn't have expected value"); +    `ASSERT_ERROR(value[1:0] == cfg.wf_div, "REG_WF_CTRL didn't have expected value"); +     +    blk_ctrl.reg_read(REG_WF_DECIM, value); +    `ASSERT_ERROR(value == cfg.wf_decim, "REG_WF_DECIM didn't have expected value"); +  endtask : verify_registers; + + +  // Generate a random Fosphor configuration to test +  task automatic randomize_cfg(output fosphor_config_t cfg, output int spp); +    // Chase a random SPP size, but make it a power of 2 (like the FFT) up to +    // the define SPP value. +    spp = 2**$urandom_range(4, $clog2(SPP)); + +    // Start by randomizing the entire fosphor configuration, but then +    cfg = Rand #($bits(cfg))::rand_bits(); + +    // Keep decimation relatively small to decrease simulation time +    cfg.hist_decim = $urandom_range(0, 8); + +    // Make sure wf_mode and wf_div are valid values +    cfg.wf_mode = wf_mode_t'($urandom_range(cfg.wf_mode.num()-1)); +    cfg.wf_div  = wf_div_t'($urandom_range(cfg.wf_div.num()-1)); +  endtask : randomize_cfg + + +  // Test the passed Fosphor configuration. This updates the registers, inputs +  // num_packets of data (spp-samples each) and verifies the output. +  task automatic test_config(fosphor_config_t cfg, int num_packets, int spp); +    item_t fft_items[$]; + +    $display("Testing . . ."); +    $display("  packets:    %0d", num_packets); +    $display("  spp:        %0d", spp); +    $display("  en_wf       %0d", cfg.en_wf); +    $display("  en_hist     %0d", cfg.en_hist); +    $display("  hist_decim: %0d", cfg.hist_decim); +    $display("  wf_decim:   %0d", cfg.wf_decim); + +    // Clear any existing data +    blk_ctrl.reg_write(REG_CLEAR, 1); + +    // Configure all the core's registers +    set_registers(cfg); + +    // Generate packets to send +    fft_items = {}; +    for (int i = 0; i < spp; i++) begin +      fft_items.push_back({ +        shortint'(i), +        shortint'(0) +      }); +    end + +    // Send the packets +    for (int i = 0; i < num_packets; i++) begin +      blk_ctrl.send_items(0, fft_items); +    end + +    fork +      begin : fork_waterfall +        item_t recv_items[$]; +        int exp_num_packets; + +        if (cfg.en_wf) begin +          // Calculate expected number of packets +          exp_num_packets = num_packets / (cfg.wf_decim + 2); +        end else begin +          exp_num_packets = 0; +        end + +        $display("Expecting %0d waterfall packets of length %0d bytes", +          exp_num_packets, spp); + +        if (exp_num_packets > 0) begin  +          for (int i = 0; i < exp_num_packets; i++) begin +            string err_string; +            blk_ctrl.recv_items(WF_PORT, recv_items); + +            // We expect one byte output per sample input +            $sformat( +              err_string, +              "Waterfall packet of %0d bytes didn't match expected length of %0d bytes", +              recv_items.size()*4, spp +            ); +            `ASSERT_ERROR(recv_items.size()*4 == spp, err_string); +          end +          $display("All waterfall packets received!"); +        end +      end + +      begin : fork_histogram +        item_t        recv_items[$]; +        chdr_word_t   mdata[$]; +        int           exp_num_packets; +        packet_info_t pkt_info; + +        if(cfg.en_hist) begin +          // Calculate expected number of packets +          exp_num_packets = num_packets / (cfg.hist_decim + 2); +          // Round it down to a multiple of HIST_PKT_PER_BURST, since it always +          // outputs HIST_PKT_PER_BURST packets at a time. +          exp_num_packets = (exp_num_packets / HIST_PKT_PER_BURST) * HIST_PKT_PER_BURST; +        end else begin +          exp_num_packets = 0; +        end + +        $display("Expecting %0d histogram packets of length %0d bytes", +          exp_num_packets, spp); + +        if (exp_num_packets > 0) begin +          for (int i = 0; i < exp_num_packets; i++) begin +            string err_string; +            blk_ctrl.recv_items_adv(HIST_PORT, recv_items, mdata, pkt_info); +            //$display("Recvd hist pkt %0d", i); + +            // We expect one byte output per sample input +            $sformat( +              err_string, +              "Histogram packet of %0d bytes didn't match expected length of %0d bytes", +              recv_items.size()*4, spp +            ); +            `ASSERT_ERROR(recv_items.size()*4 == spp, err_string); + +            // Check that the last packet of each burst has EOB set  +            if ((i+1) % HIST_PKT_PER_BURST == 0) begin +              `ASSERT_ERROR(pkt_info.eob == 1, "EOB was not set on last packet of histogram"); +            end else begin +              `ASSERT_ERROR(pkt_info.eob == 0, "EOB was set on middle packet histogram"); +            end +          end +          $display("All histogram packets received!"); +        end +      end +    join + +    // Wait until all input packets were accepted before moving on, since we +    // don't want any output from these packets to be confused with the next +    // test. +    blk_ctrl.wait_complete(0); +    #(CE_CLK_PER * SPP * 2); + +    // The current Fosphor core doesn't cleanly handle transitions between +    // settings, so we reset the core before each test. +    blk_ctrl.reg_write(REG_CLEAR, 2); + +  endtask : test_config + + +  //--------------------------------------------------------------------------- +  // Test Sequences +  //--------------------------------------------------------------------------- +   +  // Test that all the registers read/write as expected +  task automatic test_registers(); +    fosphor_config_t cfg; + +    // All registers reset to 0 +    test.start_test("Test Registers (reset values)", 50us); +    cfg = '0;  +    verify_registers(cfg); +    test.end_test(); + +    test.start_test("Test Registers (max values)", 50us); +    cfg = '{ +      en_wf      : 'h1, +      en_hist    : 'h1, +      en_noise   : 'h1, +      en_dither  : 'h1, +      hist_decim : 'hFFF, +      offset     : 'hFFFF, +      scale      : 'hFFFF, +      trise      : 'hFFFF, +      tdecay     : 'hFFFF, +      alpha      : 'hFFFF, +      epsilon    : 'hFFFF, +      wf_mode    : wf_mode_t'('h1), +      wf_div     : wf_div_t'('h3), +      wf_decim   : 'hFF +    }; +    set_registers(cfg); +    verify_registers(cfg); +    test.end_test(); + +    test.start_test("Test Registers (default values)", 50us); +    cfg = DEFAULT_CONFG; +    set_registers(cfg); +    verify_registers(cfg); +    test.end_test(); +  endtask : test_registers; + + +  // Test waterfall decimation settings +  task automatic test_wf_decimation(); +    const int spp    = 16; +    const int num_wf = 4; +    fosphor_config_t cfg; +    int num_packets; + +    test.start_test("Test waterfall decimation", 1ms); +    cfg = DEFAULT_CONFG; +    cfg.en_hist = 0; +    for (int wf_decim = 0; wf_decim < 5; wf_decim++) begin +      cfg.wf_decim = wf_decim; +      // Input enough packets to get num_wf packets out +      num_packets = (wf_decim+2) * (num_wf+1) - 1; +      test_config(cfg, num_packets, spp); +    end +    test.end_test(); +  endtask : test_wf_decimation + + +  // Test histogram decimation settings +  task automatic test_hist_decimation(); +    const int spp      = 16; +    const int num_hist = HIST_PKT_PER_BURST * 4; +    fosphor_config_t cfg; +    int num_packets; + +    test.start_test("Test histogram decimation", 1ms); +    cfg = DEFAULT_CONFG; +    cfg.en_wf = 0; +    for (int hist_decim = 0; hist_decim < 5; hist_decim++) begin +      cfg.hist_decim = hist_decim; +      // Input enough packets to get num_hist packets out +      num_packets = (hist_decim+2) * (num_hist+HIST_PKT_PER_BURST/2); +      test_config(cfg, num_packets, spp); +    end +    test.end_test(); +  endtask : test_hist_decimation + + +  // Choose num_iter random configurations and test each one +  task automatic test_rand_config(int num_iter); +    int num_packets, num_packets_wf, num_packets_hist; +    int spp; +    fosphor_config_t cfg; +    const int num_wf   = 2;                       // Get 2 waterfall packets +    const int num_hist = HIST_PKT_PER_BURST * 2;  // Get 2 histogram bursts + +    test.start_test("Test random configurations", num_iter * 10ms); +    for(int i = 0; i < num_iter; i++) begin +      string str; +      $display("<<<<<<<< RANDOM ITERATION %0d >>>>>>>>", i); +      //test.current_test = $sformatf("%0d", i); + +      // Choose a random configuration +      randomize_cfg(cfg, spp); + +      // Only allow the output of waterfall or histogram at one time. Because +      // they operate independently and their outputs overlap, we only check +      // one at a time. This way we can end testing cleanly between output +      // packets without cutting off either the waterfall or histogram output. +      if (($urandom() & 1) == 0) begin +        cfg.en_wf   = 1; +        cfg.en_hist = 0; +        num_packets = (cfg.wf_decim+2) * (num_wf+1) - 1; +      end else begin +        cfg.en_wf   = 0; +        cfg.en_hist = 1; +        num_packets = (cfg.hist_decim+2) * (num_hist+HIST_PKT_PER_BURST/2); +      end +      test_config(cfg, num_packets, spp); +    end +    test.end_test(); +  endtask : test_rand_config + + +  //--------------------------------------------------------------------------- +  // Main Test Process +  //--------------------------------------------------------------------------- + +  initial begin : tb_main + +    // Initialize the test exec object for this testbench +    test.start_tb("rfnoc_block_fosphor_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 +    //-------------------------------- +     +    test_registers(); +    test_wf_decimation(); +    test_hist_decimation(); +    test_rand_config(16); + +    //-------------------------------- +    // Finish Up +    //-------------------------------- + +    // Display final statistics and results +    test.end_tb(); +  end : tb_main + +endmodule : rfnoc_block_fosphor_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/fosphor/fifo_srl.v b/fpga/usrp3/lib/rfnoc/fosphor/fifo_srl.v index 700da18d3..822a1b769 100644 --- a/fpga/usrp3/lib/rfnoc/fosphor/fifo_srl.v +++ b/fpga/usrp3/lib/rfnoc/fosphor/fifo_srl.v @@ -28,7 +28,7 @@ module fifo_srl #(  	output wire full,  	output wire afull, -	output reg  [WIDTH-1:0] do, +	output reg  [WIDTH-1:0] do = {WIDTH{1'b0}},  	input  wire rden,  	output reg  empty, | 
