diff options
author | Wade Fife <wade.fife@ettus.com> | 2020-06-16 11:31:45 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-06-29 14:03:17 -0500 |
commit | cf82b36388880237fe7b690c16dde46afc1a3da3 (patch) | |
tree | 8e3812e5b2229c1030457a25815a668cb79ea108 /fpga/usrp3 | |
parent | 1cef9d62d698a7cc33f936ab3df32e6dcd933487 (diff) | |
download | uhd-cf82b36388880237fe7b690c16dde46afc1a3da3.tar.gz uhd-cf82b36388880237fe7b690c16dde46afc1a3da3.tar.bz2 uhd-cf82b36388880237fe7b690c16dde46afc1a3da3.zip |
fpga: rfnoc: Add Log-Power block
Diffstat (limited to 'fpga/usrp3')
6 files changed, 1006 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 |