ADS6445開發筆記(2)---- LVDS 高速接口

 

目錄

1.  數據接口時序圖

2 採樣時鐘同步處理

2.1 delayctrl 原語

2.2  idelay2 原語

2.3 ISERDESE2

2.4 手動調節對齊

2.5 自動調節對齊

3. 幀同步信號

4.數據的處理


從上一章節,芯片資料可以看到,串行輸出LVDS數據的bit clock,都是400MHZ以上的。這顯然不能用fpga Verilog 語言直接寫代碼進行採樣,需要用到專門的 iserdese 原語。

1.  數據接口時序圖

數據接口有很多中模式,這裏只貼了一種的圖

上圖可以看到以下幾點:

  • 信號線分3類,數據採集時鐘DCLK,幀同步信號FCLK,輸入數據DATA 
  • 輸入數據採樣時鐘默認是已經對齊了輸入數據的中點,但幀時鐘是和數據字邊緣對齊
  • 使用iserdese接收數據,idelay調整時鐘延遲

2 採樣時鐘同步處理

這裏主要對 採樣時鐘 bitclk  做延遲處理,使其能正確採樣到數據。框圖中的模塊,都不是IP 核,而是原語。在tools---language templates中可以搜索到。

2.1 delayctrl 原語

因FPGA的電壓偏置、製造過程、電壓、溫度的不同,可能會對整個芯片的時序造成一些小的影響。IDELAYCTRL可以通過一個較高頻率的參考時鐘REFCLK爲IDELAY或ODELAY提供延時抽頭,可選0~31。參考時鐘的頻率可選200MHz或者300MHz,它們的每個抽頭的分辨率分別約是78ps和52ps。假設我們的LVDS輸入爲600Mbps,則選用200MHz需要21taps,300MHz需要32taps,所以最終選擇200MHz爲參考時鐘。(1/600M = 1666ps, 1666ps/78ps = 21 ,1666ps/52ps= 32。抽頭個數21更合理)
使用時還需要注意需要對IDELAYCTRL進行LOC約束,實現工具將IDELAYCTRL實例自動複製到整個器件,甚至複製到未使用延遲單元的時鐘區域中。這樣做資源佔用率較高,在每個時鐘區域內都要使用一個全局 時鐘資源,並且使用佈線資源較多,因此功耗較大。參考文檔:輸入輸出延遲單元IODELAY簡介

思考:

1. IDELAYCTRL 的輸入時鐘refclk 決定idelay2 每個抽頭的延遲有多少ps , idelay2 原語,根據器件不同在原語選擇上可以有不同的抽頭,比如CNTVALUEOUT [4:0] 2^5 就是 0 - 31個抽頭。0 就是沒有延遲。

2. IDELAYCTRL 的輸出RDY 代表  IDELAYCTRL使能有效

IDELAYCTRL模塊是爲IDELAY模塊服務的。delayctrl的位置需要手動約束。可以利用plananead找到delayctrl的位置,在Device視圖中,找到約束文件中所定義的delay的位置,就是下圖橙色方塊。在他附近找到delayCtrl,圖中白色的矩形,讀出她的位置信息,再添加到你的約束文件裏就可以了。

2.2  idelay2 原語

 C:  Clock input  ,分頻時鐘

 CNTVALUEIN:   5-bit input, Counter value input 。 這個值控制輸出抽頭個數。比如一個抽頭延遲78ps.這裏輸入2 ,就是延遲78*2 = 156ps .

IDATAIN : 需要延遲的輸入數據,在這裏就是DCLK 時鐘。

DATAOUT :延遲輸出

2.3 ISERDESE2

先說ISERDESE2。該模塊接收外部輸入FPGA的高速源同步串行信號,在FPGA內部將其轉換爲用戶需要的並行數據信號。如圖1所示爲ISERDESE2的功能框圖,咱們可以將按照功能分成5個部分:

 

CLK:  -- 1-bit input: High-speed clock 。DDLY  輸入,邏輯組合後產生 O ,看總框圖,應該是O 是DDLY 直接輸出的結果

