aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v')
-rw-r--r--fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v228
1 files changed, 228 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v b/fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v
new file mode 100644
index 000000000..4b8a299b8
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v
@@ -0,0 +1,228 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: dds_freq_tune_duc
+//
+// Description:
+//
+// Performs a frequency shift on a signal by multiplying it with a complex
+// sinusoid synthesized from a DDS. This module expects samples data to be in
+// {Q,I} order.
+//
+// The din input is expected to contain a complex 24-bit signed fixed-point
+// values with 15 fractional bits. The phase input is expected to contain
+// unsigned 24-bit fixed-point with 24 fractional bits, and therefore
+// represents the range [0,1), which corresponds to the range [0,2π) radians.
+// The output will then be a complex 24-bit signed fixed-point with 15
+// fractional bits.
+//
+// This version does the same thing as dds_freq_tune, but does not
+// reset/flush the DDS between packets or when an EOB occurs, and it includes
+// a FIFO on the din data path. This separate version was created to avoid
+// affecting the behavior of the DDC.
+//
+// ┌───┐
+// phase >──┤DDS├──┐ ┌───────┐
+// └───┘ └─┤Complex│ ┌─────┐
+// │ Mult ├──┤Round├───> dout
+// ┌────┐ ┌─┤ │ └─────┘
+// din >──┤FIFO├─┘ └───────┘
+// └────┘
+//
+// Parameters:
+//
+// Note: The parameters should NOT be changed, since they depend on the IP
+// configurations.
+//
+// INPUT_W : Width of each component of din.
+// PHASE_W : Width of the phase input.
+// OUTPUT_W : Width of each component of dout.
+//
+
+`default_nettype none
+
+
+module dds_freq_tune_duc #(
+ parameter INPUT_W = 24,
+ parameter PHASE_W = 24,
+ parameter OUTPUT_W = 24
+) (
+ input wire clk,
+ input wire reset,
+
+ // IQ input (Q in the upper, I in the lower bits)
+ input wire [INPUT_W*2-1:0] s_axis_din_tdata,
+ input wire s_axis_din_tlast,
+ input wire s_axis_din_tvalid,
+ output wire s_axis_din_tready,
+
+ // Phase input from NCO
+ input wire [PHASE_W-1:0] s_axis_phase_tdata,
+ input wire s_axis_phase_tlast,
+ input wire s_axis_phase_tvalid,
+ output wire s_axis_phase_tready,
+
+ // IQ output (Q in the upper, I in the lower bits)
+ output wire [OUTPUT_W*2-1:0] m_axis_dout_tdata,
+ output wire m_axis_dout_tlast,
+ output wire m_axis_dout_tvalid,
+ input wire m_axis_dout_tready
+);
+
+ //---------------------------------------------------------------------------
+ // Reset Generation
+ //---------------------------------------------------------------------------
+
+ reg reset_d1, reset_int;
+
+ // Create a local reset, named reset_int, which will always be asserted for
+ // at least 2 clock cycles, which is required by Xilinx DDS and complex
+ // multiplier IP.
+ always @(posedge clk) begin
+ reset_d1 <= reset;
+ reset_int <= reset | reset_d1;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Data Input FIFO
+ //---------------------------------------------------------------------------
+ //
+ // We want the din and phase inputs paths to be balanced, so that a new
+ // data/phase pair can be input on each clock cycles. This FIFO allows the
+ // din data path to queue up samples while the DDS is processing.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [INPUT_W*2-1:0] s_axis_fifo_tdata;
+ wire s_axis_fifo_tlast;
+ wire s_axis_fifo_tvalid;
+ wire s_axis_fifo_tready;
+
+ axi_fifo #(
+ .WIDTH (2*INPUT_W+1),
+ .SIZE (5)
+ ) axi_fifo_i (
+ .clk (clk),
+ .reset (reset),
+ .clear (1'b0),
+ .i_tdata ({ s_axis_din_tlast, s_axis_din_tdata }),
+ .i_tvalid (s_axis_din_tvalid),
+ .i_tready (s_axis_din_tready),
+ .o_tdata ({ s_axis_fifo_tlast, s_axis_fifo_tdata }),
+ .o_tvalid (s_axis_fifo_tvalid),
+ .o_tready (s_axis_fifo_tready),
+ .space (),
+ .occupied ()
+ );
+
+
+ //---------------------------------------------------------------------------
+ // DDS/NCO
+ //---------------------------------------------------------------------------
+
+ // Width of each component of the DDS output. This width is fixed by the IP
+ // configuration.
+ parameter DDS_W = 16;
+
+ wire m_axis_dds_tlast;
+ wire m_axis_dds_tvalid;
+ wire m_axis_dds_tready;
+ wire [DDS_W*2-1:0] m_axis_dds_tdata;
+
+ // DDS to convert the phase input to a unit-length complex number with that
+ // phase. It takes in an unsigned 24-bit phase with 24 fractional bits and
+ // outputs two signed 16-bit fixed point values with 14 fractional bits. The
+ // output has sin(2*pi*phase) in the upper 16 bits and cos(2*pi*phase) in the
+ // lower 16-bits.
+ dds_wrapper dds_wrapper_i (
+ .clk (clk),
+ .rst (reset_int),
+ .s_axis_phase_tdata (s_axis_phase_tdata),
+ .s_axis_phase_tvalid (s_axis_phase_tvalid),
+ .s_axis_phase_tlast (s_axis_phase_tlast),
+ .s_axis_phase_tready (s_axis_phase_tready),
+ .m_axis_data_tdata (m_axis_dds_tdata),
+ .m_axis_data_tvalid (m_axis_dds_tvalid),
+ .m_axis_data_tlast (m_axis_dds_tlast),
+ .m_axis_data_tready (m_axis_dds_tready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Complex Multiplier
+ //---------------------------------------------------------------------------
+ //
+ // Use a complex multiplier to multiply the DDS complex sinusoid by the input
+ // data samples.
+ //
+ //---------------------------------------------------------------------------
+
+ // Width of each component on the output of the complex_multiplier_dds IP.
+ // This width is fixed by the IP configuration.
+ localparam MULT_OUT_W = 32;
+
+ // Width is set by the IP
+ wire [2*MULT_OUT_W-1:0] mult_out_tdata;
+ wire mult_out_tvalid;
+ wire mult_out_tready;
+ wire mult_out_tlast;
+
+ // The complex multiplier IP is configured so that the A input is 21 bits
+ // with 15 fractional bits, and the B input (dds) is 16 bits with 14
+ // fractional bits. Due to AXI-Stream requirements, A is rounded up to 24
+ // bits in width. The full multiplier output result would be 21+16+1 = 38
+ // bits, but the output is configured for 32, dropping the lower 6 bits.
+ // Therefore, the result has 15+14-6 = 23 fractional bits.
+ //
+ // The IP is configured to pass the TLAST from port A through, but we connect
+ // the B path anyway for completeness.
+ complex_multiplier_dds complex_multiplier_dds_i (
+ .aclk (clk),
+ .aresetn (~reset_int),
+ .s_axis_a_tvalid (s_axis_fifo_tvalid),
+ .s_axis_a_tready (s_axis_fifo_tready),
+ .s_axis_a_tlast (s_axis_fifo_tlast),
+ .s_axis_a_tdata (s_axis_fifo_tdata),
+ .s_axis_b_tvalid (m_axis_dds_tvalid),
+ .s_axis_b_tready (m_axis_dds_tready),
+ .s_axis_b_tlast (m_axis_dds_tlast),
+ .s_axis_b_tdata (m_axis_dds_tdata),
+ .m_axis_dout_tvalid (mult_out_tvalid),
+ .m_axis_dout_tready (mult_out_tready),
+ .m_axis_dout_tlast (mult_out_tlast),
+ .m_axis_dout_tdata (mult_out_tdata)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Round
+ //---------------------------------------------------------------------------
+ //
+ // Round the 32-bit multiplier result down to 24 bits. This moves the binary
+ // point so that we go from 23 fractional bits down to 15 fractional bits.
+ //
+ //---------------------------------------------------------------------------
+
+ axi_round_complex #(
+ .WIDTH_IN (MULT_OUT_W),
+ .WIDTH_OUT (OUTPUT_W)
+ ) axi_round_complex_i (
+ .clk (clk),
+ .reset (reset_int),
+ .i_tdata (mult_out_tdata),
+ .i_tlast (mult_out_tlast),
+ .i_tvalid (mult_out_tvalid),
+ .i_tready (mult_out_tready),
+ .o_tdata (m_axis_dout_tdata),
+ .o_tlast (m_axis_dout_tlast),
+ .o_tvalid (m_axis_dout_tvalid),
+ .o_tready (m_axis_dout_tready)
+ );
+
+endmodule
+
+
+`default_nettype wire