diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-06-15 14:15:03 -0500 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2021-08-08 14:59:26 -0500 |
commit | 8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a (patch) | |
tree | c0d75e54b3e472836f3e72aa211079906e852f9e /fpga/usrp3/lib/rfnoc/dds_freq_tune.v | |
parent | 18e5bdfa100a40376147d1a497645345b2a938b1 (diff) | |
download | uhd-8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a.tar.gz uhd-8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a.tar.bz2 uhd-8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a.zip |
fpga: lib: Clean up and document lib files
Clean-up and document axi_tag_time, dds_freq_tune, and axi_sync.
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/dds_freq_tune.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/dds_freq_tune.v | 349 |
1 files changed, 200 insertions, 149 deletions
diff --git a/fpga/usrp3/lib/rfnoc/dds_freq_tune.v b/fpga/usrp3/lib/rfnoc/dds_freq_tune.v index 2491c01a1..f1dc44b17 100644 --- a/fpga/usrp3/lib/rfnoc/dds_freq_tune.v +++ b/fpga/usrp3/lib/rfnoc/dds_freq_tune.v @@ -1,117 +1,139 @@ // -// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2021 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // -// DDS frequency shift with complex multiply +// 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, +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 */ + 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 //[31:16] = sin|q [15:0] cos|i + 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 reset_dds = 1'b1; // Init DDS resets to 1, since simulation model - reg reset_dds_reg = 1'b1; // requires reset at time 0 to avoid failure. - reg phase_ready_wait; - wire s_axis_phase_tready_dds; - - //when we're holding valid, make ready low so no new data comes in. + // 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 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 + // Reset needs to be 2 clk cycles minimum for Xilinx DDS IP always @(posedge clk) begin - reset_reg <= reset; + reset_reg <= reset; reset_dds_reg <= reset_dds; end - //some logic to reset the dds when data is goes from valid to not valid - //also holds valid high until the pipeline has passed tlast through. + // 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; + state <= INIT; + phase_valid_hold <= 1'b0; phase_invalid_wait_count <= 16'h00; - reset_dds <= 1'b0; - end - else begin + reset_dds <= 1'b0; + end else begin case(state) - INIT: begin//init case - phase_valid_hold <= 1'b0; + INIT : begin + phase_valid_hold <= 1'b0; phase_invalid_wait_count <= 16'h0000; - reset_dds <= 1'b0; + reset_dds <= 1'b0; if(s_axis_phase_tvalid) begin state <= VALID; end end - VALID: begin //valid data + 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. + 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 + 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, aka eob + 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 finish pipeline. Apparently the dds IP won't empty without additional valids. + 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; + state <= INIT; reset_dds <= 1'b1; end end @@ -119,90 +141,119 @@ module dds_freq_tune #( end end - //dds to generate sin/cos data from phase - dds_sin_cos_lut_only dds_inst ( - .aclk(clk), // input wire aclk - .aresetn(~(reset | reset_reg | reset_dds | reset_dds_reg)), // input wire aresetn active low rst - .s_axis_phase_tvalid(s_axis_phase_tvalid | phase_valid_hold), // input wire s_axis_phase_tvalid - .s_axis_phase_tready(s_axis_phase_tready_dds), // output wire s_axis_phase_tready - .s_axis_phase_tlast(s_axis_phase_tlast), //tlast - .s_axis_phase_tdata(s_axis_phase_tdata), // input wire [23 : 0] s_axis_phase_tdata - .m_axis_data_tvalid(m_axis_dds_tvalid), // output wire m_axis_data_tvalid - .m_axis_data_tready(m_axis_dds_tready), // input wire m_axis_data_tready - .m_axis_data_tlast(m_axis_dds_tlast), // input wire m_axis_data_tready - .m_axis_data_tdata(m_axis_dds_tdata) // output wire [31 : 0] m_axis_data_tdata + // 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 [ 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; //no connect - wire [2*32-1:0] mult_out_tdata; - wire mult_out_tvalid; - wire mult_out_tready; - wire mult_out_tlast; + 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 ( - .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})); - - //a = input i/q data stream 48 bit i/q lower bits i, upper bits q - //b = output of dds 32 bit cos/sin. lower cos, upper sin - complex_multiplier_dds complex_mult_inst ( - .aclk(clk), // input wire aclk - .aresetn(~(reset | reset_reg)), // input wire aresetn - .s_axis_a_tvalid(mult_in_a_tvalid), // input wire s_axis_a_tvalid - .s_axis_a_tready(mult_in_a_tready), // output wire s_axis_a_tready - .s_axis_a_tlast(mult_in_a_tlast), // input wire s_axis_a_tlast - .s_axis_a_tdata({mult_in_a_tdata}), // input wire [47 : 0] s_axis_a_tdata - .s_axis_b_tvalid(mult_in_b_tvalid), // input wire s_axis_b_tvalid - .s_axis_b_tready(mult_in_b_tready), // output wire s_axis_b_tready - .s_axis_b_tlast(mult_in_b_tlast), // output wire s_axis_b_tlast - .s_axis_b_tdata(mult_in_b_tdata), // input wire [31 : 0] s_axis_b_tdata - .m_axis_dout_tvalid(mult_out_tvalid), // output wire m_axis_dout_tvalid - .m_axis_dout_tready(mult_out_tready), // input wire m_axis_dout_tready - .m_axis_dout_tlast(mult_out_tlast), // output wire m_axis_dout_tlast - .m_axis_dout_tdata(mult_out_tdata) // output wire [63 : 0] m_axis_dout_tdata + .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_inst ( - .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; + .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; + 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 |