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 | |
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')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/axi_sync.v | 101 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/axi_tag_time.v | 207 | ||||
-rw-r--r-- | fpga/usrp3/lib/rfnoc/dds_freq_tune.v | 349 |
3 files changed, 411 insertions, 246 deletions
diff --git a/fpga/usrp3/lib/rfnoc/axi_sync.v b/fpga/usrp3/lib/rfnoc/axi_sync.v index a881a5556..25b43d55b 100644 --- a/fpga/usrp3/lib/rfnoc/axi_sync.v +++ b/fpga/usrp3/lib/rfnoc/axi_sync.v @@ -1,14 +1,32 @@ // -// Copyright 2016 Ettus Research -// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2021 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // -// Synchronizes AXI stream buses so data is released on every port simultaneously. +// Module: axi_sync // -// Note: If inputs have inequal bitwidths, use WIDTH_VEC instead of WIDTH to define -// the individual bit widths. Each bit width is defined with 8-bits stuffed -// into a vector of width 8*SIZE. +// Description: +// +// Synchronizes AXI stream buses so data is released on every port +// simultaneously. Multiple inputs/outputs are supported by concatenating the +// bus signals together. The number and size of each input/output bus is +// controlled using parameters. +// +// **WARNING**: This module violates the AXI4-Stream specification by not +// asserting TVALID until it receives TREADY. This will not +// work if downstream logic waits for TVALID before asserting +// TREADY, which is common. Use with care. +// +// Parameters: +// +// SIZE : The number of inputs streams to synchronize. +// WIDTH : The width of TDATA on the input streams, if they are all the +// same width. If they are different widths, then use WIDTH_VEC +// instead. +// WIDTH_VEC : A vector of widths corresponding to each stream's TDATA width. +// Each number in this vector must be 32 bits wide. This defaults +// to WIDTH bits for all inputs. +// FIFO_SIZE : Log2 the size of the FIFO to use internally for each stream. // module axi_sync #( @@ -16,48 +34,75 @@ module axi_sync #( parameter WIDTH = 32, parameter [32*SIZE-1:0] WIDTH_VEC = {SIZE{WIDTH[31:0]}}, parameter FIFO_SIZE = 0 -)( - input clk, input reset, input clear, - input [msb(SIZE,WIDTH_VEC)-1:0] i_tdata, input [SIZE-1:0] i_tlast, input [SIZE-1:0] i_tvalid, output [SIZE-1:0] i_tready, - output [msb(SIZE,WIDTH_VEC)-1:0] o_tdata, output [SIZE-1:0] o_tlast, output [SIZE-1:0] o_tvalid, input [SIZE-1:0] o_tready +) ( + input clk, + input reset, + input clear, + + // Input streams + input [len(SIZE)-1:0] i_tdata, + input [ SIZE-1:0] i_tlast, + input [ SIZE-1:0] i_tvalid, + output [ SIZE-1:0] i_tready, + + // Output streams + output [len(SIZE)-1:0] o_tdata, + output [ SIZE-1:0] o_tlast, + output [ SIZE-1:0] o_tvalid, + input [ SIZE-1:0] o_tready ); - // Helper function to calculate the MSB index based on widths stored in WIDTH_VEC. - // Note: If n is negative, returns 0 - function automatic integer msb(input integer n, input [SIZE*32-1:0] bit_vec); + // Helper function to calculate the combined length of the lower 'n' ports + // based on widths stored in WIDTH_VEC. Note: If n is negative, returns 0. + function automatic integer len(input integer n); integer i, total; begin total = 0; if (n >= 0) begin for (i = 0; i <= n; i = i + 1) begin - total = total + ((bit_vec >> 32*i) & 32'hFF); + total = total + ((WIDTH_VEC >> 32*i) & 32'hFFFF); end end - msb = total; + len = total; end endfunction - wire [msb(SIZE,WIDTH_VEC)-1:0] int_tdata; - wire [SIZE-1:0] int_tlast, int_tvalid, int_tready; + wire [len(SIZE)-1:0] int_tdata; + wire [ SIZE-1:0] int_tlast; + wire [ SIZE-1:0] int_tvalid; + wire [ SIZE-1:0] int_tready; + // Generate a FIFO for each stream genvar i; generate for (i = 0; i < SIZE; i = i + 1) begin - axi_fifo #(.WIDTH(msb(i,WIDTH_VEC)-msb(i-1,WIDTH_VEC)+1), .SIZE(FIFO_SIZE)) axi_fifo ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({i_tlast[i],i_tdata[msb(i,WIDTH_VEC)-1:msb(i-1,WIDTH_VEC)]}), - .i_tvalid(i_tvalid[i]), .i_tready(i_tready[i]), - .o_tdata({int_tlast[i],int_tdata[msb(i,WIDTH_VEC)-1:msb(i-1,WIDTH_VEC)]}), - .o_tvalid(int_tvalid[i]), .o_tready(int_tready[i]), - .space(), .occupied()); + axi_fifo #( + .WIDTH (len(i)-len(i-1)+1), + .SIZE (FIFO_SIZE) + ) axi_fifo ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata ({ i_tlast[i], i_tdata[len(i)-1 : len(i-1)] }), + .i_tvalid (i_tvalid[i]), + .i_tready (i_tready[i]), + .o_tdata ({ int_tlast[i], int_tdata[len(i)-1 : len(i-1)] }), + .o_tvalid (int_tvalid[i]), + .o_tready (int_tready[i]), + .space (), + .occupied () + ); end endgenerate - assign o_tdata = int_tdata; - assign o_tlast = int_tlast; - + // We allow a transfer and consume the outputs of the FIFOs when all + // downstream blocks are ready to accept a transfer (o_tready is true for all + // streams) and all FIFOs have data ready (int_tvalid is true for all FIFOs). wire consume = (&int_tvalid) & (&o_tready); + assign int_tready = {SIZE{consume}}; assign o_tvalid = {SIZE{consume}}; + assign o_tdata = int_tdata; + assign o_tlast = int_tlast; -endmodule
\ No newline at end of file +endmodule diff --git a/fpga/usrp3/lib/rfnoc/axi_tag_time.v b/fpga/usrp3/lib/rfnoc/axi_tag_time.v index 35698ef72..0e49df76f 100644 --- a/fpga/usrp3/lib/rfnoc/axi_tag_time.v +++ b/fpga/usrp3/lib/rfnoc/axi_tag_time.v @@ -1,79 +1,115 @@ // -// Copyright 2016 Ettus Research -// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2021 Ettus Research, a National Instruments Brand // // SPDX-License-Identifier: LGPL-3.0-or-later // -// - When the user executes a timed settings bus command, -// this module will tag the sample (on m_axis_data_tuser) -// that the command should apply on. -// - Order of operation: -// 1) Receives settings bus command -// a) If time != 0, output on non-timed settings bus -// b) If time != 0, output on timed settings bus and store time in FIFO -// It is assumed the user will use timed_settings_bus.v implementation. -// 2) +// Module: axi_tag_time +// +// Description: +// +// This module accepts samples on s_axis_data_* and outputs them on +// m_axis_data_*. When the user executes a timed settings bus command to a +// register, this module will tag the sample that the command should apply on +// by asserting the bit corresponding to that register in m_axis_data_tag. +// +// The order of operations is as follows: +// +// 1) Receives settings bus command on in_set_*. +// +// a) If in_set_has_time == 0, output on non-timed settings bus (out_set_*). +// +// b) If in_set_has_time == 1, output on timed settings bus (timed_set_*) +// and store the time in a FIFO. It is assumed the user will use +// timed_settings_bus.v implementation. +// +// 2) When the sample corresponding to the time stored in the FIFO arrives on +// s_axis_data_*, assert the corresponding bit in m_axis_data_tag at the +// same time the sample is output on m_axis_data_*. +// +// The TUSER port contains the packet header information: +// +// tuser[125] : Has timestamp +// tuser[63:0] : Timestamp +// +// This field should be valid for the duration of the packet, although in +// reality the timestamp is only read during the first sample of each packet. +// The "has time" bit will be read at various points depending on the +// requested time. +// module axi_tag_time #( - parameter WIDTH = 32, - parameter HEADER_WIDTH = 128, - parameter SR_AWIDTH = 8, - parameter SR_DWIDTH = 32, - parameter SR_TWIDTH = 64, - parameter NUM_TAGS = 1, - parameter [NUM_TAGS*SR_AWIDTH-1:0] SR_TAG_ADDRS = 0, - parameter CMD_FIFO_SIZE = 5, - parameter MAX_TICK_RATE = 2**16-1 -)( + parameter WIDTH = 32, + parameter HEADER_WIDTH = 128, + parameter SR_AWIDTH = 8, + parameter SR_DWIDTH = 32, + parameter SR_TWIDTH = 64, + parameter NUM_TAGS = 1, + parameter [NUM_TAGS*SR_AWIDTH-1:0] SR_TAG_ADDRS = 0, + parameter CMD_FIFO_SIZE = 5, + parameter MAX_TICK_RATE = 2**16-1 +) ( input clk, input reset, input clear, - input [$clog2(MAX_TICK_RATE)-1:0] tick_rate, - output timed_cmd_fifo_full, - // From AXI Wrapper - input [WIDTH-1:0] s_axis_data_tdata, - input [HEADER_WIDTH-1:0] s_axis_data_tuser, - input s_axis_data_tlast, - input s_axis_data_tvalid, - output s_axis_data_tready, - // To user - output [WIDTH-1:0] m_axis_data_tdata, + + input [$clog2(MAX_TICK_RATE)-1:0] tick_rate, + output timed_cmd_fifo_full, + + // Input sample stream + input [ WIDTH-1:0] s_axis_data_tdata, + input [HEADER_WIDTH-1:0] s_axis_data_tuser, + input s_axis_data_tlast, + input s_axis_data_tvalid, + output s_axis_data_tready, + + // Output sample string, with tag + output [ WIDTH-1:0] m_axis_data_tdata, output [HEADER_WIDTH-1:0] m_axis_data_tuser, - output [NUM_TAGS-1:0] m_axis_data_tag, - output m_axis_data_tlast, - output m_axis_data_tvalid, - input m_axis_data_tready, - // Settings bus from Noc Shell - input in_set_stb, + output [ NUM_TAGS-1:0] m_axis_data_tag, + output m_axis_data_tlast, + output m_axis_data_tvalid, + input m_axis_data_tready, + + // Settings bus + input in_set_stb, input [SR_AWIDTH-1:0] in_set_addr, input [SR_DWIDTH-1:0] in_set_data, input [SR_TWIDTH-1:0] in_set_time, - input in_set_has_time, + input in_set_has_time, + // Non-timed settings bus to user - output out_set_stb, + output out_set_stb, output [SR_AWIDTH-1:0] out_set_addr, output [SR_DWIDTH-1:0] out_set_data, + // Timed settings bus to user - output timed_set_stb, + output timed_set_stb, output [SR_AWIDTH-1:0] timed_set_addr, output [SR_DWIDTH-1:0] timed_set_data ); - assign out_set_addr = in_set_addr; - assign out_set_data = in_set_data; - assign out_set_stb = in_set_stb & ~in_set_has_time; + assign out_set_addr = in_set_addr; + assign out_set_data = in_set_data; + assign out_set_stb = in_set_stb & ~in_set_has_time; + assign timed_set_addr = in_set_addr; assign timed_set_data = in_set_data; assign timed_set_stb = in_set_stb & in_set_has_time; - // Extract vita time from tuser + // Extract vita time from s_axis_data_tuser wire [63:0] vita_time_in; cvita_hdr_decoder cvita_hdr_decoder_in ( - .header(s_axis_data_tuser), - .pkt_type(), .eob(), .has_time(), - .seqnum(), .length(), .payload_length(), - .src_sid(), .dst_sid(), - .vita_time(vita_time_in)); + .header (s_axis_data_tuser), + .pkt_type (), + .eob (), + .has_time (), + .seqnum (), + .length (), + .payload_length (), + .src_sid (), + .dst_sid (), + .vita_time (vita_time_in) + ); // Track time reg header_valid = 1'b1; @@ -97,6 +133,8 @@ module axi_tag_time #( end end + // Create the tags vector by asserting only the tag bit corresponding to the + // register being addressed. genvar i; wire [NUM_TAGS-1:0] tags; generate @@ -110,31 +148,62 @@ module axi_tag_time #( wire [NUM_TAGS-1:0] fifo_tags; wire fifo_tvalid, fifo_tready; wire timed_cmd_fifo_full_n; - axi_fifo #(.WIDTH(SR_TWIDTH+NUM_TAGS), .SIZE(CMD_FIFO_SIZE)) axi_fifo ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({in_set_time,tags}), .i_tvalid(timed_set_stb), .i_tready(timed_cmd_fifo_full_n), - .o_tdata({fifo_set_time,fifo_tags}), .o_tvalid(fifo_tvalid), .o_tready(fifo_tready), - .space(), .occupied()); + axi_fifo #( + .WIDTH (SR_TWIDTH+NUM_TAGS), + .SIZE (CMD_FIFO_SIZE) + ) axi_fifo ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata ({in_set_time,tags}), + .i_tvalid (timed_set_stb), + .i_tready (timed_cmd_fifo_full_n), + .o_tdata ({fifo_set_time,fifo_tags}), + .o_tvalid (fifo_tvalid), + .o_tready (fifo_tready), + .space (), + .occupied () + ); - // Extract has time from tuser + // Extract has_time from m_axis_data_tuser wire has_time; cvita_hdr_decoder cvita_hdr_decoder_out ( - .header(m_axis_data_tuser), - .pkt_type(), .eob(), .has_time(has_time), - .seqnum(), .length(), .payload_length(), - .src_sid(), .dst_sid(), - .vita_time()); + .header (m_axis_data_tuser), + .pkt_type (), + .eob (), + .has_time (has_time), + .seqnum (), + .length (), + .payload_length (), + .src_sid (), + .dst_sid (), + .vita_time () + ); assign timed_cmd_fifo_full = ~timed_cmd_fifo_full_n; - assign fifo_tready = m_axis_data_tvalid & m_axis_data_tready & fifo_tvalid & has_time & (vita_time_now >= fifo_set_time); - assign in_rb_stb = fifo_tready; + assign fifo_tready = m_axis_data_tvalid & + m_axis_data_tready & + fifo_tvalid & + has_time & + (vita_time_now >= fifo_set_time); - // Need a single cycle delay to allow vita_time_now to update at the start of a new packet - axi_fifo_flop #(.WIDTH(WIDTH+HEADER_WIDTH+1)) axi_fifo_flop ( - .clk(clk), .reset(reset), .clear(clear), - .i_tdata({s_axis_data_tdata,s_axis_data_tuser,s_axis_data_tlast}), .i_tvalid(s_axis_data_tvalid), .i_tready(s_axis_data_tready), - .o_tdata({m_axis_data_tdata,m_axis_data_tuser,m_axis_data_tlast}), .o_tvalid(m_axis_data_tvalid), .o_tready(m_axis_data_tready)); + // Need a single cycle delay to allow vita_time_now to update at the start of + // a new packet. + axi_fifo_flop #( + .WIDTH (WIDTH+HEADER_WIDTH+1) + ) axi_fifo_flop ( + .clk (clk), + .reset (reset), + .clear (clear), + .i_tdata ({ s_axis_data_tdata, s_axis_data_tuser, s_axis_data_tlast }), + .i_tvalid (s_axis_data_tvalid), + .i_tready (s_axis_data_tready), + .o_tdata ({ m_axis_data_tdata, m_axis_data_tuser, m_axis_data_tlast }), + .o_tvalid (m_axis_data_tvalid), + .o_tready (m_axis_data_tready) + ); - assign m_axis_data_tag = ((vita_time_now >= fifo_set_time) & fifo_tvalid & has_time) ? fifo_tags : 'd0; + assign m_axis_data_tag = + ((vita_time_now >= fifo_set_time) & fifo_tvalid & has_time) ? fifo_tags : 'd0; -endmodule
\ No newline at end of file +endmodule 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 |