diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/dds_timed.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/dds_timed.v | 718 |
1 files changed, 506 insertions, 212 deletions
diff --git a/fpga/usrp3/lib/rfnoc/dds_timed.v b/fpga/usrp3/lib/rfnoc/dds_timed.v index fd03f6a23..1af82683d 100644 --- a/fpga/usrp3/lib/rfnoc/dds_timed.v +++ b/fpga/usrp3/lib/rfnoc/dds_timed.v @@ -1,9 +1,75 @@ // -// Copyright 2016 Ettus Research, a National Instruments Company +// Copyright 2021 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // -// DDS that supports timed commands via the settings bus +// Module: dds_timed +// +// Description: +// +// DDS (direct digital synthesis) and frequency shift block that supports +// timed commands via the settings bus. +// +// This block takes in samples on i_t* and performs a complex multiplication +// with a digitally synthesized oscillator to implement a digital RF mixer. +// The output is then scaled (optionally), rounded, and clipped if necessary, +// then output on o_t*. +// +// Timed commands allow you to update the SR_FREQ register (the phase +// increment) at the desired time. +// +// The TUSER port contains the packet header information: +// +// tuser[125] : Has timestamp +// tuser[124] : End of burst (EOB) +// tuser[63:0] : Timestamp +// +// For the input, i_tuser should be valid for the duration of the packet. For +// the output, o_tuser is only guaranteed to be valid for the last sample of +// the packet. +// +// Registers: +// +// SR_FREQ : Frequency shift to apply to the input signal. This can be +// thought of as an unsigned PHASE_ACCUM_WIDTH-bit register +// with PHASE_ACCUM_WIDTH fractional bits. That is, the range +// of this register maps to the real values [0,1). This +// register controls the amount by which the phase accumulator +// for the DDS is incremented each clock cycle. It can +// therefore be thought of as a phase angle corresponding to +// the range [0,2π) radians. +// SR_SCALE_IQ : Scaler by which to multiply the IQ outputs. This is a +// SCALING_WIDTH-bit signed fixed-point register with 15 +// fractional bits. If SCALING_WIDTH is 18, then it has the +// range [-4,4). +// +// Parameters: +// +// Note: Care must be used when overriding these parameters because there are +// many dependencies on them. For example, the DDS_WIDTH and +// PHASE_WIDTH depend on the configuration of the underlying DDS IP and +// should only be modified to match that IP. +// +// SR_FREQ_ADDR : Register offset to assign to the SR_FREQ register, +// which contains the phase increment per sample needed +// to achieve the desired DDS frequency. +// SR_SCALE_IQ_ADDR : Register offset to assign to the SR_SCALE_IQ register. +// CMD_FIFO_SIZE : Log2 of the size of the timed command FIFO to use. +// WIDTH : Data width of the I/Q components of the input/output +// samples, typically 16. +// DDS_WIDTH : Bit width to use for the DDS and complex multiplier. +// PHASE_WIDTH : Bit width to use for the phase provided to the DDS IP. +// PHASE_ACCUM_WIDTH : Bit width to use for the phase increment values. +// SCALING_WIDTH : Bit width to use for the IQ scale registers. +// HEADER_WIDTH : Width of the header info (tuser). +// HEADER_FIFO_SIZE : Log2 of the size of the header FIFO. +// SR_AWIDTH : Settings bus address width. +// SR_DWIDTH : Settings bus data width. +// SR_TWIDTH : Settings bus time width. +// + +`default_nettype none + module dds_timed #( parameter SR_FREQ_ADDR = 0, @@ -19,142 +85,280 @@ module dds_timed #( parameter SR_AWIDTH = 8, parameter SR_DWIDTH = 32, parameter SR_TWIDTH = 64 -)( - input clk, input reset, input clear, - output timed_cmd_fifo_full, - input set_stb, input [SR_AWIDTH-1:0] set_addr, input [SR_DWIDTH-1:0] set_data, - input [SR_TWIDTH-1:0] set_time, input set_has_time, - input [2*WIDTH-1:0] i_tdata, input i_tlast, input i_tvalid, output i_tready, input [HEADER_WIDTH-1:0] i_tuser, - output [2*WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready, output [HEADER_WIDTH-1:0] o_tuser +) ( + input wire clk, + input wire reset, + input wire clear, + + // Indicates if the timed command FIFO is full + output wire timed_cmd_fifo_full, + + // Settings bus for register access + input wire set_stb, + input wire [SR_AWIDTH-1:0] set_addr, + input wire [SR_DWIDTH-1:0] set_data, + input wire [SR_TWIDTH-1:0] set_time, + input wire set_has_time, + + // Input sample stream + input wire [ 2*WIDTH-1:0] i_tdata, + input wire i_tlast, + input wire i_tvalid, + output wire i_tready, + input wire [HEADER_WIDTH-1:0] i_tuser, + + // Output sample stream + output wire [ 2*WIDTH-1:0] o_tdata, + output wire o_tlast, + output wire o_tvalid, + input wire o_tready, + output wire [HEADER_WIDTH-1:0] o_tuser ); - /************************************************************************** - * Track VITA time - *************************************************************************/ - wire [2*WIDTH-1:0] int_tdata; + //--------------------------------------------------------------------------- + // Time Tracking + //--------------------------------------------------------------------------- + + wire [ 2*WIDTH-1:0] int_tdata; wire [HEADER_WIDTH-1:0] int_tuser; - wire int_tlast, int_tvalid, int_tready, int_tag; - wire [SR_AWIDTH-1:0] out_set_addr, timed_set_addr; - wire [SR_DWIDTH-1:0] out_set_data, timed_set_data; - wire out_set_stb, timed_set_stb; - wire eob; + wire int_tlast; + wire int_tvalid; + wire int_tready; + wire int_tag; + wire [ SR_AWIDTH-1:0] out_set_addr; + wire [ SR_AWIDTH-1:0] timed_set_addr; + wire [ SR_DWIDTH-1:0] out_set_data; + wire [ SR_DWIDTH-1:0] timed_set_data; + wire out_set_stb; + wire timed_set_stb; + // This module checks for timed writes to SR_FREQ_ADDR and outputs the + // register write on timed_set_* (if it was timed) or set_* (if it was not + // timed). It then tags the sample for which the timed command to + // SR_FREQ_ADDR should occur by asserting m_axis_data_tag when that sample is + // output. axi_tag_time #( - .WIDTH(2*WIDTH), - .NUM_TAGS(1), - .SR_TAG_ADDRS(SR_FREQ_ADDR)) - axi_tag_time ( - .clk(clk), - .reset(reset), - .clear(clear), - .tick_rate(16'd1), - .timed_cmd_fifo_full(timed_cmd_fifo_full), - .s_axis_data_tdata(i_tdata), .s_axis_data_tlast(i_tlast), - .s_axis_data_tvalid(i_tvalid), .s_axis_data_tready(i_tready), - .s_axis_data_tuser(i_tuser), - .m_axis_data_tdata(int_tdata), .m_axis_data_tlast(int_tlast), - .m_axis_data_tvalid(int_tvalid), .m_axis_data_tready(int_tready), - .m_axis_data_tuser(int_tuser), .m_axis_data_tag(int_tag), - .in_set_stb(set_stb), .in_set_addr(set_addr), .in_set_data(set_data), - .in_set_time(set_time), .in_set_has_time(set_has_time), - .out_set_stb(out_set_stb), .out_set_addr(out_set_addr), .out_set_data(out_set_data), - .timed_set_stb(timed_set_stb), .timed_set_addr(timed_set_addr), .timed_set_data(timed_set_data)); - - wire [2*WIDTH-1:0] dds_in_tdata, unused_tdata; - wire [HEADER_WIDTH-1:0] header_in_tdata, header_out_tdata, unused_tuser; - wire dds_in_tlast, dds_in_tvalid, dds_in_tready, dds_in_tag; - wire header_in_tvalid, header_in_tready, header_in_tlast, unused_tag; - wire header_out_tvalid, header_out_tready; + .WIDTH (2*WIDTH), + .NUM_TAGS (1), + .SR_TAG_ADDRS (SR_FREQ_ADDR) + ) axi_tag_time ( + .clk (clk), + .reset (reset), + .clear (clear), + .tick_rate (16'd1), + .timed_cmd_fifo_full (timed_cmd_fifo_full), + .s_axis_data_tdata (i_tdata), + .s_axis_data_tlast (i_tlast), + .s_axis_data_tvalid (i_tvalid), + .s_axis_data_tready (i_tready), + .s_axis_data_tuser (i_tuser), + .m_axis_data_tdata (int_tdata), + .m_axis_data_tlast (int_tlast), + .m_axis_data_tvalid (int_tvalid), + .m_axis_data_tready (int_tready), + .m_axis_data_tuser (int_tuser), + .m_axis_data_tag (int_tag), + .in_set_stb (set_stb), + .in_set_addr (set_addr), + .in_set_data (set_data), + .in_set_time (set_time), + .in_set_has_time (set_has_time), + .out_set_stb (out_set_stb), + .out_set_addr (out_set_addr), + .out_set_data (out_set_data), + .timed_set_stb (timed_set_stb), + .timed_set_addr (timed_set_addr), + .timed_set_data (timed_set_data) + ); + + wire [ 2*WIDTH-1:0] dds_in_tdata; + wire [ 2*WIDTH-1:0] unused_tdata; + wire [HEADER_WIDTH-1:0] header_in_tdata; + wire [HEADER_WIDTH-1:0] header_out_tdata; + wire [HEADER_WIDTH-1:0] dds_in_tuser; + wire dds_in_tlast; + wire dds_in_tvalid; + wire dds_in_tready; + wire dds_in_tag; + wire header_in_tvalid; + wire header_in_tready; + wire header_in_tlast; + wire unused_tag; + wire header_out_tvalid; + wire header_out_tready; + + + //--------------------------------------------------------------------------- + // Split Stream + //--------------------------------------------------------------------------- + // + // Split the data stream into two streams, one with the data/tag (dds_in_t*) + // and the other with the header (header_in_t*). + // + //--------------------------------------------------------------------------- split_stream #( - .WIDTH(2*WIDTH+HEADER_WIDTH+1), .ACTIVE_MASK(4'b0011)) - split_head ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({int_tdata,int_tuser,int_tag}), .i_tlast(int_tlast), - .i_tvalid(int_tvalid), .i_tready(int_tready), - .o0_tdata({dds_in_tdata,unused_tuser,dds_in_tag}), .o0_tlast(dds_in_tlast), - .o0_tvalid(dds_in_tvalid), .o0_tready(dds_in_tready), - .o1_tdata({unused_tdata,header_in_tdata,unused_tag}), .o1_tlast(header_in_tlast), - .o1_tvalid(header_in_tvalid), .o1_tready(header_in_tready), - .o2_tready(1'b0), .o3_tready(1'b0)); + .WIDTH (2*WIDTH+HEADER_WIDTH+1), + .ACTIVE_MASK (4'b0011) + ) split_head ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata ({ int_tdata, int_tuser, int_tag }), + .i_tlast (int_tlast), + .i_tvalid (int_tvalid), + .i_tready (int_tready), + .o0_tdata ({ dds_in_tdata, dds_in_tuser, dds_in_tag }), + .o0_tlast (dds_in_tlast), + .o0_tvalid (dds_in_tvalid), + .o0_tready (dds_in_tready), + .o1_tdata ({ unused_tdata, header_in_tdata, unused_tag }), + .o1_tlast (header_in_tlast), + .o1_tvalid (header_in_tvalid), + .o1_tready (header_in_tready), + .o2_tdata (), + .o2_tlast (), + .o2_tvalid (), + .o2_tready (1'b0), + .o3_tdata (), + .o3_tlast (), + .o3_tvalid (), + .o3_tready (1'b0) + ); + + + //--------------------------------------------------------------------------- + // Header FIFO + //--------------------------------------------------------------------------- + // + // Store each packet header in a FIFO to be read out when the packet is + // output. + // + //--------------------------------------------------------------------------- axi_fifo #( - .WIDTH(HEADER_WIDTH), .SIZE(HEADER_FIFO_SIZE)) - axi_fifo_header ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata(header_in_tdata), .i_tvalid(header_in_tvalid & header_in_tlast), .i_tready(header_in_tready), - .o_tdata(header_out_tdata), .o_tvalid(header_out_tvalid), - .o_tready(header_out_tready), // Consume header on last output sample - .space(), .occupied()); - - assign eob = header_in_tdata[124]; - - /************************************************************************** - * Settings Regs - *************************************************************************/ - wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_tdata, phase_inc_timed_tdata; - wire phase_inc_tlast, phase_inc_tvalid, phase_inc_tready; - wire phase_inc_timed_tlast, phase_inc_timed_tready , phase_inc_timed_tvalid; + .WIDTH (HEADER_WIDTH), + .SIZE (HEADER_FIFO_SIZE) + ) axi_fifo_header ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata (header_in_tdata), + .i_tvalid (header_in_tvalid & header_in_tlast), + .i_tready (header_in_tready), + .o_tdata (header_out_tdata), + .o_tvalid (header_out_tvalid), + .o_tready (header_out_tready), // Consume header on last output sample + .space (), + .occupied () + ); + + + //--------------------------------------------------------------------------- + // Settings Bus Registers + //--------------------------------------------------------------------------- + wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_tdata; + wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_timed_tdata; + wire phase_inc_tlast; + wire phase_inc_tvalid; + wire phase_inc_tready; + wire phase_inc_timed_tlast; + wire phase_inc_timed_tready; + wire phase_inc_timed_tvalid; + + // Frequency register (phase increment) used for *un-timed* commands axi_setting_reg #( - .ADDR(SR_FREQ_ADDR), .AWIDTH(SR_AWIDTH), .WIDTH(PHASE_ACCUM_WIDTH), .STROBE_LAST(1)) - set_freq ( - .clk(clk), .reset(reset), - .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data), - .o_tdata(phase_inc_tdata), .o_tlast(phase_inc_tlast), .o_tvalid(phase_inc_tvalid), .o_tready(phase_inc_tready)); + .ADDR (SR_FREQ_ADDR), + .AWIDTH (SR_AWIDTH), + .WIDTH (PHASE_ACCUM_WIDTH), + .STROBE_LAST (1) + ) set_freq ( + .clk (clk), + .reset (reset), + .set_stb (out_set_stb), + .set_addr (out_set_addr), + .set_data (out_set_data), + .o_tdata (phase_inc_tdata), + .o_tlast (phase_inc_tlast), + .o_tvalid (phase_inc_tvalid), + .o_tready (phase_inc_tready) + ); + // Frequency register (phase increment) used for *timed* commands axi_setting_reg #( - .ADDR(SR_FREQ_ADDR), .USE_FIFO(1), .FIFO_SIZE(CMD_FIFO_SIZE), .AWIDTH(SR_AWIDTH), .WIDTH(PHASE_ACCUM_WIDTH), .STROBE_LAST(1)) - set_freq_timed ( - .clk(clk), .reset(reset), - .set_stb(timed_set_stb), .set_addr(timed_set_addr), .set_data(timed_set_data), - .o_tdata(phase_inc_timed_tdata), .o_tlast(phase_inc_timed_tlast), .o_tvalid(phase_inc_timed_tvalid), .o_tready(phase_inc_timed_tready)); + .ADDR (SR_FREQ_ADDR), + .USE_FIFO (1), + .FIFO_SIZE (CMD_FIFO_SIZE), + .AWIDTH (SR_AWIDTH), + .WIDTH (PHASE_ACCUM_WIDTH), + .STROBE_LAST (1) + ) set_freq_timed ( + .clk (clk), + .reset (reset), + .set_stb (timed_set_stb), + .set_addr (timed_set_addr), + .set_data (timed_set_data), + .o_tdata (phase_inc_timed_tdata), + .o_tlast (phase_inc_timed_tlast), + .o_tvalid (phase_inc_timed_tvalid), + .o_tready (phase_inc_timed_tready) + ); wire [SCALING_WIDTH-1:0] scaling_tdata; - wire scaling_tvalid, scaling_tready; + wire scaling_tready; + // Scale value register axi_setting_reg #( - .ADDR(SR_SCALE_IQ_ADDR), .AWIDTH(SR_AWIDTH), .WIDTH(SCALING_WIDTH), .REPEATS(1)) - set_scale ( - .clk(clk), .reset(reset), - .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data), - .o_tdata(scaling_tdata), .o_tlast(), .o_tvalid(scaling_tvalid), .o_tready(scaling_tready)); - - /************************************************************************** - * DDS + Complex Mult + Phase Accumulator - *************************************************************************/ + .ADDR (SR_SCALE_IQ_ADDR), + .AWIDTH (SR_AWIDTH), + .WIDTH (SCALING_WIDTH), + .REPEATS (1) + ) set_scale ( + .clk (clk), + .reset (reset), + .set_stb (out_set_stb), + .set_addr (out_set_addr), + .set_data (out_set_data), + .o_tdata (scaling_tdata), + .o_tlast (), + .o_tvalid (), + .o_tready (scaling_tready) + ); + + + //--------------------------------------------------------------------------- + // Phase Accumulator for DDS + //--------------------------------------------------------------------------- + wire [PHASE_ACCUM_WIDTH-1:0] phase_inc_mux_tdata; - reg [PHASE_ACCUM_WIDTH-1:0] phase_inc; - wire phase_inc_mux_tlast, phase_inc_mux_tvalid, phase_inc_mux_tready; - reg [PHASE_ACCUM_WIDTH-1:0] phase; - - wire [PHASE_WIDTH-1:0] phase_tdata = phase[PHASE_ACCUM_WIDTH-1:PHASE_ACCUM_WIDTH-PHASE_WIDTH]; - wire phase_tvalid, phase_tready, phase_tlast; - - wire [WIDTH*2-1:0] dds_in_fifo_tdata; - wire dds_in_fifo_tvalid, dds_in_fifo_tready, dds_in_fifo_tlast; - wire dds_out_tlast, dds_out_tvalid, dds_out_tready; - - wire [DDS_WIDTH-1:0] dds_in_i_tdata, dds_in_q_tdata; - wire [DDS_WIDTH-1:0] dds_out_i_tdata, dds_out_q_tdata; - wire [15:0] dds_input_fifo_space, dds_input_fifo_occupied; - - wire [WIDTH*2-1:0] dds_in_sync_tdata; - wire dds_in_sync_tvalid, dds_in_sync_tready, dds_in_sync_tlast; - wire [PHASE_WIDTH-1:0] phase_sync_tdata; - wire phase_sync_tvalid, phase_sync_tready, phase_sync_tlast; - - assign phase_inc_mux_tdata = phase_inc_timed_tready ? phase_inc_timed_tdata : phase_inc_tdata; - assign phase_inc_mux_tlast = phase_inc_timed_tready ? phase_inc_timed_tlast : phase_inc_tlast; + reg [PHASE_ACCUM_WIDTH-1:0] phase_inc; + wire phase_inc_mux_tlast; + wire phase_inc_mux_tvalid; + wire phase_inc_mux_tready; + reg [PHASE_ACCUM_WIDTH-1:0] phase; + + wire [PHASE_WIDTH-1:0] phase_tdata = phase[PHASE_ACCUM_WIDTH-1:PHASE_ACCUM_WIDTH-PHASE_WIDTH]; + wire phase_tvalid; + wire phase_tready; + wire phase_tlast; + + wire dds_in_teob = dds_in_tuser[124]; + + // Multiplexer to select between the timed and un-timed phase registers. + assign phase_inc_mux_tdata = phase_inc_timed_tready ? phase_inc_timed_tdata : phase_inc_tdata; + assign phase_inc_mux_tlast = phase_inc_timed_tready ? phase_inc_timed_tlast : phase_inc_tlast; assign phase_inc_mux_tvalid = phase_inc_timed_tready ? phase_inc_timed_tvalid : phase_inc_tvalid; assign phase_inc_tready = phase_inc_mux_tready; assign phase_inc_timed_tready = phase_inc_mux_tready & dds_in_tag; - assign phase_inc_mux_tready = phase_tready; + assign phase_inc_mux_tready = phase_tready; - // phase is only valid when input i/q data stream is valid + // Phase is only valid when input IQ data stream is valid assign phase_tvalid = dds_in_tvalid; - assign phase_tlast = dds_in_tlast; + assign phase_tlast = dds_in_tlast; + // Phase increment register, sourced from either the timed or un-timed + // SR_FREQ register. always @(posedge clk) begin if (reset | clear) begin phase_inc <= 0; @@ -162,129 +366,219 @@ module dds_timed #( phase_inc <= phase_inc_mux_tdata; end end - - // NCO, increment phase input to DDS SIN/COS LUT + + // Phase accumulator for DDS. This increments the "phase" input provided to + // the DDS IP. always @(posedge clk) begin - if (reset | clear | (phase_inc_mux_tvalid & phase_inc_mux_tready) | eob) begin + if (reset | clear | (phase_inc_mux_tvalid & phase_inc_mux_tready)) begin + // Reset the phase on reset or clear, but also whenever the phase + // increment is updated. phase <= 0; - end else if (dds_in_tvalid & dds_in_tready) begin //only increment phase when data into dds is valid and data fifo is ready - phase <= phase + phase_inc; + end else if (dds_in_tvalid & dds_in_tready) begin + if (dds_in_tlast & dds_in_teob) begin + // Reset the phase at the end of each burst so we get predictable + // output. + phase <= 0; + end else begin + // Increment the phase for each new sample. + phase <= phase + phase_inc; + end end end + //--------------------------------------------------------------------------- + // AXI Sync + //--------------------------------------------------------------------------- + // + // Sync the IQ and phase paths' pipeline delay. This is needed to ensure that + // applying the phase update happens on the correct sample regardless of + // differences in path delays. + // + //--------------------------------------------------------------------------- + + + wire [PHASE_WIDTH-1:0] phase_sync_tdata; + wire phase_sync_tvalid; + wire phase_sync_tready; + wire phase_sync_tlast; + + wire [ WIDTH*2-1:0] dds_in_sync_tdata; + wire dds_in_sync_tvalid; + wire dds_in_sync_tready; + wire dds_in_sync_tlast; - // Sync the two path's pipeline delay. - // This is needed to ensure that applying the phase update happens on the - // correct sample regardless of differing downstream path delays. axi_sync #( - .SIZE(2), - .WIDTH_VEC({PHASE_WIDTH,2*WIDTH}), // Vector of widths, each width is defined by a 32-bit value - .FIFO_SIZE(0)) - axi_sync ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({phase_tdata,dds_in_tdata}), - .i_tlast({phase_tlast,dds_in_tlast}), - .i_tvalid({phase_tvalid,dds_in_tvalid}), - .i_tready({phase_tready,dds_in_tready}), - .o_tdata({phase_sync_tdata,dds_in_sync_tdata}), - .o_tlast({phase_sync_tlast,dds_in_sync_tlast}), - .o_tvalid({phase_sync_tvalid,dds_in_sync_tvalid}), - .o_tready({phase_sync_tready,dds_in_sync_tready})); - - // fifo to hold input data while pipeline catches up in dds - // this is blocked by the axi_sync following the dds - axi_fifo #(.WIDTH(2*WIDTH+1), .SIZE(5)) dds_input_fifo( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({dds_in_sync_tlast,dds_in_sync_tdata}), .i_tvalid(dds_in_sync_tvalid), .i_tready(dds_in_sync_tready), - .o_tdata({dds_in_fifo_tlast,dds_in_fifo_tdata}), .o_tvalid(dds_in_fifo_tvalid), .o_tready(dds_in_fifo_tready), - .space(dds_input_fifo_space), .occupied(dds_input_fifo_occupied) + .SIZE (2), + .WIDTH_VEC ({ PHASE_WIDTH, 2*WIDTH }), // Vector of 32-bit width values + .FIFO_SIZE (0) + ) axi_sync_i ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata ({ phase_tdata, dds_in_tdata }), + .i_tlast ({ phase_tlast, dds_in_tlast }), + .i_tvalid ({ phase_tvalid, dds_in_tvalid }), + .i_tready ({ phase_tready, dds_in_tready }), + .o_tdata ({ phase_sync_tdata, dds_in_sync_tdata }), + .o_tlast ({ phase_sync_tlast, dds_in_sync_tlast }), + .o_tvalid ({ phase_sync_tvalid, dds_in_sync_tvalid }), + .o_tready ({ phase_sync_tready, dds_in_sync_tready }) ); - - // after fifo, do q quick sign extend op to get up to 24 bits. to match how the cordic deals with the data path. - sign_extend #( - .bits_in(WIDTH), .bits_out(DDS_WIDTH)) - sign_extend_dds_i ( - .in(dds_in_fifo_tdata[2*WIDTH-1:WIDTH]), .out(dds_in_i_tdata)); + + //--------------------------------------------------------------------------- + // DDS and Complex Multiplier + //--------------------------------------------------------------------------- + + wire [DDS_WIDTH-1:0] dds_in_i_tdata; + wire [DDS_WIDTH-1:0] dds_in_q_tdata; + + wire [DDS_WIDTH-1:0] dds_out_i_tdata; + wire [DDS_WIDTH-1:0] dds_out_q_tdata; + wire dds_out_tlast; + wire dds_out_tvalid; + wire dds_out_tready; + + // Sign extend I and Q to get up to 24 bits. sign_extend #( - .bits_in(WIDTH), .bits_out(DDS_WIDTH)) - sign_extend_dds_q ( - .in(dds_in_fifo_tdata[WIDTH-1:0]), .out(dds_in_q_tdata)); - - - // Wrapper for Xilinx IP AXI DDS + Complex Multiply - // NOTE: Seems Xilinx IP expects opposite I/Q combined complex data buses, so they are swapped here. - dds_freq_tune dds_freq_tune_inst ( - .clk(clk), - .reset(reset | clear), - .eob(eob), - .rate_changed(1'b0), - .dds_input_fifo_occupied(dds_input_fifo_occupied), - /* IQ input */ - .s_axis_din_tlast(dds_in_fifo_tlast), - .s_axis_din_tvalid(dds_in_fifo_tvalid), - .s_axis_din_tready(dds_in_fifo_tready), - .s_axis_din_tdata({dds_in_q_tdata, dds_in_i_tdata}), - /* Phase input from NCO */ - .s_axis_phase_tlast(phase_sync_tlast), + .bits_in (WIDTH), + .bits_out (DDS_WIDTH) + ) sign_extend_i ( + .in (dds_in_sync_tdata[2*WIDTH-1:WIDTH]), + .out (dds_in_i_tdata) + ); + sign_extend #( + .bits_in (WIDTH), + .bits_out (DDS_WIDTH) + ) sign_extend_q ( + .in (dds_in_sync_tdata[WIDTH-1:0]), + .out (dds_in_q_tdata) + ); + + // Wrapper for DDS + Complex Multiply. This block expects {q,i} instead of + // {i,q} data ordering. + dds_freq_tune_duc dds_freq_tune_duc_i ( + .clk (clk), + .reset (reset | clear), + // IQ input (signed 24-bit number with 15 fractional bits) + .s_axis_din_tlast (dds_in_sync_tlast), + .s_axis_din_tvalid (dds_in_sync_tvalid), + .s_axis_din_tready (dds_in_sync_tready), + .s_axis_din_tdata ({ dds_in_q_tdata, dds_in_i_tdata }), + // Phase input from DDS (unsigned 24-bit number with 24 fractional bits) + .s_axis_phase_tlast (phase_sync_tlast), .s_axis_phase_tvalid(phase_sync_tvalid), .s_axis_phase_tready(phase_sync_tready), - .s_axis_phase_tdata(phase_sync_tdata), //24 bit - /* IQ output */ - .m_axis_dout_tlast(dds_out_tlast), - .m_axis_dout_tvalid(dds_out_tvalid), - .m_axis_dout_tready(dds_out_tready), - .m_axis_dout_tdata({dds_out_q_tdata, dds_out_i_tdata}) - //debug signals + .s_axis_phase_tdata (phase_sync_tdata), // 24-bit + // IQ output (signed 24-bit number with 15 fractional bits) + .m_axis_dout_tlast (dds_out_tlast), + .m_axis_dout_tvalid (dds_out_tvalid), + .m_axis_dout_tready (dds_out_tready), + .m_axis_dout_tdata ({dds_out_q_tdata, dds_out_i_tdata}) ); - /************************************************************************ - * Perform scaling on the IQ output - ************************************************************************/ - wire [DDS_WIDTH+SCALING_WIDTH-1:0] scaled_i_tdata, scaled_q_tdata; - wire scaled_tlast, scaled_tvalid, scaled_tready; + + + //--------------------------------------------------------------------------- + // Scale the IQ Output + //--------------------------------------------------------------------------- + + wire [DDS_WIDTH+SCALING_WIDTH-1:0] scaled_i_tdata; + wire [DDS_WIDTH+SCALING_WIDTH-1:0] scaled_q_tdata; + wire scaled_tlast; + wire scaled_tvalid; + wire scaled_tready; mult #( - .WIDTH_A(DDS_WIDTH), - .WIDTH_B(SCALING_WIDTH), - .WIDTH_P(DDS_WIDTH+SCALING_WIDTH), - .DROP_TOP_P(4), - .LATENCY(3), - .CASCADE_OUT(0)) - i_mult ( - .clk(clk), .reset(reset | clear), - .a_tdata(dds_out_i_tdata), .a_tlast(dds_out_tlast), .a_tvalid(dds_out_tvalid), .a_tready(dds_out_tready), - .b_tdata(scaling_tdata), .b_tlast(1'b0), .b_tvalid(dds_out_tvalid /* aligning scaling_tdata with dds_tdata */), .b_tready(scaling_tready), - .p_tdata(scaled_i_tdata), .p_tlast(scaled_tlast), .p_tvalid(scaled_tvalid), .p_tready(scaled_tready)); + .WIDTH_A (DDS_WIDTH), + .WIDTH_B (SCALING_WIDTH), + .WIDTH_P (DDS_WIDTH+SCALING_WIDTH), + .DROP_TOP_P (4), + .LATENCY (3), + .CASCADE_OUT (0) + ) mult_i ( + .clk (clk), + .reset (reset | clear), + .a_tdata (dds_out_i_tdata), + .a_tlast (dds_out_tlast), + .a_tvalid (dds_out_tvalid), + .a_tready (dds_out_tready), + .b_tdata (scaling_tdata), + .b_tlast (1'b0), + .b_tvalid (dds_out_tvalid), // Align scaling_tdata with dds_out_tdata + .b_tready (scaling_tready), + .p_tdata (scaled_i_tdata), + .p_tlast (scaled_tlast), + .p_tvalid (scaled_tvalid), + .p_tready (scaled_tready) + ); mult #( - .WIDTH_A(DDS_WIDTH), - .WIDTH_B(SCALING_WIDTH), - .WIDTH_P(DDS_WIDTH+SCALING_WIDTH), - .DROP_TOP_P(4), - .LATENCY(3), - .CASCADE_OUT(0)) - q_mult ( - .clk(clk), .reset(reset | clear), - .a_tdata(dds_out_q_tdata), .a_tlast(), .a_tvalid(dds_out_tvalid), .a_tready(), - .b_tdata(scaling_tdata), .b_tlast(1'b0), .b_tvalid(dds_out_tvalid /* aligning scaling_tdata with dds_tdata */), .b_tready(), - .p_tdata(scaled_q_tdata), .p_tlast(), .p_tvalid(), .p_tready(scaled_tready)); + .WIDTH_A (DDS_WIDTH), + .WIDTH_B (SCALING_WIDTH), + .WIDTH_P (DDS_WIDTH+SCALING_WIDTH), + .DROP_TOP_P (4), + .LATENCY (3), + .CASCADE_OUT (0) + ) mult_q ( + .clk (clk), + .reset (reset | clear), + .a_tdata (dds_out_q_tdata), + .a_tlast (), + .a_tvalid (dds_out_tvalid), + .a_tready (), + .b_tdata (scaling_tdata), + .b_tlast (1'b0), + .b_tvalid (dds_out_tvalid), // Align scaling_tdata with dds_out_tdata + .b_tready (), + .p_tdata (scaled_q_tdata), + .p_tlast (), + .p_tvalid (), + .p_tready (scaled_tready) + ); + + + //--------------------------------------------------------------------------- + // Round + //--------------------------------------------------------------------------- wire [2*WIDTH-1:0] sample_tdata; - wire sample_tlast, sample_tvalid, sample_tready; + wire sample_tlast; + wire sample_tvalid; + wire sample_tready; axi_round_and_clip_complex #( - .WIDTH_IN(DDS_WIDTH+SCALING_WIDTH), .WIDTH_OUT(WIDTH), .CLIP_BITS(12)) - axi_round_and_clip_complex ( - .clk(clk), .reset(reset | clear), - .i_tdata({scaled_i_tdata, scaled_q_tdata}), .i_tlast(scaled_tlast), .i_tvalid(scaled_tvalid), .i_tready(scaled_tready), - .o_tdata(sample_tdata), .o_tlast(sample_tlast), .o_tvalid(sample_tvalid), .o_tready(sample_tready)); + .WIDTH_IN (DDS_WIDTH+SCALING_WIDTH), + .WIDTH_OUT (WIDTH), + .CLIP_BITS (12) + ) axi_round_and_clip_complex_i ( + .clk (clk), + .reset (reset | clear), + .i_tdata ({scaled_i_tdata, scaled_q_tdata}), + .i_tlast (scaled_tlast), + .i_tvalid (scaled_tvalid), + .i_tready (scaled_tready), + .o_tdata (sample_tdata), + .o_tlast (sample_tlast), + .o_tvalid (sample_tvalid), + .o_tready (sample_tready) + ); + + + //--------------------------------------------------------------------------- + // Output Logic + //--------------------------------------------------------------------------- // Throttle output on last sample if header is not valid assign header_out_tready = sample_tlast & sample_tvalid & o_tready; - assign sample_tready = (sample_tvalid & sample_tlast) ? (header_out_tvalid & o_tready) : o_tready; - assign o_tvalid = (sample_tvalid & sample_tlast) ? header_out_tvalid : sample_tvalid; + assign sample_tready = (sample_tvalid & sample_tlast) ? + (header_out_tvalid & o_tready) : o_tready; + assign o_tvalid = (sample_tvalid & sample_tlast) ? + header_out_tvalid : sample_tvalid; assign o_tlast = sample_tlast; assign o_tdata = sample_tdata; assign o_tuser = header_out_tdata; - + endmodule + + +`default_nettype wire |