Ok. I know what was stopping the RFNoC flowgraph after correction of the clock. I was setting SPP (samples-per-packet) to a value that was somehow wrong for this case. After removing that command-line parameter, the loopback through ‘Gain’ block started to work.
I’m posting my changed ‘Gain’ block, that is clocked with ‘ce’ clock, in case anyone is interested. Best Regards,\ Piotr Krysik
// // Copyright 2024 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: rfnoc_block_gain_tb // // Description: Testbench for the gain RFNoC block. // `default_nettype none module rfnoc_block_gain_tb #( string IP_OPTION = "HDL_IP" ); `include "test_exec.svh" import PkgTestExec::*; import PkgChdrUtils::*; import PkgRfnocBlockCtrlBfm::*; import PkgRfnocItemUtils::*; //--------------------------------------------------------------------------- // Testbench Configuration //--------------------------------------------------------------------------- localparam [31:0] NOC_ID = 32'h00000B16; localparam [ 9:0] THIS_PORTID = 10'h123; localparam int CHDR_W = 64; // CHDR size in bits localparam int MTU = 13; // Log2 of max transmission unit in CHDR words localparam int NUM_PORTS_I = 1; localparam int NUM_PORTS_O = 1; 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; // 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_gain #( .THIS_PORTID (THIS_PORTID), .CHDR_W (CHDR_W), .MTU (MTU), .IP_OPTION (IP_OPTION) ) 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) ); //--------------------------------------------------------------------------- // Main Test Process //--------------------------------------------------------------------------- // Multiply two signed 16-bit numbers and clip the result function shortint mult_and_clip(shortint a, shortint b); int product; shortint result; product = int'(a) * int'(b); result = product[15:0]; if (product > 16'sh7FFF) result = 16'sh7FFF; if (product < 16'sh8000) result = 16'sh8000; return result; endfunction : mult_and_clip // Generate a random signed 16-bit integer in the range [a, b] function shortint rand_shortint(int a, int b); return signed'($urandom_range(b - a)) + a; endfunction : rand_shortint localparam int REG_GAIN_ADDR = dut.REG_GAIN_ADDR; initial begin : tb_main string tb_name; tb_name = $sformatf("rfnoc_block_gain_tb: IP_OPTION = %s", IP_OPTION); // Initialize the test exec object for this testbench test.start_tb(tb_name); // 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 //-------------------------------- begin // Read and write the gain register to make sure it updates correctly. logic [31:0] val32; test.start_test("Verify gain register", 5us); blk_ctrl.reg_read(REG_GAIN_ADDR, val32); `ASSERT_ERROR( val32 == 1, "Initial value for REG_GAIN_ADDR is not 1"); // Write a value wider than the register to verify the width blk_ctrl.reg_write(REG_GAIN_ADDR, 32'h12348765); blk_ctrl.reg_read(REG_GAIN_ADDR, val32); `ASSERT_ERROR( val32 == 32'h8765, "Initial value for REG_GAIN_ADDR is not correct"); test.end_test(); end begin // Iterate through a series of gain values to test. localparam shortint MAX_TEST_VAL = 255; localparam shortint MIN_TEST_VAL = -255; static logic [15:0] test_gains[$] = { 1, // Make sure unity gain leaves data unmodified -1, // Make sure -1 negates data 0, // Make sure 0 gain results in 0 37, // Make sure a normal gain computes correctly -22, // Make sure a normal gain computes correctly 256 // Make sure a large gain causes clipping }; foreach (test_gains[gain_index]) begin shortint gain; int num_bytes; item_t send_samples[$]; // Sample words item_t recv_samples[$]; // Sample words gain = test_gains[gain_index]; test.start_test($sformatf("Test gain of %0d", int'(gain)), 10us); blk_ctrl.reg_write(REG_GAIN_ADDR, gain); // Generate a payload of random samples in the range [-255, 255], two // samples per CHDR word. send_samples = {}; for (int i = 0; i < SPP; i++) begin send_samples.push_back({ rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL), // I rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL) // Q }); end // Queue a packet for transfer blk_ctrl.send_items(0, send_samples); // Receive the output packet blk_ctrl.recv_items(0, recv_samples); // Check the resulting payload size `ASSERT_ERROR(recv_samples.size() == SPP, "Received payload didn't match size of payload sent"); // Check the resulting samples for (int i = 0; i < SPP; i++) begin item_t sample_in; item_t expected; item_t sample_out; sample_in = send_samples[i]; sample_out = recv_samples[i]; expected[31:16] = mult_and_clip(gain, sample_in[31:16]); // I expected[15: 0] = mult_and_clip(gain, sample_in[15: 0]); // Q `ASSERT_ERROR( sample_out == expected, $sformatf("For sample %0d, gain %0d, input 0x%X, received 0x%X, expected 0x%X", i, gain, sample_in, sample_out, expected)); end test.end_test(); end end //-------------------------------- // Finish Up //-------------------------------- // Display final statistics and results. End the TB, but don't $finish, // since we don't want to kill other instances of this testbench that may // be running. test.end_tb(.finish(0)); end : tb_main endmodule : rfnoc_block_gain_tb `default_nettype wire
schema: rfnoc_modtool_args module_name: gain version: "1.0" rfnoc_version: "1.0" chdr_width: 64 noc_id: 0xB16 clocks: - name: rfnoc_chdr freq: "[]" - name: rfnoc_ctrl freq: "[]" - name: ce freq: "[]" control: fpga_iface: ctrlport interface_direction: slave fifo_depth: 32 clk_domain: ce ctrlport: byte_mode: False timed: False has_status: False data: fpga_iface: axis_pyld_ctxt clk_domain: ce inputs: in: index: 0 item_width: 32 nipc: 1 context_fifo_depth: 2 payload_fifo_depth: 2 format: sc16 mdata_sig: ~ outputs: out: index: 0 item_width: 32 nipc: 1 context_fifo_depth: 2 payload_fifo_depth: 2 format: sc16 mdata_sig: ~
// // Copyright 2021 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: rfnoc_block_gain // // Description: // // This is an example RFNoC block. It applies a numeric gain to incoming // samples then outputs the result. A single register is used to control the // gain setting. // // 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). // 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 module rfnoc_block_gain #( parameter [9:0] THIS_PORTID = 10'd0, parameter CHDR_W = 64, parameter [5:0] MTU = 10, parameter IP_OPTION = "IN_TREE_IP" ) ( // RFNoC Framework Clocks and Resets input wire rfnoc_chdr_clk, input wire rfnoc_ctrl_clk, input wire ce_clk, // RFNoC Backend Interface input wire [511:0] rfnoc_core_config, output wire [511:0] rfnoc_core_status, // AXIS-CHDR Input Ports (from framework) input wire [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata, input wire [(1)-1:0] s_rfnoc_chdr_tlast, input wire [(1)-1:0] s_rfnoc_chdr_tvalid, output wire [(1)-1:0] s_rfnoc_chdr_tready, // AXIS-CHDR Output Ports (to framework) output wire [(1)*CHDR_W-1:0] m_rfnoc_chdr_tdata, output wire [(1)-1:0] m_rfnoc_chdr_tlast, output wire [(1)-1:0] m_rfnoc_chdr_tvalid, input wire [(1)-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 ); // These are examples of how to include an in-tree header file. UHD_FPGA_DIR // is defined automatically and can be referenced as needed. Tools vary // somewhat in how they support using macros in `include statements. // // This works in Vivado: // // `include `"`UHD_FPGA_DIR/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh`" // // Some tools allow this: // // `define INCLUDE_UHD_FILE(REL_PATH) `"`UHD_FPGA_DIR/REL_PATH`" // `include `INCLUDE_UHD_FILE(usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh) // // This should work in most tools: `define RFNOC_CHDR_UTILS_PATH `"`UHD_FPGA_DIR/usrp3/lib/rfnoc/core/rfnoc_chdr_utils.vh`" `include `RFNOC_CHDR_UTILS_PATH //--------------------------------------------------------------------------- // Signal Declarations //--------------------------------------------------------------------------- // Clocks and Resets wire ctrlport_clk; wire ctrlport_rst; wire axis_data_clk; wire axis_data_rst; // CtrlPort Master wire m_ctrlport_req_wr; wire m_ctrlport_req_rd; wire [19:0] m_ctrlport_req_addr; wire [31:0] m_ctrlport_req_data; reg m_ctrlport_resp_ack; reg [31:0] m_ctrlport_resp_data; // Payload Stream to User Logic: in wire [32*1-1:0] m_in_payload_tdata; wire [1-1:0] m_in_payload_tkeep; wire m_in_payload_tlast; wire m_in_payload_tvalid; wire m_in_payload_tready; // Context Stream to User Logic: in wire [CHDR_W-1:0] m_in_context_tdata; wire [3:0] m_in_context_tuser; wire m_in_context_tlast; wire m_in_context_tvalid; wire m_in_context_tready; // Payload Stream from User Logic: out wire [32*1-1:0] s_out_payload_tdata; wire [0:0] s_out_payload_tkeep; wire s_out_payload_tlast; wire s_out_payload_tvalid; wire s_out_payload_tready; // Context Stream from User Logic: out wire [CHDR_W-1:0] s_out_context_tdata; wire [3:0] s_out_context_tuser; wire s_out_context_tlast; wire s_out_context_tvalid; wire s_out_context_tready; //--------------------------------------------------------------------------- // NoC Shell //--------------------------------------------------------------------------- noc_shell_gain #( .CHDR_W (CHDR_W), .THIS_PORTID (THIS_PORTID), .MTU (MTU) ) noc_shell_gain_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 (), // 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_clk), .ctrlport_rst (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_clk), .axis_data_rst (axis_data_rst), // Payload Stream to User Logic: in .m_in_payload_tdata (m_in_payload_tdata), .m_in_payload_tkeep (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_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) ); //--------------------------------------------------------------------------- // User Logic //--------------------------------------------------------------------------- // // The code above this point is essentially unmodified from what was // generated by the tool. The code below implements the gain example. // // All registers are in the ctrlport_clk domain and the signal processing is // in the axis_data_clk domain. However, we specified in the block YAML // configuration file that we want both the control and data interfaces on // the rfnoc_chdr clock. So we don't need to worry about crossing the // register data from ctrlport_clk and axis_data_clk. // //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Registers //--------------------------------------------------------------------------- // // There's only one register now, but we'll structure the register code to // make it easier to add more registers later. // //--------------------------------------------------------------------------- localparam REG_GAIN_ADDR = 0; // Address for gain value localparam REG_GAIN_DEFAULT = 1; // Default gain value reg [15:0] reg_gain = REG_GAIN_DEFAULT; always @(posedge ctrlport_clk) begin if (ctrlport_rst) begin reg_gain = REG_GAIN_DEFAULT; end else begin // Default assignment m_ctrlport_resp_ack <= 0; // Handle read requests if (m_ctrlport_req_rd) begin case (m_ctrlport_req_addr) REG_GAIN_ADDR: begin m_ctrlport_resp_ack <= 1; m_ctrlport_resp_data <= { 16'b0, reg_gain }; end endcase end // Handle write requests if (m_ctrlport_req_wr) begin case (m_ctrlport_req_addr) REG_GAIN_ADDR: begin m_ctrlport_resp_ack <= 1; reg_gain <= m_ctrlport_req_data[15:0]; end endcase end end end //--------------------------------------------------------------------------- // Signal Processing //--------------------------------------------------------------------------- // // Multiply each complex sample by a real-valued gain. The RFNoC signals // m_in_payload_* and m_out_payload_* expect the data with the real/I // component on the upper bits [31:16] and the imaginary/Q component on the // lower bits [15:0]. // // We only input the real-valued gain (reg_gain) when we have payload data to // go in (m_in_payload_*). That way the current gain value always applies to // the current sample. This assumes that the tready of both inputs have // identical behavior. // //--------------------------------------------------------------------------- // Multiply result. I/real in [63:32], Q/imaginary in [31:0] (sc32). wire [63:0] mult_tdata; wire mult_tlast; wire mult_tvalid; wire mult_tready; 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 "cmul" module, which uses the in-tree "complex_multiplier" IP. // This 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; cmul cmul_i ( .clk (axis_data_clk), .reset (axis_data_rst), .a_tdata ({gain, 16'b0}), .a_tlast (m_in_payload_tlast ), .a_tvalid (m_in_payload_tvalid), .a_tready (), .b_tdata (m_in_payload_tdata ), .b_tlast (m_in_payload_tlast ), .b_tvalid (m_in_payload_tvalid), .b_tready (m_in_payload_tready), .o_tdata (mult_tdata), .o_tlast (mult_tlast), .o_tvalid (mult_tvalid), .o_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 expects real/I in the lower bits and imaginary/Q in the upper // bits, so we swap I and Q on the input and output to match RFNoC. // // 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}), // Real 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[15:0], // Imaginary m_in_payload_tdata[31:16]}), // Real .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[63:32] = m_axis_dout_tdata[31: 0]; // Real assign mult_tdata[31: 0] = m_axis_dout_tdata[71:40]; // Imaginary end endgenerate // Clip the results axi_clip_complex #( .WIDTH_IN (32), .WIDTH_OUT (16) ) axi_clip_complex_i ( .clk (axis_data_clk), .reset (axis_data_rst), .i_tdata (mult_tdata), .i_tlast (mult_tlast), .i_tvalid (mult_tvalid), .i_tready (mult_tready), .o_tdata (s_out_payload_tdata), .o_tlast (s_out_payload_tlast), .o_tvalid (s_out_payload_tvalid), .o_tready (s_out_payload_tready) ); // Only 1-sample per clock, so tkeep should always be asserted assign s_out_payload_tkeep = 1'b1; // We're not doing anything fancy with the context (the CHDR header info) so // we can simply pass the input context through 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; endmodule // rfnoc_block_gain `default_nettype wire
// // Copyright 2024 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: noc_shell_gain // // Description: // // This is a tool-generated NoC-shell for the gain 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 // CTRL_CLK_IDX : The index of the control clock for this block. This is used // to populate the backend interface, from where UHD can query // the clock index and thus auto-deduct which clock is used. // TB_CLK_IDX : The index of the timebase clock for this block. This is used // to populate the backend interface, from where UHD can query // the clock index and thus auto-deduct which clock is used. // MTU : Maximum transmission unit (i.e., maximum packet size in // CHDR words is 2**MTU). // `default_nettype none module noc_shell_gain #( parameter [9:0] THIS_PORTID = 10'd0, parameter CHDR_W = 64, parameter [5:0] CTRL_CLK_IDX = 6'h3F, parameter [5:0] TB_CLK_IDX = 6'h3F, parameter [5:0] MTU = 10 ) ( //--------------------- // Framework Interface //--------------------- // RFNoC Framework Clocks input wire rfnoc_chdr_clk, input wire rfnoc_ctrl_clk, input wire ce_clk, // NoC Shell Generated Resets output wire rfnoc_chdr_rst, output wire rfnoc_ctrl_rst, output wire ce_rst, // RFNoC Backend Interface input wire [511:0] rfnoc_core_config, output wire [511:0] rfnoc_core_status, // AXIS-CHDR Input Ports (from framework) input wire [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata, input wire [(1)-1:0] s_rfnoc_chdr_tlast, input wire [(1)-1:0] s_rfnoc_chdr_tvalid, output wire [(1)-1:0] s_rfnoc_chdr_tready, // AXIS-CHDR Output Ports (to framework) output wire [(1)*CHDR_W-1:0] m_rfnoc_chdr_tdata, output wire [(1)-1:0] m_rfnoc_chdr_tlast, output wire [(1)-1:0] m_rfnoc_chdr_tvalid, input wire [(1)-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 [32*1-1:0] m_in_payload_tdata, output wire [1-1:0] m_in_payload_tkeep, output wire m_in_payload_tlast, output wire m_in_payload_tvalid, input wire m_in_payload_tready, // Context Stream to User Logic: in output wire [CHDR_W-1:0] m_in_context_tdata, output wire [3:0] m_in_context_tuser, output wire m_in_context_tlast, output wire m_in_context_tvalid, input wire m_in_context_tready, // Payload Stream from User Logic: out input wire [32*1-1:0] s_out_payload_tdata, input wire [0:0] s_out_payload_tkeep, input wire s_out_payload_tlast, input wire s_out_payload_tvalid, output wire s_out_payload_tready, // Context Stream from User Logic: out input wire [CHDR_W-1:0] s_out_context_tdata, input wire [3:0] s_out_context_tuser, input wire s_out_context_tlast, input wire s_out_context_tvalid, output wire 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'h00000B16), .NUM_DATA_I (1), .NUM_DATA_O (1), .CTRL_FIFOSIZE ($clog2(32)), .MTU (MTU) ) backend_iface_i ( .rfnoc_chdr_clk (rfnoc_chdr_clk), .rfnoc_chdr_rst (rfnoc_chdr_rst), .rfnoc_ctrl_clk (rfnoc_ctrl_clk), .rfnoc_ctrl_rst (rfnoc_ctrl_rst), .rfnoc_core_config (rfnoc_core_config), .rfnoc_core_status (rfnoc_core_status), .data_i_flush_en (data_i_flush_en), .data_i_flush_timeout (data_i_flush_timeout), .data_i_flush_active (data_i_flush_active), .data_i_flush_done (data_i_flush_done), .data_o_flush_en (data_o_flush_en), .data_o_flush_timeout (data_o_flush_timeout), .data_o_flush_active (data_o_flush_active), .data_o_flush_done (data_o_flush_done) ); //--------------------------------------------------------------------------- // Reset Generation //--------------------------------------------------------------------------- wire ce_rst_pulse; pulse_synchronizer #(.MODE ("POSEDGE")) pulse_synchronizer_ce ( .clk_a(rfnoc_chdr_clk), .rst_a(1'b0), .pulse_a (rfnoc_chdr_rst), .busy_a (), .clk_b(ce_clk), .pulse_b (ce_rst_pulse) ); pulse_stretch_min #(.LENGTH(32)) pulse_stretch_min_ce ( .clk(ce_clk), .rst(1'b0), .pulse_in(ce_rst_pulse), .pulse_out(ce_rst) ); //--------------------------------------------------------------------------- // Control Path //--------------------------------------------------------------------------- assign ctrlport_clk = ce_clk; assign ctrlport_rst = ce_rst; ctrlport_endpoint #( .THIS_PORTID (THIS_PORTID), .SYNC_CLKS (0), .AXIS_CTRL_MST_EN (0), .AXIS_CTRL_SLV_EN (1), .SLAVE_FIFO_SIZE ($clog2(32)) ) ctrlport_endpoint_i ( .rfnoc_ctrl_clk (rfnoc_ctrl_clk), .rfnoc_ctrl_rst (rfnoc_ctrl_rst), .ctrlport_clk (ctrlport_clk), .ctrlport_rst (ctrlport_rst), .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata), .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast), .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid), .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready), .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata), .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast), .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid), .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready), .m_ctrlport_req_wr (m_ctrlport_req_wr), .m_ctrlport_req_rd (m_ctrlport_req_rd), .m_ctrlport_req_addr (m_ctrlport_req_addr), .m_ctrlport_req_data (m_ctrlport_req_data), .m_ctrlport_req_byte_en (), .m_ctrlport_req_has_time (), .m_ctrlport_req_time (), .m_ctrlport_resp_ack (m_ctrlport_resp_ack), .m_ctrlport_resp_status (2'b0), .m_ctrlport_resp_data (m_ctrlport_resp_data), .s_ctrlport_req_wr (1'b0), .s_ctrlport_req_rd (1'b0), .s_ctrlport_req_addr (20'b0), .s_ctrlport_req_portid (10'b0), .s_ctrlport_req_rem_epid (16'b0), .s_ctrlport_req_rem_portid (10'b0), .s_ctrlport_req_data (32'b0), .s_ctrlport_req_byte_en (4'hF), .s_ctrlport_req_has_time (1'b0), .s_ctrlport_req_time (64'b0), .s_ctrlport_resp_ack (), .s_ctrlport_resp_status (), .s_ctrlport_resp_data () ); //--------------------------------------------------------------------------- // Data Path //--------------------------------------------------------------------------- genvar i; assign axis_data_clk = ce_clk; assign axis_data_rst = ce_rst; //--------------------- // Input Data Paths //--------------------- chdr_to_axis_pyld_ctxt #( .CHDR_W (CHDR_W), .ITEM_W (32), .NIPC (1), .SYNC_CLKS (0), .CONTEXT_FIFO_SIZE ($clog2(2)), .PAYLOAD_FIFO_SIZE ($clog2(2)), .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)*CHDR_W+:CHDR_W]), .s_axis_chdr_tlast (s_rfnoc_chdr_tlast[0]), .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[0]), .s_axis_chdr_tready (s_rfnoc_chdr_tready[0]), .m_axis_payload_tdata (m_in_payload_tdata), .m_axis_payload_tkeep (m_in_payload_tkeep), .m_axis_payload_tlast (m_in_payload_tlast), .m_axis_payload_tvalid (m_in_payload_tvalid), .m_axis_payload_tready (m_in_payload_tready), .m_axis_context_tdata (m_in_context_tdata), .m_axis_context_tuser (m_in_context_tuser), .m_axis_context_tlast (m_in_context_tlast), .m_axis_context_tvalid (m_in_context_tvalid), .m_axis_context_tready (m_in_context_tready), .flush_en (data_i_flush_en), .flush_timeout (data_i_flush_timeout), .flush_active (data_i_flush_active[0]), .flush_done (data_i_flush_done[0]) ); //--------------------- // Output Data Paths //--------------------- axis_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(2)), .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)*CHDR_W+:CHDR_W]), .m_axis_chdr_tlast (m_rfnoc_chdr_tlast[0]), .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0]), .m_axis_chdr_tready (m_rfnoc_chdr_tready[0]), .s_axis_payload_tdata (s_out_payload_tdata), .s_axis_payload_tkeep (s_out_payload_tkeep), .s_axis_payload_tlast (s_out_payload_tlast), .s_axis_payload_tvalid (s_out_payload_tvalid), .s_axis_payload_tready (s_out_payload_tready), .s_axis_context_tdata (s_out_context_tdata), .s_axis_context_tuser (s_out_context_tuser), .s_axis_context_tlast (s_out_context_tlast), .s_axis_context_tvalid (s_out_context_tvalid), .s_axis_context_tready (s_out_context_tready), .framer_errors (), .flush_en (data_o_flush_en), .flush_timeout (data_o_flush_timeout), .flush_active (data_o_flush_active[0]), .flush_done (data_o_flush_done[0]) ); endmodule // noc_shell_gain `default_nettype wire
_______________________________________________ USRP-users mailing list -- usrp-users@lists.ettus.com To unsubscribe send an email to usrp-users-le...@lists.ettus.com