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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
//
// Copyright 2021 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: dds_freq_tune
//
// 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.
//
module dds_freq_tune #(
parameter WIDTH = 24,
parameter PHASE_WIDTH = 24,
parameter SIN_COS_WIDTH = 16,
parameter OUTPUT_WIDTH = 24
) (
input clk,
input reset,
input eob,
input rate_changed,
input [15:0] dds_input_fifo_occupied,
// IQ input
input [WIDTH*2-1:0] s_axis_din_tdata,
input s_axis_din_tlast,
input s_axis_din_tvalid,
output s_axis_din_tready,
// Phase input from NCO
input [PHASE_WIDTH-1:0] s_axis_phase_tdata,
input s_axis_phase_tlast,
input s_axis_phase_tvalid,
output s_axis_phase_tready,
// IQ output
output [OUTPUT_WIDTH*2-1:0] m_axis_dout_tdata,
output m_axis_dout_tlast,
output m_axis_dout_tvalid,
input m_axis_dout_tready,
// Debug signals
output [ 2:0] state_out,
output phase_valid_hold_out,
output [ 7:0] phase_invalid_wait_count_out,
output reset_dds_out,
output m_axis_dds_tlast_out,
output m_axis_dds_tvalid_out,
output m_axis_dds_tready_out,
output [SIN_COS_WIDTH*2-1:0] m_axis_dds_tdata_out
);
// Wires for DDS output
wire m_axis_dds_tlast;
wire m_axis_dds_tvalid;
wire m_axis_dds_tready;
wire [SIN_COS_WIDTH*2-1:0] m_axis_dds_tdata; // [31:16] = sin|q, [15:0]= cos|i
reg reset_reg;
reg phase_valid_hold;
reg [7:0] phase_invalid_wait_count;
reg [2:0] state;
reg phase_ready_wait;
wire s_axis_phase_tready_dds;
// Initialize DDS resets to 1, since simulation model requires reset at time
// 0 to avoid failure.
reg reset_dds = 1'b1;
reg reset_dds_reg = 1'b1;
// When we're holding valid, make ready low so no new data comes in.
assign s_axis_phase_tready = s_axis_phase_tready_dds & ~phase_valid_hold;
localparam INIT = 3'b000;
localparam VALID = 3'b001;
localparam WAIT = 3'b010;
localparam HOLD_VALID = 3'b011;
// Reset needs to be 2 clk cycles minimum for Xilinx DDS IP
always @(posedge clk) begin
reset_reg <= reset;
reset_dds_reg <= reset_dds;
end
// This state machine resets the DDS when data stops coming and also holds
// valid high until the last packet has been flushed through the DDS.
always @(posedge clk) begin
if(reset) begin
state <= INIT;
phase_valid_hold <= 1'b0;
phase_invalid_wait_count <= 16'h00;
reset_dds <= 1'b0;
end else begin
case(state)
INIT : begin
phase_valid_hold <= 1'b0;
phase_invalid_wait_count <= 16'h0000;
reset_dds <= 1'b0;
if(s_axis_phase_tvalid) begin
state <= VALID;
end
end
VALID : begin
if(~s_axis_phase_tvalid) begin
state <= WAIT;
end
end
WAIT : begin
// Wait until we either get valid data or don't.
if(m_axis_dds_tready) begin
// Only increment when the downstream can accept data.
phase_invalid_wait_count <= phase_invalid_wait_count + 4'b1;
end
if(s_axis_phase_tvalid) begin
// If we get valid data shortly after, then don't push data through
// and reset.
state <= INIT;
end else begin
if(eob | (phase_invalid_wait_count >= 16'h40) | rate_changed) begin
// If a valid never comes (EOB)
state <= HOLD_VALID;
end
end
end
HOLD_VALID : begin
// Hold valid to flush data through the DDS. The DDS IP won't empty
// without additional transfers.
phase_valid_hold <= 1'b1;
// Wait for input FIFO to be empty
if (~s_axis_din_tvalid) begin
state <= INIT;
reset_dds <= 1'b1;
end
end
endcase
end
end
// DDS to generate sin/cos data from phase. It takes in a 24-bit phase value
// and outputs two 16-bit values, with the sine value in the upper 16 bits
// and the cosine value in the lower 16-bits.
//
// The phase input can be thought of as a 24-bit unsigned fixed-point value
// with 24 fractional bits. In other words, the integer range of the input
// maps to the the range [0, 2*pi) in radians.
//
// The output consists of two 16-bit signed fixed-point values with 14
// fractional bits.
//
// This IP effectively computes Euler's formula, e^(j*2*pi*x) = cos(2*pi*x) +
// j*sin(2*pi*x), where x is the phase value, and the output has the real
// component in the lower bits and the imaginary component in the upper bits.
dds_sin_cos_lut_only dds_sin_cos_lut_only_i (
.aclk (clk),
.aresetn (~(reset | reset_reg | reset_dds | reset_dds_reg)),
.s_axis_phase_tvalid (s_axis_phase_tvalid | phase_valid_hold),
.s_axis_phase_tready (s_axis_phase_tready_dds),
.s_axis_phase_tlast (s_axis_phase_tlast),
.s_axis_phase_tdata (s_axis_phase_tdata), // [23 : 0]
.m_axis_data_tvalid (m_axis_dds_tvalid),
.m_axis_data_tready (m_axis_dds_tready),
.m_axis_data_tlast (m_axis_dds_tlast),
.m_axis_data_tdata (m_axis_dds_tdata) // [31 : 0]
);
wire [ WIDTH*2-1:0] mult_in_a_tdata;
wire mult_in_a_tvalid;
wire mult_in_a_tready;
wire mult_in_a_tlast;
wire [SIN_COS_WIDTH*2-1:0] mult_in_b_tdata;
wire mult_in_b_tvalid;
wire mult_in_b_tready;
wire mult_in_b_tlast;
wire [ 2*32-1:0] mult_out_tdata;
wire mult_out_tvalid;
wire mult_out_tready;
wire mult_out_tlast;
axi_sync #(
.SIZE (2),
.WIDTH_VEC ({SIN_COS_WIDTH*2, WIDTH*2}),
.FIFO_SIZE (0)
) axi_sync_i (
.clk (clk),
.reset (reset),
.clear (),
.i_tdata ({ m_axis_dds_tdata, s_axis_din_tdata }),
.i_tlast ({ m_axis_dds_tlast, s_axis_din_tlast }),
.i_tvalid ({ m_axis_dds_tvalid, s_axis_din_tvalid }),
.i_tready ({ m_axis_dds_tready, s_axis_din_tready }),
.o_tdata ({ mult_in_b_tdata, mult_in_a_tdata }),
.o_tlast ({ mult_in_b_tlast, mult_in_a_tlast }),
.o_tvalid ({ mult_in_b_tvalid, mult_in_a_tvalid }),
.o_tready ({ mult_in_b_tready, mult_in_a_tready })
);
// Use a complex multiplier to multiply the input sample (A) by the NCO
// output (B). This multiplier has a 21-bit input A, 16-bit input B, and
// 32-bit output. Due to AXI-Stream requirements, A is rounded up to 24-bit.
//
// Assuming default parameters and unchanged IP, The A input (sample) is
// 21-bit with 15 fractional bits, and the B input (NCO) is 16-bit with 14
// fractional bits. The full 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.
//
// a = Input IQ data stream as 48-bit, lower bits i, upper bits q.
// b = Output of DDS as 32 bit cos/sin, lower bits cos, upper bits sin.
complex_multiplier_dds complex_multiplier_dds_i (
.aclk (clk),
.aresetn (~(reset | reset_reg)),
.s_axis_a_tvalid (mult_in_a_tvalid),
.s_axis_a_tready (mult_in_a_tready),
.s_axis_a_tlast (mult_in_a_tlast),
.s_axis_a_tdata ({mult_in_a_tdata}), // [47 : 0]
.s_axis_b_tvalid (mult_in_b_tvalid),
.s_axis_b_tready (mult_in_b_tready),
.s_axis_b_tlast (mult_in_b_tlast),
.s_axis_b_tdata (mult_in_b_tdata), // [31 : 0]
.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) // [63 : 0]
);
// 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 (32),
.WIDTH_OUT (OUTPUT_WIDTH)
) axi_round_complex_i (
.clk (clk),
.reset (reset | reset_reg),
.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)
);
// Debug
assign state_out = state;
assign phase_valid_hold_out = phase_valid_hold;
assign phase_invalid_wait_count_out = phase_invalid_wait_count;
assign reset_dds_out = reset_dds;
assign m_axis_dds_tlast_out = m_axis_dds_tlast;
assign m_axis_dds_tvalid_out = m_axis_dds_tvalid;
assign m_axis_dds_tready_out = m_axis_dds_tready;
assign m_axis_dds_tdata_out = m_axis_dds_tdata;
endmodule
|