From ada6a4e46770d22d528b2b6fbc2bdd71e5e3064d Mon Sep 17 00:00:00 2001 From: Wade Fife Date: Thu, 4 Mar 2021 16:27:24 -0600 Subject: examples: Add IP to OOT RFNoC gain example This updates the gain example to show how to use RFNoC IP, in-tree Xilinx IP, and out-of-tree Xilinx IP in a custom RFNoC block. --- host/examples/rfnoc-example/fpga/Makefile.srcs | 12 +- .../rfnoc-example/fpga/ip/cmplx_mul/Makefile.inc | 17 ++ .../rfnoc-example/fpga/ip/cmplx_mul/cmplx_mul.xci | 187 +++++++++++++++++++++ .../rfnoc-example/fpga/rfnoc_block_gain/Makefile | 26 ++- .../fpga/rfnoc_block_gain/rfnoc_block_gain.v | 132 +++++++++++---- 5 files changed, 335 insertions(+), 39 deletions(-) create mode 100644 host/examples/rfnoc-example/fpga/ip/cmplx_mul/Makefile.inc create mode 100644 host/examples/rfnoc-example/fpga/ip/cmplx_mul/cmplx_mul.xci (limited to 'host') diff --git a/host/examples/rfnoc-example/fpga/Makefile.srcs b/host/examples/rfnoc-example/fpga/Makefile.srcs index f95dab6ea..88a729c97 100644 --- a/host/examples/rfnoc-example/fpga/Makefile.srcs +++ b/host/examples/rfnoc-example/fpga/Makefile.srcs @@ -6,16 +6,18 @@ # We first need to figure out our own path, in case this file is being included # from somewhere else (e.g., from a fpgadev/top/$device directory) -RFNOC_EXAMPLE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +OOT_FPGA_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) # One include statement for every RFNoC block with its own subdirectory, which # itself will contain a Makefile.srcs -include $(RFNOC_EXAMPLE_DIR)/rfnoc_block_gain/Makefile.srcs +include $(OOT_FPGA_DIR)/rfnoc_block_gain/Makefile.srcs + +include $(OOT_FPGA_DIR)/ip/cmplx_mul/Makefile.inc +LIB_IP_XCI_SRCS += $(LIB_IP_CMPLX_MUL_SRCS) # If there are additional modules or IP (other than what is in the RFNoC block # subdirectories) that needs to get installed in order to synthesize blocks from # this module, list them here: -#RFNOC_OOT_SRCS += $(abspath $(addprefix ${RFNOC_EXAMPLE_DIR}, -#my_other_module.v \ -#ip/my_ip_core/my_ip_core.xci \ +#RFNOC_OOT_SRCS += $(abspath $(addprefix ${OOT_FPGA_DIR}, \ +#$(IP_BUILD_DIR)/cmplx_mul/cmplx_mul.xci \ #)) diff --git a/host/examples/rfnoc-example/fpga/ip/cmplx_mul/Makefile.inc b/host/examples/rfnoc-example/fpga/ip/cmplx_mul/Makefile.inc new file mode 100644 index 000000000..14d456146 --- /dev/null +++ b/host/examples/rfnoc-example/fpga/ip/cmplx_mul/Makefile.inc @@ -0,0 +1,17 @@ +# +# Copyright 2021 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +include $(TOOLS_DIR)/make/viv_ip_builder.mak + +LIB_IP_CMPLX_MUL_SRCS = $(IP_BUILD_DIR)/cmplx_mul/cmplx_mul.xci + +LIB_IP_CMPLX_MUL_OUTS = $(addprefix $(IP_BUILD_DIR)/cmplx_mul/, \ +cmplx_mul.xci.out \ +synth/cmplx_mul.vhd \ +) + +$(LIB_IP_CMPLX_MUL_SRCS) $(LIB_IP_CMPLX_MUL_OUTS) : $(OOT_FPGA_DIR)/ip/cmplx_mul/cmplx_mul.xci + $(call BUILD_VIVADO_IP,cmplx_mul,$(ARCH),$(PART_ID),$(OOT_FPGA_DIR)/ip,$(IP_BUILD_DIR),0) diff --git a/host/examples/rfnoc-example/fpga/ip/cmplx_mul/cmplx_mul.xci b/host/examples/rfnoc-example/fpga/ip/cmplx_mul/cmplx_mul.xci new file mode 100644 index 000000000..9ba4231db --- /dev/null +++ b/host/examples/rfnoc-example/fpga/ip/cmplx_mul/cmplx_mul.xci @@ -0,0 +1,187 @@ + + + xilinx.com + xci + unknown + 1.0 + + + cmplx_mul + + + ACTIVE_LOW + + 10000000 + 0 + 0.000 + 0 + + 100000000 + 0 + 1 + 1 + 0 + 0 + undef + 0.000 + 10 + 0 + 0 + 0 + + 100000000 + 0 + 1 + 1 + 0 + 0 + undef + 0.000 + 4 + 0 + 0 + 0 + + 100000000 + 0 + 1 + 1 + 0 + 0 + undef + 0.000 + 4 + 0 + 0 + 0 + + 100000000 + 0 + 0 + 0 + 0 + 0 + undef + 0.000 + 0 + 0 + 0 + 0 + 16 + 16 + 0 + 1 + 1 + 0 + 1 + 0 + 0 + 0 + 7 + 1 + 80 + 1 + 1 + 33 + 32 + 1 + 32 + 1 + 8 + 1 + 1 + 1 + 0 + xc7k410t + kintex7 + 0 + 0 + 0 + 1 + false + 16 + true + 1 + 16 + 1 + 1 + testerific + Blocking + true + false + true + false + false + false + Automatic + 7 + Use_Mults + Performance + Pass_A_TLAST + 33 + Truncate + kintex7 + + + xc7k410t + fbg900 + VERILOG + + MIXED + -1 + + + TRUE + TRUE + IP_Flow + 17 + TRUE + . + + . + 2019.1.1_AR73068 + OUT_OF_CONTEXT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile index 1ff3046ee..0239041b9 100644 --- a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile +++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile @@ -19,24 +19,40 @@ include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak #------------------------------------------------- # Design Specific #------------------------------------------------- + +# In-tree IP +LIB_IP_DIR = $(BASE_DIR)/../lib/ip +include $(LIB_IP_DIR)/complex_multiplier/Makefile.inc + +# Out-of-tree IP +OOT_FPGA_DIR = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/../ +include $(OOT_FPGA_DIR)/ip/cmplx_mul/Makefile.inc + # Include makefiles and sources for the DUT and its # dependencies. +include $(BASE_DIR)/../lib/rfnoc/Makefile.srcs 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) \ +DESIGN_SRCS += $(abspath \ +$(RFNOC_SRCS) \ +$(RFNOC_CORE_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_OOT_SRCS) \ +$(LIB_IP_CMPLX_MUL_SRCS) \ +$(LIB_IP_COMPLEX_MULTIPLIER_SRCS) \ ) #------------------------------------------------- # Testbench Specific #------------------------------------------------- -SIM_TOP = rfnoc_block_gain_tb +SIM_TOP = rfnoc_block_gain_tb glbl SIM_SRCS = \ +$(abspath $(IP_BUILD_DIR)/cmplx_mul/sim/cmplx_mul.vhd) \ +$(abspath $(IP_BUILD_DIR)/complex_multiplier/sim/complex_multiplier.vhd) \ $(abspath rfnoc_block_gain_tb.sv) \ +$(VIVADO_PATH)/data/verilog/src/glbl.v \ #------------------------------------------------- # Bottom-of-Makefile diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v index 929439a52..6f050e8bc 100644 --- a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v +++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v @@ -17,6 +17,11 @@ // CHDR_W : AXIS-CHDR data bus width // MTU : Maximum transmission unit (i.e., maximum packet size in // CHDR words is 2**MTU). +// IP_OPTION : Select which IP to use for the complex multiply. Use one of +// the following options: +// HDL_IP = In-tree RFNoC HDL, with a DSP48E1 primitive +// IN_TREE_IP = In-tree "complex_multiplier" (Xilinx IP) +// OUT_OF_TREE_IP = Out-of-tree "cmplx_mul" (Xilinx IP) // `default_nettype none @@ -25,7 +30,8 @@ module rfnoc_block_gain #( parameter [9:0] THIS_PORTID = 10'd0, parameter CHDR_W = 64, - parameter [5:0] MTU = 10 + parameter [5:0] MTU = 10, + parameter IP_OPTION = "HDL_IP" )( // RFNoC Framework Clocks and Resets input wire rfnoc_chdr_clk, @@ -250,36 +256,104 @@ module rfnoc_block_gain #( wire mult_tvalid; wire mult_tready; - // Multiply complex sample by a real-valued gain. Only input the gain - // (real_tvalid) when we have payload data to go in (cplx_tdata). That way - // the current gain value always applies to the current sample. This assumes - // that real_tready and cplx_tready have identical behavior. + // Multiply each complex sample by a real-valued gain. Only input the gain + // when we have payload data to go in (m_in_payload_tdata). That way the + // current gain value always applies to the current sample. This assumes that + // the tready of both inputs have identical behavior. // // Note that we receive the data with I on bits [31:16] and Q on bits [15:0], - // but this does not matter to our multiplier. - // - mult_rc #( - .WIDTH_REAL (16), - .WIDTH_CPLX (16), - .WIDTH_P (32), - .DROP_TOP_P (5), // Must be 5 for a normal multiply in DSP48E1 - .LATENCY (4) // Turn on all pipeline registers in the DSP48E1 - ) mult_rc_i ( - .clk (axis_data_clk), - .reset (axis_data_rst), - .real_tdata (reg_gain), - .real_tlast (m_in_payload_tlast), - .real_tvalid (m_in_payload_tvalid), - .real_tready (), - .cplx_tdata (m_in_payload_tdata), - .cplx_tlast (m_in_payload_tlast), - .cplx_tvalid (m_in_payload_tvalid), - .cplx_tready (m_in_payload_tready), - .p_tdata (mult_tdata), - .p_tlast (mult_tlast), - .p_tvalid (mult_tvalid), - .p_tready (mult_tready) - ); + // but the I/Q order does not matter to our complex multiplier. + + generate + // Use a generate statement to choose which IP to use for the multiply. + // These all do the same thing and we only have multiple options to show + // how you can use IP from different locations. + + if (IP_OPTION == "HDL_IP") begin : gen_rfnoc_ip + // Use the RFNoC mult_rc Verilog module, which uses a DSP48E1 primitive + mult_rc #( + .WIDTH_REAL (16), + .WIDTH_CPLX (16), + .WIDTH_P (32), + .DROP_TOP_P (5), // Must be 5 for a normal multiply in DSP48E1 + .LATENCY (4) // Turn on all pipeline registers in the DSP48E1 + ) mult_rc_i ( + .clk (axis_data_clk), + .reset (axis_data_rst), + .real_tdata (reg_gain), + .real_tlast (m_in_payload_tlast), + .real_tvalid (m_in_payload_tvalid), + .real_tready (), + .cplx_tdata (m_in_payload_tdata), + .cplx_tlast (m_in_payload_tlast), + .cplx_tvalid (m_in_payload_tvalid), + .cplx_tready (m_in_payload_tready), + .p_tdata (mult_tdata), + .p_tlast (mult_tlast), + .p_tvalid (mult_tvalid), + .p_tready (mult_tready) + ); + + end else if (IP_OPTION == "IN_TREE_IP") begin : gen_in_tree_ip + // Use the in-tree "complex_multiplier" IP, which is a Xilinx Complex + // Multiplier LogiCORE IP located in the UHD repository in + // fpga/usrp3/lib/ip/. + + // The LSB of the output is clipped in this IP, so double the gain to + // compensate. This limits the maximum gain in this version. + wire [15:0] gain = 2*reg_gain; + + complex_multiplier complex_multiplier ( + .aclk (axis_data_clk), + .aresetn (~axis_data_rst), + .s_axis_a_tdata ({16'b0, gain}), + .s_axis_a_tlast (m_in_payload_tlast), + .s_axis_a_tvalid (m_in_payload_tvalid), + .s_axis_a_tready (), + .s_axis_b_tdata (m_in_payload_tdata), + .s_axis_b_tlast (m_in_payload_tlast), + .s_axis_b_tvalid (m_in_payload_tvalid), + .s_axis_b_tready (m_in_payload_tready), + .s_axis_ctrl_tdata (8'd0), + .s_axis_ctrl_tvalid (1'b1), + .s_axis_ctrl_tready (), + .m_axis_dout_tdata (mult_tdata), + .m_axis_dout_tlast (mult_tlast), + .m_axis_dout_tvalid (mult_tvalid), + .m_axis_dout_tready (mult_tready) + ); + + end else if (IP_OPTION == "OUT_OF_TREE_IP") begin : gen_oot_ip + // Use the out-of-tree "cmplx_mul" IP, which is a Xilinx Complex + // Multiplier LogiCORE IP located in the IP directory of this example. + + // This IP has a 33-bit output, but because it's AXI-Stream, each + // component is placed in a 5-byte word. Since our gain is real only, + // we'll never need all 33-bits. + wire [79:0] m_axis_dout_tdata; + + cmplx_mul cmplx_mul_i ( + .aclk (axis_data_clk), + .aresetn (~axis_data_rst), + .s_axis_a_tdata ({16'd0, reg_gain}), + .s_axis_a_tlast (m_in_payload_tlast), + .s_axis_a_tvalid (m_in_payload_tvalid), + .s_axis_a_tready (), + .s_axis_b_tdata (m_in_payload_tdata), + .s_axis_b_tlast (m_in_payload_tlast), + .s_axis_b_tvalid (m_in_payload_tvalid), + .s_axis_b_tready (m_in_payload_tready), + .m_axis_dout_tdata (m_axis_dout_tdata), + .m_axis_dout_tlast (mult_tlast), + .m_axis_dout_tvalid (mult_tvalid), + .m_axis_dout_tready (mult_tready) + ); + + assign mult_tdata[31: 0] = m_axis_dout_tdata[31: 0]; + assign mult_tdata[63:32] = m_axis_dout_tdata[71:40]; + + end + endgenerate // Clip the results axi_clip_complex #( -- cgit v1.2.3