硬件設計采用RTL8211FS芯片,vitis默認的IWIP庫不支持此芯片。
網口相關知識可以翻看前期文章
以太網PHY_MDIO通信(基于RTL8211)--FPGA學習筆記22-CSDN博客
以太網ARP協議——FPGA學習筆記23_fpga以太網學習-CSDN博客
以太網ICMP協議(ping指令)——FPGA學習筆記25_icmp報文 以太網類型-CSDN博客
以太網UDP協議棧實現(支持ARP、ICMP、UDP)--FPGA學習筆記26_description: 接收arp、icmp、udp三種以太網報文,并對相應的數據進行解析,輸出給-CSDN博客
感謝小梅哥論壇大佬文章
【Zynq】【Lwip】解決頻繁打印link up/down、綠燈不亮、自協商結束后插入網線無反應的問題
https://www.corecourse.cn/forum.php?mod=viewthread&tid=29789
(出處: 芯路恒電子技術論壇)
【Zynq】【Lwip】解決使用官方lwip模板時自動協商失敗的問題
https://www.corecourse.cn/forum.php?mod=viewthread&tid=29166
(出處: 芯路恒電子技術論壇)
?
關于芯片兼容修改問題,重點關注以下函數
void init_emacps(xemacpsif_s *xemacps, struct netif *netif);
static u32_t get_Realtek_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
LWIP環回代碼簡解:
1、板級前置初始化
為芯片分配MAC地址,可修改
初始化平臺,關閉 I/D Cache、初始化 UART、注冊中斷控制器、映射 GEM 寄存器等(板級支持包干的事)。
2、協議棧內存 + 網絡接口數據結構清零
把 lwIP 全局結構(內存池、定時器、PCB 鏈表等)全部清零,為后續添加網卡做準備。
3、向 lwIP 注冊“唯一”的網卡
把前面得到的 MAC、IP、網關、子網掩碼、GEM 基地址打包,創建一個 struct netif
實例 server_netif
,并掛到 lwIP 全局鏈表。
讓 lwIP 后續發送默認走這張網卡。
開啟指定網口
4、定時器與中斷
開啟中斷功能。使能 IRQ,GEM 接收完成中斷、定時器中斷等開始工作。
-
后面主循環會周期性置位:
-
TcpFastTmrFlag = 1
每 250 ms(TCP 快速定時器) -
TcpSlowTmrFlag = 1
每 500 ms(TCP 慢速定時器)
這兩標志在xemacif_input()
內部由中斷服務程序刷新。
-
5、DHCP而客戶端
#if LWIP_DHCP==1
dhcp_start(echo_netif);
啟動 DHCP 狀態機,開始 Discover-Offer-Request-Ack 四步舞。
while((ip_addr == 0) && (dhcp_timoutcntr > 0))
主循環里不斷調用 xemacif_input()
收包,讓 DHCP 狀態機跑起來;24×0.5 s = 12 s 超時。
超時仍未拿到地址就 fallback 到靜態 192.168.1.10。
6、用戶代碼綁定
print_app_header()
// 打印一條提示
start_application()
// 創建 PCB、綁定端口、注冊回調
transfer_data()
// 周期發送或處理數據
一些函數詳解:
void init_emacps(xemacpsif_s *xemacps, struct netif *netif);
Xilinx GEM(PS 端千兆以太網)在 lwIP 下的“一站式硬件啟動器”,把 MAC 控制器、PHY、鏈路速率全部配置到可收發狀態。
1、拿到MAC控制器句柄
xemacps
是 Xilinx 驅動庫XEmacPs
的實例,后續所有寄存器操作都靠它。
2、打開巨型幀 / 組播選項(可選)
- 巨型幀(9018 B)需要提前使能,否則硬件會截斷。
- 組播選項讓 MAC 接受多播哈希表過濾,為 IGMP 服務。
3、把軟件 MAC 地址寫進硬件寄存器
-
第 3 個參數
1
表示使用“地址槽 1”(0 留給特殊幀)。 -
失敗直接打印調試信息并繼續往下走(后面還可改)。
4、配置 MDIO 時鐘
-
根據 CPU 頻率把 MDC 降到 ≤2.5 MHz,滿足 IEEE 802.3 要求。
5、探測并初始化 PHY
分兩條路:
a) 板載 PCS/PMA 1000Base-X 或 SGMII 口
→ 直接調用 phy_setup_emacps(xemacpsp, 固定地址)
→ 地址由 xparameters.h
給出(例如 1 或 7)。
b) 標準 RGMII/MDIO 總線
→ detect_phy()
先把 0-31 號 PHY 掃一遍,把在位 PHY 記錄到數組 phymapemac0/1[]
;
→ 然后對每一個在位地址調用 phy_setup_emacps()
,完成:
- 軟件復位
- 設置自動協商通告(10/100/1000)
- 等待協商完成
- 回傳實際鏈路速率(10/100/1000 Mbps)
?先遍歷 1–31 號地址里所有被標記為 TRUE 的 PHY,全部初始化;如果一個都沒找到,就退而求其次用廣播地址 0 再試一次。
執行完這段代碼后:
-
phyaddrforemac
保存了實際使用的 PHY 地址; -
link_speed
要么等于 10/100/1000,要么標記失敗; -
后續
XEmacPs_SetOperatingSpeed()
就依據link_speed
給 MAC 設速。
void detect_phy(XEmacPs *xemacpsp);
????????在 MDIO 總線上把 0–31 號地址全部掃一遍,找到真正掛著的 PHY
????????(1)先確定是 EMAC0 還是 EMAC1
-
Zynq/MP 有兩路 GEM 控制器,函數根據基地址區分當前掃描的是哪一路,然后把結果寫到 全局數組
phymapemac0[]
或phymapemac1[]
????????(2)從 31 到 1 號地址倒序掃描
-
地址 0 是廣播地址,跳過;
-
倒序可以避免某些交換芯片偽應答帶來的誤判。
????????(3)讀 “PHY ID 寄存器” 做存在性檢測
????????此處注意兼容,板載PHY是否為此寄存器地址。
-
PHY_DETECT_REG
通常是 寄存器 2(PHY Identifier 1); -
如果讀到
0xFFFF
,說明該地址沒有芯片響應,直接跳過。
? ? ? ? (4)判斷芯片是否真實存在
-
PHY_DETECT_MASK
把廠商 OUI 的固定位拉出來做匹配; -
滿足條件就把
phymapemacX[phy_addr] = TRUE
,后面init_emacps()
會只對這些“真正在位”的地址進行初始化。
? ? ? ? (5)打印調試信息
-
打開
LWIP_DEBUG
時會看到類似XEmacPs detect_phy: PHY detected at address 1.
? ? ? ? (6)進一步讀寄存器 2 做廠商識別
?
-
這一步只是提示如果 PHY 不是這三家,初始化腳本里對特殊寄存器的配置可能不適用,需要你自己確認。
????????掃描結束后,phymapemac0[]
/ phymapemac1[]
里為 TRUE
的索引就是本總線上實際存在的 PHY 地址;init_emacps()
會遍歷這些地址,分別調用 phy_setup_emacps()
完成真正的配置。
6、鏈路失敗退出
-
此時 MAC 仍處于復位態,lwIP 不會收到任何包。
7、把協商到的速率寫回 MAC
-
告訴 GEM 控制器當前是 10/100/1000 Mbps,內部會重新計算 MII/RGMII 時鐘分頻。
-
后面緊跟一段空轉延時,讓硬件鎖定時鐘——經驗值 20 000 空循環 ≈ 幾十微秒。(該部分延時可適當加長)參考文章:
【Zynq】【Lwip】解決頻繁打印link up/down、綠燈不亮、自協商結束后插入網線無反應的問題
?
8、全局鏈路狀態變量供后續查詢
-
xemacpsif_input()
里會定期查這個變量,發現掉線就丟棄收包,防止錯誤中斷淹掉 CPU。
關于init_emacps()的調用:
init_emacps()
的調用路徑只有一條,而且 完全藏在 Xilinx 的 lwIP 適配層內部,用戶代碼里根本看不到它的名字:
xemac_add() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?????????????????← 用戶唯一顯式調用
?└─ netif_add(netif, ..., xemacpsif_init, ...) ? ? ? ? ? ??????← lwIP 標準 API
? ? ?└─ xemacpsif_init(netif)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ← 在 xemacpsif.c 里
? ? ? ? ?└─ init_emacps(netif)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ← 就在這里被調用
xemac_add()
只在 main()
里被用戶顯式調用一次;它的使命就是“把 MAC 地址、IP 地址、底層初始化函數掛到 lwIP 的 netif 鏈表”,成功后退出,后續數據收發不再經過它。
static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
get_IEEE_phy_speed()
是一個 “廠商識別 + 分發” 的派發函數,根據 PHY 芯片的廠商 ID,把速率解析工作轉給對應的專用函數,返回 10/100/1000 或失敗。
1、讀取 寄存器 2(PHY Identifier 1) 的高 16 位,拿到 OUI 前綴。
-
三家常量已定義:
-
PHY_TI_IDENTIFIER
0x2000 -
PHY_REALTEK_IDENTIFIER
0x001C -
其余默認走 Marvell 分支(0x0141 或其他)
-
此處注意兼容,以防進入錯誤分支
2、直接調用對應的廠商函數:
3、返回協商速率
關于static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)的調用
get_IEEE_phy_speed()
只被 phy_setup_emacps()
調用一次,負責 “識別廠商 → 轉交對應的寄存器解析函數 → 拿到鏈路速率”。
main()
?└─ xemac_add()
? ? ?└─ xemacpsif_init()
? ? ? ? ?└─ init_emacps()
? ? ? ? ? ? ?└─ phy_setup_emacps()
? ? ? ? ? ? ? ? ?└─ get_IEEE_phy_speed() ? ← 這里
static u32_t get_Realtek_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)
這段代碼就是 Realtek PHY 專用的速率獲取函數 get_Realtek_phy_speed()
,作用與之前的通用協商流程完全一致,只不過把“讀速率”這一步固定到 Realtek 的 SPECIFIC_STATUS_REG(通常是 0x1A) 上。
-
被
get_IEEE_phy_speed()
調用,參數已確認phy_identity == 0x001C
(Realtek)。 -
MDIO 時鐘已通過
XEmacPs_SetMdioDivisor()
降到 ≤2.5 MHz。 -
函數運行在裸機,中斷關閉,CPU 主頻 666 MHz(Zynq-7000 典型值)。
1、讀取并修改 10/100 通告寄存器(地址 0x04)
-
MDIO 幀:Start (2b) + Op(2b) + PHYaddr(5b) + Reg(5b) + TA(2b) + 16-bit data
-
實際總線波形 64 位,約 25 μs 完成。
-
讀回值假設為 0x01E1(出廠默認值)。
control |= IEEE_ASYMMETRIC_PAUSE_MASK // bit11 = 1| IEEE_PAUSE_MASK // bit10 = 1| ADVERTISE_100 // bit8 = 1| ADVERTISE_10; // bit7 = 1
- 新值 0x05E1 → 表示“本端支持 10/100 全雙工 + 對稱/非對稱流控”。
-
再次產生 MDIO 寫幀,PHY 內部立即更新通告寄存器。
2?、讀取并修改 1000 M 通告寄存器(地址 0x09)
寫入數據0000 0011 0000 0000
-
讀回 0x0000(Realtek RTL8211 默認未設 1000 M 通告)。
control |= ADVERTISE_1000; // bit9 = 1
-
新值 0x0200 → 表示“本端支持 1000 M 全雙工”。(實際代碼為0x0300,可以兼容YT8531)
-
寫入完成,PHY 內部把 1000 M 能力加入下次自協商 FLP 脈沖。
3、重啟自協商 + 軟復位
XEmacPs_PhyRead(xemacpsp, phy_addr, 0x00, &control);
control |= IEEE_CTRL_AUTONEGOTIATE_ENABLE // bit12 = 1| IEEE_STAT_AUTONEGOTIATE_RESTART // bit9 = 1
-
新值 0x1200(保持之前設置的 0x1200,再置 bit9)
- 寫完后 PHY 立即發出攜帶新通告的 Fast Link Pulse (FLP) 序列,鏈路伙伴開始協商。
-
軟復位:PHY 內部狀態機回到初始態,寄存器除 0x00 外全部恢復默認值,但 0x04/0x09 的通告值已被鎖存,復位后仍保留。
-
復位期間 bit15 保持 1,典型耗時 < 100 μs(RTL8211 數據手冊標稱 60 μs)。
4、輪詢等待復位完成
5、等待自協商完成(最耗時)
-
初始 status 讀回 0x7849(bit5 = 0,協商未完成)。
while (!(status & IEEE_STAT_AUTONEGOTIATE_COMPLETE)) { // bit5sleep(1); // 裸機 sleep(1) ≈ 1 stimeout_counter++;if (timeout_counter == 30) return XST_FAILURE;XEmacPs_PhyRead(xemacpsp, 0x01, &status);
}
-
最壞情況 30 s 后超時退出;正常千兆對千兆 1–2 s 內 bit5 置 1。
-
串口每秒打印一次 “Waiting for PHY to complete autonegotiation.”
-
成功后打印 “autonegotiation complete”。
6、讀取 Realtek 專用速率寄存器(注意兼容問題)
-
按位掩碼比對,立即得到整數速率。注意兼容問題。
-
若 resolved 位未置 1,說明協商失敗,走
return XST_FAILURE;
get_Realtek_phy_speed()
就是 “把 Realtek PHY 的寄存器按官方順序擼一遍,直到 0x1A 的 resolved 位拉起來,再把 bit14:13 翻譯成 Mbps
關于get_Realtek_phy_speed()
的調用
在static u32_t get_IEEE_phy_speed(XEmacPs *xemacpsp, u32_t phy_addr)中調用,其他廠商配置代碼類似。