1// 2// Copyright 2016 Ettus Research, a National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// DDS that supports timed commands via the settings bus 7 8module dds_timed #( 9 parameter SR_FREQ_ADDR = 0, 10 parameter SR_SCALE_IQ_ADDR = 1, 11 parameter CMD_FIFO_SIZE = 5, 12 parameter WIDTH = 16, 13 parameter DDS_WIDTH = 24, 14 parameter PHASE_WIDTH = 24, 15 parameter PHASE_ACCUM_WIDTH = 32, 16 parameter SCALING_WIDTH = 18, 17 parameter HEADER_WIDTH = 128, 18 parameter HEADER_FIFO_SIZE = 5, 19 parameter SR_AWIDTH = 8, 20 parameter SR_DWIDTH = 32, 21 parameter SR_TWIDTH = 64 22)( 23 input clk, input reset, input clear, 24 output timed_cmd_fifo_full, 25 input set_stb, input [SR_AWIDTH-1:0] set_addr, input [SR_DWIDTH-1:0] set_data, 26 input [SR_TWIDTH-1:0] set_time, input set_has_time, 27 input [2*WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, input [HEADER_WIDTH-1:0] i_tuser, 28 output [2*WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready, output [HEADER_WIDTH-1:0] o_tuser 29); 30 31 /************************************************************************** 32 * Track VITA time 33 *************************************************************************/ 34 wire [2*WIDTH-1:0] int_tdata; 35 wire [HEADER_WIDTH-1:0] int_tuser; 36 wire int_tlast, int_tvalid, int_tready, int_tag; 37 wire [SR_AWIDTH-1:0] out_set_addr, timed_set_addr; 38 wire [SR_DWIDTH-1:0] out_set_data, timed_set_data; 39 wire out_set_stb, timed_set_stb; 40 wire eob; 41 42 axi_tag_time #( 43 .WIDTH(2*WIDTH), 44 .NUM_TAGS(1), 45 .SR_TAG_ADDRS(SR_FREQ_ADDR)) 46 axi_tag_time ( 47 .clk(clk), 48 .reset(reset), 49 .clear(clear), 50 .tick_rate(16'd1), 51 .timed_cmd_fifo_full(timed_cmd_fifo_full), 52 .s_axis_data_tdata(i_tdata), .s_axis_data_tlast(i_tlast), 53 .s_axis_data_tvalid(i_tvalid), .s_axis_data_tready(i_tready), 54 .s_axis_data_tuser(i_tuser), 55 .m_axis_data_tdata(int_tdata), .m_axis_data_tlast(int_tlast), 56 .m_axis_data_tvalid(int_tvalid), .m_axis_data_tready(int_tready), 57 .m_axis_data_tuser(int_tuser), .m_axis_data_tag(int_tag), 58 .in_set_stb(set_stb), .in_set_addr(set_addr), .in_set_data(set_data), 59 .in_set_time(set_time), .in_set_has_time(set_has_time), 60 .out_set_stb(out_set_stb), .out_set_addr(out_set_addr), .out_set_data(out_set_data), 61 .timed_set_stb(timed_set_stb), .timed_set_addr(timed_set_addr), .timed_set_data(timed_set_data)); 62 63 wire [2*WIDTH-1:0] dds_in_tdata, unused_tdata; 64 wire [HEADER_WIDTH-1:0] header_in_tdata, header_out_tdata, unused_tuser; 65 wire dds_in_tlast, dds_in_tvalid, dds_in_tready, dds_in_tag; 66 wire header_in_tvalid, header_in_tready, header_in_tlast, unused_tag; 67 wire header_out_tvalid, header_out_tready; 68 69 split_stream #( 70 .WIDTH(2*WIDTH+HEADER_WIDTH+1), .ACTIVE_MASK(4'b0011)) 71 split_head ( 72 .clk(clk), .reset(reset), .clear(clear), 73 .i_tdata({int_tdata,int_tuser,int_tag}), .i_tlast(int_tlast), 74 .i_tvalid(int_tvalid), .i_tready(int_tready), 75 .o0_tdata({dds_in_tdata,unused_tuser,dds_in_tag}), .o0_tlast(dds_in_tlast), 76 .o0_tvalid(dds_in_tvalid), .o0_tready(dds_in_tready), 77 .o1_tdata({unused_tdata,header_in_tdata,unused_tag}), .o1_tlast(header_in_tlast), 78 .o1_tvalid(header_in_tvalid), .o1_tready(header_in_tready), 79 .o2_tready(1'b0), .o3_tready(1'b0)); 80 81 axi_fifo #( 82 .WIDTH(HEADER_WIDTH), .SIZE(HEADER_FIFO_SIZE)) 83 axi_fifo_header ( 84 .clk(clk), .reset(reset), .clear(clear), 85 .i_tdata(header_in_tdata), .i_tvalid(header_in_tvalid & header_in_tlast), .i_tready(header_in_tready), 86 .o_tdata(header_out_tdata), .o_tvalid(header_out_tvalid), 87 .o_tready(header_out_tready), // Consume header on last output sample 88 .space(), .occupied()); 89 90 assign eob = header_in_tdata[124]; 91 92 /************************************************************************** 93 * Settings Regs 94 *************************************************************************/ 95 wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_tdata, phase_inc_timed_tdata; 96 wire phase_inc_tlast, phase_inc_tvalid, phase_inc_tready; 97 wire phase_inc_timed_tlast, phase_inc_timed_tready , phase_inc_timed_tvalid; 98 99 axi_setting_reg #( 100 .ADDR(SR_FREQ_ADDR), .AWIDTH(SR_AWIDTH), .WIDTH(PHASE_ACCUM_WIDTH), .STROBE_LAST(1)) 101 set_freq ( 102 .clk(clk), .reset(reset), 103 .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data), 104 .o_tdata(phase_inc_tdata), .o_tlast(phase_inc_tlast), .o_tvalid(phase_inc_tvalid), .o_tready(phase_inc_tready)); 105 106 axi_setting_reg #( 107 .ADDR(SR_FREQ_ADDR), .USE_FIFO(1), .FIFO_SIZE(CMD_FIFO_SIZE), .AWIDTH(SR_AWIDTH), .WIDTH(PHASE_ACCUM_WIDTH), .STROBE_LAST(1)) 108 set_freq_timed ( 109 .clk(clk), .reset(reset), 110 .set_stb(timed_set_stb), .set_addr(timed_set_addr), .set_data(timed_set_data), 111 .o_tdata(phase_inc_timed_tdata), .o_tlast(phase_inc_timed_tlast), .o_tvalid(phase_inc_timed_tvalid), .o_tready(phase_inc_timed_tready)); 112 113 wire [SCALING_WIDTH-1:0] scaling_tdata; 114 wire scaling_tvalid, scaling_tready; 115 116 axi_setting_reg #( 117 .ADDR(SR_SCALE_IQ_ADDR), .AWIDTH(SR_AWIDTH), .WIDTH(SCALING_WIDTH), .REPEATS(1)) 118 set_scale ( 119 .clk(clk), .reset(reset), 120 .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data), 121 .o_tdata(scaling_tdata), .o_tlast(), .o_tvalid(scaling_tvalid), .o_tready(scaling_tready)); 122 123 /************************************************************************** 124 * DDS + Complex Mult + Phase Accumulator 125 *************************************************************************/ 126 wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_mux_tdata; 127 reg [PHASE_ACCUM_WIDTH-1:0] phase_inc; 128 wire phase_inc_mux_tlast, phase_inc_mux_tvalid, phase_inc_mux_tready; 129 reg [PHASE_ACCUM_WIDTH-1:0] phase; 130 131 wire [PHASE_WIDTH-1:0] phase_tdata = phase[PHASE_ACCUM_WIDTH-1:PHASE_ACCUM_WIDTH-PHASE_WIDTH]; 132 wire phase_tvalid, phase_tready, phase_tlast; 133 134 wire [WIDTH*2-1:0] dds_in_fifo_tdata; 135 wire dds_in_fifo_tvalid, dds_in_fifo_tready, dds_in_fifo_tlast; 136 wire dds_out_tlast, dds_out_tvalid, dds_out_tready; 137 138 wire [DDS_WIDTH-1:0] dds_in_i_tdata, dds_in_q_tdata; 139 wire [DDS_WIDTH-1:0] dds_out_i_tdata, dds_out_q_tdata; 140 wire [15:0] dds_input_fifo_space, dds_input_fifo_occupied; 141 142 wire [WIDTH*2-1:0] dds_in_sync_tdata; 143 wire dds_in_sync_tvalid, dds_in_sync_tready, dds_in_sync_tlast; 144 wire [PHASE_WIDTH-1:0] phase_sync_tdata; 145 wire phase_sync_tvalid, phase_sync_tready, phase_sync_tlast; 146 147 assign phase_inc_mux_tdata = phase_inc_timed_tready ? phase_inc_timed_tdata : phase_inc_tdata; 148 assign phase_inc_mux_tlast = phase_inc_timed_tready ? phase_inc_timed_tlast : phase_inc_tlast; 149 assign phase_inc_mux_tvalid = phase_inc_timed_tready ? phase_inc_timed_tvalid : phase_inc_tvalid; 150 assign phase_inc_tready = phase_inc_mux_tready; 151 assign phase_inc_timed_tready = phase_inc_mux_tready & dds_in_tag; 152 assign phase_inc_mux_tready = phase_tready; 153 154 // phase is only valid when input i/q data stream is valid 155 assign phase_tvalid = dds_in_tvalid; 156 assign phase_tlast = dds_in_tlast; 157 158 always @(posedge clk) begin 159 if (reset | clear) begin 160 phase_inc <= 0; 161 end else if (phase_inc_mux_tvalid & phase_inc_mux_tready) begin 162 phase_inc <= phase_inc_mux_tdata; 163 end 164 end 165 166 // NCO, increment phase input to DDS SIN/COS LUT 167 always @(posedge clk) begin 168 if (reset | clear | (phase_inc_mux_tvalid & phase_inc_mux_tready) | eob) begin 169 phase <= 0; 170 end else if (dds_in_tvalid & dds_in_tready) begin //only increment phase when data into dds is valid and data fifo is ready 171 phase <= phase + phase_inc; 172 end 173 end 174 175 176 // Sync the two path's pipeline delay. 177 // This is needed to ensure that applying the phase update happens on the 178 // correct sample regardless of differing downstream path delays. 179 axi_sync #( 180 .SIZE(2), 181 .WIDTH_VEC({PHASE_WIDTH,2*WIDTH}), // Vector of widths, each width is defined by a 32-bit value 182 .FIFO_SIZE(0)) 183 axi_sync ( 184 .clk(clk), .reset(reset), .clear(clear), 185 .i_tdata({phase_tdata,dds_in_tdata}), 186 .i_tlast({phase_tlast,dds_in_tlast}), 187 .i_tvalid({phase_tvalid,dds_in_tvalid}), 188 .i_tready({phase_tready,dds_in_tready}), 189 .o_tdata({phase_sync_tdata,dds_in_sync_tdata}), 190 .o_tlast({phase_sync_tlast,dds_in_sync_tlast}), 191 .o_tvalid({phase_sync_tvalid,dds_in_sync_tvalid}), 192 .o_tready({phase_sync_tready,dds_in_sync_tready})); 193 194 // fifo to hold input data while pipeline catches up in dds 195 // this is blocked by the axi_sync following the dds 196 axi_fifo #(.WIDTH(2*WIDTH+1), .SIZE(5)) dds_input_fifo( 197 .clk(clk), .reset(reset), .clear(clear), 198 .i_tdata({dds_in_sync_tlast,dds_in_sync_tdata}), .i_tvalid(dds_in_sync_tvalid), .i_tready(dds_in_sync_tready), 199 .o_tdata({dds_in_fifo_tlast,dds_in_fifo_tdata}), .o_tvalid(dds_in_fifo_tvalid), .o_tready(dds_in_fifo_tready), 200 .space(dds_input_fifo_space), .occupied(dds_input_fifo_occupied) 201 ); 202 203 // after fifo, do q quick sign extend op to get up to 24 bits. to match how the cordic deals with the data path. 204 sign_extend #( 205 .bits_in(WIDTH), .bits_out(DDS_WIDTH)) 206 sign_extend_dds_i ( 207 .in(dds_in_fifo_tdata[2*WIDTH-1:WIDTH]), .out(dds_in_i_tdata)); 208 209 sign_extend #( 210 .bits_in(WIDTH), .bits_out(DDS_WIDTH)) 211 sign_extend_dds_q ( 212 .in(dds_in_fifo_tdata[WIDTH-1:0]), .out(dds_in_q_tdata)); 213 214 215 // Wrapper for Xilinx IP AXI DDS + Complex Multiply 216 // NOTE: Seems Xilinx IP expects opposite I/Q combined complex data buses, so they are swapped here. 217 dds_freq_tune dds_freq_tune_inst ( 218 .clk(clk), 219 .reset(reset | clear), 220 .eob(eob), 221 .rate_changed(1'b0), 222 .dds_input_fifo_occupied(dds_input_fifo_occupied), 223 /* IQ input */ 224 .s_axis_din_tlast(dds_in_fifo_tlast), 225 .s_axis_din_tvalid(dds_in_fifo_tvalid), 226 .s_axis_din_tready(dds_in_fifo_tready), 227 .s_axis_din_tdata({dds_in_q_tdata, dds_in_i_tdata}), 228 /* Phase input from NCO */ 229 .s_axis_phase_tlast(phase_sync_tlast), 230 .s_axis_phase_tvalid(phase_sync_tvalid), 231 .s_axis_phase_tready(phase_sync_tready), 232 .s_axis_phase_tdata(phase_sync_tdata), //24 bit 233 /* IQ output */ 234 .m_axis_dout_tlast(dds_out_tlast), 235 .m_axis_dout_tvalid(dds_out_tvalid), 236 .m_axis_dout_tready(dds_out_tready), 237 .m_axis_dout_tdata({dds_out_q_tdata, dds_out_i_tdata}) 238 //debug signals 239 ); 240 /************************************************************************ 241 * Perform scaling on the IQ output 242 ************************************************************************/ 243 wire [DDS_WIDTH+SCALING_WIDTH-1:0] scaled_i_tdata, scaled_q_tdata; 244 wire scaled_tlast, scaled_tvalid, scaled_tready; 245 246 mult #( 247 .WIDTH_A(DDS_WIDTH), 248 .WIDTH_B(SCALING_WIDTH), 249 .WIDTH_P(DDS_WIDTH+SCALING_WIDTH), 250 .DROP_TOP_P(4), 251 .LATENCY(3), 252 .CASCADE_OUT(0)) 253 i_mult ( 254 .clk(clk), .reset(reset | clear), 255 .a_tdata(dds_out_i_tdata), .a_tlast(dds_out_tlast), .a_tvalid(dds_out_tvalid), .a_tready(dds_out_tready), 256 .b_tdata(scaling_tdata), .b_tlast(1'b0), .b_tvalid(dds_out_tvalid /* aligning scaling_tdata with dds_tdata */), .b_tready(scaling_tready), 257 .p_tdata(scaled_i_tdata), .p_tlast(scaled_tlast), .p_tvalid(scaled_tvalid), .p_tready(scaled_tready)); 258 259 mult #( 260 .WIDTH_A(DDS_WIDTH), 261 .WIDTH_B(SCALING_WIDTH), 262 .WIDTH_P(DDS_WIDTH+SCALING_WIDTH), 263 .DROP_TOP_P(4), 264 .LATENCY(3), 265 .CASCADE_OUT(0)) 266 q_mult ( 267 .clk(clk), .reset(reset | clear), 268 .a_tdata(dds_out_q_tdata), .a_tlast(), .a_tvalid(dds_out_tvalid), .a_tready(), 269 .b_tdata(scaling_tdata), .b_tlast(1'b0), .b_tvalid(dds_out_tvalid /* aligning scaling_tdata with dds_tdata */), .b_tready(), 270 .p_tdata(scaled_q_tdata), .p_tlast(), .p_tvalid(), .p_tready(scaled_tready)); 271 272 wire [2*WIDTH-1:0] sample_tdata; 273 wire sample_tlast, sample_tvalid, sample_tready; 274 275 axi_round_and_clip_complex #( 276 .WIDTH_IN(DDS_WIDTH+SCALING_WIDTH), .WIDTH_OUT(WIDTH), .CLIP_BITS(12)) 277 axi_round_and_clip_complex ( 278 .clk(clk), .reset(reset | clear), 279 .i_tdata({scaled_i_tdata, scaled_q_tdata}), .i_tlast(scaled_tlast), .i_tvalid(scaled_tvalid), .i_tready(scaled_tready), 280 .o_tdata(sample_tdata), .o_tlast(sample_tlast), .o_tvalid(sample_tvalid), .o_tready(sample_tready)); 281 282 // Throttle output on last sample if header is not valid 283 assign header_out_tready = sample_tlast & sample_tvalid & o_tready; 284 assign sample_tready = (sample_tvalid & sample_tlast) ? (header_out_tvalid & o_tready) : o_tready; 285 assign o_tvalid = (sample_tvalid & sample_tlast) ? header_out_tvalid : sample_tvalid; 286 assign o_tlast = sample_tlast; 287 assign o_tdata = sample_tdata; 288 assign o_tuser = header_out_tdata; 289 290endmodule 291