1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 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.
localparam 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
|