Files
b210-k7-fpga/lib/rfnoc/blocks/rfnoc_block_radio/quarter_rate_downconverter.v
T
Martin Braun 4e2da5912b fpga: Fix overflows in quarterrate downconverter
The 1/4-rate downconverter naively negated the input signals (y = -x)
which doesn't work when the signal is at full scale. In particular,
signals that are at full negative scale get converted to zeros (even
though 2's complement would be to return the same number, although that
would also be incorrect).

This fix changes it such that the smallest negative value gets converted
to the largest positive value.

This is not a mathematical correction inversion, but a minor amplitude
distortion. However, since we're already at full scale, and thus
probably clipping, this is preferable to the incorrect resolution of the
regular inversion. Put differently, there is no better value in this
case.


Original-commit: 8bd982745e6247cf3cf3da52925c65126287428a
2022-12-02 11:42:44 -08:00

150 lines
4.0 KiB
Verilog

//
// Copyright 2018 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// mixer with 90 degree angles, i.e., multiplying the input signal with 1, i, -1, -i:
// Let S(t) = I(t) + i*Q(t) be the input signal based on inputs i_in and q_in
// Multiplying with (1,i,-1,-i) then becomes:
// S(t) * 1 = I(t) + i*Q(t)
// S(t) * i = -Q(t) + i*I(t)
// S(t) * -1 = -I(t) - i*Q(t)
// S(t) * -i = Q(t) - i*I(t)
// To control the direction of rotation, the dirctn input is used
// When set to 0, the phase is increased with pi/2 every sample, i.e., rotating counter clock wise
// When set to 1, the phase is increased with -pi/2 every sample, i.e., rotating clock wise
// the input is the concatenation of the i and q signal: {i_in, q_in}
module quarter_rate_downconverter #(
parameter WIDTH=24
)(
input clk,
input reset,
input phase_sync,
input [2*WIDTH-1:0] i_tdata,
input i_tlast,
input i_tvalid,
output i_tready,
output [2*WIDTH-1:0] o_tdata,
output o_tlast,
output o_tvalid,
input o_tready,
input dirctn
);
// temporary signals for i and q after rotation
reg [WIDTH-1:0] tmp_i = {WIDTH{1'b0}};
reg [WIDTH-1:0] tmp_q = {WIDTH{1'b0}};
localparam [WIDTH-1:0] MAX_NEG_VAL = -2**(WIDTH-1);
localparam [WIDTH-1:0] MAX_POS_VAL = 2**(WIDTH-1)-1;
function [WIDTH-1:0] invert_sig(
input [WIDTH-1:0] x
);
begin
invert_sig = x == MAX_NEG_VAL ? MAX_POS_VAL : -x;
end
endfunction
// State machine types and reg
localparam S0=0, S1=1, S2=2, S3=3;
reg[1:0] cur_state;
// split input into i and q signal
wire[WIDTH-1:0] i_in, q_in;
assign i_in = i_tdata[2*WIDTH-1:WIDTH];
assign q_in = i_tdata[WIDTH-1:0];
// The state machine doing the rotations among states
always @(posedge clk) begin
if(reset || phase_sync) begin
cur_state <= S0;
end else begin
case (cur_state)
S0: begin
if(i_tvalid == 1'b1 && i_tready == 1'b1)
if(dirctn == 1'b0)
cur_state <= S1;
else
cur_state <= S3;
else
cur_state <= S0;
end
S1: begin
if(i_tvalid == 1'b1 && i_tready == 1'b1)
if(dirctn == 1'b0)
cur_state <= S2;
else
cur_state <= S0;
else
cur_state <= S1;
end
S2: begin
if(i_tvalid == 1'b1 && i_tready == 1'b1)
if(dirctn == 1'b0)
cur_state <= S3;
else
cur_state <= S1;
else
cur_state <= S2;
end
S3: begin
if(i_tvalid == 1'b1 && i_tready == 1'b1)
if(dirctn == 1'b0)
cur_state <= S0;
else
cur_state <= S2;
else
cur_state <= S3;
end
endcase
end
end
// Multiplication of input IQ signal with (1,i,-1,-i):
always @(*) begin
case (cur_state)
S0: begin
// S(t) * 1 = I(t) + iQ(t):
tmp_i = i_in;
tmp_q = q_in;
end
S1: begin
// S(t) * i = -Q(t) + iI(t):
tmp_i = invert_sig(q_in);
tmp_q = i_in;
end
S2: begin
// S(t) * -1 = -I(t) - iQ(t):
tmp_i = invert_sig(i_in);
tmp_q = invert_sig(q_in);
end
S3: begin
// S(t) * -i = Q(t) - iI(t):
tmp_i = q_in;
tmp_q = invert_sig(i_in);
end
default: begin
tmp_i = i_in;
tmp_q = q_in;
end
endcase
end
// Flop for valid and ready signals and shortening of comb. paths.
axi_fifo #(.WIDTH(2*WIDTH + 1), .SIZE(1)) flop (
.clk(clk), .reset(reset), .clear(1'b0),
.i_tdata({i_tlast, tmp_i, tmp_q}), .i_tvalid(i_tvalid), .i_tready(i_tready),
.o_tdata({o_tlast, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
.occupied(), .space());
endmodule // quarter_rate_downconverter