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.
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
|