CLKB :   ---- High-speed secondary clock   。  CLK  時鐘取反

CLKDIV: ----  經過 BUFR 分頻的時鐘

CLKDIVP:---- 接地

D : 需要對齊的信號

DDLY : 需要對齊的信號經過 DELAY 延遲後的結果

RST : 和 delay_ctrl 復位信號一致

SHIFTIN1   SHIFTIN2: ISERDESE級聯的時候需要用,對接上一個模塊的 SHIFTOUT1 ,SHIFTOUT2

BITSLIP : 這裏沒用到,在frame對齊的時候需要用到

2.4 手動調節對齊

module lvds_data (
   input   I_AD_FPGA_DC_p  ,  
   input   I_AD_FPGA_DC_n  ,
   input   I_ref_clk_200m  ,
   input   I_reset_n  
   );
   
   wire       W0_dc_clk;
     
   wire       W_delay_rdy;
   wire [4:0] W_delay_cnt;
   wire [7:0] W_allign_word;  
   
   wire       W_dc_clk;
   wire       W2_dc_clk ;
   wire       W_fc_clk;
  
   IBUFDS #(
      .DIFF_TERM("TRUE"),        // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst10 (
      .O(W0_dc_clk),            // Buffer output
      .I(I_AD_FPGA_DC_p),       // Diff_p buffer input (connect directly to top-level port)
      .IB(I_AD_FPGA_DC_n)       // Diff_n buffer input (connect directly to top-level port)
   );



   vio_0 vio_u (
     .clk(W_fc_clk),           // input wire clk
     .probe_in0(W_delay_rdy),  // input wire [0 : 0] probe_in0
     .probe_in1(W_allign_word),// input wire [7 : 0] probe_in1
     .probe_out0(W_delay_cnt)  // output wire [4 : 0] probe_out0
   );

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYCTRL IDELAYCTRL_inst1 (
      .RDY(W_delay_rdy),       // 1-bit output: Ready output
      .REFCLK(I_ref_clk_200m), // 1-bit input: Reference clock input
      .RST(~I_reset_n)         // 1-bit input: Active high reset input
   );

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYE2 #(
      .CINVCTRL_SEL("FALSE"),         // Enable dynamic clock inversion (FALSE, TRUE)
      .DELAY_SRC("IDATAIN"),          // Delay input (IDATAIN, DATAIN)
      .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
      .IDELAY_TYPE("VAR_LOAD"),       // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
      .IDELAY_VALUE(0),               // Input 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
   )
   IDELAYE2_inst1 (
      .CNTVALUEOUT(), // 5-bit output: Counter value output
      .DATAOUT(W1_dc_clk),       // 1-bit output: Delayed data output
      .C(W_fc_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(W_delay_cnt),  // 5-bit input: Counter value input
      .DATAIN(1'b0),             // 1-bit input: Internal delay data input
      .IDATAIN(W0_dc_clk),       // 1-bit input: Data input from the I/O
      .INC(1'b0),                // 1-bit input: Increment / Decrement tap delay input
      .LD(1'b1),                 // 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
   );

   BUFIO BUFIO_p (
      .O(W_dc_clk), // 1-bit output: Clock output (connect to I/O clock loads).
      .I(W2_dc_clk)   // 1-bit input: Clock input (connect to an IBUF or BUFMR).
   );

   BUFR #(
      .BUFR_DIVIDE("4"),   // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 
      .SIM_DEVICE("7SERIES")// Must be set to "7SERIES" 
   )
   BUFR_inst (
      .O(W_fc_clk),   // 1-bit output: Clock output port
      .CE(1'b1),      // 1-bit input: Active high, clock enable (Divided modes only)
      .CLR(1'b0),     // 1-bit input: Active high, asynchronous clear (Divided modes only)
      .I(W2_dc_clk)   // 1-bit input: Clock buffer input driven by an IBUF, MMCM or local interconnect
   );

   ISERDESE2 #(
      .DATA_RATE("SDR"),           // 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(1'b0),
      .INIT_Q2(1'b0),
      .INIT_Q3(1'b0),
      .INIT_Q4(1'b0),
      .INTERFACE_TYPE("NETWORKING"),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
      .IOBDELAY("IBUF"),           // NONE, BOTH, IBUF, IFD
      .NUM_CE(2),                  // Number of clock enables (1,2)
      .OFB_USED("FALSE"),          // Select OFB path (FALSE, TRUE)
      .SERDES_MODE("MASTER"),      // MASTER, SLAVE
      .SRVAL_Q1(1'b0),
      .SRVAL_Q2(1'b0),
      .SRVAL_Q3(1'b0),
      .SRVAL_Q4(1'b0) 
   )
   ISERDESE2_inst0 (
      .O(W2_dc_clk),               // 1-bit output: Combinatorial output
      .Q1(W_allign_word[0]),       // Q1 - Q8: 1-bit (each) output: Registered data outputs
      .Q2(W_allign_word[1]),
      .Q3(W_allign_word[2]),
      .Q4(W_allign_word[3]),
      .Q5(W_allign_word[4]),
      .Q6(W_allign_word[5]),
      .Q7(W_allign_word[6]),
      .Q8(W_allign_word[7]),
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .BITSLIP(),           
      .CE1(1'b1),
      .CE2(1'b1),
      .CLKDIVP(1'b0),              // 1-bit input: TBD
      .CLK(W_dc_clk),              // 1-bit input: High-speed clock
      .CLKB(~W_dc_clk),            // 1-bit input: High-speed secondary clock
      .CLKDIV(W_fc_clk),           // 1-bit input: Divided clock
      .OCLK(1'b0),                 // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY" 
      .DYNCLKDIVSEL(1'b0),         // 1-bit input: Dynamic CLKDIV inversion
      .DYNCLKSEL(1'b0),            // 1-bit input: Dynamic CLK/CLKB inversion
      .D(W0_dc_clk),               // 1-bit input: Data input
      .DDLY(W1_dc_clk),            // 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(~I_reset_n),            // 1-bit input: Active high asynchronous reset
      .SHIFTIN1(1'b0),
      .SHIFTIN2(1'b0) 
   );
 endmodule 

 首先來看看手動調節算法,用vivado的vio可以很方便的輸入輸出,可手動在線修改觀察現象,對後面的自動訓練算法也有一定的啓發作用。
默認R_delay_cnt=0時,可以看到輸入的正弦波形很亂

慢慢的增加R_delay_cnt,當R_delay_cnt=12時,開始出現穩定的正弦波,實驗發現R_delay_cnt=14,15,16時恰好採到時鐘的邊緣,也就是跟輸入的原始時鐘對齊了,可以看到採到邊緣是allign_word一直在跳變,有的是0,有的是1。一直到R_delay_cnt=18,正弦波都很穩定。有效窗口可以準確計算出來,200M的Idelay參考時鐘,78ps/tap。7tap*78ps=546ps。說明數據的有效窗口很小,畢竟是320M的DDR,半個週期都才1.56ns

 

 繼續增加R_delay_cnt,當R_delay_cnt=20時,正弦波又變得不規則了。最後取R_delay_cnt=15,可以在代碼裏面寫死。

2.5 自動調節對齊

既然有了手動調節的算法,爲什麼還要用自動訓練對齊的算法呢?在高低溫測試的時候,器件的延遲會受溫度的影響發生變化,特別是在時鐘頻率很高,數據有效窗口很小的時候,這時候就需要能夠動態的改變R_delay_cnt的值去自適應delay的變化,增加了魯棒性。
有了上面的手動調節算法,自動訓練的思路也很簡單了。上電覆位後R_delay_cnt一直自加,記下最後一個全0和第一個全1的值,取中點。這裏只考慮了一種情況,還可能是從全1到全0的情況。代碼如下
 

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2020/06/11 16:23:34
// Design Name: 
// Module Name: lvds_autodelay
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module lvds_autodelay(
   input   I_AD_FPGA_DC_p  ,  
   input   I_AD_FPGA_DC_n  ,
   input   I_ref_clk_200m  ,
   input   I_reset_n  
   );
   
   wire       W0_dc_clk;
     
   wire       W_delay_rdy;
   wire [4:0] W_delay_cnt;
   wire [7:0] W_allign_word;  
   
   wire       W_dc_clk;
   wire       W2_dc_clk ;
   wire       W_fc_clk;
  
   IBUFDS #(
      .DIFF_TERM("TRUE"),        // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst10 (
      .O(W0_dc_clk),            // Buffer output
      .I(I_AD_FPGA_DC_p),       // Diff_p buffer input (connect directly to top-level port)
      .IB(I_AD_FPGA_DC_n)       // Diff_n buffer input (connect directly to top-level port)
   );




reg  [4:0] R_delay_cnt  ;
reg        R_allign_done;
reg  [4:0] R_cnt_low    ;
reg  [4:0] R_cnt_high   ;
reg  [5:0] R_cnt_sum    ;
wire [4:0] W_half_cnt   ;
wire [4:0] W_delay_cnt  ;
wire [7:0] W_allign_word;

//auto allign fsm
//00000000->11111111
//        |
//      half


always @(posedge W_fc_clk or negedge I_reset_n)
begin
  if(~I_reset_n) 
  begin
      R_allign_done <= 0;
  end 
  else if(&W_delay_rdy)
  begin
      R_allign_done <= 0;
  end
  else if(W_allign_word==8'hff)
  begin
      R_allign_done <= 1;
  end
end

always @(posedge W_fc_clk or negedge I_reset_n)
begin
  if(~I_reset_n) 
  begin
      R_delay_cnt <= 0;
  end 
  else if(&W_delay_rdy)
  begin
      R_delay_cnt <= 5'b1;
  end
  else if(~R_allign_done)
  begin
      R_delay_cnt <= R_delay_cnt + 1'b1;
  end
end

always @(posedge W_fc_clk or negedge I_reset_n)
begin
  if(~I_reset_n) 
  begin
      R_cnt_low <= 0;
  end 
  else if(W_allign_word==8'h00)
  begin
      R_cnt_low <= R_delay_cnt;
  end
end

always @(posedge W_fc_clk or negedge I_reset_n)
begin
  if(~I_reset_n) 
  begin
      R_cnt_high <= 0;
  end 
  else if(W_allign_word==8'hff)
  begin
      R_cnt_high <= R_delay_cnt;
  end
end
always @(posedge W_fc_clk or negedge I_reset_n)
begin
  if(~I_reset_n) 
  begin
      R_cnt_sum <= 0;
  end 
  else if(R_allign_done)
  begin
      R_cnt_sum <= {{R_cnt_low[4],R_cnt_low} + {R_cnt_high[4],R_cnt_high}};
  end
end
assign W_half_cnt = R_cnt_sum[5:1];
assign W_delay_cnt = R_allign_done? W_half_cnt: R_delay_cnt;

//   vio_0 vio_u (
//     .clk(W_fc_clk),           // input wire clk
//     .probe_in0(W_delay_rdy),  // input wire [0 : 0] probe_in0
//     .probe_in1(W_allign_word),// input wire [7 : 0] probe_in1
//     .probe_out0(W_delay_cnt)  // output wire [4 : 0] probe_out0
//   );

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYCTRL IDELAYCTRL_inst1 (
      .RDY(W_delay_rdy),       // 1-bit output: Ready output
      .REFCLK(I_ref_clk_200m), // 1-bit input: Reference clock input
      .RST(~I_reset_n)         // 1-bit input: Active high reset input
   );

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYE2 #(
      .CINVCTRL_SEL("FALSE"),         // Enable dynamic clock inversion (FALSE, TRUE)
      .DELAY_SRC("IDATAIN"),          // Delay input (IDATAIN, DATAIN)
      .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
      .IDELAY_TYPE("VAR_LOAD"),       // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
      .IDELAY_VALUE(0),               // Input 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
   )
   IDELAYE2_inst1 (
      .CNTVALUEOUT(), // 5-bit output: Counter value output
      .DATAOUT(W1_dc_clk),       // 1-bit output: Delayed data output
      .C(W_fc_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(W_delay_cnt),  // 5-bit input: Counter value input
      .DATAIN(1'b0),             // 1-bit input: Internal delay data input
      .IDATAIN(W0_dc_clk),       // 1-bit input: Data input from the I/O
      .INC(1'b0),                // 1-bit input: Increment / Decrement tap delay input
      .LD(1'b1),                 // 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
   );

   BUFIO BUFIO_p (
      .O(W_dc_clk), // 1-bit output: Clock output (connect to I/O clock loads).
      .I(W2_dc_clk)   // 1-bit input: Clock input (connect to an IBUF or BUFMR).
   );

   BUFR #(
      .BUFR_DIVIDE("4"),   // Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 
      .SIM_DEVICE("7SERIES")// Must be set to "7SERIES" 
   )
   BUFR_inst (
      .O(W_fc_clk),   // 1-bit output: Clock output port
      .CE(1'b1),      // 1-bit input: Active high, clock enable (Divided modes only)
      .CLR(1'b0),     // 1-bit input: Active high, asynchronous clear (Divided modes only)
      .I(W2_dc_clk)   // 1-bit input: Clock buffer input driven by an IBUF, MMCM or local interconnect
   );

   ISERDESE2 #(
      .DATA_RATE("SDR"),           // 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(1'b0),
      .INIT_Q2(1'b0),
      .INIT_Q3(1'b0),
      .INIT_Q4(1'b0),
      .INTERFACE_TYPE("NETWORKING"),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
      .IOBDELAY("IBUF"),           // NONE, BOTH, IBUF, IFD
      .NUM_CE(2),                  // Number of clock enables (1,2)
      .OFB_USED("FALSE"),          // Select OFB path (FALSE, TRUE)
      .SERDES_MODE("MASTER"),      // MASTER, SLAVE
      .SRVAL_Q1(1'b0),
      .SRVAL_Q2(1'b0),
      .SRVAL_Q3(1'b0),
      .SRVAL_Q4(1'b0) 
   )
   ISERDESE2_inst0 (
      .O(W2_dc_clk),               // 1-bit output: Combinatorial output
      .Q1(W_allign_word[0]),       // Q1 - Q8: 1-bit (each) output: Registered data outputs
      .Q2(W_allign_word[1]),
      .Q3(W_allign_word[2]),
      .Q4(W_allign_word[3]),
      .Q5(W_allign_word[4]),
      .Q6(W_allign_word[5]),
      .Q7(W_allign_word[6]),
      .Q8(W_allign_word[7]),
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .BITSLIP(),           
      .CE1(1'b1),
      .CE2(1'b1),
      .CLKDIVP(1'b0),              // 1-bit input: TBD
      .CLK(W_dc_clk),              // 1-bit input: High-speed clock
      .CLKB(~W_dc_clk),            // 1-bit input: High-speed secondary clock
      .CLKDIV(W_fc_clk),           // 1-bit input: Divided clock
      .OCLK(1'b0),                 // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY" 
      .DYNCLKDIVSEL(1'b0),         // 1-bit input: Dynamic CLKDIV inversion
      .DYNCLKSEL(1'b0),            // 1-bit input: Dynamic CLK/CLKB inversion
      .D(W0_dc_clk),               // 1-bit input: Data input
      .DDLY(W1_dc_clk),            // 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(~I_reset_n),            // 1-bit input: Active high asynchronous reset
      .SHIFTIN1(1'b0),
      .SHIFTIN2(1'b0) 
   );
 endmodule 




3. 幀同步信號

Dclk模塊功能完成之後,用上面產生的數據採樣時鐘同時去採樣FCLK和DATA,只能保證採集到的數據的每一位都是對的,但並不知道一個10bit數據的首尾在哪,fclk模塊就是用來尋找並行數據的正確起始與結束位置,因此業界也稱fclk爲幀時鐘,但它並不是用作時鐘,只用來判斷data數據的位置。要使用一個bit_slip對串轉並的結果進行移位,移位的同時檢測FCLK轉換的輸出,當輸出是8’b11110000的時候就停止移位。

 

      該模塊又稱作Bitslip模塊,Bitslip就專門用來找到用戶需要的並行數據邊界。

    下圖給出了Bitslip是如何確定並行數據的邊界:對於SDR模式,Bitslip使能1次,則數據會左移1次,對於8bit並行數據,移動8次完成一個循環,可以這樣無止境的循環,直到找到用戶定義的並行數據。對於DDR模式,Bitslip工作方式不同,Bitslip使能1次,數據會右移1次或者左移3次,兩者交替進行,同樣移動8次完成一個循環。

思考:

    1) 注意這個模塊沒有對FCLK 進行delay延遲,只是用上面產生的時鐘對FCLK進行採樣。 

   2) 判斷移位輸出的結果W_fc_patten,如果檢測結果是我們想要的數據,則R_bit_slip 拉低,否則R_bit_slip拉高進行上圖的移位操作。

   3)R_wait 的描述,爲什麼要有這個信號呢?爲了減少R_bit_slip 拉高移位的頻率嗎?

 

//-------------------------------------FC handle bit_slip-------------------------------------
   IBUFDS #(
      .DIFF_TERM("TRUE"),        // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst9 (
      .O(W_fc_refclk),          // Buffer output
      .I(I_AD_FPGA_FC_p),       // Diff_p buffer input (connect directly to top-level port)
      .IB(I_AD_FPGA_FC_n)       // Diff_n buffer input (connect directly to top-level port)
   );

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(1'b0),
      .INIT_Q2(1'b0),
      .INIT_Q3(1'b0),
      .INIT_Q4(1'b0),
      .INTERFACE_TYPE("NETWORKING"),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
      .IOBDELAY("NONE"),           // NONE, BOTH, IBUF, IFD
      .NUM_CE(2),                  // Number of clock enables (1,2)
      .OFB_USED("FALSE"),          // Select OFB path (FALSE, TRUE)
      .SERDES_MODE("MASTER"),      // MASTER, SLAVE
      .SRVAL_Q1(1'b0),
      .SRVAL_Q2(1'b0),
      .SRVAL_Q3(1'b0),
      .SRVAL_Q4(1'b0) 
   )
   ISERDESE2_inst9 (
      .O(),                        // 1-bit output: Combinatorial output
      .Q1(W_fc_patten[0]),         // Q1 - Q8: 1-bit (each) output: Registered data outputs
      .Q2(W_fc_patten[1]),
      .Q3(W_fc_patten[2]),
      .Q4(W_fc_patten[3]),
      .Q5(W_fc_patten[4]),
      .Q6(W_fc_patten[5]),
      .Q7(W_fc_patten[6]),
      .Q8(W_fc_patten[7]),
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .BITSLIP(R_bit_slip),           
      .CE1(1'b1),
      .CE2(1'b1),
      .CLKDIVP(1'b0),              // 1-bit input: TBD
      .CLK(W_dc_clk),            // 1-bit input: High-speed clock
      .CLKB(~W_dc_clk),           // 1-bit input: High-speed secondary clock
      .CLKDIV(W_fc_clk),           // 1-bit input: Divided clock
      .OCLK(1'b0),                 // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY" 
      .DYNCLKDIVSEL(1'b0),         // 1-bit input: Dynamic CLKDIV inversion
      .DYNCLKSEL(1'b0),            // 1-bit input: Dynamic CLK/CLKB inversion
      .D(W_fc_refclk),             // 1-bit input: Data input
      .DDLY(1'b0),                 // 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(~I_reset_n),            // 1-bit input: Active high asynchronous reset
      .SHIFTIN1(1'b0),
      .SHIFTIN2(1'b0) 
   );

always @(posedge W_fc_clk or negedge I_reset_n)
begin
    if(~I_reset_n) 
    begin
        R_bit_slip <= 0;
        R_wait <= 0;
    end 
    else
    begin
        if (R_wait==2'd3 && W_fc_patten!=8'b11110000) 
        begin
            R_bit_slip <= 1;
            R_wait <= 2'd1;
        end
        else
        begin
            R_bit_slip <= 0;
            R_wait <= R_wait + 1'd1;
        end
    end
end

 另外一種思路是:用1111判斷對應數據的奇數通道,0000對應數據的偶數通路。如果輸出的14bit數據爲11111111111111,說明找到了fclk的高電平狀態,對應數據的偶數通道,此時取數即得到偶數通道的數據。如果輸出的14bit數據爲00000000000000,說明找到了fclk的低電平狀態,對應數據的奇數通道,此時取數即得到奇數通道的數據

4.數據的處理

數據採樣代碼和上面的差不多

   IBUFDS #(
      .DIFF_TERM("TRUE"),        // Differential Termination
      .IBUF_LOW_PWR("TRUE"),     // Low power="TRUE", Highest performance="FALSE" 
      .IOSTANDARD("DEFAULT")     // Specify the input I/O standard
   ) IBUFDS_inst1 (
      .O(W_data_in1[0]),         // Buffer output
      .I(I_ad_lvds_d0_p[0]),     // Diff_p buffer input (connect directly to top-level port)
      .IB(I_ad_lvds_d0_n[0])     // Diff_n buffer input (connect directly to top-level port)
   );

   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(1'b0),
      .INIT_Q2(1'b0),
      .INIT_Q3(1'b0),
      .INIT_Q4(1'b0),
      .INTERFACE_TYPE("NETWORKING"),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
      .IOBDELAY("NONE"),           // NONE, BOTH, IBUF, IFD
      .NUM_CE(2),                  // Number of clock enables (1,2)
      .OFB_USED("FALSE"),          // Select OFB path (FALSE, TRUE)
      .SERDES_MODE("MASTER"),      // MASTER, SLAVE
      .SRVAL_Q1(1'b0),
      .SRVAL_Q2(1'b0),
      .SRVAL_Q3(1'b0),
      .SRVAL_Q4(1'b0) 
   )
   ISERDESE2_inst1 (
      .O(),                        // 1-bit output: Combinatorial output
      .Q1(W_ad_data1[0]),          // Q1 - Q8: 1-bit (each) output: Registered data outputs
      .Q2(W_ad_data1[1]),
      .Q3(W_ad_data1[2]),
      .Q4(W_ad_data1[3]),
      .Q5(W_ad_data1[4]),
      .Q6(W_ad_data1[5]),
      .Q7(W_ad_data1[6]),
      .Q8(W_ad_data1[7]),
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .BITSLIP(R_bit_slip),           
      .CE1(1'b1),
      .CE2(1'b1),
      .CLKDIVP(1'b0),              // 1-bit input: TBD
      .CLK(W_dc_clk),              // 1-bit input: High-speed clock
      .CLKB(~W_dc_clk),            // 1-bit input: High-speed secondary clock
      .CLKDIV(W_fc_clk),           // 1-bit input: Divided clock
      .OCLK(1'b0),                 // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY" 
      .DYNCLKDIVSEL(1'b0),         // 1-bit input: Dynamic CLKDIV inversion
      .DYNCLKSEL(1'b0),            // 1-bit input: Dynamic CLK/CLKB inversion
      .D(W_data_in1[0]),           // 1-bit input: Data input
      .DDLY(1'b0),                 // 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(~I_reset_n),            // 1-bit input: Active high asynchronous reset
      .SHIFTIN1(1'b0),
      .SHIFTIN2(1'b0) 
   );

另一個思路:

Dclk和fclk的功能完成後,該模塊就只需要取數就就行了,不需要任何控制操作。然後在奇狀態下將取到的數通過fifo1同步到系統時鐘域,在偶狀態下將取到的數通過fifo2同步到系統時鐘域即可,如圖所示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章