fpga: lib: Clean up and document lib files

Clean-up and document axi_tag_time, dds_freq_tune, and axi_sync.


Original-commit: 8edd13e6eba61ef2bfd96c7dc88b2642decbaa7a
This commit is contained in:
Wade Fife
2021-08-08 14:59:26 -05:00
parent 3e94b6698e
commit 3a6961a3f4
3 changed files with 411 additions and 246 deletions
+199 -148
View File
@@ -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 */
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,
parameter OUTPUT_WIDTH = 24
) (
input clk,
input reset,
//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
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
);
//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;
// 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
//when we're holding valid, make ready low so no new data comes in.
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 })
);
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));
// 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]
);
//debug
assign state_out = state;
assign phase_valid_hold_out = phase_valid_hold;
// 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_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