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