aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x300/capture_ddrlvds.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x300/capture_ddrlvds.v')
-rw-r--r--fpga/usrp3/top/x300/capture_ddrlvds.v200
1 files changed, 200 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x300/capture_ddrlvds.v b/fpga/usrp3/top/x300/capture_ddrlvds.v
new file mode 100644
index 000000000..f5bd5a4a0
--- /dev/null
+++ b/fpga/usrp3/top/x300/capture_ddrlvds.v
@@ -0,0 +1,200 @@
+//
+// 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