Files
b210-k7-fpga/top/x300/capture_ddrlvds.v
T
+18 6b67702ad7 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
2020-01-28 09:35:36 -08:00

201 lines
8.5 KiB
Verilog

//
// Copyright 2011-2014 Ettus Research LLC
// Copyright 2017 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// The two clocks are aligned externally in order to eliminate the need for a FIFO.
// A FIFO cannot be used to transition between clock domains because it can cause
// alignment issues between the output of multiple modules.
module capture_ddrlvds #(
parameter WIDTH = 14, //Width of the SS data bus
parameter PATT_CHECKER = "FALSE", //{TRUE, FALSE}: Is the integrated ramp pattern checker
parameter DATA_IDELAY_MODE = "BYPASSED", //{BYPASSED, FIXED, DYNAMIC}
parameter DATA_IDELAY_VAL = 16, //IDELAY value for FIXED mode. In DYNAMIC mode, this value is used by the timing analyzer
parameter DATA_IDELAY_FREF = 200.0 //Reference clock frequency for the IDELAYCTRL
) (
// ADC IO Pins
input adc_clk_p,
input adc_clk_n,
input [WIDTH-1:0] adc_data_p,
input [WIDTH-1:0] adc_data_n,
//System synchronous clock
input radio_clk,
//IDELAY settings
input data_delay_stb,
input [4:0] data_delay_val,
//Capture clock and output data
output adc_cap_clk,
output [(2*WIDTH)-1:0] data_out,
//Pattern checker options (sync to radio_clk)
input checker_en,
output [3:0] checker_locked,
output [3:0] checker_failed
);
//-------------------------------------------------------------------
// Clock Path
wire adc_buf_clk;
// Route source synchronous clock to differential input clock buffer
// then to a global clock buffer. We route to a global buffer because
// the data bus being capture spans multiple banks.
IBUFGDS ss_clk_ibufgds_i (
.I(adc_clk_p), .IB(adc_clk_n),
.O(adc_buf_clk)
);
BUFG ss_clk_bufg_i (
.I(adc_buf_clk),
.O(adc_cap_clk)
);
//-------------------------------------------------------------------
// Data Path
wire [WIDTH-1:0] adc_data_buf, adc_data_del;
wire [(2*WIDTH)-1:0] adc_data_aclk;
reg [(2*WIDTH)-1:0] adc_data_rclk, adc_data_rclk_sync;
genvar i;
generate for(i = 0; i < WIDTH; i = i + 1) begin : gen_lvds_pins
// Use a differential IO buffer to get the data into the IOB
IBUFDS ibufds_i (
.I(adc_data_p[i]), .IB(adc_data_n[i]),
.O(adc_data_buf[i])
);
// Use an optional IDELAY to tune the capture interface from
// software. This is a clock to data delay calibration so all
// data bits are delayed by the same amount.
if (DATA_IDELAY_MODE != "BYPASSED") begin
// Pipeline IDELAY control signals to ease routing
reg data_delay_stb_reg;
reg [4:0] data_delay_val_reg;
always @(posedge radio_clk)
{data_delay_stb_reg, data_delay_val_reg} <= {data_delay_stb, data_delay_val};
IDELAYE2 #(
.DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
.IDELAY_TYPE(DATA_IDELAY_MODE=="FIXED"?"FIXED":"VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
.SIGNAL_PATTERN("DATA"), // DATA, CLOCK input signal
.HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.PIPE_SEL("FALSE"), // Select pipelined mode, FALSE, TRUE
.CINVCTRL_SEL("FALSE"), // Enable dynamic clock inversion (FALSE, TRUE)
.IDELAY_VALUE(DATA_IDELAY_VAL), // Input delay tap setting (0-31)
.REFCLK_FREQUENCY(DATA_IDELAY_FREF) // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
) idelay_i (
.DATAIN(1'b0), // Internal delay data input
.IDATAIN(adc_data_buf[i]), // Data input from the I/O
.DATAOUT(adc_data_del[i]), // Delayed data output
.C(radio_clk), // Clock input
.LD(data_delay_stb_reg), // Load IDELAY_VALUE input
.CE(1'b0), // Active high enable increment/decrement input
.INC(1'b0), // Increment / Decrement tap delay input
.CINVCTRL(1'b0), // Dynamic clock inversion input
.CNTVALUEIN(data_delay_val_reg), // Counter value input
.CNTVALUEOUT(), // Counter value output
.LDPIPEEN(1'b0), // Enable PIPELINE register to load data input
.REGRST(1'b0) // Reset for the pipeline register.Only used in VAR_LOAD_PIPE mode.
);
end else begin
assign adc_data_del[i] = adc_data_buf[i];
end
// Use the global ADC clock to capture delayed data into an IDDR.
// Each IQ sample is transferred in QDR mode i.e. odd and even on
// a rising and falling edge of the clock
IDDR #(
.DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
) iddr_i (
.C(adc_cap_clk), .CE(1'b1),
.D(adc_data_del[i]), .R(1'b0), .S(1'b0),
.Q1(adc_data_aclk[2*i]), .Q2(adc_data_aclk[(2*i)+1])
);
end endgenerate
// Transfer data from the source-synchronous ADC clock domian to the
// system synchronous radio clock domain. We assume that adc_cap_clk
// and radio_clk are generated from the same source and have the same
// frequency however, they have an unknown but constant phase offset.
// In order to cross domains, we use a simple synchronizer to avoid any
// sample-sample delay uncertainty introduced by FIFOs.
// NOTE: The path between adc_data_aclk and adc_data_rclk must be
// constrained to prevent build to build variations. Also, the
// phase of the two clocks must be aligned ensure that the data
// capture is safe
always @(posedge radio_clk)
{adc_data_rclk_sync, adc_data_rclk} <= {adc_data_rclk, adc_data_aclk};
// The synchronized output is the output of this module
assign data_out = adc_data_rclk_sync;
//-------------------------------------------------------------------
// Checkers
generate if (PATT_CHECKER == "TRUE") begin
wire checker_en_aclk;
wire [1:0] checker_locked_aclk, checker_failed_aclk;
synchronizer #(.INITIAL_VAL(1'b0)) checker_en_aclk_sync_i (
.clk(adc_cap_clk), .rst(1'b0), .in(checker_en), .out(checker_en_aclk));
synchronizer #(.INITIAL_VAL(1'b0)) checker_locked_aclk_0_sync_i (
.clk(radio_clk), .rst(1'b0), .in(checker_locked_aclk[0]), .out(checker_locked[0]));
synchronizer #(.INITIAL_VAL(1'b0)) checker_locked_aclk_1_sync_i (
.clk(radio_clk), .rst(1'b0), .in(checker_locked_aclk[1]), .out(checker_locked[1]));
synchronizer #(.INITIAL_VAL(1'b0)) checker_failed_aclk_0_sync_i (
.clk(radio_clk), .rst(1'b0), .in(checker_failed_aclk[0]), .out(checker_failed[0]));
synchronizer #(.INITIAL_VAL(1'b0)) checker_failed_aclk_1_sync_i (
.clk(radio_clk), .rst(1'b0), .in(checker_failed_aclk[1]), .out(checker_failed[1]));
cap_pattern_verifier #( // Q Channel : Synchronous to SSCLK
.WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
.RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
) aclk_q_checker_i (
.clk(adc_cap_clk), .rst(~checker_en_aclk),
.valid(1'b1), .data(~adc_data_aclk[WIDTH-1:0]),
.count(), .errors(),
.locked(checker_locked_aclk[0]), .failed(checker_failed_aclk[0])
);
cap_pattern_verifier #( // I Channel : Synchronous to SSCLK
.WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
.RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
) aclk_i_checker_i (
.clk(adc_cap_clk), .rst(~checker_en_aclk),
.valid(1'b1), .data(~adc_data_aclk[(2*WIDTH)-1:WIDTH]),
.count(), .errors(),
.locked(checker_locked_aclk[1]), .failed(checker_failed_aclk[1])
);
cap_pattern_verifier #( // Q Channel : Synchronous to Radio CLK
.WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
.RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
) rclk_q_checker_i (
.clk(radio_clk), .rst(~checker_en),
.valid(1'b1), .data(~adc_data_rclk_sync[WIDTH-1:0]),
.count(), .errors(),
.locked(checker_locked[2]), .failed(checker_failed[2])
);
cap_pattern_verifier #( // I Channel : Synchronous to Radio CLK
.WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
.RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
) rclk_i_checker_i (
.clk(radio_clk), .rst(~checker_en),
.valid(1'b1), .data(~adc_data_rclk_sync[(2*WIDTH)-1:WIDTH]),
.count(), .errors(),
.locked(checker_locked[3]), .failed(checker_failed[3])
);
end endgenerate
endmodule // capture_ddrlvds