b200: import original Kintex-7 clone port

This commit is contained in:
2026-08-23 11:42:22 +02:00
parent 2f5b331f30
commit 3dc3446559
10 changed files with 114777 additions and 568 deletions
+145 -538
View File
@@ -1,554 +1,161 @@
//
// Copyright 2013 Ettus Research LLC
// Copyright 2017 Ettus Research, a National Instruments Company
// Copyright 2015 Ettus Research, A National Instruments Company
// SPDX-License-Identifier: LGPL-3.0
//
// SPDX-License-Identifier: LGPL-3.0-or-later
// Description: E31X IO for CMOS interface
//
//
//------------------------------------------------------------------
//
// In SISO mode, we output a clock thats 1x the frequency of the Catalina
// source-synchronous bus clock to be used as the radio_clk.
// In MIMO mode, we output a clock thats 1/2 the frequency of the Catalina
// source-synchronous bus clock to be used as the radio_clk.
//
//------------------------------------------------------------------
module b200_io
(
input reset,
input mimo,
module b200_io (
input areset,
input mimo,
// Baseband sample interface
output radio_clk,
output radio_rst,
output reg [11:0] rx_i0,
output reg [11:0] rx_q0,
output reg [11:0] rx_i1,
output reg [11:0] rx_q1,
output reg rx_stb,
input [11:0] tx_i0,
input [11:0] tx_q0,
input [11:0] tx_i1,
input [11:0] tx_q1,
output reg tx_stb,
// AD9361 interface
input rx_clk,
input rx_frame,
input [11:0] rx_data,
output tx_clk,
output tx_frame,
output [11:0] tx_data
);
// Baseband sample interface
output radio_clk,
output [11:0] rx_i0,
output [11:0] rx_q0,
output [11:0] rx_i1,
output [11:0] rx_q1,
input [11:0] tx_i0,
input [11:0] tx_q0,
input [11:0] tx_i1,
input [11:0] tx_q1,
// Synchronize asynchronous reset and MIMO
synchronizer #(.STAGES(3), .INITIAL_VAL(1'b1)) sychronizer_radio_rst (
.clk(radio_clk), .rst(areset), .in(1'b0), .out(radio_rst));
// Catalina interface
input rx_clk,
input rx_frame,
input [11:0] rx_data,
output tx_clk,
output tx_frame,
output [11:0] tx_data
);
wire mimo_sync;
synchronizer synchronizer_mimo (.clk(radio_clk), .rst(radio_rst), .in(mimo), .out(mimo_sync));
/****************************************************************************
** RX Capture Interface
****************************************************************************/
wire rx_clk_bufr; // Capture clock
wire rx_clk_mimo_bufr;
BUFG bufr_rx_clk (.I(rx_clk), .O(rx_clk_bufr));
BUFR #(
.BUFR_DIVIDE("2"), // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8"
.SIM_DEVICE("7SERIES") // Must be set to "7SERIES"
)
BUFR_inst (
.O(rx_clk_mimo_bufr),
.CE(1'b1),
.CLR(areset),
.I(rx_clk)
);
BUFGMUX #(
)
BUFGMUX_inst (
.O(radio_clk),
.I0(rx_clk_bufr),
.I1(rx_clk_mimo_bufr),
.S(mimo_sync)
);
genvar z;
wire [11:0] rx_i, rx_q;
genvar n;
generate
for (n = 0; n < 12; n = n + 1) begin
IDDR #(.DDR_CLK_EDGE("SAME_EDGE")) iddr (
.C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
.D(rx_data[n]), .Q1(rx_q[n]), .Q2(rx_i[n]));
end
endgenerate
//------------------------------------------------------------------
//
// Synchronize MIMO signal from bus_clk to siso_clk.
//
//------------------------------------------------------------------
reg mimo_sync, mimo_sync2;
wire rx_frame_rising, rx_frame_falling;
IDDR #(.DDR_CLK_EDGE("SAME_EDGE")) iddr_frame (
.C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
.D(rx_frame), .Q1(rx_frame_rising), .Q2(rx_frame_falling));
always @(posedge siso_clk) begin
mimo_sync <= mimo_sync2;
mimo_sync2 <= mimo;
end
always @(posedge rx_clk_bufr or posedge radio_rst) begin
if (radio_rst) begin
rx_stb <= 1'b0;
end else begin
if (mimo_sync) begin
if (rx_frame_rising) begin
rx_i0 <= rx_i;
rx_q0 <= rx_q;
end else begin
rx_i1 <= rx_i;
rx_q1 <= rx_q;
end
rx_stb <= ~rx_frame_rising;
end else begin
rx_i0 <= rx_i;
rx_q0 <= rx_q;
rx_i1 <= rx_i;
rx_q1 <= rx_q;
rx_stb <= 1'b1;
end
end
end
/****************************************************************************
** TX Output Interface
****************************************************************************/
reg [11:0] tx_i, tx_q;
reg tx_frame_int = 1'b1;
generate
for (n = 0; n < 12; n = n + 1) begin
ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr (
.C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
.D1(tx_i[n]), .D2(tx_q[n]), .Q(tx_data[n]));
end
endgenerate
//------------------------------------------------------------------
// Clock Buffering.
// BUFIO2 drives all IDDR2 and ODDR2 cells directly in bank3.
// Need two pairs of BUFIO2 one pair each for Top Left and Bottom Left half banks.
//------------------------------------------------------------------
wire rx_clk_buf;
wire mimo_clk_unbuf;
wire siso_clk_unbuf;
wire siso2_clk_unbuf;
ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_frame (
.C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
// In SISO mode, TX frame is asserted only on the falling edge
.D1(tx_frame_int), .D2(tx_frame_int & mimo_sync), .Q(tx_frame));
IBUFG clk_ibufg (.O(rx_clk_buf), .I(rx_clk));
ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_clk (
.C(rx_clk_bufr), .CE(1'b1), .R(1'b0), .S(1'b0),
.D1(1'b1), .D2(1'b0), .Q(tx_clk));
//------------------------------------------------------------------
//
// Buffers for LEFT TOP half bank pins
// BUFIO2_X0Y22
//
//------------------------------------------------------------------
BUFIO2 #(
.DIVIDE(4),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("FALSE"),
.USE_DOUBLER("TRUE"))
clk_bufio_lt
(
.IOCLK(io_clk_lt),
.DIVCLK(mimo_clk_unbuf), // Non-inverted source of 1/2x interface clock for radio_clk
.SERDESSTROBE(),
.I(rx_clk_buf)
);
reg [11:0] tx_i1_hold, tx_q1_hold;
always @(posedge rx_clk_bufr or posedge radio_rst) begin
if (radio_rst) begin
tx_stb <= 1'b0;
tx_frame_int <= 1'b1;
end else begin
if (mimo_sync) begin
tx_stb <= ~tx_stb;
tx_frame_int <= tx_stb;
if (tx_stb) begin
tx_i <= tx_i0;
tx_q <= tx_q0;
tx_i1_hold <= tx_i1;
tx_q1_hold <= tx_q1;
end else begin
tx_i <= tx_i1_hold;
tx_q <= tx_q1_hold;
end
end else begin
tx_stb <= 1'b1;
tx_frame_int <= 1'b1;
if ({tx_i0,tx_q0} != 24'd0) begin
tx_i <= tx_i0;
tx_q <= tx_q0;
end else begin
tx_i <= tx_i1;
tx_q <= tx_q1;
end
end
end
end
// BUFIO2_X0Y23
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("TRUE"),
.USE_DOUBLER("FALSE"))
clk_bufio_lt_b
(
.IOCLK(io_clk_lt_b),
.DIVCLK(siso_clk2_unbuf), // Inverted source of 1x interface clock for radio_clk
.SERDESSTROBE(),
.I(rx_clk_buf)
);
//------------------------------------------------------------------
//
// Buffers for LEFT BOTTOM half bank pins
// BUFIO2_X1Y14
//
//------------------------------------------------------------------
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("FALSE"),
.USE_DOUBLER("FALSE"))
clk_bufio_lb
(
.IOCLK(io_clk_lb),
.DIVCLK(siso_clk_unbuf), // Non-inverted source of 1x interface clock for local IO use
.SERDESSTROBE(),
.I(rx_clk_buf)
);
// BUFIO2_X1Y15
BUFIO2 #(
.DIVIDE(1),
.DIVIDE_BYPASS("FALSE"),
.I_INVERT("TRUE"),
.USE_DOUBLER("FALSE"))
clk_bufio_lb_b
(
.IOCLK(io_clk_lb_b),
.DIVCLK(),
.SERDESSTROBE(),
.I(rx_clk_buf)
);
//------------------------------------------------------------------
// Always-on SISO clk needed to load/unload DDR2 I/O Regs
//------------------------------------------------------------------
BUFG siso_clk_bufg (
.I(siso_clk_unbuf),
.O(siso_clk)
);
//------------------------------------------------------------------
// 2-1 mux combined with BUFG to drive global radio_clk.
// Note: Not addressed setup/hold constraints of S input ...unsure if anything "bad" can happen here.
//------------------------------------------------------------------
BUFGMUX #(
.CLK_SEL_TYPE("SYNC"))
radio_clk_bufg (
.I0(siso_clk2_unbuf),
.I1(mimo_clk_unbuf),
.S(mimo_sync),
.O(radio_clk)
);
//------------------------------------------------------------------
// RX Frame Signal - In bank 3 LB
//------------------------------------------------------------------
wire rx_frame_0, rx_frame_1;
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_frame (
.Q0(rx_frame_1),
.Q1(rx_frame_0),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_frame),
.R(1'b0),
.S(1'b0));
reg rx_frame_d1, rx_frame_d2;
always @(posedge siso_clk)
if(~mimo_sync)
{ rx_frame_d2, rx_frame_d1 } <= { rx_frame_1, 1'b0 };
else
{ rx_frame_d2, rx_frame_d1 } <= { rx_frame_d1, rx_frame_1 };
//------------------------------------------------------------------
// RX Data Bus - In bank3 both LT and LB
//------------------------------------------------------------------
wire [11:0] rx_i,rx_q;
// Bit0 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i0 (
.Q0(rx_q[0]),
.Q1(rx_i[0]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[0]),
.R(1'b0),
.S(1'b0));
// Bit1 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i1 (
.Q0(rx_q[1]),
.Q1(rx_i[1]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[1]),
.R(1'b0),
.S(1'b0));
// Bit2 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i2 (
.Q0(rx_q[2]),
.Q1(rx_i[2]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[2]),
.R(1'b0),
.S(1'b0));
// Bit3 LT
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i3 (
.Q0(rx_q[3]),
.Q1(rx_i[3]),
.C0(io_clk_lt),
.C1(io_clk_lt_b),
.CE(1'b1),
.D(rx_data[3]),
.R(1'b0),
.S(1'b0));
// Bit4 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i4 (
.Q0(rx_q[4]),
.Q1(rx_i[4]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[4]),
.R(1'b0),
.S(1'b0));
// Bit5 LT
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i5 (
.Q0(rx_q[5]),
.Q1(rx_i[5]),
.C0(io_clk_lt),
.C1(io_clk_lt_b),
.CE(1'b1),
.D(rx_data[5]),
.R(1'b0),
.S(1'b0));
// Bit6 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i6 (
.Q0(rx_q[6]),
.Q1(rx_i[6]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[6]),
.R(1'b0),
.S(1'b0));
// Bit7 LT
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i7 (
.Q0(rx_q[7]),
.Q1(rx_i[7]),
.C0(io_clk_lt),
.C1(io_clk_lt_b),
.CE(1'b1),
.D(rx_data[7]),
.R(1'b0),
.S(1'b0));
// Bit8 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i8 (
.Q0(rx_q[8]),
.Q1(rx_i[8]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[8]),
.R(1'b0),
.S(1'b0));
// Bit9 LT
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i9 (
.Q0(rx_q[9]),
.Q1(rx_i[9]),
.C0(io_clk_lt),
.C1(io_clk_lt_b),
.CE(1'b1),
.D(rx_data[9]),
.R(1'b0),
.S(1'b0));
// Bit10 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i10 (
.Q0(rx_q[10]),
.Q1(rx_i[10]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[10]),
.R(1'b0),
.S(1'b0));
// Bit11 LB
IDDR2 #(
.DDR_ALIGNMENT("C0"))
iddr2_i11 (
.Q0(rx_q[11]),
.Q1(rx_i[11]),
.C0(io_clk_lb),
.C1(io_clk_lb_b),
.CE(1'b1),
.D(rx_data[11]),
.R(1'b0),
.S(1'b0));
//------------------------------------------------------------------
//
// De-mux I & Q, Ch A & B onto fullrate clock.
//
// In all modes we grab data from the IDDR2 using negedge of siso_clk.
// IDDR2 updates all Q pins on posedge of io_clk. siso_clk does not have aligned phase
// with siso_clk...siso_clk is always a little more delayed than io_clk.
// This small delay is always much smaller than half a clk cycle. Thus by sampling the Q outputs
// with negedge siso_clk we avoid any risk of a race condition (hold violation on receiveing register).
//
// In SISO mode data is replicated onto both CH0 and CH1 for max flexibility in using the DDC's.
//
//------------------------------------------------------------------
reg [11:0] rx_i_del, rx_q_del;
reg [11:0] rx_i0_siso_pos;
reg [11:0] rx_q0_siso_pos;
reg [11:0] rx_i1_siso_pos;
reg [11:0] rx_q1_siso_pos;
reg [11:0] rx_i0_siso_neg;
reg [11:0] rx_q0_siso_neg;
reg [11:0] rx_i1_siso_neg;
reg [11:0] rx_q1_siso_neg;
reg [11:0] rx_i0_siso;
reg [11:0] rx_q0_siso;
reg [11:0] rx_i1_siso;
reg [11:0] rx_q1_siso;
always @(negedge siso_clk)
if(mimo_sync)
// rx_frame_0 was sampled by same falling io_clk edge as rx_i[x]
// rx_frame_0 == 0 causes I & Q to be allocated to CH0
if(rx_frame_0) begin
rx_i_del[11:0] <= rx_i[11:0];
rx_q_del[11:0] <= rx_q[11:0];
end
else begin
// Deal with the fact that Ch A and Ch B are labelled in silkscreen opposite to their documentation in AD9361.
rx_i0_siso[11:0] <= rx_i[11:0];
rx_q0_siso[11:0] <= rx_q[11:0];
rx_i1_siso[11:0] <= rx_i_del[11:0];
rx_q1_siso[11:0] <= rx_q_del[11:0];
end
else begin
rx_i0_siso[11:0] <= rx_i[11:0];
rx_q0_siso[11:0] <= rx_q[11:0];
rx_i1_siso[11:0] <= rx_i[11:0];
rx_q1_siso[11:0] <= rx_q[11:0];
end // else: !if(rx_frame_0)
//------------------------------------------------------------------
//
// Now prepare data for crossing into radio_clk domain which can be for SISO mode (inverted) siso_clk or for MIMO mode siso_clk/2.
// In MIMO mode tx_strobe is used to maintain a known phase relationship betwwen siso_clk and radio_clk.
// (Note: Negedge or posedge is used conditionally so that we have massive margin against a fast-path race condition
// betwwen siso_clk and radio_clk). This kind of arrangement could still lead to confusion in timing analysis
// even if it works in the real world depending on how well the STA tool can do automatic case analysis.
//
//------------------------------------------------------------------
// This code lock only relevent in MIMO mode.
always @(negedge siso_clk)
if (tx_strobe)
begin
rx_i0_siso_neg[11:0] <= rx_i0_siso[11:0];
rx_q0_siso_neg[11:0] <= rx_q0_siso[11:0];
rx_i1_siso_neg[11:0] <= rx_i1_siso[11:0];
rx_q1_siso_neg[11:0] <= rx_q1_siso[11:0];
end
// This code block only relevent in SISO mode.
always @(posedge siso_clk)
begin
rx_i0_siso_pos[11:0] <= rx_i0_siso[11:0];
rx_q0_siso_pos[11:0] <= rx_q0_siso[11:0];
rx_i1_siso_pos[11:0] <= rx_i1_siso[11:0];
rx_q1_siso_pos[11:0] <= rx_q1_siso[11:0];
end
assign rx_i0 = (mimo_sync) ? rx_i0_siso_neg : rx_i0_siso_pos;
assign rx_q0 = (mimo_sync) ? rx_q0_siso_neg : rx_q0_siso_pos;
assign rx_i1 = (mimo_sync) ? rx_i1_siso_neg : rx_i1_siso_pos;
assign rx_q1 = (mimo_sync) ? rx_q1_siso_neg : rx_q1_siso_pos;
//------------------------------------------------------------------
// TX Data Bus - In bank3 LB
//------------------------------------------------------------------
reg [11:0] tx_i,tx_q;
reg tx_strobe_del;
generate
for(z = 0; z < 12; z = z + 1)
begin : gen_pins
ODDR2 #(
.DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
oddr2 (
.Q(tx_data[z]), .C0(io_clk_lb), .C1(io_clk_lb_b),
.CE(1'b1), .D0(tx_i[z]), .D1(tx_q[z]), .R(1'b0), .S(1'b0));
end
endgenerate
//------------------------------------------------------------------
// TX Frame Signal - In bank 3 LB
//------------------------------------------------------------------
ODDR2 #(
.DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
oddr2_frame (
.Q(tx_frame), .C0(io_clk_lb), .C1(io_clk_lb_b),
.CE(1'b1), .D0(tx_strobe_del), .D1(mimo_sync & tx_strobe_del), .R(1'b0), .S(1'b0));
//------------------------------------------------------------------
// TX Clock Signal - In bank 3 LB
//------------------------------------------------------------------
ODDR2 #(
.DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
oddr2_clk (
.Q(tx_clk), .C0(io_clk_lb), .C1(io_clk_lb_b),
.CE(1'b1), .D0(1'b1), .D1(1'b0), .R(1'b0), .S(1'b0));
//------------------------------------------------------------------
//
// Mux I & Q, Ch A & B onto fullrate clockTX bus to AD9361
//
//------------------------------------------------------------------
wire tx_strobe;
reg [11:0] tx_i_del, tx_q_del;
reg find_radio_clk_phase = 1'b0;
reg find_radio_clk_phase_del;
always @(posedge radio_clk)
find_radio_clk_phase <= ~find_radio_clk_phase;
always @(negedge radio_clk)
find_radio_clk_phase_del <= find_radio_clk_phase;
assign tx_strobe = mimo_sync ? (find_radio_clk_phase_del ^ find_radio_clk_phase) : 1'b1;
always @(posedge siso_clk)
tx_strobe_del <= tx_strobe;
// This strange piece of logic allows either USRP DUC to drive the AD9361 in SISO mode.
// This is principly used in the CODEC loopback test.
wire [11:0] tx_im = (mimo_sync || tx_i0 != 12'h0) ? tx_i0 : tx_i1;
wire [11:0] tx_qm = (mimo_sync || tx_q0 != 12'h0) ? tx_q0 : tx_q1;
// Deal with the fact that Ch A and Ch B are labelled in silkscreen opposite to their documentation in AD9361.
// (Except on B200 based on AD9364 where only the true Ch A is stuffed)
always @(posedge siso_clk)
if(tx_strobe)
begin
{tx_i,tx_q} <= mimo_sync ? {tx_i1,tx_q1} : {tx_im,tx_qm};
{tx_i_del,tx_q_del} <= {tx_i0,tx_q0};
end
else
{tx_i,tx_q} <= {tx_i_del,tx_q_del};
//
// Debug
//
/* -----\/----- EXCLUDED -----\/-----
wire [35:0] CONTROL0;
reg [11:0] tx_i_del_debug, tx_q_del_debug;
reg [11:0] tx_i_debug,tx_q_debug;
reg [11:0] tx_i0_debug,tx_q0_debug;
reg find_radio_clk_phase_debug;
reg find_radio_clk_phase_del_debug;
reg tx_strobe_debug;
reg tx_strobe_del_debug;
always @(posedge siso_clk) begin
tx_i_del_debug <= tx_i_del;
tx_q_del_debug <= tx_q_del;
tx_i_debug <= tx_i;
tx_q_debug <= tx_q;
tx_i0_debug <=tx_i0;
tx_q0_debug <= tx_q0;
find_radio_clk_phase_debug <= find_radio_clk_phase;
find_radio_clk_phase_del_debug <= find_radio_clk_phase_del;
tx_strobe_debug <= tx_strobe;
tx_strobe_del_debug <= tx_strobe_del;
end
chipscope_icon chipscope_icon_i0
(
.CONTROL0(CONTROL0) // INOUT BUS [35:0]
);
chipscope_ila_128 chipscope_ila_i0
(
.CONTROL(CONTROL0), // INOUT BUS [35:0]
.CLK(siso_clk), // IN
.TRIG0(
{
tx_i_del_debug[11:0],
tx_q_del_debug[11:0],
tx_i_debug[11:0],
tx_q_debug[11:0],
tx_i0_debug[11:0],
tx_q0_debug[11:0],
find_radio_clk_phase_debug,
find_radio_clk_phase_del_debug,
tx_strobe_debug,
tx_strobe_del_debug
}
)
);
-----/\----- EXCLUDED -----/\----- */
endmodule