diff options
author | Wade Fife <wade.fife@ettus.com> | 2020-05-07 14:43:32 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-05-19 14:22:55 -0500 |
commit | a40f2a4a5d04aad3ef3e222033fbacc521233782 (patch) | |
tree | 98137f9d7e2b8a00f20b6c2dd9366185ec9bf2d1 /fpga/usrp3/lib/rfnoc | |
parent | eb4bedf3133ce1ed275d03b36839ec61d75f2e60 (diff) | |
download | uhd-a40f2a4a5d04aad3ef3e222033fbacc521233782.tar.gz uhd-a40f2a4a5d04aad3ef3e222033fbacc521233782.tar.bz2 uhd-a40f2a4a5d04aad3ef3e222033fbacc521233782.zip |
fpga: rfnoc: Add Vector IIR RFNoC block
Diffstat (limited to 'fpga/usrp3/lib/rfnoc')
7 files changed, 1393 insertions, 17 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/Makefile new file mode 100644 index 000000000..656b4d5c0 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/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. 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_vector_iir_tb +SIM_SRCS = \ +$(abspath rfnoc_block_vector_iir_tb.sv) \ + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/Makefile.srcs new file mode 100644 index 000000000..4fe877305 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/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_vector_iir.v \ +noc_shell_vector_iir.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/noc_shell_vector_iir.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/noc_shell_vector_iir.v new file mode 100644 index 000000000..959ec4124 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/noc_shell_vector_iir.v @@ -0,0 +1,308 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_vector_iir +// +// Description: +// +// This is a tool-generated NoC-shell for the vector_iir 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_vector_iir #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] MTU = 10, + parameter NUM_PORTS = 1 +) ( + //--------------------- + // Framework Interface + //--------------------- + + // RFNoC Framework Clocks + input wire rfnoc_chdr_clk, + input wire rfnoc_ctrl_clk, + input wire ce_clk, + + // NoC Shell Generated Resets + output wire rfnoc_chdr_rst, + output wire rfnoc_ctrl_rst, + output wire ce_rst, + + // RFNoC Backend Interface + input wire [511:0] rfnoc_core_config, + output wire [511:0] rfnoc_core_status, + + // AXIS-CHDR Input Ports (from framework) + input wire [(0+NUM_PORTS)*CHDR_W-1:0] s_rfnoc_chdr_tdata, + input wire [(0+NUM_PORTS)-1:0] s_rfnoc_chdr_tlast, + input wire [(0+NUM_PORTS)-1:0] s_rfnoc_chdr_tvalid, + output wire [(0+NUM_PORTS)-1:0] s_rfnoc_chdr_tready, + // AXIS-CHDR Output Ports (to framework) + output wire [(0+NUM_PORTS)*CHDR_W-1:0] m_rfnoc_chdr_tdata, + output wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tlast, + output wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tvalid, + input wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tready, + + // AXIS-Ctrl Control Input Port (from framework) + input wire [31:0] s_rfnoc_ctrl_tdata, + input wire s_rfnoc_ctrl_tlast, + input wire s_rfnoc_ctrl_tvalid, + output wire s_rfnoc_ctrl_tready, + // AXIS-Ctrl Control Output Port (to framework) + output wire [31:0] m_rfnoc_ctrl_tdata, + output wire m_rfnoc_ctrl_tlast, + output wire m_rfnoc_ctrl_tvalid, + input wire m_rfnoc_ctrl_tready, + + //--------------------- + // Client Interface + //--------------------- + + // CtrlPort Clock and Reset + output wire ctrlport_clk, + output wire ctrlport_rst, + // CtrlPort Master + output wire m_ctrlport_req_wr, + output wire m_ctrlport_req_rd, + output wire [19:0] m_ctrlport_req_addr, + output wire [31:0] m_ctrlport_req_data, + input wire m_ctrlport_resp_ack, + input wire [31:0] m_ctrlport_resp_data, + + // AXI-Stream Payload Context Clock and Reset + output wire axis_data_clk, + output wire axis_data_rst, + // Payload Stream to User Logic: in + output wire [NUM_PORTS*32*1-1:0] m_in_payload_tdata, + output wire [NUM_PORTS*1-1:0] m_in_payload_tkeep, + output wire [NUM_PORTS-1:0] m_in_payload_tlast, + output wire [NUM_PORTS-1:0] m_in_payload_tvalid, + input wire [NUM_PORTS-1:0] m_in_payload_tready, + // Context Stream to User Logic: in + output wire [NUM_PORTS*CHDR_W-1:0] m_in_context_tdata, + output wire [NUM_PORTS*4-1:0] m_in_context_tuser, + output wire [NUM_PORTS-1:0] m_in_context_tlast, + output wire [NUM_PORTS-1:0] m_in_context_tvalid, + input wire [NUM_PORTS-1:0] m_in_context_tready, + // Payload Stream to User Logic: out + input wire [NUM_PORTS*32*1-1:0] s_out_payload_tdata, + input wire [NUM_PORTS*1-1:0] s_out_payload_tkeep, + input wire [NUM_PORTS-1:0] s_out_payload_tlast, + input wire [NUM_PORTS-1:0] s_out_payload_tvalid, + output wire [NUM_PORTS-1:0] s_out_payload_tready, + // Context Stream to User Logic: out + input wire [NUM_PORTS*CHDR_W-1:0] s_out_context_tdata, + input wire [NUM_PORTS*4-1:0] s_out_context_tuser, + input wire [NUM_PORTS-1:0] s_out_context_tlast, + input wire [NUM_PORTS-1:0] s_out_context_tvalid, + output wire [NUM_PORTS-1:0] s_out_context_tready +); + + //--------------------------------------------------------------------------- + // Backend Interface + //--------------------------------------------------------------------------- + + wire data_i_flush_en; + wire [31:0] data_i_flush_timeout; + wire [63:0] data_i_flush_active; + wire [63:0] data_i_flush_done; + wire data_o_flush_en; + wire [31:0] data_o_flush_timeout; + wire [63:0] data_o_flush_active; + wire [63:0] data_o_flush_done; + + backend_iface #( + .NOC_ID (32'h11120000), + .NUM_DATA_I (0+NUM_PORTS), + .NUM_DATA_O (0+NUM_PORTS), + .CTRL_FIFOSIZE ($clog2(32)), + .MTU (MTU) + ) backend_iface_i ( + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_chdr_rst (rfnoc_chdr_rst), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_ctrl_rst (rfnoc_ctrl_rst), + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status), + .data_i_flush_en (data_i_flush_en), + .data_i_flush_timeout (data_i_flush_timeout), + .data_i_flush_active (data_i_flush_active), + .data_i_flush_done (data_i_flush_done), + .data_o_flush_en (data_o_flush_en), + .data_o_flush_timeout (data_o_flush_timeout), + .data_o_flush_active (data_o_flush_active), + .data_o_flush_done (data_o_flush_done) + ); + + //--------------------------------------------------------------------------- + // Reset Generation + //--------------------------------------------------------------------------- + + wire ce_rst_pulse; + + pulse_synchronizer #(.MODE ("POSEDGE")) pulse_synchronizer_ce ( + .clk_a(rfnoc_chdr_clk), .rst_a(1'b0), .pulse_a (rfnoc_chdr_rst), .busy_a (), + .clk_b(ce_clk), .pulse_b (ce_rst_pulse) + ); + + pulse_stretch_min #(.LENGTH(32)) pulse_stretch_min_ce ( + .clk(ce_clk), .rst(1'b0), + .pulse_in(ce_rst_pulse), .pulse_out(ce_rst) + ); + + //--------------------------------------------------------------------------- + // Control Path + //--------------------------------------------------------------------------- + + assign ctrlport_clk = ce_clk; + assign ctrlport_rst = ce_rst; + + ctrlport_endpoint #( + .THIS_PORTID (THIS_PORTID), + .SYNC_CLKS (0), + .AXIS_CTRL_MST_EN (0), + .AXIS_CTRL_SLV_EN (1), + .SLAVE_FIFO_SIZE ($clog2(32)) + ) ctrlport_endpoint_i ( + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_ctrl_rst (rfnoc_ctrl_rst), + .ctrlport_clk (ctrlport_clk), + .ctrlport_rst (ctrlport_rst), + .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata), + .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast), + .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid), + .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready), + .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata), + .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast), + .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid), + .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready), + .m_ctrlport_req_wr (m_ctrlport_req_wr), + .m_ctrlport_req_rd (m_ctrlport_req_rd), + .m_ctrlport_req_addr (m_ctrlport_req_addr), + .m_ctrlport_req_data (m_ctrlport_req_data), + .m_ctrlport_req_byte_en (), + .m_ctrlport_req_has_time (), + .m_ctrlport_req_time (), + .m_ctrlport_resp_ack (m_ctrlport_resp_ack), + .m_ctrlport_resp_status (2'b0), + .m_ctrlport_resp_data (m_ctrlport_resp_data), + .s_ctrlport_req_wr (1'b0), + .s_ctrlport_req_rd (1'b0), + .s_ctrlport_req_addr (20'b0), + .s_ctrlport_req_portid (10'b0), + .s_ctrlport_req_rem_epid (16'b0), + .s_ctrlport_req_rem_portid (10'b0), + .s_ctrlport_req_data (32'b0), + .s_ctrlport_req_byte_en (4'hF), + .s_ctrlport_req_has_time (1'b0), + .s_ctrlport_req_time (64'b0), + .s_ctrlport_resp_ack (), + .s_ctrlport_resp_status (), + .s_ctrlport_resp_data () + ); + + //--------------------------------------------------------------------------- + // Data Path + //--------------------------------------------------------------------------- + + genvar i; + + assign axis_data_clk = ce_clk; + assign axis_data_rst = ce_rst; + + //--------------------- + // Input Data Paths + //--------------------- + + for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_input_in + chdr_to_axis_pyld_ctxt #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(32)), + .CONTEXT_PREFETCH_EN (1) + ) chdr_to_axis_pyld_ctxt_in_in ( + .axis_chdr_clk (rfnoc_chdr_clk), + .axis_chdr_rst (rfnoc_chdr_rst), + .axis_data_clk (axis_data_clk), + .axis_data_rst (axis_data_rst), + .s_axis_chdr_tdata (s_rfnoc_chdr_tdata[((0+i)*CHDR_W)+:CHDR_W]), + .s_axis_chdr_tlast (s_rfnoc_chdr_tlast[0+i]), + .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[0+i]), + .s_axis_chdr_tready (s_rfnoc_chdr_tready[0+i]), + .m_axis_payload_tdata (m_in_payload_tdata[(32*1)*i+:(32*1)]), + .m_axis_payload_tkeep (m_in_payload_tkeep[1*i+:1]), + .m_axis_payload_tlast (m_in_payload_tlast[i]), + .m_axis_payload_tvalid (m_in_payload_tvalid[i]), + .m_axis_payload_tready (m_in_payload_tready[i]), + .m_axis_context_tdata (m_in_context_tdata[CHDR_W*i+:CHDR_W]), + .m_axis_context_tuser (m_in_context_tuser[4*i+:4]), + .m_axis_context_tlast (m_in_context_tlast[i]), + .m_axis_context_tvalid (m_in_context_tvalid[i]), + .m_axis_context_tready (m_in_context_tready[i]), + .flush_en (data_i_flush_en), + .flush_timeout (data_i_flush_timeout), + .flush_active (data_i_flush_active[0+i]), + .flush_done (data_i_flush_done[0+i]) + ); + end + + //--------------------- + // Output Data Paths + //--------------------- + + for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_output_out + axis_pyld_ctxt_to_chdr #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(32)), + .MTU (MTU), + .CONTEXT_PREFETCH_EN (1) + ) axis_pyld_ctxt_to_chdr_out_out ( + .axis_chdr_clk (rfnoc_chdr_clk), + .axis_chdr_rst (rfnoc_chdr_rst), + .axis_data_clk (axis_data_clk), + .axis_data_rst (axis_data_rst), + .m_axis_chdr_tdata (m_rfnoc_chdr_tdata[(0+i)*CHDR_W+:CHDR_W]), + .m_axis_chdr_tlast (m_rfnoc_chdr_tlast[0+i]), + .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0+i]), + .m_axis_chdr_tready (m_rfnoc_chdr_tready[0+i]), + .s_axis_payload_tdata (s_out_payload_tdata[(32*1)*i+:(32*1)]), + .s_axis_payload_tkeep (s_out_payload_tkeep[1*i+:1]), + .s_axis_payload_tlast (s_out_payload_tlast[i]), + .s_axis_payload_tvalid (s_out_payload_tvalid[i]), + .s_axis_payload_tready (s_out_payload_tready[i]), + .s_axis_context_tdata (s_out_context_tdata[CHDR_W*i+:CHDR_W]), + .s_axis_context_tuser (s_out_context_tuser[4*i+:4]), + .s_axis_context_tlast (s_out_context_tlast[i]), + .s_axis_context_tvalid (s_out_context_tvalid[i]), + .s_axis_context_tready (s_out_context_tready[i]), + .framer_errors (), + .flush_en (data_o_flush_en), + .flush_timeout (data_o_flush_timeout), + .flush_active (data_o_flush_active[0+i]), + .flush_done (data_o_flush_done[0+i]) + ); + end + +endmodule // noc_shell_vector_iir + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir.v new file mode 100644 index 000000000..58df93b44 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir.v @@ -0,0 +1,400 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_vector_iir +// +// Description: +// +// This module implements an IIR filter with a variable length delay line. +// Transfer Function: +// beta +// H(z) = ------------------ +// 1 - alpha*z^-delay +// Where: +// - beta is the feedforward tap +// - alpha is the feedback tap +// - delay is the feedback tap delay +// +// 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 Vector IIR instances to instantiate +// MAX_DELAY : The maximum supported filter delay. This should correspond +// to the maximum SPP. Optimal values are a power of two, minus +// one (e.g, 2047). +// + +`default_nettype none + + +module rfnoc_block_vector_iir #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] MTU = 10, + parameter NUM_PORTS = 1, + parameter MAX_DELAY = (2**MTU*CHDR_W/32-1) +) ( + // 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 [(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 Input Port (from framework) + input wire [ 31:0] s_rfnoc_ctrl_tdata, + input wire s_rfnoc_ctrl_tlast, + input wire s_rfnoc_ctrl_tvalid, + output wire s_rfnoc_ctrl_tready, + // AXIS-Ctrl Output Port (to framework) + output wire [ 31:0] m_rfnoc_ctrl_tdata, + output wire m_rfnoc_ctrl_tlast, + output wire m_rfnoc_ctrl_tvalid, + input wire m_rfnoc_ctrl_tready +); + + `include "rfnoc_block_vector_iir_regs.vh" + + // Make sure MAX_DELAY isn't too big for REG_MAX_DELAY + if (MAX_DELAY >= 2**REG_MAX_DELAY_LEN) begin + MAX_DELAY_is_too_large_for_REG_MAX_DELAY(); + end + + + //--------------------------------------------------------------------------- + // Signal Declarations + //--------------------------------------------------------------------------- + + // CtrlPort Master + wire m_ctrlport_req_wr; + wire m_ctrlport_req_rd; + wire [19:0] m_ctrlport_req_addr; + wire [31:0] m_ctrlport_req_data; + wire m_ctrlport_resp_ack; + wire [31:0] m_ctrlport_resp_data; + // Payload Stream to User Logic: in + wire [NUM_PORTS*32*1-1:0] m_in_payload_tdata; + wire [ NUM_PORTS-1:0] m_in_payload_tlast; + wire [ NUM_PORTS-1:0] m_in_payload_tvalid; + wire [ NUM_PORTS-1:0] m_in_payload_tready; + // Context Stream to User Logic: in + wire [NUM_PORTS*CHDR_W-1:0] m_in_context_tdata; + wire [ NUM_PORTS*4-1:0] m_in_context_tuser; + wire [ NUM_PORTS-1:0] m_in_context_tlast; + wire [ NUM_PORTS-1:0] m_in_context_tvalid; + wire [ NUM_PORTS-1:0] m_in_context_tready; + // Payload Stream to User Logic: out + wire [NUM_PORTS*32*1-1:0] s_out_payload_tdata; + wire [ NUM_PORTS-1:0] s_out_payload_tlast; + wire [ NUM_PORTS-1:0] s_out_payload_tvalid; + wire [ NUM_PORTS-1:0] s_out_payload_tready; + // Context Stream to User Logic: out + wire [NUM_PORTS*CHDR_W-1:0] s_out_context_tdata; + wire [ NUM_PORTS*4-1:0] s_out_context_tuser; + wire [ NUM_PORTS-1:0] s_out_context_tlast; + wire [ NUM_PORTS-1:0] s_out_context_tvalid; + wire [ NUM_PORTS-1:0] s_out_context_tready; + + + //--------------------------------------------------------------------------- + // NoC Shell + //--------------------------------------------------------------------------- + + wire ce_rst; + + noc_shell_vector_iir #( + .CHDR_W (CHDR_W), + .THIS_PORTID (THIS_PORTID), + .MTU (MTU), + .NUM_PORTS (NUM_PORTS) + ) noc_shell_vector_iir_i ( + //--------------------- + // Framework Interface + //--------------------- + + // Clock Inputs + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .ce_clk (ce_clk), + // Reset Outputs + .rfnoc_chdr_rst (), + .rfnoc_ctrl_rst (), + .ce_rst (ce_rst), + // RFNoC Backend Interface + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status), + // CHDR Input Ports (from framework) + .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata), + .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast), + .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid), + .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready), + // CHDR Output Ports (to framework) + .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata), + .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast), + .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid), + .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready), + // AXIS-Ctrl Input Port (from framework) + .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata), + .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast), + .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid), + .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready), + // AXIS-Ctrl Output Port (to framework) + .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata), + .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast), + .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid), + .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready), + + //--------------------- + // Client Interface + //--------------------- + + // CtrlPort Clock and Reset + .ctrlport_clk (), + .ctrlport_rst (), + // CtrlPort Master + .m_ctrlport_req_wr (m_ctrlport_req_wr), + .m_ctrlport_req_rd (m_ctrlport_req_rd), + .m_ctrlport_req_addr (m_ctrlport_req_addr), + .m_ctrlport_req_data (m_ctrlport_req_data), + .m_ctrlport_resp_ack (m_ctrlport_resp_ack), + .m_ctrlport_resp_data (m_ctrlport_resp_data), + + // AXI-Stream Payload Context Clock and Reset + .axis_data_clk (), + .axis_data_rst (), + // Payload Stream to User Logic: in + .m_in_payload_tdata (m_in_payload_tdata), + .m_in_payload_tkeep (), + .m_in_payload_tlast (m_in_payload_tlast), + .m_in_payload_tvalid (m_in_payload_tvalid), + .m_in_payload_tready (m_in_payload_tready), + // Context Stream to User Logic: in + .m_in_context_tdata (m_in_context_tdata), + .m_in_context_tuser (m_in_context_tuser), + .m_in_context_tlast (m_in_context_tlast), + .m_in_context_tvalid (m_in_context_tvalid), + .m_in_context_tready (m_in_context_tready), + // Payload Stream from User Logic: out + .s_out_payload_tdata (s_out_payload_tdata), + .s_out_payload_tkeep (), + .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 is not used because output packets have the same format as input + // packets, so we pass through the context unchanged. + assign s_out_context_tdata = m_in_context_tdata; + assign s_out_context_tuser = m_in_context_tuser; + assign s_out_context_tlast = m_in_context_tlast; + assign s_out_context_tvalid = m_in_context_tvalid; + assign m_in_context_tready = s_out_context_tready; + + + //--------------------------------------------------------------------------- + // CtrlPort Splitter + //--------------------------------------------------------------------------- + + wire [NUM_PORTS* 1-1:0] dec_ctrlport_req_wr; + wire [NUM_PORTS* 1-1:0] dec_ctrlport_req_rd; + wire [NUM_PORTS*20-1:0] dec_ctrlport_req_addr; + wire [NUM_PORTS*32-1:0] dec_ctrlport_req_data; + wire [NUM_PORTS* 1-1:0] dec_ctrlport_resp_ack; + wire [NUM_PORTS*32-1:0] dec_ctrlport_resp_data; + + ctrlport_decoder #( + .NUM_SLAVES (NUM_PORTS), + .SLAVE_ADDR_W (VECTOR_IIR_ADDR_W) + ) ctrlport_decoder_i ( + .ctrlport_clk (ce_clk), + .ctrlport_rst (ce_rst), + .s_ctrlport_req_wr (m_ctrlport_req_wr), + .s_ctrlport_req_rd (m_ctrlport_req_rd), + .s_ctrlport_req_addr (m_ctrlport_req_addr), + .s_ctrlport_req_data (m_ctrlport_req_data), + .s_ctrlport_req_byte_en (4'hF), + .s_ctrlport_req_has_time (1'b0), + .s_ctrlport_req_time (64'b0), + .s_ctrlport_resp_ack (m_ctrlport_resp_ack), + .s_ctrlport_resp_status (), + .s_ctrlport_resp_data (m_ctrlport_resp_data), + .m_ctrlport_req_wr (dec_ctrlport_req_wr), + .m_ctrlport_req_rd (dec_ctrlport_req_rd), + .m_ctrlport_req_addr (dec_ctrlport_req_addr), + .m_ctrlport_req_data (dec_ctrlport_req_data), + .m_ctrlport_req_byte_en (), + .m_ctrlport_req_has_time (), + .m_ctrlport_req_time (), + .m_ctrlport_resp_ack (dec_ctrlport_resp_ack), + .m_ctrlport_resp_status ({NUM_PORTS{2'b0}}), + .m_ctrlport_resp_data (dec_ctrlport_resp_data) + ); + + + //--------------------------------------------------------------------------- + // Port Instances + //--------------------------------------------------------------------------- + + genvar port; + generate + for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_ports + + //----------------------------------------------------------------------- + // Signal Selection + //----------------------------------------------------------------------- + // + // Grab the appropriate CtrlPort and AXIS payload signals for this port. + // + //----------------------------------------------------------------------- + + wire ctrlport_req_wr; + wire ctrlport_req_rd; + wire [19:0] ctrlport_req_addr; + wire [31:0] ctrlport_req_data; + reg ctrlport_resp_ack; + reg [31:0] ctrlport_resp_data; + + assign ctrlport_req_wr = dec_ctrlport_req_wr[port]; + assign ctrlport_req_rd = dec_ctrlport_req_rd[port]; + assign ctrlport_req_addr = dec_ctrlport_req_addr[port*20 +: 20]; + assign ctrlport_req_data = dec_ctrlport_req_data[port*32 +: 32]; + // + assign dec_ctrlport_resp_ack[port] = ctrlport_resp_ack; + assign dec_ctrlport_resp_data[port*32 +: 32] = ctrlport_resp_data; + + wire [31:0] in_tdata; + wire in_tlast; + wire in_tvalid; + wire in_tready; + wire [31:0] out_tdata; + wire out_tlast; + wire out_tvalid; + wire out_tready; + + assign in_tdata = m_in_payload_tdata [port*32 +: 32]; + assign in_tlast = m_in_payload_tlast [port]; + assign in_tvalid = m_in_payload_tvalid[port]; + assign m_in_payload_tready[port] = in_tready; + // + assign s_out_payload_tdata [port*32+:32] = out_tdata; + assign s_out_payload_tlast [ port] = out_tlast; + assign s_out_payload_tvalid[ port] = out_tvalid; + assign out_tready = s_out_payload_tready[port]; + + + //----------------------------------------------------------------------- + // Registers + //----------------------------------------------------------------------- + + reg [$clog2(MAX_DELAY+1)-1:0] reg_delay; + reg [ REG_ALPHA_LEN-1:0] reg_alpha; + reg [ REG_BETA_LEN-1:0] reg_beta; + + reg reg_changed; + + always @(posedge ce_clk) begin + if (ce_rst) begin + reg_delay <= 'bX; + reg_alpha <= 'bX; + reg_beta <= 'bX; + reg_changed <= 1'b0; + end else begin + // Default assignments + ctrlport_resp_ack <= 1'b0; + ctrlport_resp_data <= 32'b0; + reg_changed <= 1'b0; + + //----------------------------------------- + // Register Reads + //----------------------------------------- + + if (ctrlport_req_rd) begin + ctrlport_resp_ack <= 1; + case (ctrlport_req_addr) + REG_DELAY : begin + ctrlport_resp_data[REG_MAX_DELAY_POS +: REG_DELAY_LEN] <= MAX_DELAY; + ctrlport_resp_data[REG_DELAY_POS +: REG_DELAY_LEN] <= reg_delay; + end + REG_ALPHA : + ctrlport_resp_data[REG_ALPHA_POS +: REG_ALPHA_LEN] <= reg_alpha; + REG_BETA : + ctrlport_resp_data[REG_BETA_POS +: REG_BETA_LEN] <= reg_beta; + endcase + + //----------------------------------------- + // Register Writes + //----------------------------------------- + + end else if (ctrlport_req_wr) begin + ctrlport_resp_ack <= 1; + case (ctrlport_req_addr) + REG_DELAY : begin + reg_delay <= ctrlport_req_data[REG_DELAY_POS +: REG_DELAY_LEN]; + reg_changed <= 1'b1; + end + REG_ALPHA : begin + reg_alpha <= ctrlport_req_data[REG_ALPHA_POS +: REG_ALPHA_LEN]; + reg_changed <= 1'b1; + end + REG_BETA : begin + reg_beta <= ctrlport_req_data[REG_BETA_POS +: REG_BETA_LEN]; + reg_changed <= 1'b1; + end + endcase + end + end + end + + + //----------------------------------------------------------------------- + // Vector IIR Block + //----------------------------------------------------------------------- + + vector_iir #( + .MAX_VECTOR_LEN (MAX_DELAY), + .ALPHA_W (REG_ALPHA_LEN), + .BETA_W (REG_BETA_LEN) + ) inst_vector_iir ( + .clk (ce_clk), + .reset (ce_rst | reg_changed), + .set_vector_len (reg_delay), + .set_alpha (reg_alpha), + .set_beta (reg_beta), + .i_tdata (in_tdata), + .i_tlast (in_tlast), + .i_tvalid (in_tvalid), + .i_tready (in_tready), + .o_tdata (out_tdata), + .o_tlast (out_tlast), + .o_tvalid (out_tvalid), + .o_tready (out_tready) + ); + + end + endgenerate + +endmodule // rfnoc_block_vector_iir + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_regs.vh new file mode 100644 index 000000000..e3e071d71 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_regs.vh @@ -0,0 +1,74 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_vector_iir_regs (Header) +// +// Description: +// +// This is a header file that contains the register descriptions for the +// RFNoC Vector IIR block. +// +// Each RFNoC Vector IIR block consists of NUM_PORTS independent Vector IIR +// filters. Each one has its own address space that is VECTOR_IIR_ADDR_W bits +// wide. That is, Vector IIR block N can be addressed starting at byte offset +// N*(2**VECTOR_IIR_ADDR_W). +// + +//----------------------------------------------------------------------------- +// Register Space +//----------------------------------------------------------------------------- + +// The amount of address space taken up by each Vector IIR filter. That is, the +// address space for port N starts at N*(2^VECTOR_IIR_ADDR_W). +localparam VECTOR_IIR_ADDR_W = 20'h00004; + + +//----------------------------------------------------------------------------- +// Vector IIR Register Descriptions +//----------------------------------------------------------------------------- + +// REG_DELAY (R/W) +// +// This register controls and reports the state of the filter delay. +// +// [31:16] REG_MAX_DELAY : This field reports the maximum supported vector +// length, in samples. That is, it returns the +// MAX_DELAY block parameter. +// [15: 0] REG_DELAY : This field controls/reports the current vector delay +// length in samples. Values of 5 or more are supported. +// +localparam REG_DELAY = 'h00; +// +localparam REG_MAX_DELAY_LEN = 16; +localparam REG_MAX_DELAY_POS = 16; +// +localparam REG_DELAY_LEN = 16; +localparam REG_DELAY_POS = 0; + +// REG_ALPHA (R/W) +// +// This register controls the Alpha value for the filter. This is a signed +// 16-bit value. +// +// [31:0] : Unused +// [15:0] : Alpha value to use +// +localparam REG_ALPHA = 'h04; +// +localparam REG_ALPHA_LEN = 16; +localparam REG_ALPHA_POS = 0; + +// REG_BETA (R/W) +// +// This register controls the Beta value for the filter. This is a signed +// 16-bit value. +// +// [31:0] : Unused +// [15:0] : Beta value to use +// +localparam REG_BETA = 'h08; +// +localparam REG_BETA_LEN = 16; +localparam REG_BETA_POS = 0; diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_tb.sv new file mode 100644 index 000000000..e09640a3b --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_vector_iir/rfnoc_block_vector_iir_tb.sv @@ -0,0 +1,528 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_vector_iir_tb +// +// Description: Testbench for the vector_iir RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_vector_iir_tb; + + `include "test_exec.svh" + + import PkgTestExec::*; + import PkgChdrUtils::*; + import PkgRfnocBlockCtrlBfm::*; + import PkgRfnocItemUtils::*; + + `include "rfnoc_block_vector_iir_regs.vh" + + + //--------------------------------------------------------------------------- + // Testbench Configuration + //--------------------------------------------------------------------------- + + localparam [31:0] NOC_ID = 32'h11120000; + localparam [ 9:0] THIS_PORTID = 10'h123; + localparam int CHDR_W = 64; // CHDR size in bits + localparam int MTU = 10; // Log2 of max transmission unit in CHDR words + localparam int NUM_PORTS = 2; + localparam int NUM_PORTS_I = 0+NUM_PORTS; + localparam int NUM_PORTS_O = 0+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 = 50; // Default BFM stall probability + localparam real CHDR_CLK_PER = 5.0; // 200 MHz + localparam real CTRL_CLK_PER = 8.0; // 125 MHz + localparam real CE_CLK_PER = 4.0; // 250 MHz + + localparam int MAX_DELAY = (2**MTU)*(CHDR_W/ITEM_W)-1; + localparam int NUM_PKTS = 50; // Number of packets to test + localparam int VECTOR_SIZE = SPP; // Vector size to test + localparam real ERROR = 2.0**-12; // Target 72dB of dynamic range + + + //--------------------------------------------------------------------------- + // 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_vector_iir #( + .THIS_PORTID (THIS_PORTID), + .CHDR_W (CHDR_W), + .MTU (MTU), + .NUM_PORTS (NUM_PORTS), + .MAX_DELAY (MAX_DELAY) + ) 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) + ); + + + //--------------------------------------------------------------------------- + // Filter Model + //--------------------------------------------------------------------------- + + task automatic iir_filter ( + input real alpha, + input real beta, + input real in[], + output real out[] + ); + out = new[in.size()]; + for (int i = 0; i < in.size(); i++) begin + real yd = i >= 1 ? out[i-1] : 0.0; + out[i] = in[i]*beta + yd*alpha; + `ASSERT_FATAL(abs(out[i]) <= 1.0, + "Expected value for filtered data falls outside allowed range."); + end + endtask : iir_filter + + + //--------------------------------------------------------------------------- + // Helper Tasks + //--------------------------------------------------------------------------- + + // Write a 32-bit register + task automatic write_reg(int port, bit [19:0] addr, bit [31:0] value); + blk_ctrl.reg_write((2**VECTOR_IIR_ADDR_W)*port + addr, value); + endtask : write_reg + + // Read a 32-bit register + task automatic read_reg(int port, bit [19:0] addr, output logic [63:0] value); + blk_ctrl.reg_read((2**VECTOR_IIR_ADDR_W)*port + addr, value[31: 0]); + endtask : read_reg + + // Real to fixed-point + function bit [15:0] real_to_fxp (real x); + return int'($floor(x * ((2**15)-1))); + endfunction + + // Fixed-point to real + function real fxp_to_real(bit [15:0] x); + return real'($signed(x))/((2**15)-1); + endfunction + + // Absolute value + function real abs(real x); + return (x > 0.0) ? x : -x; + endfunction + + + //--------------------------------------------------------------------------- + // Register Test Tasks + //--------------------------------------------------------------------------- + + // Test a read/write register for correct functionality + // + // port : Replay block port to use + // addr : Register byte address + // mask : Mask of the bits we expect to be writable + // initial_value : Value we expect to read initially + // + task automatic test_read_write_reg( + int port, + bit [19:0] addr, + bit [31:0] mask = 32'hFFFFFFFF, + logic [31:0] initial_value = '0 + ); + string err_msg; + logic [31:0] value; + logic [31:0] expected; + + err_msg = $sformatf("Register 0x%X failed read/write test: ", addr); + + // Check initial value + expected = initial_value; + read_reg(port, addr, value); + `ASSERT_ERROR(value === expected, {err_msg, "initial value"}); + + // Test writing 0 + expected = (initial_value & ~mask); + write_reg(port, addr, '0); + read_reg(port, addr, value); + `ASSERT_ERROR(value === expected, {err_msg, "write zero"}); + + // Write maximum value + expected = (initial_value & ~mask) | mask; + write_reg(port, addr, '1); + read_reg(port, addr, value); + `ASSERT_ERROR(value === expected, {err_msg, "write max value"}); + + // Restore original value + write_reg(port, addr, initial_value); + endtask : test_read_write_reg + + + //--------------------------------------------------------------------------- + // Test registers + //--------------------------------------------------------------------------- + + task automatic test_registers(int port = 0); + test.start_test("Test registers", 100us); + + // Test Delay (Vector Length) register. The MAX_DELAY portion is + // ready-only. DELAY portion is read/write. + test_read_write_reg( + port, + REG_DELAY, + {$clog2(MAX_DELAY+1){1'b1}} << REG_DELAY_POS, + (MAX_DELAY << REG_MAX_DELAY_POS) | ({$clog2(MAX_DELAY+1){1'bX}} << REG_DELAY_POS) + ); + + // Test Alpha register + test_read_write_reg( + port, + REG_ALPHA, + ((1<<REG_ALPHA_LEN)-1) << REG_ALPHA_POS, + {REG_ALPHA_LEN{1'bX}} << REG_ALPHA_POS + ); + + // Test Beta register + test_read_write_reg( + port, + REG_BETA, + ((1<<REG_BETA_LEN)-1) << REG_BETA_POS, + {REG_BETA_LEN{1'bX}} << REG_BETA_POS + ); + test.end_test(); + endtask : test_registers + + + //--------------------------------------------------------------------------- + // Test impulse and step response + //--------------------------------------------------------------------------- + + task automatic test_impulse_and_step(int port = 0); + real in_I[], in_Q[], out_I[], out_Q[]; + real alpha, beta; + + test.start_test("Check impulse and step response", 100us); + + alpha = 0.7; + beta = 0.3; + write_reg(port, REG_DELAY, VECTOR_SIZE); + write_reg(port, REG_ALPHA, real_to_fxp(alpha)); + write_reg(port, REG_BETA, real_to_fxp(beta)); + + // Generate input and golden output vector + in_I = new[NUM_PKTS]; + in_Q = new[NUM_PKTS]; + for (int n = 0; n < NUM_PKTS; n++) begin + // First half is an impulse, second half is a step + in_I[n] = (n == 0 || n >= NUM_PKTS/2) ? 1.0 : 0.0; + in_Q[n] = (n == 0 || n >= NUM_PKTS/2) ? -1.0 : 0.0; + end + iir_filter(alpha, beta, in_I, out_I); + iir_filter(alpha, beta, in_Q, out_Q); + // Send, receive and validate data + fork + begin : send_packets + item_t samples[$]; + for (int n = 0; n < NUM_PKTS; n++) begin + for (int k = 0; k < VECTOR_SIZE; k++) begin + samples[k] = {real_to_fxp(in_I[n]), real_to_fxp(in_Q[n])}; + end + blk_ctrl.send_items(port, samples); + end + end + begin : check_packets + item_t samples[$]; + real recv_i, recv_q; + for (int n = 0; n < NUM_PKTS; n++) begin + blk_ctrl.recv_items(port, samples); + `ASSERT_ERROR(samples.size() == VECTOR_SIZE, + "Received packet has incorrect number of samples"); + for (int k = 0; k < VECTOR_SIZE; k++) begin + recv_i = fxp_to_real(samples[k][31:16]); + recv_q = fxp_to_real(samples[k][15:0]); + `ASSERT_ERROR(abs(recv_i - out_I[n]) < ERROR, "Incorrect I value"); + `ASSERT_ERROR(abs(recv_q - out_Q[n]) < ERROR, "Incorrect Q value"); + end + end + end + join + test.end_test(); + endtask : test_impulse_and_step + + + //--------------------------------------------------------------------------- + // Test quarter rate sine response (vector stride) + //--------------------------------------------------------------------------- + + task automatic test_vector_stride(int port = 0); + real in_I[], in_Q[], out_I[], out_Q[]; + real alpha, beta; + + test.start_test("Check quarter rate complex sine response (vector stride)", 100us); + alpha = 0.9; + beta = 0.1; + write_reg(port, REG_DELAY, VECTOR_SIZE); + write_reg(port, REG_ALPHA, real_to_fxp(alpha)); + write_reg(port, REG_BETA, real_to_fxp(beta)); + // Generate input and golden output vector + in_I = new[NUM_PKTS]; + in_Q = new[NUM_PKTS]; + for (int n = 0; n < NUM_PKTS; n++) begin + // First half is an impulse, second half is a step + in_I[n] = (n % 4 == 1 || n % 4 == 3) ? 0.0 : ((n % 4 == 0) ? 1.0 : -1.0); // cos + in_Q[n] = (n % 4 == 0 || n % 4 == 2) ? 0.0 : ((n % 4 == 1) ? 1.0 : -1.0); // sin + end + iir_filter(alpha, beta, in_I, out_I); + iir_filter(alpha, beta, in_Q, out_Q); + // Send, receive and validate data + fork + begin : send_packets + item_t samples[$]; + for (int n = 0; n < NUM_PKTS; n++) begin + for (int k = 0; k < VECTOR_SIZE; k++) begin + samples[k] = {real_to_fxp(in_I[n]), real_to_fxp(in_Q[n])}; + end + blk_ctrl.send_items(port, samples); + end + end + begin : check_packets + item_t samples[$]; + real recv_i, recv_q; + for (int n = 0; n < NUM_PKTS; n++) begin + blk_ctrl.recv_items(port, samples); + `ASSERT_ERROR(samples.size() == VECTOR_SIZE, + "Received packet has incorrect number of samples"); + for (int k = 0; k < VECTOR_SIZE; k++) begin + recv_i = fxp_to_real(samples[k][31:16]); + recv_q = fxp_to_real(samples[k][15:0]); + `ASSERT_ERROR(abs(recv_i - out_I[n]) < ERROR, "Incorrect I value"); + `ASSERT_ERROR(abs(recv_q - out_Q[n]) < ERROR, "Incorrect Q value"); + end + end + end + join + test.end_test(); + endtask : test_vector_stride + + + //--------------------------------------------------------------------------- + // Test quarter rate sine response (sample stride) + //--------------------------------------------------------------------------- + + task automatic test_sample_stride(int port = 0); + real in_I[], in_Q[], out_I[], out_Q[]; + real alpha, beta; + + test.start_test("Check quarter rate complex sine response (sample stride)", 100us); + alpha = 0.01; + beta = 0.99; + write_reg(port, REG_DELAY, VECTOR_SIZE); + write_reg(port, REG_ALPHA, real_to_fxp(alpha)); + write_reg(port, REG_BETA, real_to_fxp(beta)); + // Generate input and golden output vector + in_I = new[NUM_PKTS]; + in_Q = new[NUM_PKTS]; + for (int n = 0; n < NUM_PKTS; n++) begin + // First half is an impulse, second half is a step + in_I[n] = (n % 4 == 1 || n % 4 == 3) ? 0.0 : ((n % 4 == 0) ? 1.0 : -1.0); // cos + in_Q[n] = (n % 4 == 0 || n % 4 == 2) ? 0.0 : ((n % 4 == 1) ? 1.0 : -1.0); // sin + end + iir_filter(alpha, beta, in_I, out_I); + iir_filter(alpha, beta, in_Q, out_Q); + // Send, receive and validate data + fork + begin + item_t samples[$]; + for (int n = 0; n < NUM_PKTS; n++) begin + for (int k = 0; k < VECTOR_SIZE; k++) begin + if (k % 4 == 0) + samples[k] = {real_to_fxp(in_I[n]), real_to_fxp(in_Q[n])}; + else if (k % 4 == 2) + samples[k] = {real_to_fxp(in_Q[n]), real_to_fxp(in_I[n])}; + else + samples[k] = {real_to_fxp(0.0), real_to_fxp(0.0)}; + end + blk_ctrl.send_items(port, samples); + end + end + begin + item_t samples[$]; + real recv_i, recv_q; + for (int n = 0; n < NUM_PKTS; n++) begin + blk_ctrl.recv_items(port, samples); + `ASSERT_ERROR(samples.size() == VECTOR_SIZE, + "Received packet has incorrect number of samples"); + for (int k = 0; k < VECTOR_SIZE; k++) begin + recv_i = fxp_to_real(samples[k][31:16]); + recv_q = fxp_to_real(samples[k][15:0]); + if (k % 4 == 0) begin + `ASSERT_ERROR(abs(recv_i - out_I[n]) < 0.01, "Incorrect I value"); + `ASSERT_ERROR(abs(recv_q - out_Q[n]) < 0.01, "Incorrect Q value"); + end else if (k % 4 == 2) begin + `ASSERT_ERROR(abs(recv_i - out_Q[n]) < 0.01, "Incorrect I value"); + `ASSERT_ERROR(abs(recv_q - out_I[n]) < 0.01, "Incorrect Q value"); + end else begin + `ASSERT_ERROR(abs(recv_i) < 0.01, "Incorrect I value"); + `ASSERT_ERROR(abs(recv_q) < 0.01, "Incorrect Q value"); + end + end + end + end + join + test.end_test(); + endtask : test_sample_stride + + + //--------------------------------------------------------------------------- + // Main Test Process + //--------------------------------------------------------------------------- + + initial begin : tb_main + + // Initialize the test exec object for this testbench + test.start_tb("rfnoc_block_vector_iir_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 + //-------------------------------- + + for (int port = 0; port < NUM_PORTS; port++) begin + // Run these tests on all ports + test_registers(port); + test_impulse_and_step(port); + end + test_vector_stride(); + test_sample_stride(); + + //-------------------------------- + // Finish Up + //-------------------------------- + + // Display final statistics and results + test.end_tb(); + end : tb_main + +endmodule : rfnoc_block_vector_iir_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/vector_iir.v b/fpga/usrp3/lib/rfnoc/vector_iir.v index a875f34fe..041bb15f3 100644 --- a/fpga/usrp3/lib/rfnoc/vector_iir.v +++ b/fpga/usrp3/lib/rfnoc/vector_iir.v @@ -33,7 +33,7 @@ // module vector_iir #( - parameter MAX_VECTOR_LEN = 1024, + parameter MAX_VECTOR_LEN = 1023, parameter IN_W = 16, parameter OUT_W = 16, parameter ALPHA_W = 16, @@ -41,19 +41,19 @@ module vector_iir #( parameter FEEDBACK_W = 25, parameter ACCUM_HEADROOM = 4 )( - input wire clk, - input wire reset, - input wire [$clog2(MAX_VECTOR_LEN)-1:0] set_vector_len, - input wire [BETA_W-1:0] set_beta, - input wire [ALPHA_W-1:0] set_alpha, - input wire [IN_W*2-1:0] i_tdata, - input wire i_tlast, - input wire i_tvalid, - output wire i_tready, - output wire [OUT_W*2-1:0] o_tdata, - output wire o_tlast, - output wire o_tvalid, - input wire o_tready + input wire clk, + input wire reset, + input wire [$clog2(MAX_VECTOR_LEN+1)-1:0] set_vector_len, + input wire [BETA_W-1:0] set_beta, + input wire [ALPHA_W-1:0] set_alpha, + input wire [IN_W*2-1:0] i_tdata, + input wire i_tlast, + input wire i_tvalid, + output wire i_tready, + output wire [OUT_W*2-1:0] o_tdata, + output wire o_tlast, + output wire o_tvalid, + input wire o_tready ); // There are four registers between the input and output @@ -70,9 +70,9 @@ module vector_iir #( localparam MIN_FB_DELAY = 4; // Pipeline settings for timing - reg [$clog2(MAX_VECTOR_LEN)-1:0] reg_fb_delay; - reg signed [BETA_W-1:0] reg_beta; - reg signed [ALPHA_W-1:0] reg_alpha; + reg [$clog2(MAX_VECTOR_LEN-MIN_FB_DELAY)-1:0] reg_fb_delay; + reg signed [ BETA_W-1:0] reg_beta; + reg signed [ ALPHA_W-1:0] reg_alpha; always @(posedge clk) begin reg_fb_delay <= set_vector_len - MIN_FB_DELAY - 1; //Adjust for pipeline delay |