aboutsummaryrefslogtreecommitdiffstats
path: root/fpga
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-06-15 14:15:03 -0500
committerWade Fife <wade.fife@ettus.com>2021-08-08 14:59:26 -0500
commit8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a (patch)
treec0d75e54b3e472836f3e72aa211079906e852f9e /fpga
parent18e5bdfa100a40376147d1a497645345b2a938b1 (diff)
downloaduhd-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.v101
-rw-r--r--fpga/usrp3/lib/rfnoc/axi_tag_time.v207
-rw-r--r--fpga/usrp3/lib/rfnoc/dds_freq_tune.v349
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