FPGA实现千兆/百兆自适应以太网UDP传输
0、前言
筆者最近在項目中需要使用到ZYNQ中PL端做以太網UDP傳輸并且需要支持100M/1000M自適應切換。使用的PHY型號為RTL8211。以下分享的主要為利用已有的1000M協議棧修改為100M并且實現二者自適應切換,IP核主要實現以下功能
1、實現100M/1000M自適應
2、回環測試
PS:完整的IP核文件下載地址:https://download.csdn.net/download/qq_24025329/87019436
1、軟硬件環境和前置條件
筆者采用的接口是RGMII接口,即100M模式下單邊沿采樣,時鐘頻率為25M。在1000M模式下使用雙邊沿采樣,時鐘頻率為125M。所以在千兆模式下需要使用原語對數據采樣,這里就不過多贅述了,這里默認已經擁有了可以實現1000M通訊的協議棧了。
2、實現步驟
第一步:千兆-百兆接收數據轉換模塊
這里筆者偷了個懶,既然已經有了千兆模式下讀取的數據,那么我們只需要根據千兆與百兆之間的采樣模式進行轉換就可以讀出來正確的數據了。具體思想如下
1、千兆為雙邊沿采樣,百兆為單邊沿采樣。所以在千兆模式RGMII轉換為GMII接口下跑百兆的速度讀出來的8bit數據中高四位=低四位的。所以我們將讀出來的數據只保留四位即可。下圖為千兆模式下的采樣
?2、時鐘采用PHY提供的時鐘,即百兆模式下25M千兆模式下為125M即可。具體實現代碼如下
`timescale 1ns / 1ps module eth_speed(input Rst_n, //系統復位//以太網GMII接口input gmii_rx_clk , //GMII接收時鐘input gmii_rx_dv , //GMII接收數據有效信號input [7:0] gmii_rxd , //GMII接收數據//速度轉換后以太網GMII接口output gmii_rx_clk_s , //GMII接收時鐘output gmii_rx_dv_s , //GMII接收數據有效信號output reg[7:0] gmii_rxd_s //GMII接收數據); //==========參數定義=============// parameter SPEED = 1000;//1000Mbps//==========寄存器定義===========// reg out_clk; //輸出時鐘 reg count_s; //二分數據計數 reg data0_en; reg data1_en; //數據有效位//==========組合邏輯運算=============// assign gmii_rx_clk_s = out_clk;//輸出時鐘二分頻 assign gmii_rx_dv_s = data0_en && data1_en;//接收數據有效//==========時序邏輯============// //輸入時鐘二分頻 always @(posedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)out_clk <= 1'd0; elseout_clk <= ~out_clk;//二分數據計數 //第一個數據來臨后的第一個下降沿=1 // always @(negedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)count_s <= 1'd0; else if(!gmii_rx_dv)//數據無效 清零count_s <= 1'd0; elsecount_s = ~count_s;//反轉//采集數據1 下降沿采集數據 always @(negedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)gmii_rxd_s[3:0] <= 4'd0; else if(gmii_rx_dv && (!count_s))//接收數據有效并且二分頻時鐘位低電平gmii_rxd_s[3:0] <= gmii_rxd[3:0];//采集數據2 下降沿采集數據 always @(negedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)gmii_rxd_s[7:4] <= 4'd0; else if(gmii_rx_dv && (count_s))//接收數據有效并且二分頻時鐘位低電平gmii_rxd_s[7:4] <= gmii_rxd[3:0];//采集數據1有效位 always @(posedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)data0_en <= 1'd0; else if(!count_s) //第一個數據采樣的周期if(gmii_rx_dv) //有效data0_en <= 1'd1;elsedata0_en <= 1'd0; elsedata0_en <= data0_en;//采集數據2有效位 always @(posedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)data1_en <= 1'd0; else if(count_s) //第二個數據采樣的周期if(gmii_rx_dv) //有效data1_en <= 1'd1;elsedata1_en <= 1'd0; elsedata1_en <= data1_en;endmodule第二步:千兆-百兆接收數據轉換模塊
發送方面就不能偷懶了,只能自己寫了一個百兆模式下的發送,代碼如下
`timescale 1ns / 1psmodule eth_speed_tx(input Rst_n,input gmii_rx_clk, //來自PHY的時鐘//獲得數據接口input [7:0] data_in, //GMII數據input data_en, //GMII使能output data_clk, //GMII時鐘,用于獲取發送數據,是RGMII的1/2//RGMII接口output rgmii_txc, //RGMII的時鐘output reg[3:0]rgmii_txd, //RGMII發送的數據output rgmii_tx_ctl //RGMII發送控制 );//========寄存器定義===========// reg rgmii_txc_2_s;//發送時鐘的二分頻 并滯后90° reg en_delay;//輸入使能延遲//==========邏輯==============// assign data_clk = rgmii_txc_2_s; assign rgmii_tx_ctl = data_en|en_delay; assign rgmii_txc = gmii_rx_clk;//==========時序邏輯==========// //時鐘二分頻 用于獲得數據 并滯后90° always@(negedge rgmii_txc or negedge Rst_n) if(!Rst_n)rgmii_txc_2_s <= 1'd0; elsergmii_txc_2_s <= ~rgmii_txc_2_s;//發送數據賦值 always @(negedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)rgmii_txd <= 4'd0; else if(data_en && rgmii_txc_2_s)//要發送第一個4bit數據 低位rgmii_txd <= data_in[3:0]; else if(data_en && (!rgmii_txc_2_s))//要發送第二個4bit數據 高位rgmii_txd <= data_in[7:4]; elsergmii_txd <= rgmii_txd;//使能延遲 always @(negedge gmii_rx_clk or negedge Rst_n) if(!Rst_n)en_delay <= 1'd0; else if(data_en && (!rgmii_txc_2_s))en_delay <= 1'd1; elseen_delay <= 1'd0;endmodule第三步:百兆千兆切換邏輯
百兆和千兆模式下切換本質上就是切換RGMII的接口信號源,具體的邏輯較為簡單如圖所示
?其中SPEED為選擇開關,該值可以是定義的參數,也可以當作接口有外部提供。
最后
該IP核是筆者在項目中解決自己的問題所編寫,水平有限如有疏漏敬請指正。
總結
以上是生活随笔為你收集整理的FPGA实现千兆/百兆自适应以太网UDP传输的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VUE 拦截浏览器后退弹窗,弹窗一闪立刻
- 下一篇: 大数据环境中资源优化配置策略研究(非原创