aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/io_cap_gen
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/io_cap_gen')
-rw-r--r--fpga/usrp3/lib/io_cap_gen/Makefile.srcs23
-rw-r--r--fpga/usrp3/lib/io_cap_gen/cap_pattern_verifier.v125
-rw-r--r--fpga/usrp3/lib/io_cap_gen/cat_input_lvds.v609
-rw-r--r--fpga/usrp3/lib/io_cap_gen/cat_io_lvds.v200
-rw-r--r--fpga/usrp3/lib/io_cap_gen/cat_io_lvds_dual_mode.v397
-rw-r--r--fpga/usrp3/lib/io_cap_gen/cat_output_lvds.v396
-rw-r--r--fpga/usrp3/lib/io_cap_gen/catcap_ddr_cmos.v95
-rw-r--r--fpga/usrp3/lib/io_cap_gen/catcodec_ddr_cmos.v123
-rw-r--r--fpga/usrp3/lib/io_cap_gen/catgen_ddr_cmos.v90
9 files changed, 2058 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/io_cap_gen/Makefile.srcs b/fpga/usrp3/lib/io_cap_gen/Makefile.srcs
new file mode 100644
index 000000000..a068103cd
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/Makefile.srcs
@@ -0,0 +1,23 @@
+#
+# Copyright 2014 Ettus Research LLC
+# Copyright 2016 Ettus Research, a National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# IO Capture and Generation Sources
+##################################################
+CAT_CAP_GEN_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/io_cap_gen/, \
+./catcap_ddr_cmos.v \
+./catgen_ddr_cmos.v \
+./catcodec_ddr_cmos.v \
+./cat_input_lvds.v \
+./cat_output_lvds.v \
+./cat_io_lvds.v \
+./cat_io_lvds_dual_mode.v \
+))
+
+CAP_GEN_GENERIC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/io_cap_gen/, \
+./cap_pattern_verifier.v \
+))
diff --git a/fpga/usrp3/lib/io_cap_gen/cap_pattern_verifier.v b/fpga/usrp3/lib/io_cap_gen/cap_pattern_verifier.v
new file mode 100644
index 000000000..0d4877187
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/cap_pattern_verifier.v
@@ -0,0 +1,125 @@
+//
+// Copyright 2015 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+//
+// Synthesizable test pattern checker
+//
+
+module cap_pattern_verifier #(
+ parameter WIDTH = 16, //Width of data bus
+ parameter PATTERN = "RAMP", //Pattern to detect. Choose from {RAMP, ONES, ZEROS, TOGGLE, LEFT_BARREL, RIGHT_BARREL}
+ parameter RAMP_START = 'h0000, //Start value for ramp (PATTERN=RAMP only)
+ parameter RAMP_STOP = 'hFFFF, //Stop value for ramp (PATTERN=RAMP only)
+ parameter RAMP_INCR = 'h0001, //Increment for ramp (PATTERN=RAMP only)
+ parameter BARREL_INIT = 'h0001, //Initial value for the barrel shifter (PATTERN=*_BARREL only)
+ parameter HOLD_CYCLES = 1 //Number of cycles to hold each value in the pattern
+) (
+ input clk,
+ input rst,
+
+ //Data input
+ input valid,
+ input [WIDTH-1:0] data,
+
+ //Status output (2 cycle latency)
+ output reg [31:0] count,
+ output reg [31:0] errors,
+ output locked,
+ output failed
+);
+
+ //Create a synchronous version of rst
+ wire sync_rst;
+ reset_sync reset_sync_i (
+ .clk(clk), .reset_in(rst), .reset_out(sync_rst));
+
+ // Register the data to minimize fanout at source
+ reg [WIDTH-1:0] data_reg;
+ reg valid_reg;
+ always @(posedge clk)
+ {data_reg, valid_reg} <= {data, valid};
+
+ // Define pattern start and next states
+ wire [WIDTH-1:0] patt_start, patt_next;
+ reg [WIDTH-1:0] patt_next_reg;
+ generate if (PATTERN == "RAMP") begin
+ assign patt_start = RAMP_START;
+ assign patt_next = (data_reg==RAMP_STOP) ? RAMP_START : data_reg+RAMP_INCR;
+ end else if (PATTERN == "ZEROS") begin
+ assign patt_start = {WIDTH{1'b0}};
+ assign patt_next = {WIDTH{1'b0}};
+ end else if (PATTERN == "ONES") begin
+ assign patt_start = {WIDTH{1'b1}};
+ assign patt_next = {WIDTH{1'b1}};
+ end else if (PATTERN == "TOGGLE") begin
+ assign patt_start = {(WIDTH/2){2'b10}};
+ assign patt_next = ~data_reg;
+ end else if (PATTERN == "LEFT_BARREL") begin
+ assign patt_start = BARREL_INIT;
+ assign patt_next = {data_reg[WIDTH-2:0],data_reg[WIDTH-1]};
+ end else if (PATTERN == "RIGHT_BARREL") begin
+ assign patt_start = BARREL_INIT;
+ assign patt_next = {data_reg[0],data_reg[WIDTH-1:1]};
+ end endgenerate
+
+ reg [1:0] state;
+ localparam ST_IDLE = 2'd0;
+ localparam ST_LOCKED = 2'd1;
+
+ reg [7:0] cyc_count;
+
+ //All registers in this state machine need to have an
+ //asynchronous reset because the "data" and "valid" can
+ //be metastable coming into this module, and can possibly
+ //corrupt "state".
+ always @(posedge clk or posedge rst) begin
+ if (rst) begin //Asynchronous reset
+ count <= 32'd0;
+ errors <= 32'd0;
+ state <= ST_IDLE;
+ cyc_count <= 8'd0;
+ patt_next_reg <= {WIDTH{1'b0}};
+ end else begin
+ //Only do something if data is valid
+ if (valid_reg & ~sync_rst) begin
+ case (state)
+ ST_IDLE: begin
+ //Trigger on start of pattern
+ //We use a case equality here to ensure that this module
+ //does the right thing in simulation. In HW this should
+ //infer a "=="
+ if (data_reg === patt_start) begin
+ state <= ST_LOCKED;
+ count <= 32'd1;
+ cyc_count <= HOLD_CYCLES - 1;
+ end
+ end
+ ST_LOCKED: begin
+ if (cyc_count == 0) begin //Hold counter has expired. Check next word
+ count <= count + 32'd1;
+ //We use a case equality here to ensure that this module
+ //does the right thing in simulation. In HW this should
+ //infer a "!="
+ if (data_reg !== patt_next_reg) begin
+ errors <= errors + 32'd1;
+ end
+ cyc_count <= HOLD_CYCLES - 1;
+ end else begin //Hold until the next update
+ cyc_count <= cyc_count - 1;
+ end
+ end
+ endcase
+ patt_next_reg <= patt_next; //Update next pattern
+ end
+ end
+ end
+
+ assign locked = (state == ST_LOCKED);
+ assign failed = (errors != 32'd0) && locked;
+
+endmodule
+
+
+
diff --git a/fpga/usrp3/lib/io_cap_gen/cat_input_lvds.v b/fpga/usrp3/lib/io_cap_gen/cat_input_lvds.v
new file mode 100644
index 000000000..9b24ca6ca
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/cat_input_lvds.v
@@ -0,0 +1,609 @@
+//
+// Copyright 2016 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: cat_input_lvds
+//
+// Description:
+//
+// Receive interface to AD9361 (Catalina) in LVDS mode.
+//
+// Use Xilinx SERDES to deserialize interleaved sample data off
+// half-word-width LVDS differential data bus from the Catalina.
+//
+// Use FRAME signal to initially synchronize to incoming data after reset
+// de-asserts.
+//
+// In all modes (SISO or MIMO) we output a clock of 1/4 the frequency
+// of the Catalina source-synchronous bus clock to be used as the radio_clk.
+//
+// In SISO mode, every cycle of the radio_clk supplies a new RX sample which
+// is routed to both radios, even if only one is actively receiving.
+//
+// In MIMO mode, every cycle of the radio clock supplies a pair of
+// time aligned MIMO samples which are routed to different radios.
+//
+// The frame_sample signal controls the expected frame signal timing. When
+// frame_sample is 0, the period of the ddr_frame signal is expected to equal
+// two samples (e.g., one from each channel). When frame_sample is 1, the frame
+// period is expected to equal the length of one sample. This allows the module
+// to be used for 2R2T (frame_sample = 1) or 1R1T mode (frame_sample = 0).
+//
+
+
+module cat_input_lvds #(
+ parameter INVERT_FRAME_RX = 0,
+ parameter INVERT_DATA_RX = 6'b00_0000,
+ parameter USE_CLOCK_DELAY = 1,
+ parameter USE_DATA_DELAY = 1,
+ parameter CLOCK_DELAY_MODE = "VAR_LOAD",
+ parameter DATA_DELAY_MODE = "VAR_LOAD",
+ parameter CLOCK_DELAY = 0,
+ parameter DATA_DELAY = 0,
+ parameter WIDTH = 6,
+ parameter GROUP = "DEFAULT",
+ parameter USE_BUFG = 1
+) (
+ input clk200,
+ input rst,
+
+ // Data and frame timing (synchronous to radio_clk)
+ input mimo, // Output one channel (MIMO=0) or two (MIMO=1)
+ input frame_sample, // Two samples per frame period (frame_sample=0) or one sample per frame (frame_sample=1)
+
+ // Region local Clocks for I/O cells.
+ output ddr_clk,
+ output sdr_clk,
+
+ // Source Synchronous external input clock
+ input ddr_clk_p,
+ input ddr_clk_n,
+
+ // Source Synchronous data lines
+ input [WIDTH-1:0] ddr_data_p,
+ input [WIDTH-1:0] ddr_data_n,
+ input ddr_frame_p,
+ input ddr_frame_n,
+
+ // Delay control interface
+ input ctrl_clk,
+ input [4:0] ctrl_data_delay,
+ input [4:0] ctrl_clk_delay,
+ input ctrl_ld_data_delay,
+ input ctrl_ld_clk_delay,
+
+ // Global output clocks, ddr_clk/4 & ddr_clk/2
+ output radio_clk,
+ output radio_clk_2x,
+
+ // SDR Data buses
+ output reg [(WIDTH*2)-1:0] i0,
+ output reg [(WIDTH*2)-1:0] q0,
+ output reg [(WIDTH*2)-1:0] i1,
+ output reg [(WIDTH*2)-1:0] q1,
+ output reg rx_aligned
+
+);
+
+ //------------------------------------------------------------------
+ // UG471 says take reset high asynchronously, and de-assert
+ // synchronized to CLKDIV (sdr_clk) for SERDES.
+ //------------------------------------------------------------------
+ (* ASYNC_REG = "TRUE" *) reg rst_sdr_sync, rst_sdr_sync_ms;
+
+ always @(posedge sdr_clk or posedge rst)
+ if (rst) begin
+ rst_sdr_sync_ms <= 1'b1;
+ rst_sdr_sync <= 1'b1;
+ end else begin
+ rst_sdr_sync_ms <= 1'b0;
+ rst_sdr_sync <= rst_sdr_sync_ms;
+ end
+
+
+ //------------------------------------------------------------------
+ // IDELAY is calibrated using (mandatory) IDELAYCTRL cell.
+ // Must be feed stable free running clock specified by: FIDELAYCTRL_REF.(200MHz)
+ // Mandatory async reset required, min pulse of: TIDELAYCTRL_RPW (~60nS)
+ //------------------------------------------------------------------
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ IDELAYCTRL IDELAYCTRL_i0 (
+ .REFCLK (clk200),
+ .RST (rst_sdr_sync),
+ .RDY ()
+ );
+
+
+ //------------------------------------------------------------------
+ // Clock input
+ //------------------------------------------------------------------
+ wire ddr_clk_dly, ddr_clk_unbuf;
+
+ IBUFDS #(
+ .DIFF_TERM("TRUE")
+ ) clk_ibufds (
+ .O(ddr_clk_unbuf),
+ .I(ddr_clk_p),
+ .IB(ddr_clk_n)
+ );
+
+ generate
+ if (USE_CLOCK_DELAY) begin : gen_clock_delay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ IDELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("IDATAIN"), // Delay input (IDATAIN, DATAIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .IDELAY_TYPE (CLOCK_DELAY_MODE),
+ .IDELAY_VALUE (CLOCK_DELAY),
+ .PIPE_SEL ("FALSE"),
+ .REFCLK_FREQUENCY (200.0),
+ .SIGNAL_PATTERN ("CLOCK")
+ ) ddr_clk_idelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_clk_dly), // 1-bit output: Delayed data output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CNTVALUEIN (ctrl_clk_delay), // 5-bit input: Counter value input
+ .DATAIN (1'b0), // 1-bit input: Internal delay data input
+ .IDATAIN (ddr_clk_unbuf), // 1-bit input: Data input from the I/O
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_clk_delay), // 1-bit input: Load IDELAY_VALUE input
+ .LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register to load data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end
+ else begin
+ assign ddr_clk_dly = ddr_clk_unbuf;
+ end
+
+ endgenerate
+
+ // IO CLock is DDR freq. This drives SERDES and other I/O elements with minimal clock skew.
+ BUFIO ddr_clk_bufio (.O(ddr_clk),.I(ddr_clk_dly));
+
+ // SDR clock is one quarter DDR freq and local to regio using BUFR
+ // BUFR is a constraint of the SERDES since we need frequency agnostic clock division.
+ // UG471 states can use BUFIO and BUFR divided to directly drive a SERDES legally.
+ // (Other option is pair of BUFG's plus an MMCM - But MMCM has fixed frequency)
+ wire sdr_clk_2x;
+
+ BUFR #(
+ .BUFR_DIVIDE ("2"),
+ .SIM_DEVICE ("7SERIES")
+ ) sdr_clk_2x_bufr (
+ .O (sdr_clk_2x),
+ .CE (1'b1),
+ .CLR (1'b0),
+ .I (ddr_clk_dly)
+ );
+
+ BUFR #(
+ .BUFR_DIVIDE("4"),
+ .SIM_DEVICE("7SERIES")
+ ) sdr_clk_bufr (
+ .O(sdr_clk),
+ .CE(1'b1),
+ .CLR(1'b0),
+ .I(ddr_clk_dly)
+ );
+
+ generate
+ if (USE_BUFG) begin : gen_BUFG
+ // radio_clock is sdr_clk re-buffered with BUFG, and radio_clk_2x is
+ // sdr_clk_2x re-buffered, so both can be used globally. This introduces skew
+ // between sdr_clk -> radio_clock so we must hand data between them carefully
+ // even though they have a fixed phase relationship.
+ BUFG radio_clk_1x_bufg (.O(radio_clk), .I(sdr_clk));
+ BUFG radio_clk_2x_bufg (.O(radio_clk_2x), .I(sdr_clk_2x));
+ end else begin
+ assign radio_clk = sdr_clk;
+ assign radio_clk_2x = sdr_clk_2x;
+ end
+ endgenerate
+
+
+
+ //------------------------------------------------------------------
+ // Frame Signal
+ //------------------------------------------------------------------
+ wire ddr_frame, ddr_frame_dly;
+ wire [7:0] des_frame; // deserialized frame signal
+ reg bitslip;
+ reg aligned;
+
+
+ //
+ // Use FRAME signal to get bitstream word aligned.
+ //
+ // In MIMO mode, FRAME is asserted during the entirety of channel 0, and
+ // deasserts during the entirety of channel 1.
+ //
+ localparam IDLE = 0;
+ localparam SEARCH = 1;
+ localparam SLIP1 = 3;
+ localparam SLIP2 = 2;
+ localparam SLIP3 = 4;
+ localparam SLIP4 = 5;
+ localparam SYNC = 6;
+
+
+ reg [2:0] frame_state;
+
+ //
+ // Delay start of framesync operation for 64 clocks after reset de-asserts to
+ // SERDES to be sure they are in a steady state.
+ //
+ // Each time we assert bitslip we then have to wait 2 cycles before we can
+ // examine the results.
+ //
+ // Checking for 0xF0 and 0xCC allows us to support 1R1T and 2R2T timing,
+ // which have different frame periods.
+ wire frame_is_aligned =
+ (!frame_sample && (des_frame[7:0] == (INVERT_FRAME_RX ? 8'h0F : 8'hF0))) ||
+ ( frame_sample && (des_frame[7:0] == (INVERT_FRAME_RX ? 8'h33 : 8'hCC)));
+
+ reg [5:0] sync_delay;
+ reg run_sync;
+
+
+ always @(posedge sdr_clk)
+ if (rst_sdr_sync) begin
+ sync_delay <= 6'h0;
+ run_sync <= 1'b0;
+ end else if (sync_delay == 6'h3F)
+ run_sync <= 1'b1;
+ else
+ sync_delay <= sync_delay + 1'b1;
+
+ always @(posedge sdr_clk)
+ begin
+ if (!run_sync) begin
+ frame_state <= IDLE;
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ end else begin
+ case (frame_state)
+ IDLE: begin
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ frame_state <= SEARCH;
+ end
+
+ SEARCH: begin
+ if (frame_is_aligned) begin
+ frame_state <= SYNC;
+ bitslip <= 1'b0;
+ aligned <= 1'b1;
+ end else begin
+ // Bitslip until captured frame is aligned
+ bitslip <= 1'b1;
+ frame_state <= SLIP1;
+ aligned <= 1'b0;
+ end
+ end
+
+ SLIP1: begin
+ frame_state <= SLIP2;
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ end
+
+ SLIP2: begin
+ frame_state <= SLIP3;
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ end
+
+ SLIP3: begin
+ frame_state <= SLIP4;
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ end
+
+ SLIP4: begin
+ frame_state <= SEARCH;
+ bitslip <= 1'b0;
+ aligned <= 1'b0;
+ end
+
+ SYNC: begin
+ if (frame_is_aligned) begin
+ frame_state <= SYNC;
+ aligned <= 1'b1;
+ end else begin
+ frame_state <= SEARCH;
+ aligned <= 1'b0;
+ end
+ end
+
+ endcase // case(frame_state)
+
+ end
+ end
+
+
+ IBUFDS #(
+ .DIFF_TERM ("TRUE")
+ ) ddr_frame_ibufds (
+ .O (ddr_frame),
+ .I (ddr_frame_p),
+ .IB (ddr_frame_n)
+ );
+
+ generate
+ if (USE_DATA_DELAY) begin : gen_frame_delay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ IDELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("IDATAIN"), // Delay input (IDATAIN, DATAIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .IDELAY_TYPE (DATA_DELAY_MODE),
+ .IDELAY_VALUE (DATA_DELAY),
+ .PIPE_SEL ("FALSE"),
+ .REFCLK_FREQUENCY (200.0),
+ .SIGNAL_PATTERN ("DATA")
+ ) ddr_frame_idelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_frame_dly), // 1-bit output: Delayed data output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CNTVALUEIN (ctrl_data_delay), // 5-bit input: Counter value input
+ .DATAIN (1'b0), // 1-bit input: Internal delay data input
+ .IDATAIN (ddr_frame), // 1-bit input: Data input from the I/O
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_data_delay), // 1-bit input: Load IDELAY_VALUE input
+ .LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register to load data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end
+ else begin
+ assign ddr_frame_dly = ddr_frame;
+ end
+ endgenerate
+
+ ISERDESE2 #(
+ .DATA_RATE ("DDR"), // DDR, SDR
+ .DATA_WIDTH (8), // Parallel data width (2-8,10,14)
+ .DYN_CLKDIV_INV_EN ("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
+ .DYN_CLK_INV_EN ("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
+ // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
+ .INIT_Q1 (1'b0),
+ .INIT_Q2 (1'b0),
+ .INIT_Q3 (1'b0),
+ .INIT_Q4 (1'b0),
+ .INTERFACE_TYPE ("NETWORKING"),
+ .IOBDELAY ("BOTH"),
+ .NUM_CE (1),
+ .OFB_USED ("FALSE"),
+ .SERDES_MODE ("MASTER"),
+ // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
+ .SRVAL_Q1 (1'b0),
+ .SRVAL_Q2 (1'b0),
+ .SRVAL_Q3 (1'b0),
+ .SRVAL_Q4 (1'b0)
+ ) ddr_frame_serdese2 (
+ .O (), // 1-bit output: Combinatorial output
+ // Q1 - Q8: 1-bit (each) output: Registered data outputs
+ .Q1 (des_frame[0]),
+ .Q2 (des_frame[1]),
+ .Q3 (des_frame[2]),
+ .Q4 (des_frame[3]),
+ .Q5 (des_frame[4]),
+ .Q6 (des_frame[5]),
+ .Q7 (des_frame[6]),
+ .Q8 (des_frame[7]),
+ // SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
+ .SHIFTOUT1 (),
+ .SHIFTOUT2 (),
+ // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
+ // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
+ // to Q8 output ports will shift, as in a barrel-shifter operation, one
+ // position every time Bitslip is invoked (DDR operation is different from SDR)
+ .BITSLIP (bitslip),
+ // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
+ .CE1 (1'b1),
+ .CE2 (1'b1),
+ .CLKDIVP (1'b0), // 1-bit input: TBD
+ // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
+ .CLK (ddr_clk), // 1-bit input: High-speed clock
+ .CLKB (~ddr_clk), // 1-bit input: High-speed secondary clock
+ .CLKDIV (sdr_clk), // 1-bit input: Divided clock
+ .OCLK (1'b0), // 1-bit input: High-speed output clock used when INTERFACE_TYPE="MEMORY"
+ // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
+ .DYNCLKDIVSEL (1'b0), // 1-bit input: Dynamic CLKDIV inversion
+ .DYNCLKSEL (1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
+ // Input Data: 1-bit (each) input: ISERDESE2 data input ports
+ .D (1'b0), // 1-bit input: Data input
+ .DDLY (ddr_frame_dly), // 1-bit input: Serial data from IDELAYE2
+ .OFB (1'b0), // 1-bit input: Data feedback from OSERDESE2
+ .OCLKB (1'b0), // 1-bit input: High-speed negative edge output clock
+ .RST (rst_sdr_sync), // 1-bit input: Active high asynchronous reset
+ // SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
+ .SHIFTIN1 (1'b0),
+ .SHIFTIN2 (1'b0)
+ );
+
+
+ //------------------------------------------------------------------
+ // Data Bus
+ //------------------------------------------------------------------
+ wire [WIDTH-1:0] ddr_data;
+ wire [WIDTH-1:0] ddr_data_dly;
+ wire [(WIDTH*2)-1:0] data_i0, data_i1;
+ wire [(WIDTH*2)-1:0] data_q0, data_q1;
+
+
+ genvar i;
+ generate
+ for (i=0 ; i<WIDTH ; i=i+1) begin : generate_data_bus
+
+ IBUFDS #(
+ .DIFF_TERM ("TRUE")
+ ) ddr_data_ibufds (
+ .O (ddr_data[i]),
+ .I (ddr_data_p[i]),
+ .IB (ddr_data_n[i])
+ );
+
+ if (USE_DATA_DELAY) begin : gen_data_delay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ IDELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("IDATAIN"), // Delay input (IDATAIN, DATAIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .IDELAY_TYPE (DATA_DELAY_MODE),
+ .IDELAY_VALUE (DATA_DELAY),
+ .PIPE_SEL ("FALSE"),
+ .REFCLK_FREQUENCY (200.0),
+ .SIGNAL_PATTERN ("DATA")
+ ) ddr_data_idelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_data_dly[i]), // 1-bit output: Delayed data output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CNTVALUEIN (ctrl_data_delay), // 5-bit input: Counter value input
+ .DATAIN (1'b0), // 1-bit input: Internal delay data input
+ .IDATAIN (ddr_data[i]), // 1-bit input: Data input from the I/O
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_data_delay), // 1-bit input: Load IDELAY_VALUE input
+ .LDPIPEEN (1'b0), // 1-bit input: Enable PIPELINE register to load data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end
+ else begin
+ assign ddr_data_dly[i] = ddr_data[i];
+ end
+
+ ISERDESE2 #(
+ .DATA_RATE ("DDR"), // DDR, SDR
+ .DATA_WIDTH (8), // Parallel data width (2-8,10,14)
+ .DYN_CLKDIV_INV_EN ("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
+ .DYN_CLK_INV_EN ("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
+ // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
+ .INIT_Q1 (1'b0),
+ .INIT_Q2 (1'b0),
+ .INIT_Q3 (1'b0),
+ .INIT_Q4 (1'b0),
+ .INTERFACE_TYPE ("NETWORKING"),
+ .IOBDELAY ("BOTH"),
+ .NUM_CE (1),
+ .OFB_USED ("FALSE"),
+ .SERDES_MODE ("MASTER"),
+ // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
+ .SRVAL_Q1 (1'b0),
+ .SRVAL_Q2 (1'b0),
+ .SRVAL_Q3 (1'b0),
+ .SRVAL_Q4 (1'b0)
+ ) ddr_data_serdese2 (
+ .O (), // 1-bit output: Combinatorial output
+ // Q1 - Q8: 1-bit (each) output: Registered data outputs
+ .Q1 (data_q1[i]),
+ .Q2 (data_i1[i]),
+ .Q3 (data_q1[WIDTH+i]),
+ .Q4 (data_i1[WIDTH+i]),
+ .Q5 (data_q0[i]),
+ .Q6 (data_i0[i]),
+ .Q7 (data_q0[WIDTH+i]),
+ .Q8 (data_i0[WIDTH+i]),
+ // SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
+ .SHIFTOUT1 (),
+ .SHIFTOUT2 (),
+ // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
+ // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
+ // to Q8 output ports will shift, as in a barrel-shifter operation, one
+ // position every time Bitslip is invoked (DDR operation is different from SDR)
+ .BITSLIP (bitslip),
+ // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
+ .CE1 (1'b1),
+ .CE2 (1'b1),
+ .CLKDIVP (1'b0), // 1-bit input: TBD
+ // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
+ .CLK (ddr_clk), // 1-bit input: High-speed clock
+ .CLKB (~ddr_clk), // 1-bit input: High-speed secondary clock
+ .CLKDIV (sdr_clk), // 1-bit input: Divided clock
+ .OCLK (1'b0), // 1-bit input: High-speed output clock used when INTERFACE_TYPE="MEMORY"
+ // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
+ .DYNCLKDIVSEL (1'b0), // 1-bit input: Dynamic CLKDIV inversion
+ .DYNCLKSEL (1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
+ // Input Data: 1-bit (each) input: ISERDESE2 data input ports
+ .D (1'b0), // 1-bit input: Data input
+ .DDLY (ddr_data_dly[i]), // 1-bit input: Serial data from IDELAYE2
+ .OFB (1'b0), // 1-bit input: Data feedback from OSERDESE2
+ .OCLKB (1'b0), // 1-bit input: High-speed negative edge output clock
+ .RST (rst_sdr_sync), // 1-bit input: Active high asynchronous reset
+ // SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
+ .SHIFTIN1 (1'b0),
+ .SHIFTIN2 (1'b0)
+ );
+
+ end // block: generate_data_bus
+ endgenerate
+
+ //
+ // Cross these cycles to radio_clk using negative edge. This give 1/2 a radio
+ // clock period + BUFG insertion delay for signals to propagate. Thats > 6nS.
+ //
+ reg [(WIDTH*2)-1:0] radio_data_i0, radio_data_i1, radio_data_q0, radio_data_q1;
+ reg radio_aligned;
+
+ always @(negedge radio_clk)
+ begin
+ radio_data_i0[(WIDTH*2)-1:0] <= data_i0[(WIDTH*2)-1:0] ^ {INVERT_DATA_RX,INVERT_DATA_RX};
+ radio_data_q0[(WIDTH*2)-1:0] <= data_q0[(WIDTH*2)-1:0] ^ {INVERT_DATA_RX,INVERT_DATA_RX};
+ radio_data_i1[(WIDTH*2)-1:0] <= data_i1[(WIDTH*2)-1:0] ^ {INVERT_DATA_RX,INVERT_DATA_RX};
+ radio_data_q1[(WIDTH*2)-1:0] <= data_q1[(WIDTH*2)-1:0] ^ {INVERT_DATA_RX,INVERT_DATA_RX};
+ radio_aligned <= aligned;
+ end
+
+ always @(posedge radio_clk)
+ begin
+ i0 <= radio_data_i0;
+ q0 <= radio_data_q0;
+ if (mimo) { i1, q1 } <= { radio_data_i1, radio_data_q1 };
+ else { i1, q1 } <= { radio_data_i0, radio_data_q0 }; // dup single valid channel to both radios
+ rx_aligned <= radio_aligned;
+ end
+
+ /*******************************************************************
+ * Debug only logic below here.
+ ******************************************************************/
+/*-----\/----- EXCLUDED -----\/-----
+ (* keep = "true", max_fanout = 10 *) reg [7:0] des_frame_reg;
+ (* keep = "true", max_fanout = 10 *) reg rst_sdr_sync_reg;
+ (* keep = "true", max_fanout = 10 *) reg run_sync_reg;
+ (* keep = "true", max_fanout = 10 *) reg bitslip_reg;
+ (* keep = "true", max_fanout = 10 *) reg aligned_reg;
+ (* keep = "true", max_fanout = 10 *) reg [2:0] frame_state_reg;
+
+ always @(posedge sdr_clk)
+ begin
+ des_frame_reg <= des_frame;
+ rst_sdr_sync_reg <= rst_sdr_sync;
+ run_sync_reg <= run_sync;
+ bitslip_reg <= bitslip;
+ aligned_reg <= aligned;
+ frame_state_reg <= frame_state;
+ end
+
+ ila64 ila64_i (
+ .clk(sdr_clk), // input clk
+ .probe0(
+ {
+ des_frame_reg,
+ rst_sdr_sync_reg,
+ run_sync_reg,
+ bitslip_reg,
+ aligned_reg,
+ frame_state_reg
+ }
+ )
+ );
+ -----/\----- EXCLUDED -----/\----- */
+
+endmodule
+
diff --git a/fpga/usrp3/lib/io_cap_gen/cat_io_lvds.v b/fpga/usrp3/lib/io_cap_gen/cat_io_lvds.v
new file mode 100644
index 000000000..e43e26e00
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/cat_io_lvds.v
@@ -0,0 +1,200 @@
+//
+// Copyright 2016 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: cat_io_lvds
+//
+// Description:
+//
+// This is an LVDS interface for the AD9361 (Catalina). It consists of of an
+// input module for Rx and an output module for Tx. See the AD9361 Interface
+// Specification and the AD9361 data sheet for details.
+//
+// This module assumes a dual-port, full-duplex topology, in 2R2T timing mode.
+// The mimo signal allows you to support one (mimo = 0) or two channels (mimo =
+// 1). When mimo = 0, the data from one channel is simply ignored and radio_clk
+// frequency remains equal to rx_clk_p / 4.
+//
+
+module cat_io_lvds #(
+ parameter INVERT_FRAME_RX = 0,
+ parameter INVERT_DATA_RX = 6'b00_0000,
+ parameter INVERT_FRAME_TX = 0,
+ parameter INVERT_DATA_TX = 6'b00_0000,
+ parameter USE_CLOCK_IDELAY = 1,
+ parameter USE_DATA_IDELAY = 1,
+ parameter DATA_IDELAY_MODE = "VAR_LOAD",
+ parameter CLOCK_IDELAY_MODE = "VAR_LOAD",
+ parameter INPUT_CLOCK_DELAY = 16,
+ parameter INPUT_DATA_DELAY = 0,
+ parameter USE_CLOCK_ODELAY = 0,
+ parameter USE_DATA_ODELAY = 0,
+ parameter DATA_ODELAY_MODE = "VAR_LOAD",
+ parameter CLOCK_ODELAY_MODE = "VAR_LOAD",
+ parameter OUTPUT_CLOCK_DELAY = 16,
+ parameter OUTPUT_DATA_DELAY = 0,
+ parameter USE_BUFG = 1
+) (
+ input rst,
+ input clk200,
+
+ // Data and frame timing (synchronous to radio_clk)
+ input mimo,
+ input frame_sample,
+
+ // Delay Control Interface
+ input ctrl_clk,
+ input [4:0] ctrl_in_data_delay,
+ input [4:0] ctrl_in_clk_delay,
+ input ctrl_ld_in_data_delay,
+ input ctrl_ld_in_clk_delay,
+ input [4:0] ctrl_out_data_delay,
+ input [4:0] ctrl_out_clk_delay,
+ input ctrl_ld_out_data_delay,
+ input ctrl_ld_out_clk_delay,
+
+ // Baseband sample interface
+ output radio_clk,
+ output radio_clk_2x,
+ output rx_aligned,
+ 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,
+
+ // Catalina LVDS interface
+ input rx_clk_p,
+ input rx_clk_n,
+ input rx_frame_p,
+ input rx_frame_n,
+ input [5:0] rx_d_p,
+ input [5:0] rx_d_n,
+ output tx_clk_p,
+ output tx_clk_n,
+ output tx_frame_p,
+ output tx_frame_n,
+ output [5:0] tx_d_p,
+ output [5:0] tx_d_n
+);
+
+ wire sdr_clk, ddr_clk;
+
+ //---------------------------------------------------------------------------
+ // Input (Rx) Interface
+ //---------------------------------------------------------------------------
+
+ cat_input_lvds #(
+ .INVERT_FRAME_RX (INVERT_FRAME_RX),
+ .INVERT_DATA_RX (INVERT_DATA_RX),
+ .USE_CLOCK_DELAY (USE_CLOCK_IDELAY),
+ .USE_DATA_DELAY (USE_DATA_IDELAY),
+ .CLOCK_DELAY_MODE (CLOCK_IDELAY_MODE),
+ .DATA_DELAY_MODE (DATA_IDELAY_MODE),
+ .CLOCK_DELAY (INPUT_CLOCK_DELAY),
+ .DATA_DELAY (INPUT_DATA_DELAY),
+ .WIDTH (6),
+ .GROUP ("CATALINA"),
+ .USE_BUFG (USE_BUFG)
+ ) cat_input_lvds_i0 (
+ .clk200 (clk200),
+ .rst (rst),
+
+ // Data and frame timing
+ .mimo (mimo),
+ .frame_sample (frame_sample),
+
+ // Region local Clocks for I/O cells.
+ .ddr_clk (ddr_clk),
+ .sdr_clk (sdr_clk),
+
+ // Source Synchronous external input clock
+ .ddr_clk_p (rx_clk_p),
+ .ddr_clk_n (rx_clk_n),
+
+ // Source Synchronous data lines
+ .ddr_data_p (rx_d_p),
+ .ddr_data_n (rx_d_n),
+ .ddr_frame_p (rx_frame_p),
+ .ddr_frame_n (rx_frame_n),
+
+ // Delay control interface
+ .ctrl_clk (ctrl_clk),
+ .ctrl_data_delay (ctrl_in_data_delay),
+ .ctrl_clk_delay (ctrl_in_clk_delay),
+ .ctrl_ld_data_delay (ctrl_ld_in_data_delay),
+ .ctrl_ld_clk_delay (ctrl_ld_in_clk_delay),
+
+ // SDR output clock(s)
+ .radio_clk (radio_clk),
+ .radio_clk_2x (radio_clk_2x),
+
+ // SDR Data buses
+ .i0 (rx_i0),
+ .q0 (rx_q0),
+ .i1 (rx_i1),
+ .q1 (rx_q1),
+ .rx_aligned (rx_aligned)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Output (Tx) Interface
+ //---------------------------------------------------------------------------
+
+ cat_output_lvds #(
+ .INVERT_FRAME_TX (INVERT_FRAME_TX),
+ .INVERT_DATA_TX (INVERT_DATA_TX),
+ .USE_CLOCK_DELAY (USE_CLOCK_ODELAY),
+ .USE_DATA_DELAY (USE_DATA_ODELAY),
+ .CLOCK_DELAY_MODE (CLOCK_ODELAY_MODE),
+ .DATA_DELAY_MODE (DATA_ODELAY_MODE),
+ .CLOCK_DELAY (OUTPUT_CLOCK_DELAY),
+ .DATA_DELAY (OUTPUT_DATA_DELAY),
+ .WIDTH (6),
+ .GROUP ("CATALINA")
+ ) cat_output_lvds_i0 (
+ .clk200 (clk200),
+ .rst (rst),
+
+ // Two samples per frame period (frame_sample=0; e.g., for two-channel
+ // mode) or one sample per frame (frame_sample=1)
+ .frame_sample(frame_sample),
+
+ // Region local Clocks for I/O cells.
+ .ddr_clk (ddr_clk),
+ .sdr_clk (sdr_clk),
+
+ // Source Synchronous external input clock
+ .ddr_clk_p (tx_clk_p),
+ .ddr_clk_n (tx_clk_n),
+
+ // Source Synchronous data lines
+ .ddr_data_p (tx_d_p),
+ .ddr_data_n (tx_d_n),
+ .ddr_frame_p (tx_frame_p),
+ .ddr_frame_n (tx_frame_n),
+
+ // Delay control interface
+ .ctrl_clk (ctrl_clk),
+ .ctrl_data_delay (ctrl_out_data_delay),
+ .ctrl_clk_delay (ctrl_out_clk_delay),
+ .ctrl_ld_data_delay (ctrl_ld_out_data_delay),
+ .ctrl_ld_clk_delay (ctrl_ld_out_clk_delay),
+
+ // SDR global input clock
+ .radio_clk (radio_clk),
+
+ // SDR Data buses
+ .i0 (tx_i0),
+ .q0 (tx_q0),
+ .i1 (tx_i1),
+ .q1 (tx_q1)
+ );
+
+
+endmodule // cat_io_lvds
diff --git a/fpga/usrp3/lib/io_cap_gen/cat_io_lvds_dual_mode.v b/fpga/usrp3/lib/io_cap_gen/cat_io_lvds_dual_mode.v
new file mode 100644
index 000000000..13b6ae1f9
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/cat_io_lvds_dual_mode.v
@@ -0,0 +1,397 @@
+//
+// Copyright 2016 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: cat_io_lvds_dual_mode
+//
+// Description:
+//
+// This is an LVDS interface for the AD9361 (Catalina). It uses the cat_io_lvds
+// module to implement the interface, but supports both 1R1T and 2R2T timing
+// modes while using full LVDS bandwidth. That is, it can support 1R1T at twice
+// the sample rate of 2R2T.
+//
+// This is controlled by the a_mimo control signal. When MIMO = 0 (1R1T mode),
+// the radio_clk frequency equals that of rx_clk/2 and the data is output to
+// both radio channels. If MIMO = 1 (2R2T), the frequency of radio_clk equals
+// rx_clk/4 and the data stream is split between channel 0 and channel 1. This is used
+// for 2R2T mode.
+//
+
+module cat_io_lvds_dual_mode #(
+ parameter INVERT_FRAME_RX = 0,
+ parameter INVERT_DATA_RX = 6'b00_0000,
+ parameter INVERT_FRAME_TX = 0,
+ parameter INVERT_DATA_TX = 6'b00_0000,
+ parameter USE_CLOCK_IDELAY = 1,
+ parameter USE_DATA_IDELAY = 1,
+ parameter DATA_IDELAY_MODE = "FIXED",
+ parameter CLOCK_IDELAY_MODE = "FIXED",
+ parameter INPUT_CLOCK_DELAY = 16,
+ parameter INPUT_DATA_DELAY = 0,
+ parameter USE_CLOCK_ODELAY = 0,
+ parameter USE_DATA_ODELAY = 0,
+ parameter DATA_ODELAY_MODE = "FIXED",
+ parameter CLOCK_ODELAY_MODE = "FIXED",
+ parameter OUTPUT_CLOCK_DELAY = 16,
+ parameter OUTPUT_DATA_DELAY = 0
+) (
+ input rst,
+ input clk200,
+
+ // Data and frame timing (asynchronous, glitch free)
+ input a_mimo, // MIMO vs. SISO mode
+ input a_tx_ch, // Which channel to transmit when MIMO=0
+
+ // Delay Control Interface
+ input ctrl_clk,
+ input [4:0] ctrl_in_data_delay,
+ input [4:0] ctrl_in_clk_delay,
+ input ctrl_ld_in_data_delay,
+ input ctrl_ld_in_clk_delay,
+ input [4:0] ctrl_out_data_delay,
+ input [4:0] ctrl_out_clk_delay,
+ input ctrl_ld_out_data_delay,
+ input ctrl_ld_out_clk_delay,
+
+ // Baseband sample interface
+ output radio_clk,
+ //
+ output reg rx_aligned,
+ output reg [11:0] rx_i0,
+ output reg [11:0] rx_q0,
+ output reg [11:0] rx_i1,
+ output reg [11:0] rx_q1,
+ //
+ input [11:0] tx_i0,
+ input [11:0] tx_q0,
+ input [11:0] tx_i1,
+ input [11:0] tx_q1,
+
+ // Catalina LVDS interface
+ input rx_clk_p,
+ input rx_clk_n,
+ input rx_frame_p,
+ input rx_frame_n,
+ input [5:0] rx_d_p,
+ input [5:0] rx_d_n,
+ //
+ output tx_clk_p,
+ output tx_clk_n,
+ output tx_frame_p,
+ output tx_frame_n,
+ output [5:0] tx_d_p,
+ output [5:0] tx_d_n
+);
+
+ wire radio_clk_1x; // rx_clk_p divided by 4
+ wire radio_clk_2x; // rx_clk_p divided by 2
+
+
+ //---------------------------------------------------------------------------
+ // Mode Selection
+ //---------------------------------------------------------------------------
+
+ wire r_mimo;
+ wire r_tx_ch;
+
+ // Double synchronize the MIMO signal
+ synchronizer mimo_sync (
+ .clk(radio_clk_1x),
+ .rst(1'b0),
+ .in(a_mimo),
+ .out(r_mimo));
+
+ // Double synchronize the Tx channel signal
+ synchronizer tx_ch_sync (
+ .clk(radio_clk_1x),
+ .rst(1'b0),
+ .in(a_tx_ch),
+ .out(r_tx_ch));
+
+
+ //---------------------------------------------------------------------------
+ // Clock Mux
+ //---------------------------------------------------------------------------
+
+ // Use radio_clk_1x when MIMO = 1, radio_clk_2x when MIMO = 0
+ BUFGCTRL BUFGCTRL_radio_clk (
+ .I0 (radio_clk_1x),
+ .I1 (radio_clk_2x),
+ .S0 (r_mimo),
+ .S1 (~r_mimo),
+ .CE0 (1),
+ .CE1 (1),
+ .O (radio_clk),
+ .IGNORE0 (0),
+ .IGNORE1 (0)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Generate Alignment Strobes
+ //---------------------------------------------------------------------------
+ //
+ // The LVDS input logic generates the following two clocks:
+ //
+ // radio_clk_1x |‾‾‾‾‾|_____|‾‾‾‾‾|_____|‾‾‾‾‾|_____|‾‾‾‾‾|_____|‾‾‾‾‾|
+ //
+ // radio_clk_2x |‾‾|__|‾‾|__|‾‾|__|‾‾|__|‾‾|__|‾‾|__|‾‾|__|‾‾|__|‾‾|__|
+ //
+ //
+ // Using simple logic, we create the following two signals from these clocks:
+ //
+ // align_1x |‾‾‾‾‾‾‾‾‾‾‾|___________|‾‾‾‾‾‾‾‾‾‾‾|___________|‾‾‾‾‾‾
+ //
+ // align_2x ______|‾‾‾‾‾‾‾‾‾‾‾|___________|‾‾‾‾‾‾‾‾‾‾‾|___________|
+ //
+ // These two alignment signals allow us to tell where in the frame period we
+ // are so that we can deserialize in the correct order.
+ //
+ //---------------------------------------------------------------------------
+
+ reg align_1x = 0;
+ reg align_2x = 0;
+
+ always @(posedge radio_clk_1x)
+ begin
+ align_1x <= ~align_1x;
+ end
+
+ always @(posedge radio_clk_2x)
+ begin
+ // Align data capture to 1x clock so that we stay in sync with data.
+ // Otherwise, the data might be serialized in the wrong order.
+ align_2x <= align_1x;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Rx MIMO/SISO Serialization
+ //---------------------------------------------------------------------------
+ //
+ // This block of code takes the dual outputs when in SISO mode and serializes
+ // them. Because we use the 2x clock when in SISO mode, this allows us to
+ // double the data rate when using a single channel.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [11:0] rx_i0_ser;
+ reg [11:0] rx_q0_ser;
+ reg [11:0] rx_i1_ser;
+ reg [11:0] rx_q1_ser;
+
+ reg [11:0] rx_i0_out;
+ reg [11:0] rx_q0_out;
+ reg [11:0] rx_i1_out;
+ reg [11:0] rx_q1_out;
+
+ always @(posedge radio_clk_2x)
+ begin
+ rx_aligned <= rx_aligned_t;
+
+ if (align_1x ^ align_2x) begin
+ // This clock cycle corresponds to the first 1x cycle in which two
+ // samples are output, so grab data from port 0.
+ rx_i0_ser <= rx_i0_t;
+ rx_q0_ser <= rx_q0_t;
+ rx_i1_ser <= rx_i0_t;
+ rx_q1_ser <= rx_q0_t;
+ end else begin
+ // This radio_clk_2x cycle corresponds to the second 1x cycle in which
+ // two samples are output, so grab data from port 1.
+ rx_i0_ser <= rx_i1_t;
+ rx_q0_ser <= rx_q1_t;
+ rx_i1_ser <= rx_i1_t;
+ rx_q1_ser <= rx_q1_t;
+ end
+
+ // Select the correct Rx output based on MIMO setting
+ if (r_mimo) begin
+ rx_i0_out <= rx_i0_t;
+ rx_q0_out <= rx_q0_t;
+ rx_i1_out <= rx_i1_t;
+ rx_q1_out <= rx_q1_t;
+ end else begin
+ rx_i0_out <= rx_i0_ser;
+ rx_q0_out <= rx_q0_ser;
+ rx_i1_out <= rx_i1_ser;
+ rx_q1_out <= rx_q1_ser;
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Synchronize Rx to radio_clk Domain
+ //---------------------------------------------------------------------------
+ //
+ // This crosses the radio data from the radio_clk_1x domain to the radio_clk
+ // domain. We use the falling edge of radio_clk to allow for the BUFG
+ // insertion delay.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [11:0] rx_i0_fall;
+ reg [11:0] rx_q0_fall;
+ reg [11:0] rx_i1_fall;
+ reg [11:0] rx_q1_fall;
+
+ always @(negedge radio_clk)
+ begin
+ rx_i0_fall <= rx_i0_out;
+ rx_q0_fall <= rx_q0_out;
+ rx_i1_fall <= rx_i1_out;
+ rx_q1_fall <= rx_q1_out;
+ end
+
+ // Re-clock data on the rising edge to present the whole period to external IP
+ always @(posedge radio_clk)
+ begin
+ rx_i0 <= rx_i0_fall;
+ rx_q0 <= rx_q0_fall;
+ rx_i1 <= rx_i1_fall;
+ rx_q1 <= rx_q1_fall;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Tx MIMO/SISO Deserialization
+ //---------------------------------------------------------------------------
+ //
+ // This block of code takes the serialized output from the radios and
+ // parallelizes it onto the two radio ports of the Catalina interface. It
+ // also takes the radio data, output on the radio_clk domain, and crosses it
+ // to the radio_clk_1x domain.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [11:0] tx_i0_del;
+ reg [11:0] tx_q0_del;
+ reg [11:0] tx_i1_del;
+ reg [11:0] tx_q1_del;
+
+ always @(posedge radio_clk_2x)
+ begin
+ // Capture copy of the data delayed by one radio_clk_2c cycle.
+ tx_i0_del <= tx_i0;
+ tx_q0_del <= tx_q0;
+ tx_i1_del <= tx_i1;
+ tx_q1_del <= tx_q1;
+ end
+
+ always @(posedge radio_clk_1x)
+ begin
+ if (r_mimo) begin
+ // In MIMO mode, radio_clk is radio_clk_1x, so we just capture the same
+ // data for each radio_clk_1x cycle.
+ tx_i0_t <= tx_i0;
+ tx_q0_t <= tx_q0;
+ tx_i1_t <= tx_i1;
+ tx_q1_t <= tx_q1;
+ end else begin
+ // In SISO mode, data is updated every radio_clk_2x cycle, so we output
+ // the data from the previous radio_clk_2x cycle onto channel 0 and the
+ // data from the current radio_clk_2x cycle onto channel 1. This puts the
+ // data in the correct order when in 1R1T mode.
+ if (r_tx_ch == 0) begin
+ tx_i0_t <= tx_i0_del;
+ tx_q0_t <= tx_q0_del;
+ tx_i1_t <= tx_i0;
+ tx_q1_t <= tx_q0;
+ end else begin
+ tx_i0_t <= tx_i1_del;
+ tx_q0_t <= tx_q1_del;
+ tx_i1_t <= tx_i1;
+ tx_q1_t <= tx_q1;
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Catalina TX/RX Interface
+ //---------------------------------------------------------------------------
+
+ wire rx_aligned_t;
+ wire [11:0] rx_i0_t;
+ wire [11:0] rx_q0_t;
+ wire [11:0] rx_i1_t;
+ wire [11:0] rx_q1_t;
+
+ reg [11:0] tx_i0_t;
+ reg [11:0] tx_q0_t;
+ reg [11:0] tx_i1_t;
+ reg [11:0] tx_q1_t;
+
+ cat_io_lvds #(
+ .INVERT_FRAME_RX (0),
+ .INVERT_DATA_RX (6'b00_0000),
+ .INVERT_FRAME_TX (0),
+ .INVERT_DATA_TX (6'b00_0000),
+ .USE_CLOCK_IDELAY (USE_CLOCK_IDELAY),
+ .USE_DATA_IDELAY (USE_DATA_IDELAY),
+ .DATA_IDELAY_MODE (DATA_IDELAY_MODE),
+ .CLOCK_IDELAY_MODE (CLOCK_IDELAY_MODE),
+ .INPUT_CLOCK_DELAY (INPUT_CLOCK_DELAY),
+ .INPUT_DATA_DELAY (INPUT_DATA_DELAY),
+ .USE_CLOCK_ODELAY (USE_CLOCK_ODELAY),
+ .USE_DATA_ODELAY (USE_DATA_ODELAY),
+ .DATA_ODELAY_MODE (DATA_ODELAY_MODE),
+ .CLOCK_ODELAY_MODE (CLOCK_ODELAY_MODE),
+ .OUTPUT_CLOCK_DELAY (OUTPUT_CLOCK_DELAY),
+ .OUTPUT_DATA_DELAY (OUTPUT_DATA_DELAY),
+ .USE_BUFG (0)
+ ) cat_io_lvds_i0 (
+ .rst (rst),
+ .clk200 (clk200),
+
+ // Data and frame timing
+ .mimo (1), // Set to 1 to always return all samples
+ .frame_sample (~r_mimo), // Frame timing corresponds to SISO/MIMO setting
+
+ // Delay control interface
+ .ctrl_clk (ctrl_clk),
+ //
+ .ctrl_in_data_delay (ctrl_in_data_delay),
+ .ctrl_in_clk_delay (ctrl_in_clk_delay),
+ .ctrl_ld_in_data_delay (ctrl_ld_in_data_delay),
+ .ctrl_ld_in_clk_delay (ctrl_ld_in_clk_delay),
+ //
+ .ctrl_out_data_delay (ctrl_out_data_delay),
+ .ctrl_out_clk_delay (ctrl_out_clk_delay),
+ .ctrl_ld_out_data_delay (ctrl_ld_out_data_delay),
+ .ctrl_ld_out_clk_delay (ctrl_ld_out_clk_delay),
+
+ // Baseband sample interface
+ .radio_clk (radio_clk_1x),
+ .radio_clk_2x (radio_clk_2x),
+ .rx_aligned (rx_aligned_t),
+ //
+ .rx_i0 (rx_i0_t),
+ .rx_q0 (rx_q0_t),
+ .rx_i1 (rx_i1_t),
+ .rx_q1 (rx_q1_t),
+ //
+ .tx_i0 (tx_i0_t),
+ .tx_q0 (tx_q0_t),
+ .tx_i1 (tx_i1_t),
+ .tx_q1 (tx_q1_t),
+
+ // Catalina interface
+ .rx_clk_p (rx_clk_p),
+ .rx_clk_n (rx_clk_n),
+ .rx_frame_p (rx_frame_p),
+ .rx_frame_n (rx_frame_n),
+ .rx_d_p (rx_d_p),
+ .rx_d_n (rx_d_n),
+ //
+ .tx_clk_p (tx_clk_p),
+ .tx_clk_n (tx_clk_n),
+ .tx_frame_p (tx_frame_p),
+ .tx_frame_n (tx_frame_n),
+ .tx_d_p (tx_d_p),
+ .tx_d_n (tx_d_n)
+ );
+
+endmodule // cat_io_lvds_dual_mode
diff --git a/fpga/usrp3/lib/io_cap_gen/cat_output_lvds.v b/fpga/usrp3/lib/io_cap_gen/cat_output_lvds.v
new file mode 100644
index 000000000..2c412d7f3
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/cat_output_lvds.v
@@ -0,0 +1,396 @@
+//
+// Copyright 2016 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: cat_output_lvds
+// Description: Transmit interface to AD9361 in LVDS mode.
+//
+// The frame_sample signal controls the expected frame signal timing. When
+// frame_sample is 0, the period of the ddr_frame signal will be equal to two
+// samples (e.g., one from each channel). When frame_sample is 1, the frame
+// period will be equal to the length of one sample. This allows the module to
+// be used for 2R2T (frame_sample = 1) or 1R1T mode (frame_sample = 0).
+
+
+module cat_output_lvds #(
+ parameter INVERT_FRAME_TX = 0,
+ parameter INVERT_DATA_TX = 6'b00_0000,
+ parameter USE_CLOCK_DELAY = 1,
+ parameter USE_DATA_DELAY = 1,
+ parameter CLOCK_DELAY_MODE = "VAR_LOAD",
+ parameter DATA_DELAY_MODE = "VAR_LOAD",
+ parameter CLOCK_DELAY = 0,
+ parameter DATA_DELAY = 0,
+ parameter WIDTH = 6,
+ parameter GROUP = "DEFAULT"
+) (
+ input clk200,
+ input rst,
+
+ // Data and frame timing (synchronous to radio_clk)
+ input frame_sample, // Two samples per frame period (frame_sample=0) or one sample per frame (frame_sample=1)
+
+ // Region local Clocks for I/O cells.
+ input ddr_clk,
+ input sdr_clk,
+
+ // Source synchronous external input clock
+ output ddr_clk_p,
+ output ddr_clk_n,
+
+ // Source synchronous data lines
+ output [WIDTH-1:0] ddr_data_p,
+ output [WIDTH-1:0] ddr_data_n,
+ output ddr_frame_p,
+ output ddr_frame_n,
+
+ // Delay control interface
+ input ctrl_clk,
+ input [4:0] ctrl_data_delay,
+ input [4:0] ctrl_clk_delay,
+ input ctrl_ld_data_delay,
+ input ctrl_ld_clk_delay,
+
+ // Global input clock
+ input radio_clk,
+
+ // SDR data buses
+ input [(WIDTH*2)-1:0] i0,
+ input [(WIDTH*2)-1:0] q0,
+ input [(WIDTH*2)-1:0] i1,
+ input [(WIDTH*2)-1:0] q1
+
+);
+
+
+ //------------------------------------------------------------------
+ // UG471 says take reset high asynchronously, and de-assert
+ // synchronized to CLKDIV (sdr_clk) for SERDES.
+ //------------------------------------------------------------------
+ reg rst_sdr_sync;
+
+ always @(posedge sdr_clk or posedge rst)
+ if (rst)
+ rst_sdr_sync <= 1'b1;
+ else
+ rst_sdr_sync <= 1'b0;
+
+ //------------------------------------------------------------------
+ // Route radio data to SERDES for SISO and MIMO modes.
+ //------------------------------------------------------------------
+ reg [(WIDTH*2)-1:0] radio_data_i0, radio_data_q0;
+ reg [(WIDTH*2)-1:0] radio_data_i1, radio_data_q1;
+ reg [(WIDTH*2)-1:0] data_i0, data_q0, data_i1, data_q1;
+ //
+ //
+ always @(posedge radio_clk)
+ begin
+ // When in 2R2T mode, data mapping is the same for SISO and MIMO. The data
+ // for channel 1 will be ignored.
+ radio_data_i0 <= i0;
+ radio_data_q0 <= q0;
+ radio_data_i1 <= i1;
+ radio_data_q1 <= q1;
+ end
+
+ //
+ // Cross data into sdr_clock domain.
+ // sdr_clock leads radio_clk in phase by insertion delay of BUFG.
+ // We can transfer data in this direction with no special logic.
+ // Path length must be radio_clk period - BUFG delay to make timing.
+ //
+ always @(posedge sdr_clk)
+ begin
+ data_i0 <= radio_data_i0 ^ {INVERT_DATA_TX,INVERT_DATA_TX};
+ data_q0 <= radio_data_q0 ^ {INVERT_DATA_TX,INVERT_DATA_TX};
+ data_i1 <= radio_data_i1 ^ {INVERT_DATA_TX,INVERT_DATA_TX};
+ data_q1 <= radio_data_q1 ^ {INVERT_DATA_TX,INVERT_DATA_TX};
+ end
+
+ //------------------------------------------------------------------
+ // Clock output
+ //------------------------------------------------------------------
+ wire ddr_clk_out, ddr_clk_dly;
+
+ OSERDESE2 #(
+ .DATA_RATE_OQ ("DDR"), // DDR, SDR
+ .DATA_RATE_TQ ("DDR"), // DDR, BUF, SDR
+ .DATA_WIDTH (8), // Parallel data width (2-8,10,14)
+ .INIT_OQ (1'b0), // Initial value of OQ output (1'b0,1'b1)
+ .INIT_TQ (1'b0), // Initial value of TQ output (1'b0,1'b1)
+ .SERDES_MODE ("MASTER"), // MASTER, SLAVE
+ .SRVAL_OQ (1'b0), // OQ output value when SR is used (1'b0,1'b1)
+ .SRVAL_TQ (1'b0), // TQ output value when SR is used (1'b0,1'b1)
+ .TBYTE_CTL ("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
+ .TBYTE_SRC ("FALSE"), // Tristate byte source (FALSE, TRUE)
+ .TRISTATE_WIDTH (1) // 3-state converter width (1,4)
+ ) ddr_clk_oserdese2 (
+ .OFB (), // High-speed data output to ODELAYE2
+ .OQ (ddr_clk_out), // High-speed data output direct to OBUF
+ // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
+ .SHIFTOUT1 (),
+ .SHIFTOUT2 (),
+ .TBYTEOUT (),
+ .TFB (),
+ .TQ (),
+ .CLK (ddr_clk),
+ .CLKDIV (sdr_clk),
+ // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
+ .D1 (1'b1), // Canned Clock waveform synthesized as data.
+ .D2 (1'b0),
+ .D3 (1'b1),
+ .D4 (1'b0),
+ .D5 (1'b1),
+ .D6 (1'b0),
+ .D7 (1'b1),
+ .D8 (1'b0),
+ .OCE (1'b1), // Active high clock enable
+ .RST (rst_sdr_sync),
+ // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
+ .SHIFTIN1 (1'b0),
+ .SHIFTIN2 (1'b0),
+ // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
+ .T1 (1'b0),
+ .T2 (1'b0),
+ .T3 (1'b0),
+ .T4 (1'b0),
+ .TBYTEIN (1'b0),
+ .TCE (1'b0)
+ );
+
+ generate
+ if (USE_CLOCK_DELAY) begin : gen_clock_odelay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ ODELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("ODATAIN"), // Delay input (ODATAIN, CLKIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .ODELAY_TYPE (CLOCK_DELAY_MODE), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
+ .ODELAY_VALUE (CLOCK_DELAY), // Output delay tap setting (0-31)
+ .PIPE_SEL ("FALSE"), // Select pipelined mode, FALSE, TRUE
+ .REFCLK_FREQUENCY (200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
+ .SIGNAL_PATTERN ("CLOCK") // DATA, CLOCK input signal
+ ) ddr_clk_odelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_clk_dly), // 1-bit output: Delayed data/clock output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CLKIN (1'b0), // 1-bit input: Clock delay input
+ .CNTVALUEIN (ctrl_clk_delay), // 5-bit input: Counter value input
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_clk_delay), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
+ // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
+ .LDPIPEEN (1'b0), // 1-bit input: Enables the pipeline register to load data
+ .ODATAIN (ddr_clk_out), // 1-bit input: Output delay data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end else begin
+ assign ddr_clk_dly = ddr_clk_out;
+ end
+ endgenerate
+
+
+
+ OBUFDS ddr_clk_obuf (
+ .O (ddr_clk_p), // Diff_p output (connect directly to top-level port)
+ .OB (ddr_clk_n), // Diff_n output (connect directly to top-level port)
+ .I (ddr_clk_dly) // Buffer input
+ );
+
+
+
+ //------------------------------------------------------------------
+ // Frame Signal
+ //------------------------------------------------------------------
+ wire ddr_frame, ddr_frame_dly;
+
+ OSERDESE2 #(
+ .DATA_RATE_OQ ("DDR"), // DDR, SDR
+ .DATA_RATE_TQ ("DDR"), // DDR, BUF, SDR
+ .DATA_WIDTH (8), // Parallel data width (2-8,10,14)
+ .INIT_OQ (1'b0), // Initial value of OQ output (1'b0,1'b1)
+ .INIT_TQ (1'b0), // Initial value of TQ output (1'b0,1'b1)
+ .SERDES_MODE ("MASTER"), // MASTER, SLAVE
+ .SRVAL_OQ (1'b0), // OQ output value when SR is used (1'b0,1'b1)
+ .SRVAL_TQ (1'b0), // TQ output value when SR is used (1'b0,1'b1)
+ .TBYTE_CTL ("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
+ .TBYTE_SRC ("FALSE"), // Tristate byte source (FALSE, TRUE)
+ .TRISTATE_WIDTH (1) // 3-state converter width (1,4)
+ ) ddr_frame_oserdese2 (
+ .OFB (), // High-speed data output to ODELAYE2
+ .OQ (ddr_frame), // High-speed data output direct to OBUF
+ // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
+ .SHIFTOUT1 (),
+ .SHIFTOUT2 (),
+ .TBYTEOUT (),
+ .TFB (),
+ .TQ (),
+ .CLK (ddr_clk),
+ .CLKDIV (sdr_clk),
+ // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each). Frame is
+ // either 11110000 or 11001100 depending on if frame_sample is true or
+ // false, respectively, and it can be inverted by INVERT_FRAME_TX, becoming
+ // 00001111 or 00110011.
+ .D1 (~INVERT_FRAME_TX[0]),
+ .D2 (~INVERT_FRAME_TX[0]),
+ .D3 (INVERT_FRAME_TX[0] ~^ frame_sample),
+ .D4 (INVERT_FRAME_TX[0] ~^ frame_sample),
+ .D5 (INVERT_FRAME_TX[0] ^ frame_sample),
+ .D6 (INVERT_FRAME_TX[0] ^ frame_sample),
+ .D7 (INVERT_FRAME_TX[0]),
+ .D8 (INVERT_FRAME_TX[0]),
+ .OCE (1'b1), // Active high clock enable
+ .RST (rst_sdr_sync),
+ // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
+ .SHIFTIN1 (1'b0),
+ .SHIFTIN2 (1'b0),
+ // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
+ .T1 (1'b0),
+ .T2 (1'b0),
+ .T3 (1'b0),
+ .T4 (1'b0),
+ .TBYTEIN (1'b0),
+ .TCE (1'b0)
+ );
+
+ generate
+ if (USE_DATA_DELAY) begin : gen_frame_odelay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ ODELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("ODATAIN"), // Delay input (ODATAIN, CLKIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .ODELAY_TYPE (DATA_DELAY_MODE), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
+ .ODELAY_VALUE (DATA_DELAY), // Output delay tap setting (0-31)
+ .PIPE_SEL ("FALSE"), // Select pipelined mode, FALSE, TRUE
+ .REFCLK_FREQUENCY (200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
+ .SIGNAL_PATTERN ("DATA") // DATA, CLOCK input signal
+ ) ddr_frame_odelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_frame_dly), // 1-bit output: Delayed data/clock output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CLKIN (1'b0), // 1-bit input: Clock delay input
+ .CNTVALUEIN (ctrl_data_delay), // 5-bit input: Counter value input
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_data_delay), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
+ // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
+ .LDPIPEEN (1'b0), // 1-bit input: Enables the pipeline register to load data
+ .ODATAIN (ddr_frame), // 1-bit input: Output delay data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end else begin
+ assign ddr_frame_dly = ddr_frame;
+ end
+ endgenerate
+
+ OBUFDS ddr_frame_obuf (
+ .O (ddr_frame_p), // Diff_p output (connect directly to top-level port)
+ .OB (ddr_frame_n), // Diff_n output (connect directly to top-level port)
+ .I (ddr_frame_dly) // Buffer input
+ );
+
+
+ //------------------------------------------------------------------
+ // Data Bus
+ //------------------------------------------------------------------
+ wire [WIDTH-1:0] ddr_data;
+ wire [WIDTH-1:0] ddr_data_dly ;
+
+
+ // wire [(WIDTH*2)-1:0] sdr_data_i;
+ // wire [(WIDTH*2)-1:0] sdr_data_q;
+
+ genvar i;
+ generate
+ for (i=0 ; i<WIDTH ; i=i+1) begin : generate_data_bus
+
+ OSERDESE2 #(
+ .DATA_RATE_OQ ("DDR"), // DDR, SDR
+ .DATA_RATE_TQ ("DDR"), // DDR, BUF, SDR
+ .DATA_WIDTH (8), // Parallel data width (2-8,10,14)
+ .INIT_OQ (1'b0), // Initial value of OQ output (1'b0,1'b1)
+ .INIT_TQ (1'b0), // Initial value of TQ output (1'b0,1'b1)
+ .SERDES_MODE ("MASTER"), // MASTER, SLAVE
+ .SRVAL_OQ (1'b0), // OQ output value when SR is used (1'b0,1'b1)
+ .SRVAL_TQ (1'b0), // TQ output value when SR is used (1'b0,1'b1)
+ .TBYTE_CTL ("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
+ .TBYTE_SRC ("FALSE"), // Tristate byte source (FALSE, TRUE)
+ .TRISTATE_WIDTH (1) // 3-state converter width (1,4)
+ ) ddr_data_oserdese2 (
+ .OFB (), // High-speed data output to ODELAYE2
+ .OQ (ddr_data[i]), // High-speed data output direct to OBUF
+ // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
+ .SHIFTOUT1 (),
+ .SHIFTOUT2 (),
+ .TBYTEOUT (),
+ .TFB (),
+ .TQ (),
+ .CLK (ddr_clk),
+ .CLKDIV (sdr_clk),
+ // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
+ .D1 (data_i0[WIDTH+i]),
+ .D2 (data_q0[WIDTH+i]),
+ .D3 (data_i0[i]),
+ .D4 (data_q0[i]),
+ .D5 (data_i1[WIDTH+i]),
+ .D6 (data_q1[WIDTH+i]),
+ .D7 (data_i1[i]),
+ .D8 (data_q1[i]),
+ .OCE (1'b1), // Active high clock enable
+ .RST (rst_sdr_sync),
+ // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
+ .SHIFTIN1 (1'b0),
+ .SHIFTIN2 (1'b0),
+ // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
+ .T1 (1'b0),
+ .T2 (1'b0),
+ .T3 (1'b0),
+ .T4 (1'b0),
+ .TBYTEIN (1'b0),
+ .TCE (1'b0)
+ );
+
+ if (USE_DATA_DELAY) begin : gen_data_odelay
+ (* IODELAY_GROUP = GROUP *) // Specifies group name for associated IDELAYs/ODELAYs and IDELAYCTRL
+ ODELAYE2 #(
+ .CINVCTRL_SEL ("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
+ .DELAY_SRC ("ODATAIN"), // Delay input (ODATAIN, CLKIN)
+ .HIGH_PERFORMANCE_MODE ("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
+ .ODELAY_TYPE (DATA_DELAY_MODE), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
+ .ODELAY_VALUE (DATA_DELAY), // Output delay tap setting (0-31)
+ .PIPE_SEL ("FALSE"), // Select pipelined mode, FALSE, TRUE
+ .REFCLK_FREQUENCY (200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
+ .SIGNAL_PATTERN ("DATA") // DATA, CLOCK input signal
+ ) ddr_data_odelaye2 (
+ .CNTVALUEOUT (), // 5-bit output: Counter value output
+ .DATAOUT (ddr_data_dly[i]), // 1-bit output: Delayed data/clock output
+ .C (ctrl_clk), // 1-bit input: Clock input
+ .CE (1'b0), // 1-bit input: Active high enable increment/decrement input
+ .CINVCTRL (1'b0), // 1-bit input: Dynamic clock inversion input
+ .CLKIN (1'b0), // 1-bit input: Clock delay input
+ .CNTVALUEIN (ctrl_data_delay), // 5-bit input: Counter value input
+ .INC (1'b0), // 1-bit input: Increment / Decrement tap delay input
+ .LD (ctrl_ld_data_delay), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
+ // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
+ .LDPIPEEN (1'b0), // 1-bit input: Enables the pipeline register to load data
+ .ODATAIN (ddr_data[i]), // 1-bit input: Output delay data input
+ .REGRST (1'b0) // 1-bit input: Active-high reset tap-delay input
+ );
+ end else begin
+ assign ddr_data_dly[i] = ddr_data[i];
+ end
+
+ OBUFDS ddr_data_obuf (
+ .O (ddr_data_p[i]), // Diff_p output (connect directly to top-level port)
+ .OB (ddr_data_n[i]), // Diff_n output (connect directly to top-level port)
+ .I (ddr_data_dly[i]) // Buffer input
+ );
+ end // block: generate_data_bus
+
+ endgenerate
+
+endmodule // cat_output_lvds
diff --git a/fpga/usrp3/lib/io_cap_gen/catcap_ddr_cmos.v b/fpga/usrp3/lib/io_cap_gen/catcap_ddr_cmos.v
new file mode 100644
index 000000000..bb4c77f0d
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/catcap_ddr_cmos.v
@@ -0,0 +1,95 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module catcap_ddr_cmos
+#(
+ parameter DEVICE = "7SERIES" // "7SERIES" or "SPARTAN6"
+)
+(
+ input data_clk,
+ input mimo,
+ input rx_frame,
+ input [11:0] rx_d,
+ output rx_clk, output rx_strobe,
+ output reg [11:0] i0, output reg [11:0] q0,
+ output reg [11:0] i1, output reg [11:0] q1
+);
+
+ wire [11:0] i,q;
+ wire frame_0, frame_1;
+
+ genvar z;
+
+ generate
+ for(z = 0; z < 12; z = z + 1)
+ begin : gen_pins
+ if (DEVICE == "SPARTAN6") begin
+ // i[] & q[] swapped
+ IDDR2 #(
+ .DDR_ALIGNMENT("C0"))
+ iddr2 (
+ .Q0(q[z]), .Q1(i[z]), .C0(data_clk), .C1(~data_clk),
+ .CE(1'b1), .D(rx_d[z]), .R(1'b0), .S(1'b0));
+ end
+ else if (DEVICE == "7SERIES") begin
+ IDDR #(
+ .DDR_CLK_EDGE("SAME_EDGE"))
+ iddr (
+ .Q1(q[z]), .Q2(i[z]), .C(data_clk),
+ .CE(1'b1), .D(rx_d[z]), .R(1'b0), .S(1'b0));
+ end
+ end
+ endgenerate
+
+ generate
+ if (DEVICE == "SPARTAN6") begin
+ IDDR2 #(
+ .DDR_ALIGNMENT("C0"))
+ iddr2_frame (
+ .Q0(frame_0), .Q1(frame_1), .C0(data_clk), .C1(~data_clk),
+ .CE(1'b1), .D(rx_frame), .R(1'b0), .S(1'b0));
+ end
+ else if (DEVICE == "7SERIES") begin
+ IDDR #(
+ .DDR_CLK_EDGE("SAME_EDGE"))
+ iddr_frame (
+ .Q1(frame_0), .Q2(frame_1), .C(data_clk),
+ .CE(1'b1), .D(rx_frame), .R(1'b0), .S(1'b0));
+ end
+ endgenerate
+
+ reg frame_d1, frame_d2;
+ always @(posedge data_clk)
+ if(~mimo)
+ { frame_d2, frame_d1 } <= { frame_1, 1'b0 };
+ else
+ { frame_d2, frame_d1 } <= { frame_d1, frame_1 };
+
+ assign rx_strobe = frame_d2;
+
+ reg [11:0] i_del, q_del;
+ always @(posedge data_clk)
+ if(mimo)
+ if(frame_0) begin
+ i_del <= i;
+ q_del <= q;
+ end
+ else begin
+ i1 <= i;
+ q1 <= q;
+ i0 <= i_del;
+ q0 <= q_del;
+ end
+ else begin
+ i0 <= i;
+ q0 <= q;
+ i1 <= i;
+ q1 <= q;
+ end
+ assign rx_clk = data_clk;
+
+endmodule // catcap_ddr_cmos
diff --git a/fpga/usrp3/lib/io_cap_gen/catcodec_ddr_cmos.v b/fpga/usrp3/lib/io_cap_gen/catcodec_ddr_cmos.v
new file mode 100644
index 000000000..44aa4d8f9
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/catcodec_ddr_cmos.v
@@ -0,0 +1,123 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module catcodec_ddr_cmos
+#(
+ parameter DEVICE = "SPARTAN6" // "7SERIES" or "SPARTAN6", determines device specific implementation of clock divider
+)
+(
+ //output source sync clock for baseband data
+ output radio_clk,
+
+ //async reset for clocking
+ input arst,
+
+ //control mimo mode
+ input mimo,
+
+ //baseband sample interface
+ output reg [31:0] rx1,
+ output reg [31:0] rx2,
+ input [31:0] tx1,
+ input [31:0] tx2,
+
+ //capture interface
+ input rx_clk,
+ input rx_frame,
+ input [11:0] rx_d,
+
+ //generate interface
+ output tx_clk,
+ output tx_frame,
+ output [11:0] tx_d
+);
+
+ wire clk0, clkdv;
+ wire codec_clk, half_clk;
+ wire radio_clk_locked;
+
+ // Synchronize MIMO signal into codec_clk domain
+ wire mimo_r;
+ synchronizer mimo_sync (
+ .clk(codec_clk),
+ .rst(1'b0),
+ .in(mimo),
+ .out(mimo_r));
+
+ generate
+ if (DEVICE == "SPARTAN6") begin
+ DCM_SP #(
+ .CLKDV_DIVIDE(2),
+ .CLK_FEEDBACK("1X"))
+ DCM_SP_codec_clk (
+ .RST(arst),
+ .CLKIN(rx_clk), .CLKFB(clk0),
+ .CLK0(clk0), .CLKDV(clkdv),
+ .LOCKED(radio_clk_locked));
+ BUFG BUFG_codec_clk(.I(clk0), .O(codec_clk));
+ BUFG BUFG_half_clk(.I(clkdv), .O(half_clk));
+ BUFGMUX BUFGMUX_radio_clk (.I0(codec_clk), .I1(half_clk), .S(mimo_r), .O(radio_clk));
+ end
+ else if (DEVICE == "7SERIES") begin
+ wire rx_clk_ibufg, clkfb_out, clkfb_in;
+ // Create clocks for source synchronous interface
+ // Goal is to create a capture clock (codec_clk) and a sample clock (radio_clk).
+ // - Capture clock's and source clock's (rx_clk) phase are aligned due to
+ // the MMCM's deskew ability (see the BUFG in the feedback clock path).
+ // - BUFGCTRL muxes between the 1x and 1/2x clocks depending on MIMO mode. In MIMO mode, the 1/2x
+ // clock is used, because the sample clock rate is half the source clock rate.
+ // - Locked signal is used to ensure the BUFG's output is disabled if the MMCM is not locked.
+ // - Avoided cascading BUFGs to ensure minimal skew between codec_clk and radio_clk.
+ catcodec_mmcm inst_catcodec_mmcm (
+ .CLK_IN1(rx_clk_ibufg),
+ .CLK_OUT(clk0),
+ .CLK_OUT_DIV2(clkdv),
+ .CLKFB_IN(clkfb_in),
+ .CLKFB_OUT(clkfb_out),
+ .RESET(arst),
+ .LOCKED(radio_clk_locked));
+ IBUFG (.I(rx_clk), .O(rx_clk_ibufg));
+ BUFG (.I(clkfb_out), .O(clkfb_in));
+ BUFGCE (.I(clk0), .O(codec_clk), .CE(radio_clk_locked));
+ BUFGCTRL BUFGCTRL_radio_clk (.I0(clk0), .I1(clkdv), .S0(~mimo_r), .S1(mimo_r), .CE0(radio_clk_locked), .CE1(radio_clk_locked), .O(radio_clk));
+ end
+ endgenerate
+
+ //assign baseband sample interfaces
+ //all samples are registered on strobe
+ wire rx_strobe, tx_strobe;
+ wire [11:0] rx_i0, rx_q0, rx_i1, rx_q1;
+ reg [11:0] tx_i0, tx_q0, tx_i1, tx_q1;
+ //tx mux to feed single channel mode from either input
+ wire [31:0] txm = (mimo_r || (tx1 != 32'b0))? tx1: tx2;
+ always @(posedge codec_clk) begin
+ if (rx_strobe) rx2 <= {rx_i1, 4'b0, rx_q1, 4'b0};
+ if (rx_strobe) rx1 <= {rx_i0, 4'b0, rx_q0, 4'b0};
+ if (tx_strobe) {tx_i0, tx_q0} <= {txm[31:20], txm[15:4]};
+ if (tx_strobe) {tx_i1, tx_q1} <= {tx2[31:20], tx2[15:4]};
+ end
+
+ // CMOS Data interface to AD9361
+ catcap_ddr_cmos #(
+ .DEVICE(DEVICE))
+ catcap (
+ .data_clk(codec_clk), .mimo(mimo_r),
+ .rx_frame(rx_frame), .rx_d(rx_d),
+ .rx_clk(/*out*/), .rx_strobe(rx_strobe),
+ .i0(rx_i0), .q0(rx_q0),
+ .i1(rx_i1), .q1(rx_q1));
+
+ catgen_ddr_cmos #(
+ .DEVICE(DEVICE))
+ catgen (
+ .data_clk(tx_clk), .mimo(mimo_r),
+ .tx_frame(tx_frame), .tx_d(tx_d),
+ .tx_clk(codec_clk), .tx_strobe(tx_strobe),
+ .i0(tx_i0), .q0(tx_q0),
+ .i1(tx_i1), .q1(tx_q1));
+
+endmodule // catcodec_ddr_cmos
diff --git a/fpga/usrp3/lib/io_cap_gen/catgen_ddr_cmos.v b/fpga/usrp3/lib/io_cap_gen/catgen_ddr_cmos.v
new file mode 100644
index 000000000..017a2f432
--- /dev/null
+++ b/fpga/usrp3/lib/io_cap_gen/catgen_ddr_cmos.v
@@ -0,0 +1,90 @@
+//
+// Copyright 2014 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module catgen_ddr_cmos
+#(
+ parameter DEVICE = "7SERIES" // "7SERIES" or "SPARTAN6"
+)
+(
+ output data_clk,
+ input mimo,
+ output tx_frame,
+ output [11:0] tx_d,
+ input tx_clk,
+ output reg tx_strobe,
+ input [11:0] i0,
+ input [11:0] q0,
+ input [11:0] i1,
+ input [11:0] q1
+);
+
+ reg [11:0] i,q;
+ genvar z;
+ reg tx_strobe_d;
+
+ generate
+ for(z = 0; z < 12; z = z + 1)
+ begin : gen_pins
+ if (DEVICE == "SPARTAN6") begin
+ ODDR2 #(
+ .DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
+ oddr2 (
+ .Q(tx_d[z]), .C0(tx_clk), .C1(~tx_clk),
+ .CE(1'b1), .D0(i[z]), .D1(q[z]), .R(1'b0), .S(1'b0));
+ end
+ else if (DEVICE == "7SERIES") begin
+ ODDR #(
+ .DDR_CLK_EDGE("SAME_EDGE"), .SRTYPE("ASYNC"))
+ oddr (
+ .Q(tx_d[z]), .C(tx_clk),
+ .CE(1'b1), .D1(i[z]), .D2(q[z]), .R(1'b0), .S(1'b0));
+ end
+ end
+ endgenerate
+
+ generate
+ if (DEVICE == "SPARTAN6") begin
+ ODDR2 #(
+ .DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
+ oddr2_frame (
+ .Q(tx_frame), .C0(tx_clk), .C1(~tx_clk),
+ .CE(1'b1), .D0(tx_strobe_d), .D1(mimo&tx_strobe_d), .R(1'b0), .S(1'b0));
+
+ ODDR2 #(
+ .DDR_ALIGNMENT("C0"), .SRTYPE("ASYNC"))
+ oddr2_clk (
+ .Q(data_clk), .C0(tx_clk), .C1(~tx_clk),
+ .CE(1'b1), .D0(1'b1), .D1(1'b0), .R(1'b0), .S(1'b0));
+ end
+ else if (DEVICE == "7SERIES") begin
+ ODDR #(
+ .DDR_CLK_EDGE("SAME_EDGE"), .SRTYPE("ASYNC"))
+ oddr_frame (
+ .Q(tx_frame), .C(tx_clk),
+ .CE(1'b1), .D1(tx_strobe_d), .D2(mimo&tx_strobe_d), .R(1'b0), .S(1'b0));
+
+ ODDR #(
+ .DDR_CLK_EDGE("SAME_EDGE"), .SRTYPE("ASYNC"))
+ oddr_clk (
+ .Q(data_clk), .C(tx_clk),
+ .CE(1'b1), .D1(1'b1), .D2(1'b0), .R(1'b0), .S(1'b0));
+ end
+ endgenerate
+
+ always @(posedge tx_clk)
+ tx_strobe <= (mimo)? ~tx_strobe : 1'b1;
+
+ always @(posedge tx_clk)
+ tx_strobe_d <= tx_strobe;
+
+ always @(posedge tx_clk)
+ if(tx_strobe)
+ {i,q} <= {i0,q0};
+ else
+ {i,q} <= {i1,q1};
+
+endmodule // catgen_ddr_cmos