Merge FPGA repository back into UHD repository

The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.

This commit also updates the license files and paths therein.

We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.

== Original Codebase and Rebasing ==

The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/

It can be used for bisecting, reference, and a more detailed history.

The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.

If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:

- Create a directory to store patches (this should be an empty
  directory):

    mkdir ~/patches

- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:

    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge

  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.

- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:

    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches

  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.

- Go to the UHD repository and apply the patches:

    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup

== Contributors ==

The following people have contributed mainly to these files (this list
is not complete):

Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>


Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
This commit is contained in:
Martin Braun
2020-01-28 09:35:36 -08:00
co-authored by Alex Williams Andrej Rode Ashish Chaudhari Ben Hilburn Ciro Nishiguchi Daniel Jepson Derek Kozel EJ Kreinar Humberto Jimenez Ian Buckley Jörg Hofrichter Jon Kiser Josh Blum Jonathon Pendlum Matt Ettus Michael West Moritz Fischer Nick Foster Nicolas Cuervo Paul Butler Paul David Ryan Marlow Sugandha Gupta Sylvain Munaut Trung Tran Vidush Vishwanath Wade Fife
parent 74893643ca
commit 6b67702ad7
2157 changed files with 1282567 additions and 0 deletions
+23
View File
@@ -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 \
))
+125
View File
@@ -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
+609
View File
@@ -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
+200
View File
@@ -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
+397
View File
@@ -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
+396
View File
@@ -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
+95
View File
@@ -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
+123
View File
@@ -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
+90
View File
@@ -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