diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-06-15 14:14:14 -0500 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2021-08-08 14:59:26 -0500 |
commit | 77975d108a704ce18ec52b4ee1764381b1893752 (patch) | |
tree | 21df90b7b78b67e8f28c6dc0a1de8a8e23d9e8fa /fpga/usrp3/lib/rfnoc/dds_freq_tune_duc.v | |
parent | da4202e6f74796603072aa14544581604e81df02 (diff) | |
download | uhd-77975d108a704ce18ec52b4ee1764381b1893752.tar.gz uhd-77975d108a704ce18ec52b4ee1764381b1893752.tar.bz2 uhd-77975d108a704ce18ec52b4ee1764381b1893752.zip |
fpga: rfnoc: Fix EOB loss in DUC
There were some rare corner cases where the EOB could get lost in the
DUC due to the dds_timed logic not always passing it through as it
should. This resulted in an underflow error message at the end of
transmission.
This commit also fixes an issue where part of the last packet
used a frequency shift of 0 instead of the requested frequency
shift, and an issue where the first few samples of a burst used the
wrong frequency shift value.
Part of the fix includes adding a TUSER port to dds_sin_cos_lut_only.
The TUSER port is built into the IP but was disabled. It is now
enabled and set to 1 bit wide. This has a very small effect on
resource usage and can be left unconnected when not needed.
The dds_freq_tune block was shared by the DUC and DDC. To avoid
affecting the DDC, a new version, dds_freq_tune_duc, is being
added for the DUC to use that has the necessary fixes.
The new dds_wrapper.v is a wrapper for the dds_sin_cos_lut_only IP.
This IP has the undesirable behavior that new inputs must be provided
to push previous outputs through the IP. This wrapper hides that
complexity by adding some logic to ensure all data gets pushed through
automatically. This logic uses the TUSER port on the IP.
Finally, a testbench for dds_timed was added.
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 |