天天看點

Xilinx的IP核gig_ethernet_pcs_pma例化案例1G/2.5G Ethernet PCS/PMA or SGMII v16.1

Xilinx的IP核gig_ethernet_pcs_pma例化案例

  • 1G/2.5G Ethernet PCS/PMA or SGMII v16.1
    • IP核例化
    • 計數開頭的55,判斷1000M或者100M
    • 以太網資料異步緩存FIFO
    • 總結

1G/2.5G Ethernet PCS/PMA or SGMII v16.1

廢話不BB,直接用。手冊看完,一禮拜沒了解透。了解出現偏差。最後實驗出真知。

IP核例化

gig_ethernet_pcs_pma_0 gig_ethernet_pcs_pma_0 (
  .gtrefclk_p				(gtrefclk1_p), //不廢話  
  .gtrefclk_n				(gtrefclk1_n), //不廢話  
  .gtrefclk_out				(),                      	
  .gtrefclk_bufg_out		(),           				
  .txn						(sfp_tx_n),    //不廢話  
  .txp						(sfp_tx_p),    //不廢話
  .rxn						(sfp_rx_n),    //不廢話    
  .rxp						(sfp_rx_p),    //不廢話    
  .independent_clock_bufg	(clk_200M),  //200M時鐘
  .userclk_out				(),                       	
  .userclk2_out				(gmii_clk),    //125M時鐘,死的,不會變
  .rxuserclk_out			(),                    		
  .rxuserclk2_out			(),                    		
  .resetdone				(),                           
  .pma_reset_out			(),                    		
  .mmcm_locked_out			(),               			
  .sgmii_clk_r				(),                        	
  .sgmii_clk_f				(),                        	
  .sgmii_clk_en				(sgmii_clk_en), //可變脈沖,125M,12.5M,1.25M             
  .gmii_txd					(gmii_txd	),  //發送資料,必須順應百兆或千兆  
  .gmii_tx_en				(gmii_tx_en	),  //發送資料,必須順應百兆或千兆    
  .gmii_tx_er				('b0),        	//甭管,直接置0
  .gmii_rxd					(gmii_rxd	),  //接受資料,随百兆或千兆  
  .gmii_rx_dv				(gmii_rx_dv	),  //接受資料,随百兆或千兆
  .gmii_rx_er				(			),  //不需要          
  .gmii_isolate				(			),  //不需要
  .configuration_vector		(5'b10000	),  //寫死,[4]=1,啟動自協商    		
  .an_interrupt				(an_interrupt), //=1,鍊路已連結,Link up   
  .an_adv_config_vector		(16'h8821	),  //媽的,就是這個坑死人,寫死半雙工千兆,這是我的最佳選擇    		
  .an_restart_config		('b0		),  //一樣坑死人,上面寫死了,不用上升沿觸發重新開機         	
  .speed_is_10_100			(speed_is_10_100), //和下面一起控制sgmii_clk_en的頻率的
  .speed_is_100				(speed_is_100	), //和上面一起控制sgmii_clk_en的頻率的
  .status_vector			(			),     //最坑人,對端是強制端,根本就不反應協商資訊,要了何用           
  .reset					(!rst_n			), //随便複位一下       
  .signal_detect			(1'b1			), //1表示不是光口。           
  .gt0_pll0outclk_out		(),   					      
  .gt0_pll0outrefclk_out	(),   					 	
  .gt0_pll1outclk_out		(),   					      
  .gt0_pll1outrefclk_out	(),   					 	
  .gt0_pll0lock_out			(),              			
  .gt0_pll0refclklost_out	()  						
);	    
           

計數開頭的55,判斷1000M或者100M

對端是強制端。比如設定電腦端為,100M半雙工。即使an_adv_config_vector設定1000M,底層自協商就能連接配接上。自協商結束,an_interrupt=1’b1;但status_vector[11:10]的值完全不反應實際的速率。還是010,千兆的值。實際是百兆。能正常接受資料,gmii_clk一直為125M。是以計數前導碼55,1000M,就是7個55,100M就是70個55.簡單區分百兆和千兆。再去配置speed_is_100 ,speed_is_10_100 。這樣sgmii_clk_en就是我想要的頻率了。如果speed_is_100 = 0 ,speed_is_10_100 = 0;sgmii_clk_en常1;如果speed_is_100 = 1 ,speed_is_10_100 = 1;sgmii_clk_en是12.5M脈沖;詳細見手冊。

這裡做了幾次實驗。如果speed_is_100 = 0 ,speed_is_10_100 = 0;寫死,百兆千兆都能正常接受資料。如果speed_is_100 = 1 ,speed_is_10_100 = 1;寫死,千兆就無法正常接受資料了,表現為,gmii_rxd還是百兆時鐘切換資料。

//  計數開頭的55,判斷1000M或者100M
    reg             gmii_rx_dv_r ;
    always @(posedge gmii_clk)      gmii_rx_dv_r <= gmii_rx_dv ;
    reg         judge_flag ;
    always @(posedge gmii_clk)
        if      (!rst_n)                                                judge_flag <= 'b0 ;
        else if (gmii_rx_dv && !gmii_rx_dv_r)                           judge_flag <= 'b1 ;
        else if (judge_flag && gmii_rxd==8'hd5)                         judge_flag <= 'b0 ;
    reg  [7:0]  cnt_55 ;
    always @(posedge gmii_clk)
        if      (!rst_n)                                                cnt_55 <= 'd0 ;
        else if (judge_flag && gmii_rxd==8'h55)                         cnt_55 <= cnt_55 + 'd1 ;
        else                                                            cnt_55 <= 'd0 ;

    always @(posedge gmii_clk)
        if      (!rst_n)                                                flag_1000M <= 'b0 ;
        else if (judge_flag && gmii_rxd==8'hd5 && cnt_55 < 'd10)        flag_1000M <= 'b1 ;
        else if (judge_flag && gmii_rxd==8'hd5 && cnt_55 > 'd10)        flag_1000M <= 'b0 ;
    always @(posedge gmii_clk)
        if      (!rst_n)                                                flag_100M <= 'b0 ;
        else if (judge_flag && gmii_rxd==8'hd5 && cnt_55 > 'd10)        flag_100M <= 'b1 ;
        else if (judge_flag && gmii_rxd==8'hd5 && cnt_55 < 'd10)        flag_100M <= 'b0 ;
    //  speed_is_100 speed_is_10_100
    reg     speed_is_100 ;
    reg     speed_is_10_100 ;
    always @(posedge gmii_clk)
        if      (!rst_n)        begin speed_is_100 <= 1'b0 ; speed_is_10_100 <= 1'b0 ; end 
        else if (flag_1000M)    begin speed_is_100 <= 1'b0 ; speed_is_10_100 <= 1'b0 ; end 
        else if (flag_100M)     begin speed_is_100 <= 1'b1 ; speed_is_10_100 <= 1'b1 ; end 
           

以太網資料異步緩存FIFO

跨始終域問題。千兆都是125M.沒有問題。百兆就要設計一下了。接受資料,等接受完成後,再讀。發送資料簡單了。

wire  eth_clk = flag_1000M ? gmii_clk : sgmii_clk_en ;   //  125M:12.5M
//	發送資料
fifo_gig_ethernet fifo_gig_ethernet_T (
    .wr_clk     (gmii_clk       ),      // input wire wr_clk
    .din        (eth_tx_data    ),      // input wire [7 : 0] din
    .wr_en      (eth_tx_en      ),      // input wire wr_en
    
    .rd_clk     (eth_clk        ),      // input wire rd_clk
    .rd_en      ('b1            ),      // input wire rd_en
    .dout       (gmii_txd       ),      // output wire [7 : 0] dout
    .valid      (gmii_tx_en     ),      // output wire valid
    
    .full       (               ),      // output wire full
    .empty      (               )       // output wire empty
);
//  接受資料
wire            empty_R ;
reg             rd_en_R ;
always @(posedge gmii_clk)
    if      (!rst_n)                          rd_en_R <= 1'b0 ;
    else if (gmii_rx_dv_r && !gmii_rx_dv)     rd_en_R <= 1'b1 ;  // 開始讀取
    else if (empty_R)                         rd_en_R <= 1'b0 ;
fifo_gig_ethernet fifo_gig_ethernet_R (
    .wr_clk     (eth_clk        ),      // input wire wr_clk
    .din        (gmii_rxd       ),      // input wire [7 : 0] din
    .wr_en      (gmii_rx_dv     ),      // input wire wr_en
    
    .rd_clk     (gmii_clk       ),      // input wire rd_clk
    .rd_en      (rd_en_R        ),      // input wire rd_en
    .dout       (eth_rx_data    ),      // output wire [7 : 0] dout
    .valid      (eth_rx_vaild   ),      // output wire valid
    
    .full       (               ),      // output wire full
    .empty      (empty_R        )       // output wire empty
);
           

總結

這裡就發現坑的地方了。明明an_adv_config_vector設定的千兆。卻能連結上。接受發送資料都沒有問題。底層自協商完成了,已經是百兆的工作模式了。這和我配置an_adv_config_vector沒有半點關系。之前花了好長時間,想去得到協商結果,再去配置an_adv_config_vector,還得an_restart_config上升沿重新開機。實際半毛錢關系都沒。

我的了解是an_adv_config_vector是我希望的最佳工作模式。如果對端也是自協商端,就能接受到我的FLP,得到這個值,我也能在status_vector裡得到對方的能力值。如果對方強制端,不發FLP,我就得不到參數,status_vector的值就是無意義的。但是底層還是通過NLP得知對方是100M,就自協商100M建立了連結。an_interrupt 置 1 了;而我就沒法得到速度參數。

如圖,接受資料

百兆

Xilinx的IP核gig_ethernet_pcs_pma例化案例1G/2.5G Ethernet PCS/PMA or SGMII v16.1

千兆

Xilinx的IP核gig_ethernet_pcs_pma例化案例1G/2.5G Ethernet PCS/PMA or SGMII v16.1

如圖,發送資料

百兆

Xilinx的IP核gig_ethernet_pcs_pma例化案例1G/2.5G Ethernet PCS/PMA or SGMII v16.1

千兆

Xilinx的IP核gig_ethernet_pcs_pma例化案例1G/2.5G Ethernet PCS/PMA or SGMII v16.1