Iwip驱动8211FS项目——MPSOC实战1
硬件设计采用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)中调用,其他厂商配置代码类似。