1//
2// Copyright 2011-2014 Ettus Research LLC
3// Copyright 2017 Ettus Research, a National Instruments Company
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6//
7
8// The two clocks are aligned externally in order to eliminate the need for a FIFO.
9// A FIFO cannot be used to transition between clock domains because it can cause
10// alignment issues between the output of multiple modules.
11
12module capture_ddrlvds #(
13  parameter WIDTH            = 14,          //Width of the SS data bus
14  parameter PATT_CHECKER     = "FALSE",     //{TRUE, FALSE}: Is the integrated ramp pattern checker
15  parameter DATA_IDELAY_MODE = "BYPASSED",  //{BYPASSED, FIXED, DYNAMIC}
16  parameter DATA_IDELAY_VAL  = 16,          //IDELAY value for FIXED mode. In DYNAMIC mode, this value is used by the timing analyzer
17  parameter DATA_IDELAY_FREF = 200.0        //Reference clock frequency for the IDELAYCTRL
18) (
19  // ADC IO Pins
20  input             adc_clk_p,
21  input             adc_clk_n,
22  input [WIDTH-1:0] adc_data_p,
23  input [WIDTH-1:0] adc_data_n,
24
25  //System synchronous clock
26  input             radio_clk,
27
28  //IDELAY settings
29  input             data_delay_stb,
30  input [4:0]       data_delay_val,
31
32  //Capture clock and output data
33  output            adc_cap_clk,
34  output [(2*WIDTH)-1:0] data_out,
35
36  //Pattern checker options (sync to radio_clk)
37  input             checker_en,
38  output [3:0]      checker_locked,
39  output [3:0]      checker_failed
40);
41
42  //-------------------------------------------------------------------
43  // Clock Path
44
45  wire adc_buf_clk;
46
47  // Route source synchronous clock to differential input clock buffer
48  // then to a global clock buffer. We route to a global buffer because
49  // the data bus being capture spans multiple banks.
50  IBUFGDS ss_clk_ibufgds_i (
51    .I(adc_clk_p), .IB(adc_clk_n),
52    .O(adc_buf_clk)
53  );
54
55  BUFG ss_clk_bufg_i (
56    .I(adc_buf_clk),
57    .O(adc_cap_clk)
58  );
59
60  //-------------------------------------------------------------------
61  // Data Path
62
63  wire [WIDTH-1:0]     adc_data_buf, adc_data_del;
64  wire [(2*WIDTH)-1:0] adc_data_aclk;
65  reg  [(2*WIDTH)-1:0] adc_data_rclk, adc_data_rclk_sync;
66
67  genvar i;
68  generate for(i = 0; i < WIDTH; i = i + 1) begin : gen_lvds_pins
69
70    // Use a differential IO buffer to get the data into the IOB
71    IBUFDS ibufds_i (
72      .I(adc_data_p[i]), .IB(adc_data_n[i]),
73      .O(adc_data_buf[i])
74    );
75
76    // Use an optional IDELAY to tune the capture interface from
77    // software. This is a clock to data delay calibration so all
78    // data bits are delayed by the same amount.
79    if (DATA_IDELAY_MODE != "BYPASSED") begin
80      // Pipeline IDELAY control signals to ease routing
81      reg       data_delay_stb_reg;
82      reg [4:0] data_delay_val_reg;
83      always @(posedge radio_clk)
84        {data_delay_stb_reg, data_delay_val_reg} <= {data_delay_stb, data_delay_val};
85
86      IDELAYE2 #(
87        .DELAY_SRC("IDATAIN"),              // Delay input (IDATAIN, DATAIN)
88        .IDELAY_TYPE(DATA_IDELAY_MODE=="FIXED"?"FIXED":"VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
89        .SIGNAL_PATTERN("DATA"),            // DATA, CLOCK input signal
90        .HIGH_PERFORMANCE_MODE("TRUE"),     // Reduced jitter ("TRUE"), Reduced power ("FALSE")
91        .PIPE_SEL("FALSE"),                 // Select pipelined mode, FALSE, TRUE
92        .CINVCTRL_SEL("FALSE"),             // Enable dynamic clock inversion (FALSE, TRUE)
93        .IDELAY_VALUE(DATA_IDELAY_VAL),     // Input delay tap setting (0-31)
94        .REFCLK_FREQUENCY(DATA_IDELAY_FREF) // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
95      ) idelay_i (
96        .DATAIN(1'b0),                      // Internal delay data input
97        .IDATAIN(adc_data_buf[i]),          // Data input from the I/O
98        .DATAOUT(adc_data_del[i]),          // Delayed data output
99        .C(radio_clk),                      // Clock input
100        .LD(data_delay_stb_reg),            // Load IDELAY_VALUE input
101        .CE(1'b0),                          // Active high enable increment/decrement input
102        .INC(1'b0),                         // Increment / Decrement tap delay input
103        .CINVCTRL(1'b0),                    // Dynamic clock inversion input
104        .CNTVALUEIN(data_delay_val_reg),    // Counter value input
105        .CNTVALUEOUT(),                     // Counter value output
106        .LDPIPEEN(1'b0),                    // Enable PIPELINE register to load data input
107        .REGRST(1'b0)                       // Reset for the pipeline register.Only used in VAR_LOAD_PIPE mode.
108      );
109    end else begin
110      assign adc_data_del[i] = adc_data_buf[i];
111    end
112
113    // Use the global ADC clock to capture delayed data into an IDDR.
114    // Each IQ sample is transferred in QDR mode i.e. odd and even on
115    // a rising and falling edge of the clock
116    IDDR #(
117      .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")
118    ) iddr_i (
119      .C(adc_cap_clk), .CE(1'b1),
120      .D(adc_data_del[i]), .R(1'b0), .S(1'b0),
121      .Q1(adc_data_aclk[2*i]), .Q2(adc_data_aclk[(2*i)+1])
122    );
123  end endgenerate
124
125  // Transfer data from the source-synchronous ADC clock domian to the
126  // system synchronous radio clock domain. We assume that adc_cap_clk
127  // and radio_clk are generated from the same source and have the same
128  // frequency however, they have an unknown but constant phase offset.
129  // In order to cross domains, we use a simple synchronizer to avoid any
130  // sample-sample delay uncertainty introduced by FIFOs.
131  // NOTE: The path between adc_data_aclk and adc_data_rclk must be
132  //       constrained to prevent build to build variations. Also, the
133  //       phase of the two clocks must be aligned ensure that the data
134  //       capture is safe
135  always @(posedge radio_clk)
136    {adc_data_rclk_sync, adc_data_rclk} <= {adc_data_rclk, adc_data_aclk};
137
138  // The synchronized output is the output of this module
139  assign data_out = adc_data_rclk_sync;
140
141  //-------------------------------------------------------------------
142  // Checkers
143
144  generate if (PATT_CHECKER == "TRUE") begin
145    wire        checker_en_aclk;
146    wire [1:0]  checker_locked_aclk, checker_failed_aclk;
147
148    synchronizer #(.INITIAL_VAL(1'b0)) checker_en_aclk_sync_i (
149      .clk(adc_cap_clk), .rst(1'b0), .in(checker_en), .out(checker_en_aclk));
150    synchronizer #(.INITIAL_VAL(1'b0)) checker_locked_aclk_0_sync_i (
151      .clk(radio_clk), .rst(1'b0), .in(checker_locked_aclk[0]), .out(checker_locked[0]));
152    synchronizer #(.INITIAL_VAL(1'b0)) checker_locked_aclk_1_sync_i (
153      .clk(radio_clk), .rst(1'b0), .in(checker_locked_aclk[1]), .out(checker_locked[1]));
154    synchronizer #(.INITIAL_VAL(1'b0)) checker_failed_aclk_0_sync_i (
155      .clk(radio_clk), .rst(1'b0), .in(checker_failed_aclk[0]), .out(checker_failed[0]));
156    synchronizer #(.INITIAL_VAL(1'b0)) checker_failed_aclk_1_sync_i (
157      .clk(radio_clk), .rst(1'b0), .in(checker_failed_aclk[1]), .out(checker_failed[1]));
158
159    cap_pattern_verifier #(   // Q Channel : Synchronous to SSCLK
160      .WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
161      .RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
162    ) aclk_q_checker_i (
163      .clk(adc_cap_clk), .rst(~checker_en_aclk),
164      .valid(1'b1), .data(~adc_data_aclk[WIDTH-1:0]),
165      .count(), .errors(),
166      .locked(checker_locked_aclk[0]), .failed(checker_failed_aclk[0])
167    );
168
169    cap_pattern_verifier #(   // I Channel : Synchronous to SSCLK
170      .WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
171      .RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
172    ) aclk_i_checker_i (
173      .clk(adc_cap_clk), .rst(~checker_en_aclk),
174      .valid(1'b1), .data(~adc_data_aclk[(2*WIDTH)-1:WIDTH]),
175      .count(), .errors(),
176      .locked(checker_locked_aclk[1]), .failed(checker_failed_aclk[1])
177    );
178
179    cap_pattern_verifier #(   // Q Channel : Synchronous to Radio CLK
180      .WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
181      .RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
182    ) rclk_q_checker_i (
183      .clk(radio_clk), .rst(~checker_en),
184      .valid(1'b1), .data(~adc_data_rclk_sync[WIDTH-1:0]),
185      .count(), .errors(),
186      .locked(checker_locked[2]), .failed(checker_failed[2])
187    );
188
189    cap_pattern_verifier #(   // I Channel : Synchronous to Radio CLK
190      .WIDTH(WIDTH), .PATTERN("RAMP"), .HOLD_CYCLES(1),
191      .RAMP_START(0), .RAMP_STOP({WIDTH{1'b1}}), .RAMP_INCR(1)
192    ) rclk_i_checker_i (
193      .clk(radio_clk), .rst(~checker_en),
194      .valid(1'b1), .data(~adc_data_rclk_sync[(2*WIDTH)-1:WIDTH]),
195      .count(), .errors(),
196      .locked(checker_locked[3]), .failed(checker_failed[3])
197    );
198  end endgenerate
199
200endmodule // capture_ddrlvds
201