diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v | 228 |
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 |