API GUIDE / MAC / Middleware
G32S10 MAC Middleware 接口
Middleware 层接口:MID 的 Kconfig 分布在四个位置:
1. 构建配置与文件组织
1.1 Kconfig
MID 的 Kconfig 分布在四个位置:
code/middleware/net/lwip/Kconfig:lwIP 协议栈和 IPv6 开关。code/middleware/net/net_adapter/Kconfig:网络适配层与 Ethernet adapter 开关、IP 模式。code/drivers/g32s10/mac/Kconfig:MAC HAL 开关和调试 helper。code/devices/mac_phy/下各Kconfig:PHY 抽象和具体 PHY 驱动。
此外,code/middleware/net/Kconfig 必须通过下列语句把 lwIP 与 net adapter 选项纳入顶层 Kconfig 树:
source "code/middleware/net/lwip/Kconfig" source "code/middleware/net/net_adapter/Kconfig"
核心选项:
| 选项 | 类型/依赖 | 默认值 | 说明 |
|---|---|---|---|
MIDDLEWARE_NET_LWIP | bool;depends on OS && OS_BACKEND_FREERTOS;select THIRD_PARTY_LWIP | n | 编译 lwIP 2.2.1 和 FreeRTOS port |
MIDDLEWARE_NET_LWIP_IPV6 | bool;依赖 MIDDLEWARE_NET_LWIP | n | 启用 IPv6;当前默认配置关闭,IPv6 相关接口与事件不可用 |
MIDDLEWARE_NET_ADAPTER | bool;依赖 MIDDLEWARE_NET_LWIP | y | 编译网络接口适配层 |
MIDDLEWARE_NETIF | 隐藏 bool | MIDDLEWARE_NET_ADAPTER | 适配层公共代码开关 |
MIDDLEWARE_NET_ADAPTER_ETH | bool;依赖 DRIVER_MAC && DEVICE_MAC_PHY;select DRIVER_DTRNG、select DRIVER_EFUSE | y | 编译 Ethernet adapter(im_net_eth.c + im_net_eth_phy.c) |
MIDDLEWARE_NET_ADAPTER_ETH_STATIC_IP | bool(IP 模式二选一) | 选中 | 使用静态 IPv4 |
MIDDLEWARE_NET_ADAPTER_ETH_DHCP | bool(IP 模式二选一) | 未选中 | 启动后运行 DHCP client |
MIDDLEWARE_NET_ADAPTER_ETH_IPADDR | string | "10.5.4.111" | 静态 IPv4 地址 |
MIDDLEWARE_NET_ADAPTER_ETH_GWADDR | string | "10.5.4.1" | 静态网关 |
MIDDLEWARE_NET_ADAPTER_ETH_NETMASK | string | "255.255.255.0" | 静态掩码 |
MIDDLEWARE_NET_ADAPTER_ETH_DNS1 | string | "223.5.5.5" | 首选 DNS;不受 IP 模式影响 |
MIDDLEWARE_NET_ADAPTER_ETH_DNS2 | string | "0.0.0.0" | 备用 DNS;0.0.0.0 表示不设置 |
MIDDLEWARE_NETIF_ETH | 隐藏 bool | MIDDLEWARE_NET_ADAPTER_ETH | Ethernet netif 辅助开关 |
DRIVER_MAC | bool;select DRIVER_CPM | n | 编译 G32S10 MAC HAL |
MAC_ENABLE_DEBUG_HELPERS | bool;依赖 SDK_ENABLE_DEBUG_HELPERS | n | 编译 HAL 寄存器/描述符 dump |
DEVICE_MAC_PHY | 隐藏 bool | 被具体 PHY 选中 | PHY 抽象层公共开关 |
DEVICE_MAC_PHY_LAN8720A | bool;select DEVICE_MAC_PHY | n | 编译 LAN8720A PHY 驱动 |
DEVICE_MAC_PHY_SZ18201 | bool;select DEVICE_MAC_PHY | n | 编译 SZ18201 PHY 驱动(当前 EVB/SK 板选用) |
Shell 调试选项(code/middleware/shell/Kconfig):
| 选项 | 依赖 | 默认值 | 命令 |
|---|---|---|---|
MIDDLEWARE_SHELL_CMD_NET_INFO | MIDDLEWARE_NET_LWIP | y | net_info |
MIDDLEWARE_SHELL_CMD_PING | MIDDLEWARE_NET_LWIP | y | ping |
MIDDLEWARE_SHELL_CMD_IPERF | MIDDLEWARE_NET_LWIP | y | iperf、iperf_duplex |
MIDDLEWARE_SHELL_CMD_NET_CONFIG | MIDDLEWARE_NET_LWIP | y | net_config |
MIDDLEWARE_SHELL_CMD_MAC_PHY | MIDDLEWARE_NET_ADAPTER_ETH | y | mac_phy |
说明:
只打开
DRIVER_MAC而不打开任一 PHY 驱动时,DEVICE_MAC_PHY不会自动为真,Ethernet adapter 不可用。MIDDLEWARE_NET_ADAPTER_ETH会通过select自动打开DRIVER_DTRNG和DRIVER_EFUSE,用于生成 MAC 地址;如果目标芯片配置缺少这两个驱动,需在配置阶段一并裁剪。当既未定义
CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_STATIC_IP也未定义CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_DHCP时,im_net_eth.c在编译期强制回退到静态 IP;这只是防御性兜底,不应依赖。lwIP 内部参数(内存池、pbuf 池、TCP 窗口、MSS、TCP/IP 线程栈与优先级、hostname 等)不在 Kconfig 中暴露,硬编码在
code/middleware/net/lwip/include/lwipopts.h;修改这些参数需要直接修改该头文件或工程提供的覆盖版本。板级 Kconfig 不选择具体 PHY 型号。defconfig 中的
CONFIG_DEVICE_MAC_PHY_LAN8720A/CONFIG_DEVICE_MAC_PHY_SZ18201必须与板级board_mac.c中调用的lan8720a_device_get()/sz18201_device_get()保持一致,否则会在链接阶段缺少符号。静态 IPv4 配置字符串必须是合法的点分十进制地址。当前实现对解析失败采取静默忽略,失败字段会变成
0.0.0.0,不会在启动时报告配置错误;请在net_info中确认实际地址。
1.2 文件职责
| 文件 | 说明 |
|---|---|
code/middleware/net/include/im_net_eth_adapter.h | MID 公开接口、im_net_eth_phy_config_t、事件回调、链路信息和 PHY 访问接口 |
code/middleware/net/include/im_net_common_adapter.h | 公共网络类型:接口类型、IP 模式、链路状态、事件位、IPv4/IPv6 信息、metric |
code/middleware/net/include/im_net_common.h | 公共 lwIP 辅助接口:IPv4 解析/转换、IP 事件更新、默认 netif 更新、lwIP core 初始化 |
code/middleware/net/net_adapter/ethernet/im_net_eth.c | 适配器核心:配置生成、生命周期、worker 线程、TX/RX 路径、事件派发、查询接口 |
code/middleware/net/net_adapter/ethernet/im_net_eth_phy.c | PHY 绑定:模板复制、MDIO 回调注入、地址扫描、PHY 复位/自协商/链路状态编排 |
code/middleware/net/net_adapter/ethernet/im_net_eth_internal.h | 适配器内部配置与运行上下文字段、PHY 绑定函数声明 |
code/middleware/net/common/im_net_common.c | 公共辅助实现(IP 事件、DNS、默认 netif、lwIP core 初始化) |
code/include/devices/mac_phy/mac_phy.h | PHY 抽象公共宏、类型、MDIO 总线和操作表契约 |
code/devices/mac_phy/lan8720a/ | LAN8720A PHY 驱动与 lan8720a_device_get() |
code/devices/mac_phy/sz18201/ | SZ18201 PHY 驱动与 sz18201_device_get() |
code/bsp/common/include/board_mac.h | 板级 board_mac_init() 契约 |
code/bsp/g32s10/<board>/board_mac.c | 板级 MAC 时钟、PHY 电源/复位、PHY 注册参考实现 |
code/middleware/net/example/eth/ | Ethernet 示例工程与 defconfig |
code/middleware/shell/cmds/net/ | net_info、ping、iperf、net_config、mac_phy 调试命令 |
test/g32s10/module/gmac/ | GMAC 模块级测试(寄存器、中断、MTU、吞吐、压力等) |
1.3 最小配置与 defconfig
启用 Ethernet MAC MID 的最小配置:
CONFIG_DRIVER_MAC=y CONFIG_DEVICE_MAC_PHY_SZ18201=y CONFIG_MIDDLEWARE_NET_LWIP=y CONFIG_MIDDLEWARE_NET_ADAPTER=y CONFIG_MIDDLEWARE_NET_ADAPTER_ETH=y CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_STATIC_IP=y CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_IPADDR="10.5.4.111" CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_GWADDR="10.5.4.1" CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_NETMASK="255.255.255.0"
如需 Shell 调试和链路诊断,再打开:
CONFIG_MIDDLEWARE_SHELL=y CONFIG_MIDDLEWARE_SHELL_CONSOLE_IO=y CONFIG_MIDDLEWARE_SHELL_CMD_NET_INFO=y CONFIG_MIDDLEWARE_SHELL_CMD_PING=y CONFIG_MIDDLEWARE_SHELL_CMD_IPERF=y CONFIG_MIDDLEWARE_SHELL_CMD_MAC_PHY=y
可直接参考的 defconfig:
| defconfig | 说明 |
|---|---|
code/middleware/net/example/eth/eth_example_defconfig | Ethernet 中间件示例;EVB + SZ18201 + 静态 IPv4 |
test/g32s10/module/gmac/g32s10_v1_gmac_function_defconfig | GMAC 模块测试公共配置;FPGA IMAGE10 + LAN8720A + DHCP |
test/g32s10/module/gmac/clock/g32s10_v1_gmac_clock_defconfig | GMAC 时钟门控专项测试 |
构建组件关系:
CONFIG_MIDDLEWARE_NET_ADAPTER_ETH打开时,CMake 注册im_net_adapter组件,源文件为im_net_eth.c、im_net_eth_phy.c,依赖im_net、im_net_lwip。PHY 驱动分别注册为
dev_lan8720a、dev_sz18201组件,每个组件只编译一个.c文件。板级组件在
CONFIG_MIDDLEWARE_NET_ADAPTER_ETH打开时把board_mac.c加入源文件列表,并依赖im_net。
2. MAC MID 资源模型与公开类型
2.1 单实例与静态资源
MID 是单实例设计:im_net_eth.c 内部只有一个 im_net_eth_t 上下文,所有公开接口都作用于它,不支持第二个 Ethernet MAC 网口。WiFi 使用独立的 im_net_wifi 适配器,不共享该上下文。
| 资源 | 取值 | 说明 |
|---|---|---|
| TX 描述符数量 | 256 | IM_NET_ETH_TX_DESC_COUNT,静态数组,按 64 字节对齐 |
| RX 描述符数量 | 256 | IM_NET_ETH_RX_DESC_COUNT,静态数组,按 64 字节对齐 |
| 单个 DMA 缓冲区大小 | 1536 字节 | IM_NET_ETH_BUFFER_SIZE,每个 TX/RX 描述符槽位一个 |
| DMA 突发长度 | 8 | IM_NET_ETH_DMA_PBL,同时用于 TX/RX |
| MDC 时钟范围 | 0x3 | IM_NET_ETH_PHY_MDC_CLOCK_RANGE,写入 HAL mdc_clock_range |
| worker 线程栈 | 4096 字节 | IM_NET_ETH_WORK_THREAD_STACK_SIZE |
| worker 等待/链路轮询周期 | 1000 ms | IM_NET_ETH_WORK_TIMEOUT_MS |
| IRQ 优先级 | 1 | IM_NET_ETH_IRQ_PRIORITY,作用于 IRQ_GMAC |
| 单次 RX 排空上限 | 256 帧 | IM_NET_ETH_DRAIN_LIMIT,与 RX 描述符数量相同 |
| TX HAL 超时 | 0 | 单次轮询,不做等待 |
| RX HAL 超时 | 0 | 单次轮询,不做等待 |
| 默认 MAC 地址 | 02:32:10:00:00:01 | EFUSE/DTRNG 都不可用时的兜底值 |
| netif 名称 | en<num> | 前两个字符固定为 'e'、'n',编号由 lwIP 分配 |
| netif metric | 0x10 | IM_NETIF_METRIC_ETHERNET |
静态内存占用说明:
描述符:
256 × 16 × 2 = 8192字节。DMA 缓冲区:
256 × 1536 × 2 = 786432字节。两者合计约 776 KiB(
794624字节),全部位于静态数据区,不从 FreeRTOS heap 分配。每个 DMA 缓冲区槽位为 1536 字节(24 × 64),天然按 cache line 对齐。
堆内存占用:
初始化时 MID 通过
malloc(rx_buffer_size)申请一个 1536 字节 的 RX 落地缓冲区,用于接收 HAL 交出的帧,再复制到pbuf。该缓冲区在
im_net_eth_deinit()成功路径释放;init()失败时在回滚路径释放。
内核对象:
一个名为
"eth"的 worker 线程(栈 4096 字节,优先级OS_priority_high)。一个
thread_waiter(单等待者,IRQ → worker 唤醒)。一个
IRQ_GMAC中断注册项(首次start()时申请,stop()/deinit()时释放)。
2.2 硬件能力摘要
| 项目 | 说明 |
|---|---|
| MAC 实例 | 单实例 GMAC;MID 不支持多 MAC |
| 速率 | 仅 10 Mbps / 100 Mbps;mac_phy_speed_t 没有 1000M 编码 |
| 双工 | 半双工 / 全双工 |
| PHY 接口 | Clause 22 MDIO,PHY 地址 0~31;0xFF 只作为自动扫描哨兵 |
| MAC 地址 | 6 字节;优先从 EFUSE CHIP_ID 派生,其次 DTRNG,最后使用默认值,并强制为本地管理单播地址 |
| 流控 | 仅解析对称 pause;最终结果只可能是 NONE 或 RX_TX,且要求全双工 |
| PHY 中断 | 当前 MID 与两个 PHY 驱动都不使用 PHY 中断,链路靠轮询 |
| MAC 中断 | IRQ_GMAC,软件 IRQ 号 11;由 MID 注册、设优先级 1 并使能 |
| 帧格式 | IEEE 802.3 Ethernet II;单帧必须完整落在一个描述符/缓冲区中(≤ 1536 字节,含 FCS 处理) |
| IPv6 | 当前默认配置下 lwIP 以 IPv4-only 构建,IPv6 查询接口返回 IM_ERR_UNSUPPORTED |
2.3 MID 公开宏与事件
| 宏/枚举 | 取值 | 说明 |
|---|---|---|
IM_NET_ETH_PHY_ADDR_AUTO | MAC_PHY_ADDR_INVALID(0xFF) | im_net_eth_phy_config_t.phy_addr 使用该值时,MID 在 0~31 范围内扫描 PHY |
IM_NET_EVENT_NONE | 0U | 无事件 |
IM_NET_EVENT_LINK_UP | 1U << 0 | PHY 链路由 down 变 up |
IM_NET_EVENT_LINK_DOWN | 1U << 1 | PHY 链路由 up 变 down |
IM_NET_EVENT_GOT_IPV4 | 1U << 2 | netif 首次观察到有效 IPv4 地址 |
IM_NET_EVENT_LOST_IPV4 | 1U << 3 | netif IPv4 地址消失 |
IM_NET_EVENT_RX | 1U << 4 | worker 向 lwIP 成功投递了至少一个 RX 帧 |
IM_NET_EVENT_TX | 1U << 5 | GMAC 报告 TX 完成(worker 会回收描述符) |
IM_NET_EVENT_ERROR | 1U << 6 | HAL、PHY、pbuf、netif input 或 DMA 异常 |
IM_NET_EVENT_GOT_IPV6 | 1U << 7 | netif 首次观察到有效 IPv6 地址(启用 IPv6 时) |
IM_NET_EVENT_LOST_IPV6 | 1U << 8 | netif IPv6 地址全部失效(启用 IPv6 时) |
事件是 32 位位图,不是队列:同一位在一次 worker 处理周期内只派发一次,多类事件可以在同一次回调中合并出现;回调未注册时事件直接丢弃。
2.4 公开类型
完整定义以公开头文件为准:im_net_eth_adapter.h、im_net_common_adapter.h、mac_phy.h。
2.4.1 im_net_eth_t
typedef struct im_net_eth im_net_eth_t;
im_net_eth_t 是 MID 的唯一适配器上下文,定义在内部头文件中,对应用是不透明类型。事件回调会收到该指针,可用于回调归属判断,但应用不得解引用其字段,也不得保存后在对象生命周期之外使用。
2.4.2 im_net_eth_phy_config_t
typedef struct {
const mac_phy_device_t *phy_device;
uint8_t phy_addr;
uint32_t mdio_timeout_ms;
uint16_t pause_time;
} im_net_eth_phy_config_t;| 字段 | 说明 |
|---|---|
phy_device | 板级选定的 PHY driver 模板指针;必须指向固件生存期有效的静态对象(例如 sz18201_device_get() 的返回值) |
phy_addr | PHY 地址 0~31;IM_NET_ETH_PHY_ADDR_AUTO 表示自动扫描 |
mdio_timeout_ms | MDIO 事务超时(毫秒);0 表示单次轮询/不等待。该值同时被 PHY 驱动的软件复位流程用作复位等待预算 |
pause_time | 流控启用时写入 MAC_FCR.PT 的 pause quantum;EVB 取 0x0200 |
2.4.3 im_net_eth_event_callback_t
typedef void (*im_net_eth_event_callback_t)(im_net_eth_t *eth, uint32_t events, void *user_data);
回调在以下上下文执行:Ethernet worker 线程、im_net_eth_start() 调用线程、im_net_eth_stop() 调用线程、im_net_eth_deinit() 调用线程。回调不得长时间阻塞,不得在回调中调用 im_net_eth_stop()/im_net_eth_deinit()/im_net_eth_start() 等生命周期接口,也不得调用任何 PHY/MDIO 接口。
2.4.4 im_net_eth_link_info_t
typedef struct {
im_net_link_state_t state;
bool link_up;
bool autoneg_enabled;
bool autoneg_complete;
} im_net_eth_link_info_t;| 字段 | 说明 |
|---|---|
state | 适配器状态,见 4.5 |
link_up | PHY 缓存链路状态 |
autoneg_enabled / autoneg_complete | PHY 自协商状态 |
该结构返回的是 worker 最近一次采样并缓存的 PHY 状态,不触发新的 MDIO 事务,因此可能滞后最多约一个空闲轮询周期(约 1 秒)。
2.4.5 公共网络状态与事件类型
im_net_link_state_t:
typedef enum {
IM_NET_LINK_STATE_RESET = 0,
IM_NET_LINK_STATE_READY,
IM_NET_LINK_STATE_STARTED,
IM_NET_LINK_STATE_LINK_DOWN,
IM_NET_LINK_STATE_LINK_UP,
IM_NET_LINK_STATE_GOT_IP,
IM_NET_LINK_STATE_ERROR,
} im_net_link_state_t;| 值 | 说明 |
|---|---|
RESET | 未初始化或已 deinit |
READY | HAL/lwIP 已初始化,MAC 数据流未启动(或已 stop) |
STARTED | 内部过渡状态,start() 赋值为该状态后立即被状态重算覆盖,应用几乎观察不到 |
LINK_DOWN | netif 已 up,但 PHY 链路 down |
LINK_UP | netif 与 PHY 链路均 up,但尚未获得有效 IPv4 地址 |
GOT_IP | netif 与 PHY 链路均 up,且已获得有效 IPv4 地址(地址、掩码、网关均非 0 且掩码合法) |
ERROR | 最近一次运行期失败,需 stop() 恢复或 deinit() 结束 |
im_net_event_t 见 4.3。
2.4.6 IPv4 / IPv6 信息类型
typedef struct {
im_netif_ip_mode_t ip_mode;
uint32_t addr;
uint32_t netmask;
uint32_t gateway;
uint32_t dns1;
uint32_t dns2;
} im_net_ipv4_addr_t;地址字段为主机字节序。
dns1/dns2返回的是配置值(来自 Kconfig),不是 DHCP 或 DNS 客户端实际使用的服务器列表。
typedef struct {
uint8_t addr[IM_NET_IPV6_ADDR_SIZE];
uint8_t index;
im_net_ipv6_addr_state_t state;
} im_net_ipv6_addr_t;addr为 16 字节网络序地址。index是底层 netif 的 IPv6 地址槽位号。state取值见im_net_ipv6_addr_state_t:INVALID/TENTATIVE/VALID/PREFERRED/DEPRECATED/DUPLICATED。
2.4.7 PHY 抽象类型
mac_phy_mdio_bus_t:
typedef struct mac_phy_mdio_bus {
void *user_data;
uint32_t timeout_ms;
error_code_t (*read)(struct mac_phy_mdio_bus *mdio,
uint8_t phy_addr,
uint8_t reg_addr,
uint16_t *data_out);
error_code_t (*write)(struct mac_phy_mdio_bus *mdio,
uint8_t phy_addr,
uint8_t reg_addr,
uint16_t data);
} mac_phy_mdio_bus_t;read/write的调用方必须保证mdio、data_out非空,phy_addr/reg_addr在 0~31。回调可能轮询或阻塞,只能在任务/线程上下文调用,禁止在中断、临界区或不可阻塞路径中调用。
timeout_ms == 0表示单次轮询/不等待。
mac_phy_ops_t:
| 操作 | 契约 |
|---|---|
probe(device) | 判断当前 phy_addr 是否匹配该驱动;匹配返回 IM_OK,不匹配返回 IM_ERR_UNSUPPORTED |
read_id(device, id_out) | 读取 PHYID1/PHYID2 原始值 |
reset(device) | 软件复位 PHY 并等待完成;超时返回 IM_ERR_TIMEOUT,no-wait 仍未完成返回 IM_ERR_BUSY,时间源未初始化返回 IM_ERR_NOT_READY |
set_autoneg(device, config) | 写自协商广告并重启自协商,不等待完成 |
set_config(device, config) | 关闭自协商并强制 speed/duplex;当前 MID 不调用该操作 |
get_link_state(device, state_out) | 读取并解码当前链路状态 |
mac_phy_device_t 是 PHY 运行时对象,包含 name、ops、mdio 和 phy_addr。板级只持有驱动提供的静态模板,MID 在初始化时把模板按值复制到适配器上下文,并注入 mdio.user_data、mdio.timeout_ms、mdio.read、mdio.write 与 phy_addr。
mac_phy_link_state_t 的 speed/duplex/flow_control 只在 link_up == true 时有效;link down 时是占位值,调用方不得用于配置 MAC。
2.5 适配器状态模型
对外可见状态由 im_net_eth_state_update() 按下列优先级计算:
netif 尚未加入 lwIP -> RESET netif 未 up -> READY netif up 但链路 down -> LINK_DOWN netif up、链路 up、无有效 IPv4 -> LINK_UP netif up、链路 up、有有效 IPv4 -> GOT_IP 运行期错误路径显式设置 -> ERROR
状态迁移:
| 当前状态 | 操作/事件 | 目标状态 |
|---|---|---|
RESET | im_net_eth_init() 成功 | READY |
RESET | im_net_eth_init() 失败 | ERROR |
READY | im_net_eth_start() 成功 | LINK_DOWN / LINK_UP / GOT_IP(由链路和 IP 决定) |
READY / LINK_* / GOT_IP | im_net_eth_stop() 成功 | READY |
READY / LINK_* / GOT_IP | 运行期错误 | ERROR |
ERROR | im_net_eth_stop() 成功 | READY |
| 任意状态 | im_net_eth_deinit() 成功 | RESET |
要点:
ERROR是粘滞状态:im_net_eth_start()在ERROR下返回IM_ERR_NOT_READY,必须先im_net_eth_stop()。LINK_UP与GOT_IP由 worker 周期刷新,不由应用设置。IM_NET_LINK_STATE_STARTED只是内部过渡值,不应作为外部判断依据。
2.6 对象与资源所有权
适配器上下文归 MID 所有,静态分配,应用不能创建第二个实例。
TX/RX 描述符和 DMA 缓冲区是 MID 静态数组,通过 HAL 配置交给 MAC HAL;生命周期与固件一致。
RX 落地缓冲区由 MID 在
init()中malloc,在deinit()或init()失败回滚中释放。PHY 模板归 PHY 驱动所有,必须静态生存;MID 只复制
im_net_eth_phy_config_t,并在初始化时按值复制模板到自己的上下文。板级传入的栈上配置结构体在本函数返回后即可失效。PHY 运行时对象归 MID 所有,
mdio回调和phy_addr由 MID 注入,应用不得直接操作。netif 内嵌在 MID 上下文中,由 MID 添加/移除;应用通过 lwIP 标准接口(socket/netconn)使用。
事件回调归应用所有;回调函数与
user_data必须保持有效,直到注销或固件结束。板级资源(时钟、电源、复位、pinmux)归板级 BSP;
board_mac_init()必须在 MID 初始化前完成。
2.7 全生命周期
下图描述板级、MID、PHY 与 lwIP 的完整生命周期。im_net_eth_* 为公开接口;link_state_process、rx_process、tx_process、irq_handler 为 MID 内部流程。
板级与应用输入
+ pause_time + flow_control
MID 生命周期
PHY 扫描/probe/reset/autoneg;
lwIP tcpip + netif + metric
MAC 数据流未启动
DHCP/静态 IP;注册 IRQ;创建 worker
netif down;停止 MAC
释放 RX 缓冲区
PHY 与链路
数据面与事件
board_pinmux: RMII / MDC / MDIO 引脚 → board_mac_init(): CPM 25 MHz
im_net_eth_register_phy(): → 注册 PHY 配置
可选 im_net_eth_register_callback() → READY
RESET → 注册 PHY 配置
im_net_eth_init() → READY
im_net_eth_init() → ERROR · 失败
READY → im_net_eth_start()
im_net_eth_start() → READY · 失败回滚
im_net_eth_start() → LINK_DOWN / LINK_UP / GOT_IP
LINK_DOWN / LINK_UP / GOT_IP → GMAC IRQ 11
GMAC IRQ 11 → pending_events
worker: link / TX reclaim / RX drain → netif.input(pbuf)
worker: link / TX reclaim / RX drain → 事件合并后 dispatch
事件合并后 dispatch → LINK_DOWN / LINK_UP / GOT_IP
LINK_DOWN / LINK_UP / GOT_IP → ERROR · 运行期错误
ERROR → im_net_eth_stop()
LINK_DOWN / LINK_UP / GOT_IP → im_net_eth_stop()
READY → im_net_eth_stop()
im_net_eth_stop() → READY
READY → im_net_eth_deinit()
ERROR → im_net_eth_deinit()
LINK_DOWN / LINK_UP / GOT_IP → im_net_eth_deinit()
im_net_eth_deinit() → RESET
PHY driver 模板 → MDIO 回调注入
扫描 0..31 或固定地址 → reset -> set_autoneg
im_net_eth_init() → MDIO 回调注入
im_net_eth_init() → 扫描 0..31 或固定地址
im_net_eth_init() → reset -> set_autoneg
worker: link / TX reclaim / RX drain → get_link_state
阅读约定:
注册 PHY 只保存配置,不访问硬件;真正的 PHY 初始化和 MDIO 访问发生在
im_net_eth_init()内部。im_net_eth_start()在未初始化时会自动调用init();start()失败会回滚到READY(回滚中也派发 ERROR 事件)。数据面只在
RUN状态工作;ERROR状态下 IRQ 可能仍在产生,但start()会被拒绝,需先stop()。im_net_eth_deinit()成功后回到RESET,但 PHY 注册配置仍保留(无需重新register_phy,但需要重新注册事件回调)。link_state_process只在 worker 空闲轮询时执行,链路状态与GOT_IP状态因此最多滞后约一个轮询周期。
3. 接口规范
本章逐个说明 MID 公开接口。除特别说明外,所有接口都:
操作唯一的静态适配器实例,没有实例句柄参数。
只能在线程/任务上下文调用,禁止在中断或临界区中调用。
没有内部互斥锁,需要由调用方串行化对同一适配器的并发访问。
依赖
CONFIG_SDK_PARAM_CHECK(当前工程配置为打开)执行空指针与取值范围检查;关闭该配置时相应检查消失。
接口能力概览:
| 接口 | 前置条件 | 访问硬件 | 典型上下文 |
|---|---|---|---|
im_net_eth_register_phy() | 尚未完成 init() | 否 | 线程 |
im_net_eth_register_callback() | 已完成 init() | 否 | 线程 |
im_net_eth_init() | 已注册 PHY | 是 | 线程 |
im_net_eth_start() | 可自动 init() | 是 | 线程 |
im_net_eth_stop() | 无(未初始化时直接成功) | 是 | 线程 |
im_net_eth_deinit() | 无 | 是 | 线程 |
im_net_eth_link_state_get() | 已完成 init() | 否(读缓存) | 任意可读上下文 |
im_net_eth_mac_addr_get() | 已完成 init() | 否 | 任意可读上下文 |
im_net_eth_ip_get() | 已完成 init() | 否 | 任意可读上下文 |
im_net_eth_ipv6_addr_get() | 已完成 init() 且启用 IPv6 | 否 | 任意可读上下文 |
im_net_eth_phy_addr_get() | 已完成 init() | 否 | 任意可读上下文 |
im_net_eth_phy_read() / phy_write() | 已完成 init() 与 PHY 初始化 | 是(MDIO) | 线程 |
3.1 事件回调注册
3.1.1 im_net_eth_register_callback
error_code_t im_net_eth_register_callback(im_net_eth_event_callback_t callback, void *user_data);
功能说明:
注册或注销 Ethernet 事件回调。事件包括链路变化、IPv4/IPv6 地址获得/丢失、RX/TX 和错误。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
callback | im_net_eth_event_callback_t | 输入 | 回调函数;传 NULL 表示注销 |
user_data | void * | 输入 | 原样传给回调的应用上下文 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 回调已设置或已注销 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
补充说明:
与
im_net_eth_register_phy()相反,本接口必须在 init 之后调用;im_net_eth_start()内部会自动 init,因此在首次 start 前调用本接口会返回IM_ERR_NOT_READY。典型顺序是init()→register_callback()→start()。可以重复调用覆盖旧回调和
user_data;callback = NULL时user_data也一并保存但不会被使用。im_net_eth_deinit()之后适配器不再派发事件;下一次im_net_eth_init()会清零适配器上下文并清除已注册的回调,因此重新初始化后需要重新注册事件回调。事件不是队列:回调未注册期间产生的事件会被丢弃。
3.2 状态与信息查询
3.2.1 im_net_eth_link_state_get
error_code_t im_net_eth_link_state_get(im_net_eth_link_info_t *info_out);
功能说明:
读取适配器状态与最近一次缓存的 PHY 链路信息,不触发新的 MDIO 事务。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
info_out | im_net_eth_link_info_t * | 输出 | 链路信息输出 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 已填充 |
IM_ERR_INVALID_PARAM | info_out 为空 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
补充说明:
link down 时
speed/duplex/flow_control分别返回UNKNOWN/UNKNOWN/NONE。缓存更新来自 worker 的空闲轮询(约 1 秒一次)以及
start()中的首次采样,因此信息可能滞后约一个轮询周期。只读、不访问硬件,不构成跨线程同步屏障。
3.2.2 im_net_eth_mac_addr_get
error_code_t im_net_eth_mac_addr_get(uint8_t *addr_out, uint32_t addr_size);
功能说明:
读取 init() 时生成并写入 netif 的 MAC 地址。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
addr_out | uint8_t * | 输出 | MAC 地址输出缓冲区 |
addr_size | uint32_t | 输入 | 输出缓冲区字节数,必须 ≥ 6 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 已复制 6 字节 |
IM_ERR_INVALID_PARAM | addr_out 为空或 addr_size < 6 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
补充说明:只写 6 字节,不修改 addr_out[6..addr_size-1];返回网络字节序地址。
3.2.3 im_net_eth_ip_get
error_code_t im_net_eth_ip_get(im_net_ipv4_addr_t *info_out);
功能说明:
读取当前 IPv4 配置和 netif 上的地址信息。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
info_out | im_net_ipv4_addr_t * | 输出 | IPv4 信息输出 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 已填充 |
IM_ERR_INVALID_PARAM | info_out 为空 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
补充说明:
地址、掩码、网关来自 netif,主机字节序;未获取到地址时为 0。
ip_mode来自适配器配置(静态或 DHCP)。dns1/dns2返回配置值(Kconfig),不是 DNS 客户端当前实际使用的服务器列表。
3.2.4 im_net_eth_ipv6_addr_get
error_code_t im_net_eth_ipv6_addr_get(im_net_ipv6_addr_t *addr_out, uint32_t addr_count, uint32_t *actual_count_out);
功能说明:
读取 netif 上所有状态非 INVALID 的 IPv6 地址。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
addr_out | im_net_ipv6_addr_t * | 输出 | 地址数组 |
addr_count | uint32_t | 输入 | 数组容量,必须 > 0 |
actual_count_out | uint32_t * | 输出 | 实际写入的地址数量 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 已填充;*actual_count_out 为实际数量(可能为 0) |
IM_ERR_INVALID_PARAM | 启用 IPv6 时:任一指针为空或 addr_count == 0 |
IM_ERR_NOT_READY | 启用 IPv6 时:适配器尚未完成 init() |
IM_ERR_UNSUPPORTED | 当前 lwIP 以 IPv4-only 构建(LWIP_IPV6 == 0);此路径不校验参数 |
补充说明:
当前默认配置下
LWIP_IPV6 == 0,本接口恒返回IM_ERR_UNSUPPORTED。启用 IPv6 时,地址按 netif 槽位顺序复制,最多
addr_count条;index为槽位号,state为 lwIP 地址状态映射值。
3.2.5 im_net_eth_phy_addr_get
error_code_t im_net_eth_phy_addr_get(uint8_t *addr_out);
功能说明:
读取初始化时确定并回写到配置中的 PHY 地址(自动扫描模式下是扫描结果)。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
addr_out | uint8_t * | 输出 | PHY 地址输出 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 已填充 |
IM_ERR_INVALID_PARAM | addr_out 为空,或已初始化但配置中的 PHY 地址大于 31 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
3.3 PHY 访问与诊断
3.3.1 im_net_eth_phy_read
error_code_t im_net_eth_phy_read(uint8_t reg_addr, uint16_t *data_out);
功能说明:
通过 MID 绑定的 MDIO 总线读取当前 PHY 的寄存器。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
reg_addr | uint8_t | 输入 | PHY 寄存器地址 0~31 |
data_out | uint16_t * | 输出 | 寄存器值输出 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK | 读取成功 |
IM_ERR_INVALID_PARAM | data_out 为空;PHY 未初始化;PHY 地址非法;reg_addr > 31 |
IM_ERR_NOT_READY | 适配器尚未完成 init() |
IM_ERR_BUSY | MDIO 总线正忙(例如与 worker 的链路轮询并发) |
IM_ERR_TIMEOUT | MDIO 事务超时,或系统时基未初始化且超时值非 0 |
补充说明:
本接口直接调用 HAL MDIO,不经过 PHY 驱动;可在不初始化 PHY 驱动语义的前提下做寄存器级诊断。
必须在任务上下文调用;可能在
mdio_timeout_ms内轮询阻塞。MID 没有为 MDIO 访问提供锁;与 worker 的链路轮询或另一线程并发调用时,可能返回
IM_ERR_BUSY,调用方应自行串行化或重试。写后读、恢复寄存器等序列由调用方自行保证。
3.3.2 im_net_eth_phy_write
error_code_t im_net_eth_phy_write(uint8_t reg_addr, uint16_t data);
功能说明:
通过 MID 绑定的 MDIO 总线写当前 PHY 的寄存器。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
reg_addr | uint8_t | 输入 | PHY 寄存器地址 0~31 |
data | uint16_t | 输入 | 写入值 |
返回值表:与 im_net_eth_phy_read() 相同(无 data_out 空指针检查)。
补充说明:写寄存器可能破坏 PHY 驱动维护的自协商/链路状态,只建议在调试和恢复流程中使用;诊断结束后应重新复位 PHY 或重新 init()。
3.4 板级 PHY 注册
3.4.1 im_net_eth_register_phy
error_code_t im_net_eth_register_phy(const im_net_eth_phy_config_t *config);
功能说明:
注册板级选定的 PHY 模板、PHY 地址策略、MDIO 超时、pause time 和流控策略。MID 只复制该配置,不访问硬件、不申请资源。必须在 im_net_eth_init()(或会自动调用 init 的 im_net_eth_start())之前调用。
参数表:
| 参数 | 类型 | 方向 | 说明 |
|---|---|---|---|
config |
const im_net_eth_phy_config_t * |
输入 | 板级 PHY 配置;函数返回后调用方即可释放该结构体,但 config->phy_device 指向的模板必须永久有效 |
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK |
注册成功;配置已按值复制 |
IM_ERR_INVALID_PARAM |
config 为空;phy_device 为空;phy_addr 既不是 IM_NET_ETH_PHY_ADDR_AUTO 又大于 31;flow_control 含 HAL_MAC_FLOW_CONTROL_RX_TX 之外的位;或 PHY 操作表缺少 probe/read_id/reset/set_autoneg/get_link_state 之一 |
IM_ERR_BUSY |
适配器已完成初始化;不能在 init() 之后重新注册 |
补充说明:
- 成功注册后适配器会记录
phy_registered标志;该标志在deinit()中不会被清除,因此 deinit 后重新 init 无需再次注册。 im_net_eth_init()失败后all_initialized仍为 false,可以重新注册 PHY 配置再重试。- 注册本身不校验
mdio_timeout_ms和pause_time;mdio_timeout_ms = 0是合法值(单次轮询/不等待)。 phy_device必须来自 PHY 驱动的静态模板获取接口(如sz18201_device_get()、lan8720a_device_get()),不能指向栈上或动态分配的结构体。- 同一适配器可以重复调用本接口覆盖配置,只要尚未完成初始化。
3.5 初始化与启停
3.5.1 im_net_eth_init
error_code_t im_net_eth_init(void);
功能说明:
初始化 Ethernet MID:生成/选择 MAC 地址、初始化 MAC HAL、扫描或绑定 PHY、复位并配置 PHY 自协商、初始化 lwIP 核心并添加 netif、注册 metric 和默认 netif。该接口不启动 MAC 数据流,不注册 IRQ,不创建 worker 线程。
参数表:
无参数。
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK |
初始化成功,适配器进入 READY;已初始化时立即返回 IM_OK |
IM_ERR_NOT_READY |
未注册 PHY;或 lwIP tcpip 初始化失败;或 netif 添加失败;或 IPv6 link-local 初始化失败 |
IM_ERR_BUSY |
RX 落地缓冲区 malloc 失败,或 metric 注册失败 |
IM_ERR_TIMEOUT |
lwIP tcpip 线程初始化 5 秒超时 |
IM_ERR_NO_MEMORY |
lwIP 初始化等待器创建失败 |
| 其他 HAL/PHY 错误码 | HAL handle/init 失败、PHY 扫描/probe/reset/autoneg 失败等原样返回 |
补充说明:
- 幂等:
all_initialized已为真时立即返回IM_OK,不重复初始化。 - 前置条件:必须已通过
im_net_eth_register_phy()注册 PHY;未注册时打印错误日志、把状态置为ERROR并返回IM_ERR_NOT_READY。 init()会清零整个适配器上下文,包括之前注册的事件回调;重新 init 后需要重新调用im_net_eth_register_callback()。- 初始化失败时 MID 会回滚已申请的 netif、HAL 和 RX 缓冲区,状态置为
ERROR;all_initialized保持 false,可以修正原因后重试init()。 init()生成的 MAC 地址来自 EFUSE CHIP_ID → DTRNG → 默认值的回退链;EFUSE 无有效 CHIP_ID 时每次 init 可能得到不同的随机 MAC。- 初始化内部顺序:HAL handle/init → PHY 模板复制与 MDIO 回调注入 → 地址扫描或固定地址 probe → PHY 复位 → 默认自协商广告 → 首次链路采样 → lwIP core → netif 添加 → IPv6 link-local(若启用)→ metric 与默认 netif。
- 建议在
sys_time_init()(或等价时基初始化)之后调用;当mdio_timeout_ms != 0时,PHY 软件复位与 MDIO 事务依赖系统时基,时基未初始化会返回IM_ERR_NOT_READY(PHY 复位)或IM_ERR_TIMEOUT(MDIO)。
3.5.2 im_net_eth_start
error_code_t im_net_eth_start(void);
功能说明:
启动 Ethernet 数据面:启动 MAC RX/TX 与 DMA、把 netif 置 up、采样链路并配置 MAC、按 IP 模式启动 DHCP 或使用静态 IPv4、注册并使能 IRQ_GMAC、创建 Ethernet worker 线程。
参数表:
无参数。
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK |
数据面已启动;或此前已经处于启动状态 |
IM_ERR_NOT_READY |
init() 失败(含未注册 PHY);当前状态不是 READY(例如 ERROR);netif up 或 DHCP 启动失败 |
IM_ERR_BUSY |
worker 线程创建失败 |
| 其他 IRQ/PLIC 错误码 | IRQ_GMAC 申请、优先级设置或使能失败时返回底层错误码 |
| 其他 HAL/PHY 错误码 | hal_mac_start() 或首次链路配置失败等原样返回 |
补充说明:
- 若尚未初始化,
start()会先自动调用init(),因此未注册 PHY 时错误由init()返回。 - 幂等:已经处于
STARTED/LINK_DOWN/LINK_UP/GOT_IP时立即返回IM_OK,不会重复创建线程或 IRQ。 ERROR状态下不会尝试恢复,必须先stop()(stop()成功会把状态恢复为READY),再重新start()。- 启动过程中会主动采样一次 PHY 链路状态;若自协商尚未完成或链路 down,本次启动仍会成功,链路状态由后续 worker 周期采样更新。
- 失败回滚:MID 先派发一次
IM_NET_EVENT_ERROR,再执行 stop 流程(会额外派发LINK_DOWN和LOST_IPV4);回滚中的hal_mac_stop()成功时回到READY,失败时保持ERROR。函数返回原始错误码,READY下可以重试start()。 - 副作用:使能
IRQ_GMAC(优先级 1),创建名为"eth"、栈 4096 字节、优先级OS_priority_high的 worker 线程,并立即唤醒一次。
3.5.3 im_net_eth_stop
error_code_t im_net_eth_stop(void);
功能说明:
停止 Ethernet 数据面并恢复为 READY:停止 worker 线程、禁用并释放 IRQ、停止 DHCP、把 netif link 和 netif 置 down、停止 MAC RX/TX/DMA。
参数表:
无参数。
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK |
已停止;未初始化或本已处于 READY 时也返回 IM_OK |
| 其他 HAL 错误码 | hal_mac_stop() 失败;状态置为 ERROR 并派发 IM_NET_EVENT_ERROR |
补充说明:
- 幂等:未初始化、或状态已经是
READY时立即返回成功,不访问硬件、不派发事件。 - 在
ERROR状态下调用本接口是正式恢复手段;成功后会清除运行期错误并回到READY。 - 停止成功会派发
LINK_DOWN、LOST_IPV4,若停止前存在有效 IPv6 地址还会派发LOST_IPV6。 stop()不释放 RX 落地缓冲区、不反初始化 HAL、不移除 netif;这些由deinit()完成。stop()之后再start()会重新初始化 DHCP/IRQ/worker,但不会重新初始化 HAL 和 PHY。
3.5.4 im_net_eth_deinit
error_code_t im_net_eth_deinit(void);
功能说明:
结束 MID 生命周期:先按 stop() 语义停止数据面,再移除 lwIP netif、反初始化 MAC HAL、释放 RX 落地缓冲区并将状态置回 RESET。
参数表:
无参数。
返回值表:
| 返回值 | 说明 |
|---|---|
IM_OK |
已释放;未初始化时也返回 IM_OK |
im_net_eth_stop() 的错误 |
stop() 失败时立即返回,后续 netif/HAL 资源不会释放 |
| HAL 反初始化错误 | hal_mac_deinit() 失败时返回该错误并派发 IM_NET_EVENT_ERROR |
补充说明:
- 幂等:未初始化时立即返回
IM_OK。 - 只要
init()曾成功返回,应用就应在退出前调用deinit();中途start()/stop()失败不改变这一要求。 deinit()不会清除 PHY 注册配置,因此再次初始化不需要重新register_phy();但再次init()会清零上下文并清除事件回调,需要重新注册。- 已知限制:若
stop()返回错误,deinit()直接返回且不释放资源;若hal_mac_deinit()失败,netif 和 RX 缓冲区已经释放但all_initialized仍为 true,此时不应直接再次start(),应视为需要复位的异常状态。
4. 行为约束与实现说明
4.1 初始化顺序与幂等性
推荐的初始化顺序:
板级 pinmux:完成 RMII、MDC、MDIO、PHY 电源/复位引脚配置。
board_mac_init():使能 MAC 时钟、释放 MAC 软复位、给 PHY 上电并复位、调用im_net_eth_register_phy()。时基初始化(
im_systick_init()或等价接口):PHY 软复位和 MDIO 超时依赖系统时基。im_net_eth_init()(可选):提前完成 HAL/PHY/lwIP 初始化。im_net_eth_register_callback()(可选):注册事件回调。im_net_eth_start():启动数据面。退出前
im_net_eth_stop()、im_net_eth_deinit()。
幂等性汇总:
| 操作 | 未初始化时 | 已初始化/已启动时 |
|---|---|---|
register_phy() | 可重复注册,后注册覆盖先注册 | 返回 IM_ERR_BUSY |
register_callback() | 返回 IM_ERR_NOT_READY | 可重复注册,覆盖旧值 |
init() | 执行初始化 | 立即返回 IM_OK |
start() | 自动先 init | 已启动则立即返回 IM_OK;ERROR 则返回 IM_ERR_NOT_READY |
stop() | 立即返回 IM_OK | READY 时立即返回 IM_OK;其它状态执行停止 |
deinit() | 立即返回 IM_OK | 执行停止+释放 |
失败语义:
init()失败会自动回滚并进入ERROR,不会留下半初始化资源;可以再次init()。start()失败会回滚到READY,但会派发两轮事件。stop()失败会把状态置为ERROR并派发错误事件。deinit()在stop()失败时直接返回错误,不释放后续资源。
4.2 并发与上下文
MID 不提供全局锁;下列状态可能被多个上下文访问:
pending_events:IRQ 写、worker 读清,已用短临界区保护。link_state:IRQ 错误路径和 worker/线程路径都会写,未加锁。HAL handle、PHY 缓存、初始化状态位:无锁。
调用方应保证同一时刻只有一个线程执行生命周期接口(init/start/stop/deinit)和 PHY 访问接口;不要在事件回调中调用这些接口。
im_net_eth_phy_read()/im_net_eth_phy_write()与 worker 的链路轮询可能并发访问 MDIO,冲突时可能返回IM_ERR_BUSY;需要可靠诊断序列时先stop(),或在应用层加互斥。CONFIG_SDK_PARAM_CHECK关闭时,所有空指针/参数检查消失;生产工程建议保持开启。日志等级:链路 up 为 INFO、链路 down 为 WARN、初始化/PHY/MDIO 失败为 ERROR;TX 失败细节为 DEBUG,在默认日志级别下不输出。
4.3 已知限制与注意事项
单实例:只支持一个 Ethernet MAC 网口,所有接口没有实例参数。
只支持 10/100 Mbps:PHY 抽象和 MID 都不支持 1000M;SZ18201 的 1000M 编码被视为无效。
单帧单描述符:不支持跨描述符分段,单帧上限 1536 字节;超过或分段帧会被丢弃并可能上报错误。
TX 无排队、无重试:TX ring 满时
linkoutput返回ERR_IF,由 lwIP 上层决定是否丢弃或重传。RX 每轮排空上限 256 帧:没有额外背压,超出部分依赖后续中断/轮询继续处理。
链路只在 worker 空闲时轮询:MDIO 事务会干扰该板的 RMII 接收通路,每次轮询约丢一帧;因此链路状态最多滞后约 1 秒,PHY 寄存器诊断与链路轮询并发时可能返回
IM_ERR_BUSY。流控仅对称:两个 PHY 驱动都只解析对称 pause,结果只可能是
NONE或RX_TX,非对称广告无法在链路上解析回来。无 PHY 中断:链路变化完全依赖轮询,PHY 事件类寄存器未被使用。
无内部锁:生命周期接口、PHY 访问和 worker 之间需要应用自行串行化;ISR 会写
link_state,与 worker 存在理论竞态。事件不是队列:未注册回调或回调未及时处理的事件会丢失;一次回调可能同时携带多个事件位。
hostname 当前不生效:MID 配置了
"eth"主机名,但当前 lwIP 配置未启用LWIP_NETIF_HOSTNAME,netif->hostname不会被赋值。IPv6 当前默认不可用:当前默认配置未打开
CONFIG_MIDDLEWARE_NET_LWIP_IPV6,lwIP 以 IPv4-only 构建,im_net_eth_ipv6_addr_get()返回IM_ERR_UNSUPPORTED,GOT_IPV6/LOST_IPV6事件不会产生。MAC 地址可能每次 init 变化:EFUSE 无有效 CHIP_ID 时回退到 DTRNG;只有 EFUSE 有效时同一芯片的 MAC 才稳定。
deinit()失败分支状态不一致:HAL 反初始化失败时 netif 与 RX 缓冲区已释放,但all_initialized仍为 true;此时不应直接start()。静态 RAM 占用约 776 KiB:描述符和 DMA 缓冲区全部静态分配,对内存紧张的工程需要评估。
不暴露 HAL 统计:MID 没有 MAC 收发计数、错误计数接口;需要统计时直接使用 HAL API 或外围系统监控。
mdio_timeout_ms语义双重:既用于单次 MDIO 事务超时,也用于 PHY 软件复位等待预算;调整该值会同时影响两者。参数检查依赖配置:
IM_PARAM_CHECK在CONFIG_SDK_PARAM_CHECK关闭时不生效。
5. 典型用法
5.1 最小启动
板级已实现 board_mac_init() 时,应用只需两步:
#include "board_mac.h"
#include "im_error.h"
#include "im_log.h"
#include "im_net_eth_adapter.h"
error_code_t app_eth_start(void)
{
error_code_t ret;
ret = board_mac_init();
if (ret != IM_OK) {
return ret;
}
ret = im_net_eth_start();
if (ret != IM_OK) {
return ret;
}
return IM_OK;
}board_mac_init() 负责板级时钟、PHY 电源/复位和 PHY 注册;im_net_eth_start() 在未初始化时自动调用 init()。工程中可参考 code/middleware/net/example/eth/main/main.c。
5.2 事件回调与状态查询
#include "im_net_eth_adapter.h"
static void app_eth_event(im_net_eth_t *eth, uint32_t events, void *user_data)
{
(void)eth;
(void)user_data;
if ((events & IM_NET_EVENT_LINK_UP) != 0U) {
/* 链路 up:可触发应用层重连、重新开始业务。 */
}
if ((events & IM_NET_EVENT_LINK_DOWN) != 0U) {
/* 链路 down。 */
}
if ((events & IM_NET_EVENT_GOT_IPV4) != 0U) {
/* 已获得 IPv4 地址。 */
}
if ((events & IM_NET_EVENT_LOST_IPV4) != 0U) {
/* IPv4 地址丢失。 */
}
if ((events & IM_NET_EVENT_ERROR) != 0U) {
/* 记录错误;不要在此回调内调用 stop/start/deinit。 */
}
}
error_code_t app_eth_bringup(void)
{
error_code_t ret;
im_net_eth_link_info_t link;
im_net_ipv4_addr_t ip;
ret = im_net_eth_init();
if (ret != IM_OK) {
return ret;
}
ret = im_net_eth_register_callback(app_eth_event, NULL);
if (ret != IM_OK) {
return ret;
}
ret = im_net_eth_start();
if (ret != IM_OK) {
return ret;
}
(void)im_net_eth_link_state_get(&link);
(void)im_net_eth_ip_get(&ip);
return IM_OK;
}5.3 DHCP 模式
把 Kconfig 的 IP 模式改为 DHCP:
CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_DHCP=y # CONFIG_MIDDLEWARE_NET_ADAPTER_ETH_STATIC_IP is not set
应用代码不变:im_net_eth_start() 在 netif up 后调用 netifapi_dhcp_start()。地址获取成功时 worker 会产生 GOT_IPV4 事件,im_net_eth_ip_get() 可读到实际地址。stop() 会调用 netifapi_dhcp_release_and_stop() 并忽略其返回值。
6. 示例程序与验证
6.1 示例程序
code/middleware/net/example/eth/:
入口:
main/main.c,流程为board_mac_init()→im_net_eth_start()→ 打印启动结果。默认配置:
eth_example_defconfig,EVB 板 + SZ18201 PHY + 静态 IPv410.5.4.111/24,网关10.5.4.1。示例不直接调用 PHY 模板获取接口、不重复执行 pinmux/PHY 复位,符合板级职责边界。
code/middleware/net/example/eth/README.md仍描述 “FPGA IMAGE10 + LAN8720A”,与当前 defconfig(EVB + SZ18201 + 静态 IPv4)和板级注册代码不一致,属于过期描述;以本文件和源码为准。
构建示例的基本流程:
tools/imde/bin/imde use-defconfig code/middleware/net/example/eth/eth_example_defconfig tools/imde/bin/imde -D SDK_APP_DIR=code/middleware/net/example/eth configure tools/imde/bin/imde build
SDK_APP_DIR 用于把示例的 main/main.c 接入固件;平台启动代码仍使用 main/core_v1。
启动后使用 net_info 查看接口与地址,使用 ping、iperf 验证链路和吞吐。
6.2 GMAC 模块测试
test/g32s10/module/gmac/ 是 GMAC 模块级测试集合,覆盖:
| 子目录 | 覆盖内容 |
|---|---|
register | 寄存器默认值、有效位和读写行为 |
clock | 时钟门控打开/关闭及恢复后的寄存器访问 |
basic | 10/100M、全/半双工 TCP/UDP 收发 |
interrupt | DMA RX/TX 中断及异常事件 |
ahb | DMA 突发配置、地址边界 |
mtu | 小帧、46/1500/超 MTU 帧 |
rate | 10M/100M 反复切换后的收发 |
robustness | 地址空间遍历和阈值寄存器读写 |
stress | 长时间压力测试 |
注意事项:
该测试目录基于 FPGA IMAGE10,公共 defconfig 为
g32s10_v1_gmac_function_defconfig,使用 LAN8720A 和 DHCP;它验证的是 GMAC/MAC 路径,不是当前 EVB 的 SZ18201 板级组合。测试程序负责初始化 PHY、启动 DHCP 并记录 Ethernet 事件;网络类用例需要 PC 作为对端。
clock用例只做门控和寄存器访问,不需要 PC 对端,使用独立 defconfig。该目录中的
ahb/im_net_eth.c/im_net_eth_adapter.h是测试专用副本,不应作为生产 MID 的引用;生产实现以code/middleware/net/net_adapter/ethernet/为准。
6.3 Shell 调试命令
| 命令 | 语法 | 说明 |
|---|---|---|
net_info | net_info | 列出 lwIP netif、up/link 状态、MTU、MAC、flags、IPv4、IPv6、DNS |
ping | ping <host_address> [count] [interval_ms] [packet_size] [timeout_ms] | 通过 lwIP raw socket 发送 ICMP Echo |
iperf | iperf [-u] [-s|-c host] [-p port] [-i seconds] [-b bandwidth] [-t seconds] [-l length] | TCP/UDP 吞吐测试(兼容 iperf 2.0.9 参数) |
iperf_duplex | iperf_duplex -c <peer> [-p <peer_port>] [-lp <local_port>] [-i seconds] [-b bandwidth] [-t seconds] [-l length] | 同时运行 UDP 服务端与客户端做双向测试 |
mac_phy | mac_phy <register_offset> [value] | 通过 MID 读写当前 Ethernet PHY 寄存器;register_offset 范围 0~31 |
net_config | net_config <action> <ifname> [args...] | 运行期配置:ip <ifname> <ip> <netmask> <gw>、up <ifname>、down <ifname>、default <ifname>、help |
示例:
net_info ping 10.5.4.1 iperf -c 10.5.4.10 -t 10 iperf -s mac_phy 0 mac_phy 0 0x1140
mac_phy 属于调试接口,不属于 MID 公共业务 API;生产业务代码应使用 im_net_eth_phy_read()/im_net_eth_phy_write() 或应用自己的 PHY 管理逻辑。
net_config 的修改只在运行期有效,复位后恢复 Kconfig 中的配置;ip 动作会先停止 DHCP,default 设置可能被后续链路/IP 事件触发的 metric 重算覆盖。以太网接口名通常为 en0,可用 net_info 确认。
Shell 中的 ping、iperf、iperf_duplex 使用 lwIP socket 实现,不是 PHY/MAC MID 的接口;它们用于验证 MID 启动后数据面是否正常。
7. MID 错误码与边界
7.1 error_code_t 错误码
| 公共错误码 | 出现场景 |
|---|---|
IM_OK | 操作成功 |
IM_ERR_INVALID_PARAM | 空指针、PHY 配置非法、PHY 地址或寄存器地址越界、输出缓冲区过小、addr_count == 0 |
IM_ERR_NOT_READY | 未注册 PHY;适配器未初始化;状态非 READY 时 start;未注册回调时 register_callback;PHY 扫描/链路未就绪;lwIP 初始化失败 |
IM_ERR_BUSY | 初始化完成后注册 PHY;RX 缓冲区分配失败;worker 线程创建失败;MDIO 总线忙 |
IM_ERR_TIMEOUT | lwIP tcpip 初始化超时;MDIO 超时;PHY 软件复位超时 |
IM_ERR_UNSUPPORTED | IPv6 未启用时查询 IPv6;PHY 不匹配;HAL 不支持的能力 |
IM_ERR_NO_MEMORY | lwIP 初始化等待器创建失败 |
IM_ERR_HARDWARE/IM_ERR_IO | HAL/PHY 运行期硬件或 IO 错误 |
IM_ERR_ALREADY_REGISTERED | IRQ_GMAC 已被其他模块注册(MID 内部的 IRQ 标志通常可避免该路径) |
7.2 lwIP err_t 映射
err_t | 触发场景 |
|---|---|
ERR_OK | 发送成功 |
ERR_ARG | linkoutput 参数空指针 |
ERR_RTE | netif 未 up 或链路 down |
ERR_BUF | 帧长度为 0、超过 1536 字节,或链式 pbuf 长度不一致 |
ERR_MEM | 链式 pbuf 线性化分配失败 |
ERR_IF | HAL 发送失败(含 TX ring 满) |
7.3 HAL 事件到 MID 事件的映射
见 6.4。
7.4 与 HAL/LL 文档的分界
| 主题 | 本文档 | HAL/LL 文档 |
|---|---|---|
| MAC 寄存器、位域、描述符协议 | 不展开 | mac_hal_ll_api.md |
| PHY 寄存器逐位定义 | 只说明驱动内部使用 | 不属于 HAL/LL 文档,见 PHY 驱动源码 |
| 板级时钟/电源/复位/pinmux | 只定义 board_mac_init() 契约和 EVB 参考流程 | HAL/LL 文档说明 HAL 不负责这些工作 |
| lwIP netif/pbuf/DHCP | 本文档定义 MID 的接入行为 | 不属于 HAL/LL 文档 |
7.5 版本与兼容性约定
MID 公开接口的签名、返回码和状态语义按本文档承诺;
im_net_eth_t内部字段、静态资源数量、worker 时序、日志文本属于实现细节,可能在不通知的情况下调整。PHY 抽象层的枚举数值是与 HAL 的隐式契约(
mac_phy_speed_t、mac_phy_duplex_t、mac_phy_flow_control_t),新增 PHY 驱动不得改变这些数值。新增 PHY 驱动必须实现
probe/read_id/reset/set_autoneg/get_link_state五个操作,并保持模板静态生存期;set_config为可选能力,当前 MID 不使用。如果后续引入多 MAC 实例,现有“无实例参数”的接口形态需要重新设计,本文档的所有接口契约都需要同步更新。