diff options
Diffstat (limited to 'fpga')
12 files changed, 1925 insertions, 18 deletions
diff --git a/fpga/usrp3/lib/axi/axis_packetize.v b/fpga/usrp3/lib/axi/axis_packetize.v index 6ee018d9a..bb1297e5c 100644 --- a/fpga/usrp3/lib/axi/axis_packetize.v +++ b/fpga/usrp3/lib/axi/axis_packetize.v @@ -69,7 +69,7 @@ module axis_packetize #(    reg [SIZE_W-1:0] word_count      = 0;    // Count of output words    reg [SIZE_W-1:0] current_size    = DEFAULT_SIZE;  // Current packet size -  reg gating     = 1'b1;     // Indicate if output is blocked +  reg gating     = 1'b0;     // Indicate if output is blocked    reg mid_packet = 1'b0;     // Indicate if we're in the middle of a packet    //--------------------------------------------------------------------------- @@ -80,27 +80,27 @@ module axis_packetize #(    always @(posedge clk) begin      if (rst) begin -      start_of_packet <= 1; -      word_count      <= 0; +      start_of_packet <= 1'b1;        current_size    <= DEFAULT_SIZE; -      o_tlast         <= 0; +      word_count      <= 0; +      o_tlast         <= (DEFAULT_SIZE == 1);      end else begin        if (gating) begin          // Wait until we're enabled. Setup for the start of the next packet. -        start_of_packet <= 1; +        start_of_packet <= 1'b1;          current_size    <= size; -        word_count      <= 0; +        word_count      <= size;          o_tlast         <= (size == 1);        end else if (o_tvalid && o_tready) begin          start_of_packet <= 1'b0; -        word_count      <= word_count + 1; +        word_count      <= word_count - 1;          if (o_tlast) begin            // This is the last sample, so restart everything for a new packet. -          o_tlast         <= (size == 1); -          current_size    <= size; -          word_count      <= 0;            start_of_packet <= 1'b1; -        end else if (word_count == current_size-2) begin +          current_size    <= size; +          word_count      <= size; +          o_tlast         <= (size == 1); +        end else if (word_count == 2) begin            // This is the second to last sample, so we assert tlast for the            // last sample.            o_tlast <= 1'b1; @@ -109,7 +109,7 @@ module axis_packetize #(          // We're waiting for the start of the next packet. Keep checking the          // size input until the next packet starts.          current_size <= size; -        word_count   <= 0; +        word_count   <= size;          o_tlast      <= (size == 1);        end      end @@ -119,13 +119,11 @@ module axis_packetize #(    // Handshake Monitor    //--------------------------------------------------------------------------- -  // We start out gated to allow a clock cycle for the length to be loaded. -    // Monitor the state of the handshake so we know when it's OK to    // enable/disable data transfer.    always @(posedge clk) begin      if (rst) begin -      gating     = 1'b1; +      gating     = 1'b0;        mid_packet = 1'b0;      end else begin        // Keep track of if we are in the middle of a packet or not. Note that @@ -142,8 +140,9 @@ module axis_packetize #(          // We can stop gating any time          if (!gate) gating <= 0;        end else begin -        // Only start gating between packets or at the end of a packet -        if ((mid_packet && !o_tvalid) || (o_tvalid && o_tready && o_tlast)) begin +        // Only start gating between packets when the output is idle, or after +        // the output transfer completes at the end of packet. +        if ((!mid_packet && !o_tvalid) || (o_tvalid && o_tready && o_tlast)) begin            gating <= gate;          end        end @@ -154,6 +153,11 @@ module axis_packetize #(    // Data Pass-Through    //--------------------------------------------------------------------------- +  // Note that "gating" only asserts when a transfer completes at the end of a +  // packet, or between packets when the output is idle. This ensures that +  // o_tvalid won't deassert during a transfer and cause a handshake protocol +  // violation. +    assign o_tdata  = i_tdata;    assign o_tvalid = i_tvalid && !gating;    assign i_tready = FLUSH ? (o_tready || gating) : (o_tready && !gating); diff --git a/fpga/usrp3/lib/control/axi_setting_reg.v b/fpga/usrp3/lib/control/axi_setting_reg.v index 9d419ec32..c231540aa 100644 --- a/fpga/usrp3/lib/control/axi_setting_reg.v +++ b/fpga/usrp3/lib/control/axi_setting_reg.v @@ -40,7 +40,7 @@ module axi_setting_reg #(    reg init;    reg [WIDTH-1:0] o_tdata_int; -  reg o_tlast_int, o_tvalid_int; +  reg o_tlast_int, o_tvalid_int = VALID_AT_RESET;    wire o_tready_int;    always @(posedge clk) begin diff --git a/fpga/usrp3/lib/rfnoc/Makefile.srcs b/fpga/usrp3/lib/rfnoc/Makefile.srcs index cb97542ce..d6e079f67 100644 --- a/fpga/usrp3/lib/rfnoc/Makefile.srcs +++ b/fpga/usrp3/lib/rfnoc/Makefile.srcs @@ -102,6 +102,7 @@ ddc.v \  duc.v \  cic_decimate.v \  cic_interpolate.v \ +sine_tone.v \  axi_fir_filter.v \  fir_filter_slice.v \  axi_fir_filter_dec.v \ diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/Makefile new file mode 100644 index 000000000..8fffd1c2e --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/Makefile @@ -0,0 +1,49 @@ +# +# 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 $(LIB_IP_DIR)/cordic_rotator/Makefile.inc +include Makefile.srcs + +DESIGN_SRCS += $(abspath \ +$(RFNOC_CORE_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_OOT_SRCS)  \ +$(LIB_IP_CORDIC_ROTATOR_SRCS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +SIM_TOP = rfnoc_block_siggen_all_tb glbl +SIM_SRCS = \ +$(abspath $(IP_BUILD_DIR)/cordic_rotator/sim/cordic_rotator.vhd) \ +$(VIVADO_PATH)/data/verilog/src/glbl.v \ +$(abspath rfnoc_block_siggen_tb.sv) \ +$(abspath rfnoc_block_siggen_all_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_siggen/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/Makefile.srcs new file mode 100644 index 000000000..336cc8cfa --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/Makefile.srcs @@ -0,0 +1,24 @@ +# +# 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)))), \ +noc_shell_siggen.v \ +rfnoc_siggen_core.v \ +rfnoc_block_siggen_regs.vh \ +rfnoc_block_siggen.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/noc_shell_siggen.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/noc_shell_siggen.v new file mode 100644 index 000000000..6f14430b7 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/noc_shell_siggen.v @@ -0,0 +1,263 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_siggen +// +// Description: +// +//   This is a tool-generated NoC-shell for the siggen 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_siggen #( +  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 [(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 [(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 Data Clock and Reset +  output wire               axis_data_clk, +  output wire               axis_data_rst, +  // Data Stream to User Logic: out +  input  wire [NUM_PORTS*32*1-1:0]   s_out_axis_tdata, +  input  wire [NUM_PORTS*1-1:0]      s_out_axis_tkeep, +  input  wire [NUM_PORTS-1:0]        s_out_axis_tlast, +  input  wire [NUM_PORTS-1:0]        s_out_axis_tvalid, +  output wire [NUM_PORTS-1:0]        s_out_axis_tready, +  input  wire [NUM_PORTS*64-1:0]     s_out_axis_ttimestamp, +  input  wire [NUM_PORTS-1:0]        s_out_axis_thas_time, +  input  wire [NUM_PORTS*16-1:0]     s_out_axis_tlength, +  input  wire [NUM_PORTS-1:0]        s_out_axis_teov, +  input  wire [NUM_PORTS-1:0]        s_out_axis_teob +); + +  //--------------------------------------------------------------------------- +  //  Backend Interface +  //--------------------------------------------------------------------------- + +  wire         data_i_flush_en; +  wire [31:0]  data_i_flush_timeout; +  wire [63:0]  data_i_flush_active; +  wire [63:0]  data_i_flush_done; +  wire         data_o_flush_en; +  wire [31:0]  data_o_flush_timeout; +  wire [63:0]  data_o_flush_active; +  wire [63:0]  data_o_flush_done; + +  backend_iface #( +    .NOC_ID        (32'h51663110), +    .NUM_DATA_I    (1), +    .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 +  //--------------------- + +  // No input data paths for this block +  assign s_rfnoc_chdr_tready[0] = 1'b1; +  assign data_i_flush_done[0] = 1'b1; + +  //--------------------- +  // Output Data Paths +  //--------------------- + +  for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_output_out +    axis_data_to_chdr #( +      .CHDR_W          (CHDR_W), +      .ITEM_W          (32), +      .NIPC            (1), +      .SYNC_CLKS       (0), +      .INFO_FIFO_SIZE  ($clog2(32)), +      .PYLD_FIFO_SIZE  ($clog2(32)), +      .MTU             (MTU), +      .SIDEBAND_AT_END (0) +    ) axis_data_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_tdata       (s_out_axis_tdata[(32*1)*i+:(32*1)]), +      .s_axis_tkeep       (s_out_axis_tkeep[1*i+:1]), +      .s_axis_tlast       (s_out_axis_tlast[i]), +      .s_axis_tvalid      (s_out_axis_tvalid[i]), +      .s_axis_tready      (s_out_axis_tready[i]), +      .s_axis_ttimestamp  (s_out_axis_ttimestamp[64*i+:64]), +      .s_axis_thas_time   (s_out_axis_thas_time[i]), +      .s_axis_tlength     (s_out_axis_tlength[16*i+:16]), +      .s_axis_teov        (s_out_axis_teov[i]), +      .s_axis_teob        (s_out_axis_teob[i]), +      .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_siggen + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen.v new file mode 100644 index 000000000..8a90c3e28 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen.v @@ -0,0 +1,242 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_siggen +// +// Description: +// +//   Signal generator RFNoC block. This block outputs packets of one of three +//   output types based on the REG_WAVEFORM register setting. Supported modes +//   include constant, sinusoidal, and noise/random. The output is also run +//   through a gain stage that is configurable using the REG_GAIN register. +//   See the register descriptions in rfnoc_block_siggen_regs.vh for details. +// +//   The sine output is based on the Xilinx CORDIC IP, configured for the +//   rotate function, with scaled radians as the units. See the CORDIC user +//   guide (PG105) and register descriptions for details. +// +// Parameters: +// +//   THIS_PORTID : Control crossbar port to which this block is connected +//   CHDR_W      : AXIS-CHDR data bus width +//   MTU         : Maximum transmission unit (i.e., maximum packet size in +//                 CHDR words is 2**MTU). +//   NUM_PORTS   : Number of siggen cores to instantiate. +// + +`default_nettype none + + +module rfnoc_block_siggen #( +  parameter [9:0] THIS_PORTID = 10  'd0, +  parameter       CHDR_W      = 64, +  parameter [5:0] MTU         = 10, +  parameter       NUM_PORTS   = 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 [          CHDR_W-1:0] s_rfnoc_chdr_tdata, +  input  wire                        s_rfnoc_chdr_tlast, +  input  wire                        s_rfnoc_chdr_tvalid, +  output wire                        s_rfnoc_chdr_tready, +  // AXIS-CHDR Output Ports (to framework) +  output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata, +  output wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tlast, +  output wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tvalid, +  input  wire [       NUM_PORTS-1:0] m_rfnoc_chdr_tready, +  // AXIS-Ctrl Input Port (from framework) +  input  wire [                31:0] s_rfnoc_ctrl_tdata, +  input  wire                        s_rfnoc_ctrl_tlast, +  input  wire                        s_rfnoc_ctrl_tvalid, +  output wire                        s_rfnoc_ctrl_tready, +  // AXIS-Ctrl Output Port (to framework) +  output wire [                31:0] m_rfnoc_ctrl_tdata, +  output wire                        m_rfnoc_ctrl_tlast, +  output wire                        m_rfnoc_ctrl_tvalid, +  input  wire                        m_rfnoc_ctrl_tready +); + +  `include "rfnoc_block_siggen_regs.vh" + + +  //--------------------------------------------------------------------------- +  // Signal Declarations +  //--------------------------------------------------------------------------- + +  // CtrlPort Master +  wire        m_ctrlport_req_wr; +  wire        m_ctrlport_req_rd; +  wire [19:0] m_ctrlport_req_addr; +  wire [31:0] m_ctrlport_req_data; +  wire        m_ctrlport_resp_ack; +  wire [31:0] m_ctrlport_resp_data; +  // Data Stream to User Logic: out +  wire [NUM_PORTS*32*1-1:0] s_out_axis_tdata; +  wire [     NUM_PORTS-1:0] s_out_axis_tlast; +  wire [     NUM_PORTS-1:0] s_out_axis_tvalid; +  wire [     NUM_PORTS-1:0] s_out_axis_tready; +  wire [  NUM_PORTS*16-1:0] s_out_axis_tlength; + + +  //--------------------------------------------------------------------------- +  // NoC Shell +  //--------------------------------------------------------------------------- + +  wire ce_rst; + +  noc_shell_siggen #( +    .CHDR_W      (CHDR_W), +    .THIS_PORTID (THIS_PORTID), +    .MTU         (MTU), +    .NUM_PORTS   (NUM_PORTS) +  ) noc_shell_siggen_i ( +    //--------------------- +    // Framework Interface +    //--------------------- + +    // Clock Inputs +    .rfnoc_chdr_clk        (rfnoc_chdr_clk), +    .rfnoc_ctrl_clk        (rfnoc_ctrl_clk), +    .ce_clk                (ce_clk), +    // Reset Outputs +    .rfnoc_chdr_rst        (), +    .rfnoc_ctrl_rst        (), +    .ce_rst                (ce_rst), +    // RFNoC Backend Interface +    .rfnoc_core_config     (rfnoc_core_config), +    .rfnoc_core_status     (rfnoc_core_status), +    // CHDR Input Ports  (from framework) +    .s_rfnoc_chdr_tdata    (s_rfnoc_chdr_tdata), +    .s_rfnoc_chdr_tlast    (s_rfnoc_chdr_tlast), +    .s_rfnoc_chdr_tvalid   (s_rfnoc_chdr_tvalid), +    .s_rfnoc_chdr_tready   (s_rfnoc_chdr_tready), +    // CHDR Output Ports (to framework) +    .m_rfnoc_chdr_tdata    (m_rfnoc_chdr_tdata), +    .m_rfnoc_chdr_tlast    (m_rfnoc_chdr_tlast), +    .m_rfnoc_chdr_tvalid   (m_rfnoc_chdr_tvalid), +    .m_rfnoc_chdr_tready   (m_rfnoc_chdr_tready), +    // AXIS-Ctrl Input Port (from framework) +    .s_rfnoc_ctrl_tdata    (s_rfnoc_ctrl_tdata), +    .s_rfnoc_ctrl_tlast    (s_rfnoc_ctrl_tlast), +    .s_rfnoc_ctrl_tvalid   (s_rfnoc_ctrl_tvalid), +    .s_rfnoc_ctrl_tready   (s_rfnoc_ctrl_tready), +    // AXIS-Ctrl Output Port (to framework) +    .m_rfnoc_ctrl_tdata    (m_rfnoc_ctrl_tdata), +    .m_rfnoc_ctrl_tlast    (m_rfnoc_ctrl_tlast), +    .m_rfnoc_ctrl_tvalid   (m_rfnoc_ctrl_tvalid), +    .m_rfnoc_ctrl_tready   (m_rfnoc_ctrl_tready), + +    //--------------------- +    // Client Interface +    //--------------------- + +    // CtrlPort Clock and Reset +    .ctrlport_clk          (), +    .ctrlport_rst          (), +    // CtrlPort Master +    .m_ctrlport_req_wr     (m_ctrlport_req_wr), +    .m_ctrlport_req_rd     (m_ctrlport_req_rd), +    .m_ctrlport_req_addr   (m_ctrlport_req_addr), +    .m_ctrlport_req_data   (m_ctrlport_req_data), +    .m_ctrlport_resp_ack   (m_ctrlport_resp_ack), +    .m_ctrlport_resp_data  (m_ctrlport_resp_data), + +    // AXI-Stream Clock and Reset +    .axis_data_clk         (), +    .axis_data_rst         (), +    // Data Stream from User Logic: out +    .s_out_axis_tdata      (s_out_axis_tdata), +    .s_out_axis_tkeep      ({NUM_PORTS{1'b1}}), +    .s_out_axis_tlast      (s_out_axis_tlast), +    .s_out_axis_tvalid     (s_out_axis_tvalid), +    .s_out_axis_tready     (s_out_axis_tready), +    .s_out_axis_ttimestamp ({NUM_PORTS{64'b0}}), +    .s_out_axis_thas_time  ({NUM_PORTS{1'b0}}), +    .s_out_axis_tlength    (s_out_axis_tlength), +    .s_out_axis_teov       ({NUM_PORTS{1'b0}}), +    .s_out_axis_teob       ({NUM_PORTS{1'b0}}) +  ); + + +  //--------------------------------------------------------------------------- +  // CtrlPort Splitter +  //--------------------------------------------------------------------------- + +  // Create a CtrlPort bus for each port instance + +  wire [ 1*NUM_PORTS-1:0] ctrlport_req_wr; +  wire [ 1*NUM_PORTS-1:0] ctrlport_req_rd; +  wire [20*NUM_PORTS-1:0] ctrlport_req_addr; +  wire [32*NUM_PORTS-1:0] ctrlport_req_data; +  wire [ 1*NUM_PORTS-1:0] ctrlport_resp_ack; +  wire [32*NUM_PORTS-1:0] ctrlport_resp_data; + +  ctrlport_decoder #( +    .NUM_SLAVES   (NUM_PORTS), +    .BASE_ADDR    (0), +    .SLAVE_ADDR_W (SIGGEN_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       (ctrlport_req_wr), +    .m_ctrlport_req_rd       (ctrlport_req_rd), +    .m_ctrlport_req_addr     (ctrlport_req_addr), +    .m_ctrlport_req_data     (ctrlport_req_data), +    .m_ctrlport_req_byte_en  (), +    .m_ctrlport_req_has_time (), +    .m_ctrlport_req_time     (), +    .m_ctrlport_resp_ack     (ctrlport_resp_ack), +    .m_ctrlport_resp_status  ({NUM_PORTS{2'b0}}), +    .m_ctrlport_resp_data    (ctrlport_resp_data) +  ); + + +  //--------------------------------------------------------------------------- +  // Port Instances +  //--------------------------------------------------------------------------- + +  genvar port; +  generate +    for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_ports + +      rfnoc_siggen_core rfnoc_siggen_core_i ( +        .clk                  (ce_clk), +        .rst                  (ce_rst), +        .s_ctrlport_req_wr    (ctrlport_req_wr    [port* 1 +:  1]), +        .s_ctrlport_req_rd    (ctrlport_req_rd    [port* 1 +:  1]), +        .s_ctrlport_req_addr  (ctrlport_req_addr  [port*20 +: 20]), +        .s_ctrlport_req_data  (ctrlport_req_data  [port*32 +: 32]), +        .s_ctrlport_resp_ack  (ctrlport_resp_ack  [port* 1 +:  1]), +        .s_ctrlport_resp_data (ctrlport_resp_data [port*32 +: 32]), +        .m_tdata              (s_out_axis_tdata   [port*32 +: 32]), +        .m_tlast              (s_out_axis_tlast   [port* 1 +:  1]), +        .m_tvalid             (s_out_axis_tvalid  [port* 1 +:  1]), +        .m_tready             (s_out_axis_tready  [port* 1 +:  1]), +        .m_tlength            (s_out_axis_tlength [port*16 +: 16]) +      ); + +    end +  endgenerate + +endmodule // rfnoc_block_siggen + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_all_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_all_tb.sv new file mode 100644 index 000000000..0b23dcd0f --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_all_tb.sv @@ -0,0 +1,28 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_logpwr_all_tb +// +// Description: Top-level testbench for the Signal Generator RFNoC block. This +// instantiates rfnoc_block_siggen_tb with different parameters to test +// multiple configurations. +// + +`default_nettype none + + +module rfnoc_block_siggen_all_tb; + +  // Test multiple CHDR widths +  rfnoc_block_siggen_tb #(.CHDR_W(64),  .NUM_PORTS(1))  test_siggen_0(); +  rfnoc_block_siggen_tb #(.CHDR_W(64),  .NUM_PORTS(2))  test_siggen_1(); +  rfnoc_block_siggen_tb #(.CHDR_W(64),  .NUM_PORTS(3))  test_siggen_2(); +  rfnoc_block_siggen_tb #(.CHDR_W(128), .NUM_PORTS(2))  test_siggen_3(); +  rfnoc_block_siggen_tb #(.CHDR_W(256), .NUM_PORTS(1))  test_siggen_4(); +   +endmodule : rfnoc_block_siggen_all_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_regs.vh new file mode 100644 index 000000000..10cda5425 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_regs.vh @@ -0,0 +1,134 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module:  rfnoc_block_siggen_regs (Header) +// +// Description: RFNoC Signal Generator block register descriptions +// + + +// Address space size, per signal generator core. That is, each signal +// generator core's address space is separated in the CtrlPort address space +// by 2^SIGGEN_ADDR_W bytes. +localparam SIGGEN_ADDR_W = 5; + + + +// REG_ENABLE (R/W) +// +// Starts or stops the waveform output. Write a 1 to enable waveform output, 0 +// to disable waveform output. Starting and stopping occurs on packet +// boundaries. +// +// [31:1] Reserved +// [0]    Enable bit +// +localparam REG_ENABLE = 'h00; +// +localparam REG_ENABLE_LEN = 1; + + +// REG_SPP (R/W) +// +// The number of samples per packet to output for the selected waveform. This +// is read at the start of each new packet. +// +localparam REG_SPP = 'h04; +// +localparam REG_SPP_LEN = 14; + + +// REG_WAVEFORM (R/W) +// +// Selects the type of waveform to output. The possible values are: +// +//   0 : (WAVE_CONST) Constant data +//   1 : (WAVE_SINE)  Sine wave +//   2 : (WAVE_NOISE) Noise / random data +// +localparam REG_WAVEFORM = 'h08; +// +localparam REG_WAVEFORM_LEN = 2; +// +localparam WAVE_CONST = 2'h0; +localparam WAVE_SINE  = 2'h1; +localparam WAVE_NOISE = 2'h2; + + +// REG_GAIN (R/W) +// +// Sets the gain for the output. This is a 16-bit signed fixed point value +// with 15 fractional bits. The gain is applied to both the real and imaginary +// parts of each output sample. This gain is applied to all waveform output +// types. +// +localparam REG_GAIN = 'h0C; +// +localparam REG_GAIN_LEN = 16; + + +// REG_CONSTANT (R/W) +// +// Sets the value for the sample to output for the constant waveform. Both the +// real and imaginary components are treated as 16-bit signed fixed point +// values with 15 fractional bits. +// +// [31:16] Real/I component +// [15: 0] Imaginary/Q component +// +localparam REG_CONSTANT = 'h10; +// +localparam REG_CONSTANT_LEN = 32; + + +// REG_PHASE_INC (R/W) +// +// Sets the phase increment, in "scaled radians", for the sine waveform +// generator. This is the amount by which REG_CARTESIAN is rotated each clock +// cycle. In other words, it controls the rate of rotation, or the frequency, +// of the sine wave. The range of the phase value is -1.0 to +1.0. In scaled +// radians, the value range -1 to +1 corresponds to -Pi to Pi in radians. +// +// In other words, the normalized frequency (in cycles/sample) of the +// sinusoidal output is equal to 0.5*REG_PHASE_INC. +// +// [31:16] : Reserved +// [15: 0] : Signed fixed-point phase value with 3 integer bits and 13 +//           fractional bits. +// +localparam REG_PHASE_INC = 'h14; +// +localparam REG_PHASE_INC_LEN = 16; + + +// REG_CARTESIAN (R/W) +// +// Sets the (X,Y) Cartesian coordinate that will be rotated to generate the +// sine output. The rate of rotation is controlled by REG_PHASE_INC. Note that +// this input vector is also scaled by a "CORDIC scale factor" that equals +// about 1.16444 (the product of sqrt(1 + 2^(-2i)) for i = 1 to n, where n = +// 14, the number of fractional bits used by the CORDIC IP). +// +// Both the X and Y coordinates are signed fixed-point values with 15 +// fractional bits. +// +// For example, supposed you wanted a sinusoidal output with an amplitude of +// about 0.9. In that case, you could set the Y coordinate to 0 and the X +// coordinate to 0.9/1.16444 = 0.7729. In fixed-point, that's 0.7729 * 2^15 = +// 0x62EE. +// +// NOTE: The Xilinx CORDIC IP describes the input and output as 16-bit signed +// fixed point with 2 integer and 14 fractional bits, which is accurate. +// However, since we treat the output as sc16 (15 fractional bits), we need to +// double the value of the CARTESIAN inputs to get the output we want for sc16. +// This is mathematically inequivalent to simply saying the CARTESIAN inputs +// have 15 fractional bits instead of 14. +// +// [31:16] : Y (Imaginary) component +// [15: 0] : X (Real) component +// +localparam REG_CARTESIAN = 'h18; +// +localparam REG_CARTESIAN_LEN = 32; diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_tb.sv new file mode 100644 index 000000000..4cb701aaf --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_block_siggen_tb.sv @@ -0,0 +1,719 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_siggen_tb +// +// Description: Testbench for the siggen RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_siggen_tb #( +  parameter CHDR_W    = 64, +  parameter NUM_PORTS = 1 +); + +  `include "test_exec.svh" + +  import PkgTestExec::*; +  import PkgChdrUtils::*; +  import PkgRfnocBlockCtrlBfm::*; +  import PkgRfnocItemUtils::*; + +  `include "rfnoc_block_siggen_regs.vh" + + +  //--------------------------------------------------------------------------- +  // Testbench Configuration +  //--------------------------------------------------------------------------- + +  localparam [31:0] NOC_ID          = 32'h51663110; +  localparam [ 9:0] THIS_PORTID     = 10'h123; +  localparam int    MTU             = 10;    // Log2 of max transmission unit in CHDR words +  localparam int    NUM_PORTS_I     = 1; +  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      = 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 + +  localparam real PI = 2*$acos(0); + +  // Number of fractional bits used for fixed point values of the different +  // settings (derived from the DUT). +  localparam int GAIN_FRAC  = 15; +  localparam int CONST_FRAC = 15; +  localparam int PHASE_FRAC = 13; +  localparam int CART_FRAC  = 14; + +  // Maximum real (floating point) values allowed for the different fixed +  // point formats (for range checking). All of the fixed point values are +  // signed 16-bit.  +  localparam real MAX_GAIN_R  =  (2.0**15-1) / (2.0**GAIN_FRAC); +  localparam real MIN_GAIN_R  = -(2.0**15)   / (2.0**GAIN_FRAC); +  localparam real MAX_CONST_R =  (2.0**15-1) / (2.0**CONST_FRAC); +  localparam real MIN_CONST_R = -(2.0**15)   / (2.0**CONST_FRAC); +  localparam real MAX_CART_R  =  (2.0**15-1) / (2.0**CART_FRAC); +  localparam real MIN_CART_R  = -(2.0**15)   / (2.0**CART_FRAC); +  // Note that the CORDIC only supports phase values from -1.0 to +1.0. +  localparam real MAX_PHASE_R = +1.0; +  localparam real MIN_PHASE_R = -1.0; + + +  //--------------------------------------------------------------------------- +  // Clocks and Resets +  //--------------------------------------------------------------------------- + +  bit rfnoc_chdr_clk; +  bit rfnoc_ctrl_clk; +  bit ce_clk; + +  sim_clock_gen #(.PERIOD(CHDR_CLK_PER), .AUTOSTART(0)) +   rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); +  sim_clock_gen #(.PERIOD(CTRL_CLK_PER), .AUTOSTART(0)) +   rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); +  sim_clock_gen #(.PERIOD(CE_CLK_PER), .AUTOSTART(0)) +     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_siggen #( +    .THIS_PORTID         (THIS_PORTID), +    .CHDR_W              (CHDR_W), +    .MTU                 (MTU), +    .NUM_PORTS           (NUM_PORTS) +  ) dut ( +    .rfnoc_chdr_clk      (rfnoc_chdr_clk), +    .rfnoc_ctrl_clk      (rfnoc_ctrl_clk), +    .ce_clk              (ce_clk), +    .rfnoc_core_config   (backend.cfg), +    .rfnoc_core_status   (backend.sts), +    .s_rfnoc_chdr_tdata  (s_rfnoc_chdr_tdata), +    .s_rfnoc_chdr_tlast  (s_rfnoc_chdr_tlast), +    .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid), +    .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready), +    .m_rfnoc_chdr_tdata  (m_rfnoc_chdr_tdata), +    .m_rfnoc_chdr_tlast  (m_rfnoc_chdr_tlast), +    .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid), +    .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready), +    .s_rfnoc_ctrl_tdata  (m_ctrl.tdata), +    .s_rfnoc_ctrl_tlast  (m_ctrl.tlast), +    .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid), +    .s_rfnoc_ctrl_tready (m_ctrl.tready), +    .m_rfnoc_ctrl_tdata  (s_ctrl.tdata), +    .m_rfnoc_ctrl_tlast  (s_ctrl.tlast), +    .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid), +    .m_rfnoc_ctrl_tready (s_ctrl.tready) +  ); + + +  //--------------------------------------------------------------------------- +  // Helper Tasks +  //--------------------------------------------------------------------------- + +  // Write a 32-bit register +  task automatic write_reg(int port, bit [19:0] addr, bit [31:0] value); +    blk_ctrl.reg_write(port * (2**SIGGEN_ADDR_W) + addr, value); +  endtask : write_reg + +  // Read a 32-bit register +  task automatic read_reg(int port, bit [19:0] addr, output logic [31:0] value); +    blk_ctrl.reg_read(port * (2**SIGGEN_ADDR_W) + addr, value); +  endtask : read_reg + + +  // Check if two samples are within a given distance from each other (i.e., +  // check if the Cartesian distance is < threshold). +  function bit samples_are_close( +    logic [31:0] samp_a, samp_b, +    real         threshold = 3.0 +  ); +    real ax, ay, bx, by; +    real distance; + +    // Treat the samples and signed 16-bit numbers (not fixed point) +    ax = signed'(samp_a[31:16]); +    ay = signed'(samp_a[15: 0]); +    bx = signed'(samp_b[31:16]); +    by = signed'(samp_b[15: 0]); + +    distance = $sqrt( (ax-bx)*(ax-bx) + (ay-by)*(ay-by) ); + +    return distance <= threshold; +  endfunction : samples_are_close + + +  // Convert real to signed 16-bit fixed point with "frac" fractional bits +  function automatic logic [15:0] real_to_fixed(real value, int frac = 15); +    // Convert to fixed point value +    value = value * 2.0**frac; + +    // Round +    value = $floor(value + 0.5); + +    // Saturate +    if (value > 16'sh7FFF) value = 16'sh7FFF; +    if (value < 16'sh8000) value = 16'sh8000; +    return int'(value); +  endfunction : real_to_fixed + + +  // Convert signed 16-bit fixed point to real, where the fixed point has +  // "frac" fractional bits. +  function automatic real fixed_to_real( +    logic signed [15:0] value, +    int                 frac = 15 +  ); +    return real'(value) / (2.0 ** frac); +  endfunction : fixed_to_real + + +  // Compute the next sine value we expect based on the previous +  function automatic logic [31:0] next_sine_value( +    logic [31:0] sample, +    logic [15:0] phase_inc +  ); +    real x, y, phase, new_x, new_y; +    x        = fixed_to_real(sample[31:16], CART_FRAC); +    y        = fixed_to_real(sample[15: 0], CART_FRAC); +    phase    = fixed_to_real(phase_inc, PHASE_FRAC) * PI; + +    // Compute the rotated coordinates +    new_x =  x*$cos(phase) + y*$sin(phase); +    new_y = -x*$sin(phase) + y*$cos(phase); +     +    return { real_to_fixed(new_x, CART_FRAC), real_to_fixed(new_y, CART_FRAC) }; +  endfunction : next_sine_value + + +  // Apply a gain to an input value, then round and clip the same way the DUT +  // does. +  function automatic logic [15:0] apply_gain( +    logic signed [15:0] gain, +    logic signed [15:0] value +  ); +    logic signed [31:0] result; +    bit round; + +    // Apply gain +    result = gain * value; + +    // Now we "round and clip". The round and clip block is configured with +    // 32-bit input, 16-bit output, and one "clip_bit". This means it takes +    // the upper 17-bits of the result, rounded, then converts that to a +    // 16-bit result, saturated. + +    // Round the value in the upper 17 bits to nearest (biased towards +inf, +    // but don't allow overflow). +    if (result[31:15] != 17'h0FFFF) begin +      round = result[14]; +    end else begin +      round = 0; +    end +    result = result >>> 15;   // Arithmetic right shift +    result = result + round;  // Round the result + +    // Saturate to 16-bit number +    if (result < 16'sh8000) begin +      result = 16'sh8000; +    end else if (result > 16'sh7FFF) begin +      result = 16'sh7FFF; +    end + +    return result[15:0]; +  endfunction : apply_gain + + +  // Flush (drop) any queued up packets on the output +  task automatic flush_output(int port, timeout = 100); +    item_t items[$]; + +    forever begin +      fork +        begin : wait_for_data_fork +          // Wait for tvalid to rise for up to "timeout" clock cycles +          if (m_rfnoc_chdr_tvalid[port]) +            wait(!m_rfnoc_chdr_tvalid[port]); +          wait(m_rfnoc_chdr_tvalid[port]); +        end +        begin : wait_for_timeout_fork +          #(CHDR_CLK_PER*timeout); +        end +      join_any + +      // Check if we timed out or if new data arrived +      if (!m_rfnoc_chdr_tvalid[port]) break; +    end + +    // Dump all the packets that were received +    while (blk_ctrl.num_received(port)) begin  +      blk_ctrl.recv_items(port, items); +    end +  endtask : flush_output + + +  // 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"}); + +    // 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"}); + +    // 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"}); + +    // Restore original value +    write_reg(port, addr, initial_value); +  endtask : test_read_write_reg + + +  // Run the block using the indicated settings and verify the output. +  task automatic run_waveform( +    int                 port, +    logic signed [15:0] gain        = 16'h7FFF,  // 0.99997 +    logic         [2:0] mode        = WAVE_CONST, +    int                 num_packets = 1, +    int                 spp         = SPP, +    logic signed [15:0] const_re    = 16'h7FFF,  // 0.99997 +    logic signed [15:0] const_im    = 16'h7FFF,  // 0.99997 +    logic signed [15:0] phase_inc   = real_to_fixed(0.5, 13), //real_to_fixed(2.0/16, 13), // 2*pi/16 radians +    logic signed [15:0] cart_x      = real_to_fixed(1.0, 14), +    logic signed [15:0] cart_y      = real_to_fixed(0.0, 14) +  ); +    write_reg(port, REG_SPP, spp); +    write_reg(port, REG_WAVEFORM, mode); +    write_reg(port, REG_GAIN, gain); +    if (mode == WAVE_CONST) begin +      write_reg(port, REG_CONSTANT, {const_re, const_im}); +    end else if (mode == WAVE_SINE) begin +      write_reg(port, REG_PHASE_INC, phase_inc); +      write_reg(port, REG_CARTESIAN, {cart_y, cart_x}); +    end +    write_reg(port, REG_ENABLE, 1); + +    for (int packet_count = 0; packet_count < num_packets; packet_count++) begin +      item_t items[$]; +      item_t expected_const, expected_sine, actual; + +      // Receive the next packet +      blk_ctrl.recv_items(port, items); + +      // Verify the length +      `ASSERT_ERROR( +        items.size() == spp,  +        "Packet length didn't match configured SPP" +      ); + +      // Verify the payload +      foreach (items[i]) begin +        actual = items[i]; + +        // Determine the expected constant output +        expected_const[31:16] = apply_gain(gain, const_re); +        expected_const[15: 0] = apply_gain(gain, const_im); + +        // Determine the expected sine output +        if (i == 0) begin +          // We have no basis for comparison on the first sample, so don't +          // check it. It will be used to compute the next output. +          expected_sine = actual; +        end else begin +          expected_sine = next_sine_value(items[i-1], phase_inc); +        end + +        // Check the output +        if (mode == WAVE_CONST) begin +          // For the constant, we expect the output to match exactly +          `ASSERT_ERROR( +            actual == expected_const, +            $sformatf("Incorrect constant sample on packet %0d. Expected 0x%X, received 0x%X.", +              packet_count, expected_const, actual) +          ); +        end else if (mode == WAVE_SINE) begin +          // For sine, it's hard to reproduce the rounding behavior of the IP +          // exactly, so we just check if we're close to the expected answer. +          `ASSERT_ERROR( +            samples_are_close(actual, expected_sine), +            $sformatf("Incorrect sine sample on packet %0d. Expected 0x%X, received 0x%X.", +              packet_count, expected_sine, actual) +          ); +        end else if (mode == WAVE_NOISE) begin +          if (i != 0) begin +            // For noise, it's hard to even estimate the output, so make sure +            // it's changing. +            `ASSERT_ERROR(items[i] !== items[i-1], +              $sformatf("Noise output didn't update on packet %0d.Received 0x%X.", +                packet_count, actual) +            ); +          end +        end +        +      end +    end + +    // Disable the output and flush any output +    write_reg(port, REG_ENABLE, 0); +    flush_output(port); +  endtask : run_waveform + + +  // Run the block using the "constant" waveform mode using the indicated +  // settings and verify the output. +  task automatic run_const( +    int  port, +    int  num_packets = 50, +    int  spp         = SPP, +    real gain, +    real re, +    real im +  ); +    logic signed [15:0] fgain, fre, fim;   // Fixed-point versions + +    // Check the ranges +    `ASSERT_FATAL(gain <= MAX_GAIN_R   || gain >= MIN_GAIN_R,  "Gain out of range"); +    `ASSERT_FATAL(re   <= MAX_CONST_R  || re   >= MIN_CONST_R, "Real out of range"); +    `ASSERT_FATAL(im   <= MAX_CONST_R  || im   >= MIN_CONST_R, "Imag out of range"); + +    // Convert arguments to fixed point +    fgain  = real_to_fixed(gain, GAIN_FRAC); +    fre    = real_to_fixed(re, CONST_FRAC); +    fim    = real_to_fixed(im, CONST_FRAC); + +    // Test the waveform +    run_waveform( +      .port(port), +      .gain(fgain), +      .mode(WAVE_CONST), +      .num_packets(num_packets), +      .spp(spp), +      .const_re(fre), +      .const_im(fim) +    ); +  endtask : run_const + + +  // Run the block using the "sine" waveform mode using the indicated settings +  // and verify the output. +  task automatic run_sine( +    int  port, +    int  num_packets = 50, +    int  spp         = SPP, +    real gain, +    real x, +    real y, +    real phase +  ); +    logic signed [15:0] fgain, fx, fy, fphase;   // Fixed-point versions + +    // Check the ranges +    `ASSERT_FATAL(gain  <= MAX_GAIN_R  || gain  >= MIN_GAIN_R,  "Gain out of range"); +    `ASSERT_FATAL(x     <= MAX_CART_R  || x     >= MIN_CART_R,  "X out of range"); +    `ASSERT_FATAL(y     <= MAX_CART_R  || y     >= MIN_CART_R,  "Y out of range"); +    `ASSERT_FATAL(phase <= MAX_PHASE_R || phase >= MIN_PHASE_R, "Phase out of range"); + +    // Convert arguments to fixed point. +    fgain  = real_to_fixed(gain, GAIN_FRAC); +    fx     = real_to_fixed(x, CART_FRAC); +    fy     = real_to_fixed(y, CART_FRAC); +    fphase = real_to_fixed(phase, PHASE_FRAC); + +    // Test the waveform +    run_waveform( +      .port(port), +      .gain(fgain), +      .mode(WAVE_SINE), +      .num_packets(num_packets), +      .spp(spp), +      .cart_x(fx), +      .cart_y(fy), +      .phase_inc(fphase) +    ); +  endtask : run_sine + + +  // Run the block using the "noise" waveform mode using the indicated +  // settings and verify the output. +  task automatic run_noise( +    int  port, +    int  num_packets = 50, +    int  spp         = SPP, +    real gain +  ); +    logic signed [15:0] fgain;   // Fixed-point versions + +    // Check the ranges +    `ASSERT_FATAL(gain <= MAX_GAIN_R || gain >= MIN_GAIN_R, "Gain out of range"); + +    // Convert arguments to fixed point +    fgain = real_to_fixed(gain, GAIN_FRAC); + +    // Test the waveform +    run_waveform( +      .port(port), +      .gain(fgain), +      .mode(WAVE_NOISE), +      .num_packets(num_packets), +      .spp(spp) +    ); +  endtask : run_noise + + +  //--------------------------------------------------------------------------- +  // Test Procedures +  //--------------------------------------------------------------------------- + +  // Test the min and max allowed values on all registers +  task automatic test_registers(int port); +    test.start_test($sformatf("Test registers (port %0d)", port), 1ms); +    // REG_ENABLE and REG_WAVEFORM will be tested during the other tests +    test_read_write_reg(port, REG_SPP,       {REG_SPP_LEN{1'b1}},       32'd16); +    test_read_write_reg(port, REG_GAIN,      {REG_GAIN_LEN{1'b1}},      32'h7FFF); +    test_read_write_reg(port, REG_CONSTANT,  {REG_CONSTANT_LEN{1'b1}},  32'h0); +    test_read_write_reg(port, REG_PHASE_INC, {REG_PHASE_INC_LEN{1'b1}}, {REG_PHASE_INC_LEN{1'bX}}); +    test_read_write_reg(port, REG_CARTESIAN, {REG_CARTESIAN_LEN{1'b1}}, {REG_CARTESIAN_LEN{1'bX}}); +    test.end_test(); +  endtask : test_registers + + +  // Run through all the waveform modes to make sure they work as expected +  task automatic test_waveforms(int port); +    test.start_test($sformatf("Test waveforms (port %0d)", port), 1ms); +    run_const(.port(port), .gain(0.5), .re(0.25), .im(0.5)); +    run_sine(.port(port), .gain(0.75), .x(0.25), .y(0.5), .phase(2.0/64)); +    run_noise(.port(port), .gain(0.999)); +    test.end_test(); +  endtask : test_waveforms + + +  // Use the constant waveform to test the gain. The gain logic is shared by +  // all modes, but using "const" waveform makes it easy to control the values +  // we're testing. +  task automatic test_gain(int port); +    logic signed [15:0] min_val; +    logic signed [15:0] max_val; + +    test.start_test($sformatf("Test gain (port %0d)", port), 1ms); + +    max_val = 16'sh7FFF; +    min_val = 16'sh8000; +     +    // Test max gain with min and max sample values +    run_waveform(.port(port), .mode(WAVE_CONST), .gain(max_val), +      .const_re(max_val), .const_im(min_val)); +    // Test min gain with max and min sample values +    run_waveform(.port(port), .mode(WAVE_CONST), .gain(min_val), +      .const_re(min_val), .const_im(max_val)); +    // Test zero +    run_waveform(.port(port), .mode(WAVE_CONST), .gain(0), +      .const_re(max_val), .const_im(min_val)); +    // Test 0.5 * 0.5 = 0.25 and 0.25 * 0.5 = 0.125 +    run_waveform( +      .port(port), +      .mode(WAVE_CONST), +      .const_re(real_to_fixed(0.5, CONST_FRAC)),   +      .const_im(real_to_fixed(0.25, CONST_FRAC)), +      .gain(real_to_fixed(0.5, GAIN_FRAC)) +      ); +    test.end_test(); +  endtask : test_gain + + +  // Test the phase setting for the sine waveform +  task automatic test_phase(int port); +    test.start_test($sformatf("Test phase (port %0d)", port), 1ms); +    // Test typical phase +    run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(2.0/16), .num_packets(2)); +    // Test max phase +    run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(MAX_PHASE_R), .num_packets(2)); +    // Test min phase +    run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(MIN_PHASE_R), .num_packets(2)); +    test.end_test(); +  endtask : test_phase + + +  // Use constant waveform to test min and max packet lengths +  task automatic test_packet_length(int port); +    test.start_test($sformatf("Test packet length (port %0d)", port), 1ms); +    run_waveform(.port(port), .spp(2)); +    run_waveform(.port(port), .spp(SPP)); +    run_waveform(.port(port), .spp((2**MTU-1) * (CHDR_W / ITEM_W))); // Test MTU size +    test.end_test(); +  endtask : test_packet_length + + +  //--------------------------------------------------------------------------- +  // Main Test Process +  //--------------------------------------------------------------------------- + +  initial begin : tb_main +    int port; + +    // Initialize the test exec object for this testbench +    test.start_tb( +      $sformatf("rfnoc_block_siggen_tb (CHDR_W = %0d, NUM_PORTS = %0d)", +      CHDR_W, NUM_PORTS)); + +    // Don't start the clocks until after start_tb() returns. This ensures that +    // the clocks aren't toggling while other instances of this testbench are +    // running, which speeds up simulation time. +    rfnoc_chdr_clk_gen.start(); +    rfnoc_ctrl_clk_gen.start(); +    ce_clk_gen.start(); + +    // Start the BFMs running +    blk_ctrl.run(); + +    //-------------------------------- +    // Reset +    //-------------------------------- + +    test.start_test("Flush block then reset it", 10us); +    blk_ctrl.flush_and_reset(); +    test.end_test(); + +    //-------------------------------- +    // Verify Block Info +    //-------------------------------- + +    test.start_test("Verify Block Info", 2us); +    `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect NOC_ID Value"); +    `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS_I, "Incorrect NUM_DATA_I Value"); +    `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS_O, "Incorrect NUM_DATA_O Value"); +    `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value"); +    test.end_test(); + +    //-------------------------------- +    // Test Sequences +    //-------------------------------- + +    // Run basic test all ports +    for(port = 0; port < NUM_PORTS; port++) begin +      test_registers(port); +      test_waveforms(port); +    end + +    // Run remaining tests on single port +    port = 0; +    test_gain(port); +    test_packet_length(port); +    test_phase(port); + +    //-------------------------------- +    // Finish Up +    //-------------------------------- + +    // Display final statistics and results, but don't call $finish, since we +    // don't want to kill other instances of this testbench that may be +    // running. +    test.end_tb(0); + +    // Kill the clocks to end this instance of the testbench +    rfnoc_chdr_clk_gen.kill(); +    rfnoc_ctrl_clk_gen.kill(); +    ce_clk_gen.kill(); +  end : tb_main + +endmodule : rfnoc_block_siggen_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_siggen_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_siggen_core.v new file mode 100644 index 000000000..bcb664fe0 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_siggen/rfnoc_siggen_core.v @@ -0,0 +1,284 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_siggen_core +// +// Description: +// +//   This module contains the registers and core logic for a single RFNoC +//   Signal Generator module instance. +// + + +module rfnoc_siggen_core ( +  input wire clk, +  input wire rst, + +  // CtrlPort Slave +  input  wire        s_ctrlport_req_wr, +  input  wire        s_ctrlport_req_rd, +  input  wire [19:0] s_ctrlport_req_addr, +  input  wire [31:0] s_ctrlport_req_data, +  output reg         s_ctrlport_resp_ack, +  output reg  [31:0] s_ctrlport_resp_data, + +  // Output data stream +  output wire [31:0] m_tdata, +  output wire        m_tlast, +  output wire        m_tvalid, +  input  wire        m_tready, +  output wire [15:0] m_tlength +); + +  `include "rfnoc_block_siggen_regs.vh" + + +  //--------------------------------------------------------------------------- +  // Registers +  //--------------------------------------------------------------------------- + +  // Define maximum fixed point value for the gain, equal to about 0.9999 +  localparam MAX_GAIN = {REG_GAIN_LEN-1{1'b1}}; + +  reg [   REG_ENABLE_LEN-1:0] reg_enable    = 0; +  reg [      REG_SPP_LEN-1:0] reg_spp       = 16; +  reg [ REG_WAVEFORM_LEN-1:0] reg_waveform  = WAVE_CONST; +  reg [     REG_GAIN_LEN-1:0] reg_gain      = MAX_GAIN; +  reg [ REG_CONSTANT_LEN-1:0] reg_constant  = 0; +  reg [REG_PHASE_INC_LEN-1:0] reg_phase_inc; +  reg [REG_CARTESIAN_LEN-1:0] reg_cartesian; + +  reg reg_phase_inc_stb; +  reg reg_cartesian_stb; + +  always @(posedge clk) begin +    if (rst) begin +      reg_enable           <= 0; +      reg_spp              <= 16; +      reg_waveform         <= WAVE_CONST; +      reg_gain             <= MAX_GAIN; +      reg_constant         <= 0; +      reg_phase_inc        <= 'bX; +      reg_cartesian        <= 'bX; +      s_ctrlport_resp_ack  <= 1'b0; +      s_ctrlport_resp_data <= 'bX; +      reg_phase_inc_stb    <= 1'b0; +      reg_cartesian_stb    <= 1'b0; +    end else begin + +      // Default assignments +      s_ctrlport_resp_ack  <= 1'b0; +      s_ctrlport_resp_data <= 0; +      reg_phase_inc_stb    <= 1'b0; +      reg_cartesian_stb    <= 1'b0; + +      // Handle register writes +      if (s_ctrlport_req_wr) begin +        s_ctrlport_resp_ack <= 1; +        case (s_ctrlport_req_addr) +          REG_ENABLE    : reg_enable    <= s_ctrlport_req_data[REG_ENABLE_LEN-1:0]; +          REG_SPP       : reg_spp       <= s_ctrlport_req_data[REG_SPP_LEN-1:0]; +          REG_WAVEFORM  : reg_waveform  <= s_ctrlport_req_data[REG_WAVEFORM_LEN-1:0]; +          REG_GAIN      : reg_gain      <= s_ctrlport_req_data[REG_GAIN_LEN-1:0]; +          REG_CONSTANT  : reg_constant  <= s_ctrlport_req_data[REG_CONSTANT_LEN-1:0]; +          REG_PHASE_INC : begin +            reg_phase_inc     <= s_ctrlport_req_data[REG_PHASE_INC_LEN-1:0]; +            reg_phase_inc_stb <= 1'b1; +          end +          REG_CARTESIAN : begin +            reg_cartesian     <= s_ctrlport_req_data[REG_CARTESIAN_LEN-1:0]; +            reg_cartesian_stb <= 1'b1; +          end +        endcase +      end + +      // Handle register reads +      if (s_ctrlport_req_rd) begin +        s_ctrlport_resp_ack <= 1; +        case (s_ctrlport_req_addr) +          REG_ENABLE    : s_ctrlport_resp_data[REG_ENABLE_LEN-1:0]     <= reg_enable; +          REG_SPP       : s_ctrlport_resp_data[REG_SPP_LEN-1:0]        <= reg_spp; +          REG_WAVEFORM  : s_ctrlport_resp_data[REG_WAVEFORM_LEN-1:0]   <= reg_waveform; +          REG_GAIN      : s_ctrlport_resp_data[REG_GAIN_LEN-1:0]       <= reg_gain; +          REG_CONSTANT  : s_ctrlport_resp_data[REG_CONSTANT_LEN-1:0]   <= reg_constant; +          REG_PHASE_INC : s_ctrlport_resp_data[REG_PHASE_INC_LEN-1:0]  <= reg_phase_inc; +          REG_CARTESIAN : s_ctrlport_resp_data[REG_CARTESIAN_LEN-1:0]  <= reg_cartesian; +        endcase +      end +    end +  end + + +  //--------------------------------------------------------------------------- +  // Waveform Generation +  //--------------------------------------------------------------------------- + +  wire [31:0]  axis_sine_tdata; +  wire         axis_sine_tvalid; +  wire         axis_sine_tready; +  wire [31:0]  axis_const_tdata; +  wire         axis_const_tvalid; +  wire         axis_const_tready; +  wire [31:0]  axis_noise_tdata; +  wire         axis_noise_tvalid; +  wire         axis_noise_tready; + +  //------------------------------------ +  // Sine waveform generation +  //------------------------------------ + +  // Convert the registers writes to settings bus transactions. Only one +  // register strobe will assert at a time. +  wire        sine_set_stb  = reg_cartesian_stb | reg_phase_inc_stb; +  wire [31:0] sine_set_data = reg_cartesian_stb ? reg_cartesian : reg_phase_inc; +  wire [ 7:0] sine_set_addr = reg_cartesian_stb; + +  sine_tone #( +    .WIDTH             (32), +    .SR_PHASE_INC_ADDR (0), +    .SR_CARTESIAN_ADDR (1) +  ) sine_tone_i ( +    .clk      (clk), +    .reset    (rst), +    .clear    (1'b0), +    .enable   (1'b1), +    .set_stb  (sine_set_stb), +    .set_data (sine_set_data), +    .set_addr (sine_set_addr), +    .o_tdata  (axis_sine_tdata), +    .o_tlast  (), +    .o_tvalid (axis_sine_tvalid), +    .o_tready (axis_sine_tready) +  ); + +  //------------------------------------ +  // Constant waveform generation +  //------------------------------------ + +  assign axis_const_tdata  = reg_constant; +  assign axis_const_tvalid = 1'b1; + +  //------------------------------------ +  // Noise waveform generation +  //------------------------------------ + +  assign axis_noise_tvalid = 1'b1; + +  // Random number generator +  rng rng_i ( +    .clk (clk), +    .rst (rst), +    .out (axis_noise_tdata) +  ); + + +  //--------------------------------------------------------------------------- +  // Waveform Selection +  //--------------------------------------------------------------------------- + +  wire [31:0]  axis_mux_tdata; +  wire         axis_mux_tvalid; +  wire         axis_mux_tready; + +  axi_mux_select #( +    .WIDTH          (32), +    .SIZE           (3), +    .SWITCH_ON_LAST (0) +  ) axi_mux_select_i ( +    .clk      (clk), +    .reset    (rst), +    .clear    (1'b0), +    .select   (reg_waveform), +    .i_tdata  ({axis_noise_tdata, axis_sine_tdata, axis_const_tdata}), +    .i_tlast  ({3'd0}),   // Length controlled by SPP register +    .i_tvalid ({axis_noise_tvalid, axis_sine_tvalid, axis_const_tvalid}), +    .i_tready ({axis_noise_tready, axis_sine_tready, axis_const_tready}), +    .o_tdata  (axis_mux_tdata), +    .o_tlast  (), +    .o_tvalid (axis_mux_tvalid), +    .o_tready (axis_mux_tready) +  ); + + +  //--------------------------------------------------------------------------- +  // Gain +  //--------------------------------------------------------------------------- + +  wire [63:0]  axis_gain_tdata; +  wire         axis_gain_tvalid; +  wire         axis_gain_tready; +  wire [31:0]  axis_round_tdata; +  wire         axis_round_tvalid; +  wire         axis_round_tready; + +  mult_rc #( +    .WIDTH_REAL (16), +    .WIDTH_CPLX (16), +    .WIDTH_P    (32), +    .DROP_TOP_P (5), +    .LATENCY    (4) +  ) mult_rc_i ( +    .clk         (clk), +    .reset       (rst), +    .real_tdata  (reg_gain), +    .real_tlast  (1'b0), +    .real_tvalid (1'b1), +    .real_tready (), +    .cplx_tdata  (axis_mux_tdata), +    .cplx_tlast  (1'b0), +    .cplx_tvalid (axis_mux_tvalid), +    .cplx_tready (axis_mux_tready), +    .p_tdata     (axis_gain_tdata), +    .p_tlast     (), +    .p_tvalid    (axis_gain_tvalid), +    .p_tready    (axis_gain_tready) +  ); + +  axi_round_and_clip_complex #( +    .WIDTH_IN  (32), +    .WIDTH_OUT (16), +    .CLIP_BITS (1) +  ) axi_round_and_clip_complex_i ( +    .clk      (clk), +    .reset    (rst), +    .i_tdata  (axis_gain_tdata), +    .i_tlast  (1'b0), +    .i_tvalid (axis_gain_tvalid), +    .i_tready (axis_gain_tready), +    .o_tdata  (axis_round_tdata), +    .o_tlast  (), +    .o_tvalid (axis_round_tvalid), +    .o_tready (axis_round_tready) +  ); + + +  //--------------------------------------------------------------------------- +  // Packet Length Control +  //--------------------------------------------------------------------------- + +  wire [REG_SPP_LEN-1:0] m_tlength_samples; + +  assign m_tlength = { m_tlength_samples, 2'b0 };   // 4 bytes per sample + +  axis_packetize #( +    .DATA_W  (32), +    .SIZE_W  (REG_SPP_LEN), +    .FLUSH   (1) +  ) axis_packetize_i ( +    .clk      (clk), +    .rst      (rst), +    .gate     (~reg_enable), +    .size     (reg_spp), +    .i_tdata  (axis_round_tdata), +    .i_tvalid (axis_round_tvalid), +    .i_tready (axis_round_tready), +    .o_tdata  (m_tdata), +    .o_tlast  (m_tlast), +    .o_tvalid (m_tvalid), +    .o_tready (m_tready), +    .o_tuser  (m_tlength_samples) +  ); + +endmodule diff --git a/fpga/usrp3/lib/rfnoc/sine_tone.v b/fpga/usrp3/lib/rfnoc/sine_tone.v new file mode 100644 index 000000000..a687472eb --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/sine_tone.v @@ -0,0 +1,159 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: sine_tone +// +// Description: +// +//   Sine tone generator. This block uses the Xilinx CORDIC IP configured to +//   perform the rotate function in units of scaled radians. See the CORDIC IP +//   Product Guide (PG105) for details. +// +//   The SR_PHASE_INC register controls the phase increment, in scaled +//   radians, for the sine waveform generator. It is a 16-bit signed +//   fixed-point phase value with 3 integer bits and 13 fractional bits. This +//   is the amount by which REG_CARTESIAN is rotated each clock cycle. In +//   other words, it controls the rate of rotation, or the frequency, of the +//   sine wave. In scaled radians, the phase value range -1 to +1 corresponds +//   to -Pi to Pi in radians. +// +//   The SR_CARTESIAN register sets the sets the (X,Y) Cartesian coordinate +//   that will be rotated to generate the sine output. Both X and Y are 16-bit +//   signed fixed-point values with 2 integer bits and 14 fractional bits. Y +//   is in the upper 16-bits and X is in the lower 16-bits. +//  +//   In addition to rotation, the SR_CARTESIAN input vector is also scaled by +//   a "CORDIC scale factor" that equals about 1.1644 (that is, the product of +//   sqrt(1 + 2^(-2i)) for i = 1 to n, where n = 14, the number of fractional +//   bits). +// +// Parameters: +// +//   SR_PHASE_INC_ADDR : The address to use for SR_PHASE_INC. +//   SR_CARTESIAN_ADDR : The address to use for SR_CARTESIAN. +// + + +module sine_tone #( +  parameter WIDTH             = 32, +  parameter SR_PHASE_INC_ADDR = 129, +  parameter SR_CARTESIAN_ADDR = 130 +) ( +  input              clk, +  input              reset, +  input              clear, +  input              enable, + +  // Settings bus +  input              set_stb, +  input  [WIDTH-1:0] set_data, +  input  [      7:0] set_addr, + +  // Output sinusoid +  output [WIDTH-1:0] o_tdata, +  output             o_tlast, +  output             o_tvalid, +  input              o_tready +); + +  wire [15:0] phase_in_tdata; +  wire        phase_in_tlast; +  wire        phase_in_tvalid; +  wire        phase_in_tready; + +  wire [15:0] phase_out_tdata; +  wire        phase_out_tlast; +  wire        phase_out_tvalid; +  wire        phase_out_tready; + +  wire [WIDTH-1:0] cartesian_tdata; +  wire             cartesian_tlast; +  wire             cartesian_tvalid; +  wire             cartesian_tready; + +  wire [WIDTH-1:0] sine_out_tdata; +  wire             sine_out_tlast; +  wire             sine_out_tvalid; +  wire             sine_out_tready; + +  // AXI settings bus for phase values +  axi_setting_reg #( +    .ADDR        (SR_PHASE_INC_ADDR), +    .AWIDTH      (8), +    .WIDTH       (16), +    .STROBE_LAST (1), +    .REPEATS     (1) +  ) set_phase_acc ( +    .clk       (clk), +    .reset     (reset), +    .error_stb (), +    .set_stb   (set_stb), +    .set_addr  (set_addr), +    .set_data  (set_data), +    .o_tdata   (phase_in_tdata), +    .o_tlast   (phase_in_tlast), +    .o_tvalid  (phase_in_tvalid), +    .o_tready  (phase_in_tready & enable) +  ); + +  // AXI settings bus for Cartesian values +  axi_setting_reg #( +    .ADDR    (SR_CARTESIAN_ADDR), +    .AWIDTH  (8), +    .WIDTH   (32), +    .REPEATS (1) +  ) set_axis_cartesian ( +    .clk       (clk), +    .reset     (reset), +    .error_stb (), +    .set_stb   (set_stb), +    .set_addr  (set_addr), +    .set_data  (set_data), +    .o_tdata   (cartesian_tdata), +    .o_tlast   (), +    .o_tvalid  (cartesian_tvalid), +    .o_tready  (cartesian_tready & enable) +  ); + +  assign cartesian_tlast = 1; + +  // Phase accumulator +  phase_accum phase_acc ( +    .clk      (clk), +    .reset    (reset), +    .clear    (clear), +    .i_tdata  (phase_in_tdata), +    .i_tlast  (phase_in_tlast), +    .i_tvalid (1'b1), +    .i_tready (phase_in_tready), +    .o_tdata  (phase_out_tdata), +    .o_tlast  (phase_out_tlast), +    .o_tvalid (phase_out_tvalid), +    .o_tready (phase_out_tready & enable) +  ); + +  // CORDIC +  cordic_rotator cordic_inst ( +    .aclk                    (clk), +    .aresetn                 (~(reset|clear)), +    .s_axis_phase_tdata      (phase_out_tdata), +    .s_axis_phase_tvalid     (phase_out_tvalid & cartesian_tvalid & enable), +    .s_axis_phase_tready     (phase_out_tready), +    .s_axis_cartesian_tdata  (cartesian_tdata), +    .s_axis_cartesian_tlast  (cartesian_tlast), +    .s_axis_cartesian_tvalid (phase_out_tvalid & cartesian_tvalid & enable), +    .s_axis_cartesian_tready (cartesian_tready), +    .m_axis_dout_tdata       (sine_out_tdata), +    .m_axis_dout_tlast       (sine_out_tlast), +    .m_axis_dout_tvalid      (sine_out_tvalid), +    .m_axis_dout_tready      (sine_out_tready & enable) +  ); + +  assign o_tdata         = sine_out_tdata; +  assign o_tlast         = sine_out_tlast; +  assign o_tvalid        = sine_out_tvalid; +  assign sine_out_tready = o_tready; + +endmodule  // sine_tone  | 
