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 了;而我就沒法得到速度參數。
如圖,接受資料
百兆
千兆
如圖,發送資料
百兆
千兆