API GUIDE / MAC / OVERVIEW
G32S10 MAC API 概述
模块概述:本文档定义 G32S10 SDK 当前 Ethernet MAC(GMAC)模块的 HAL 与 LL 正式接口,覆盖:
1. 文档范围
本文档定义 G32S10 SDK 当前 Ethernet MAC(GMAC)模块的 HAL 与 LL 正式接口,覆盖:
HAL MAC:面向 BSP、驱动适配层和网络中间件的 MAC/DMA/MDIO 功能级接口LL MAC:面向 GMAC 寄存器与位操作的低层static inline接口mac_regs.h:GMAC 寄存器布局、位域 mask、实例与基地址定义
本文档基于以下实现与头文件编写:
| 文件 | 层次 | 说明 |
|---|---|---|
code/include/drivers/g32s10/mac/hal_mac.h | HAL MAC | 19 个公开 HAL 函数、公开宏、枚举、配置/描述符/统计/句柄结构体 |
code/include/drivers/g32s10/mac/ll_mac.h | LL MAC | 全部 static inline 寄存器访问接口 |
code/include/drivers/g32s10/mac/mac_regs.h | 寄存器定义 | 寄存器结构体、实例宏、基地址、全部位域 mask |
code/drivers/g32s10/mac/hal_mac.c | HAL MAC | 初始化、状态机、超时、描述符协议、DMA、RX FCS、MDIO、IRQ、调试 dump |
code/drivers/g32s10/mac/Kconfig | 构建配置 | CONFIG_DRIVER_MAC、CONFIG_MAC_ENABLE_DEBUG_HELPERS |
code/drivers/g32s10/mac/CMakeLists.txt | 构建脚本 | 组件名 g32s10_mac,源文件 hal_mac.c |
补充说明:HAL 层负责描述符环与缓冲区管理、DMA/MAC 基本编程、MDIO 访问、DMA 数据通路中断状态处理、统计和调试 dump,不负责 CPM 时钟门控、MAC 软复位、SoC IRQ 注册与 PLIC 配置、PHY 驱动语义、链路自动协商和 lwIP 接入;LL 层接口全部为 static inline,直接读写寄存器,不做参数保护、不维护软件状态、不实现业务语义;本文档只定义 HAL/LL 接口契约、寄存器布局和位域约定,MAC 与 PHY、lwIP 的整体数据流见 docs/api/g32s10/middleware/mac_mid_api.md。
2. 分层关系
分层职责:Application / BSP / Driver 负责 CPM 时钟门控、MAC 软复位、管脚复用、PHY 驱动与链路管理,提供对齐的描述符/缓冲区,注册并使能 IRQ_GMAC 并在 IRQ 入口调用 hal_mac_irq_handler();HAL MAC 封装单实例 GMAC 的初始化、启停、TX/RX 描述符环、DMA、MDIO、流控、统计和调试,维护句柄状态与描述符索引,并负责描述符/缓冲区 cache 维护;LL MAC 只提供寄存器位级访问,调用方必须保证实例指针有效、参数已按位域裁剪、并发访问已串行化;mac_regs.h 定义 mac_regs_t 寄存器结构体、实例宏 MAC、基地址 GMAC_IOBASE 和全部位域 mask。
与上层模块的边界:
| 边界 | 归属 | 说明 |
|---|---|---|
| CPM 时钟门控与 MAC 软复位 | BSP + CPM 驱动 | hal_cpm_clk_enable(HAL_CPM_ID_GATE_MAC)、ll_cpm_set_mac_soft_reset()、ll_cpm_clear_mac_soft_reset() |
| 描述符与缓冲区内存 | 调用方 | 调用方提供,HAL 只编程与维护 cache |
| PHY 寄存器语义与自动协商 | PHY 驱动 | HAL 只提供 Clause 22 MDIO 读写 |
| 链路状态与速率决策 | PHY 驱动 / Ethernet adapter | HAL 只按请求写 MAC_CR 速率/双工位 |
| SoC IRQ 注册、优先级与使能 | 调用方 | im_irq_request(IRQ_GMAC, ...)、im_irq_set_priority()、im_irq_enable() |
| lwIP netif 与 pbuf 数据路径 | Middleware | 见 docs/api/g32s10/middleware/mac_mid_api.md |
3. 资源模型与公开类型
3.1 模块实例与基地址
| 项目 | 值/宏 | 说明 |
|---|---|---|
| 实例宏 | MAC | ((mac_regs_t *)(uintptr_t)GMAC_IOBASE) |
| 基地址宏 | GMAC_IOBASE | 0x134B0000 |
| 地址空间 | 64 KB | base.h 注释标注为 64KB GMAC/MAC |
| IRQ | IRQ_GMAC | IRQ_PLIC0_START + 11,当前 IRQ_PLIC0_START == 0,即软件 IRQ 号 11 |
| CPM 时钟门控 | HAL_CPM_ID_GATE_MAC | 由 BSP 通过 hal_cpm_clk_enable() / hal_cpm_clk_disable() 控制 |
| MAC 地址寄存器数量 | MAC_ADDRESS_REGISTER_COUNT | 2U |
| 实例检查 | IS_MAC_ALL_INSTANCE(INSTANCE) | 映射到 ll_mac_is_instance(INSTANCE),仅比较 macx == MAC |
#define MAC ((mac_regs_t *)(uintptr_t)GMAC_IOBASE) #define MAC_ADDRESS_REGISTER_COUNT 2U
说明:G32S10 当前只暴露 1 个 Ethernet MAC 实例,hal_mac_handle_init() 在内部直接绑定 MAC,调用方无需传实例指针。
3.2 HAL 公开宏
| 宏 | 取值 | 说明/约束 |
|---|---|---|
IS_MAC_ALL_INSTANCE(INSTANCE) | 映射到 ll_mac_is_instance() | 判断 mac_regs_t * 是否为受支持实例 |
HAL_MAC_ADDRESS_SIZE | 6U | Ethernet MAC 地址字节数 |
HAL_MAC_DEFAULT_MTU | 1500U | 默认 Ethernet MTU |
HAL_MAC_MAX_MTU | 2000U | HAL 接受的最大 MTU |
HAL_MAC_MIN_DESCRIPTOR_COUNT | 2U | TX/RX 描述符环各自的最小描述符数量 |
HAL_MAC_DMA_ALIGN | IM_CACHE_MAX_LINE_SIZE_BYTES(当前 64U) | 描述符链与数据缓冲区要求的对齐字节数 |
HAL_MAC_MAX_BUFFER_SIZE | 2047U | 单个 normal 描述符缓冲区的最大字节数(对应 DES1[10:0] 位宽) |
CONFIG_MAC_ENABLE_DEBUG_HELPERS | 默认 0U | 调试辅助代码开关,未配置时在头文件中补默认值 |
3.3 HAL 返回状态约定
HAL 可失败接口统一返回 SDK 公共错误码类型 error_code_t:
| 返回值 | 说明 |
|---|---|
IM_OK | 操作成功 |
IM_ERR_INVALID_PARAM | 参数为空、handle 未绑定、实例非法、索引或枚举非法、配置字段越界 |
IM_ERR_NOT_READY | 状态机不允许当前操作,或单次轮询下条件未满足 |
IM_ERR_BUSY | MDIO 忙、TX 环满且超时、或没有可回收的 TX 描述符 |
IM_ERR_TIMEOUT | 轮询等待超时(DMA 软复位、TX 环可用、RX 帧到达、MDIO 完成) |
IM_ERR_UNSUPPORTED | 请求 enhanced 描述符、帧跨多个 RX 描述符、调试 helper 未编译 |
IM_ERR_NO_SPACE | 接收缓冲区容量小于实际帧长 |
HAL 不返回 IM_ERR_NO_MEMORY:描述符与缓冲区全部由调用方提供,HAL 内部不做动态分配。
3.4 公开枚举
hal_mac_speed_t / hal_mac_duplex_t:
| 枚举 | 枚举值 | 说明 |
|---|---|---|
hal_mac_speed_t | HAL_MAC_SPEED_UNKNOWN = 0 | 速率未知;hal_mac_set_link_config() 不接受 |
hal_mac_speed_t | HAL_MAC_SPEED_10M = 10 | 10 Mbps |
hal_mac_speed_t | HAL_MAC_SPEED_100M = 100 | 100 Mbps |
hal_mac_duplex_t | HAL_MAC_DUPLEX_HALF = 0 | 半双工 |
hal_mac_duplex_t | HAL_MAC_DUPLEX_FULL = 1 | 全双工 |
hal_mac_duplex_t | HAL_MAC_DUPLEX_UNKNOWN = 2 | 双工未知;hal_mac_set_link_config() 不接受 |
hal_mac_state_t:
| 状态 | 说明 |
|---|---|
HAL_MAC_STATE_RESET = 0 | handle 尚未初始化,或已 hal_mac_deinit() |
HAL_MAC_STATE_READY = 1 | 硬件与 DMA 描述符已初始化,MAC/DMA 未启动 |
HAL_MAC_STATE_BUSY = 2 | MAC RX/TX 与 DMA RX/TX 已启动,允许收发 |
HAL_MAC_STATE_ERROR = 3 | HAL 记录了致命错误(DMA 软复位超时或 fatal bus error) |
状态迁移:RESET --hal_mac_init()--> READY --hal_mac_start()--> BUSY --hal_mac_stop()--> READY;hal_mac_deinit() 从任意状态回到 RESET;DMA 软复位超时或 DMA_SR.FBI 进入 ERROR,重新 hal_mac_init() 成功后回到 READY。
约束:hal_mac_start() 只接受 READY;hal_mac_transmit()/hal_mac_receive() 只接受 BUSY;hal_mac_stop()/hal_mac_deinit() 不要求特定状态,可重复调用。
hal_mac_flow_control_t / hal_mac_desc_type_t / hal_mac_desc_mode_t:
| 枚举 | 枚举值 | 值 | MAC_FCR 结果/说明 |
|---|---|---|---|
hal_mac_flow_control_t | HAL_MAC_FLOW_CONTROL_NONE | 0x0 | RFE=0、TFE=0 |
hal_mac_flow_control_t | HAL_MAC_FLOW_CONTROL_RX | 0x1 | RFE=1、TFE=0 |
hal_mac_flow_control_t | HAL_MAC_FLOW_CONTROL_TX | 0x2 | RFE=0、TFE=1 |
hal_mac_flow_control_t | HAL_MAC_FLOW_CONTROL_RX_TX | 0x3 | RFE=1、TFE=1 |
hal_mac_desc_type_t | HAL_MAC_DESC_NORMAL | 0 | 标准 4 字(16 字节)描述符,当前唯一支持格式 |
hal_mac_desc_type_t | HAL_MAC_DESC_ENHANCED | 1 | 扩展描述符,hal_mac_init() 返回 IM_ERR_UNSUPPORTED |
hal_mac_desc_mode_t | HAL_MAC_DESC_MODE_RING | 0 | 描述符连续排列,最后一个置 END_RING |
hal_mac_desc_mode_t | HAL_MAC_DESC_MODE_CHAIN | 1 | 每个描述符置 CHAINED,DES3 存下一描述符地址(尾元素回绕首元素) |
3.5 描述符结构与 TX/RX 位协议
typedef struct {
uint32_t des0; /* status/control */
uint32_t des1; /* control/buffer size */
uint32_t des2; /* buffer 1 address */
uint32_t des3; /* buffer 2 or next descriptor */
} hal_mac_dma_desc_t;TX normal 描述符字段由 hal_mac.c 内部宏定义:
| 字 | 位域 | 宏 | 含义 |
|---|---|---|---|
DES0 | [31] | HAL_MAC_TX_DESC_OWN | 1 表示 DMA 拥有该描述符 |
DES0 | [30] | HAL_MAC_TX_DESC_IC | 发送完成时请求 DMA 中断 |
DES0 | [29] | HAL_MAC_TX_DESC_LS | 该描述符包含帧尾 |
DES0 | [28] | HAL_MAC_TX_DESC_FS | 该描述符包含帧首 |
DES0 | [23:22] | HAL_MAC_TX_DESC_CIC_SHIFT | 校验和插入控制;checksum_offload 时写 3(full) |
DES0 | [21] | HAL_MAC_TX_DESC_END_RING | 该描述符是环的最后一个元素 |
DES0 | [20] | HAL_MAC_TX_DESC_CHAINED | DES3 存下一描述符地址 |
DES0 | [15] | HAL_MAC_TX_DESC_ES | 发送错误汇总(由 DMA 写) |
DES1 | [10:0] | HAL_MAC_TX_DESC_BUFFER1_MASK | buffer1 字节长度 |
DES2 | [31:0] | — | buffer1 32 位地址 |
DES3 | [31:0] | — | CHAINED=1 时为下一描述符地址,否则保留 |
RX normal 描述符字段:
| 字 | 位域 | 宏 | 含义 |
|---|---|---|---|
DES0 | [31] | HAL_MAC_RX_DESC_OWN | 1 表示 DMA 拥有该描述符 |
DES0 | [29:16] | HAL_MAC_RX_DESC_FL_SHIFT/MASK | 接收帧字节长度(14 bit) |
DES0 | [15] | HAL_MAC_RX_DESC_ES | 该帧的错误汇总 |
DES0 | [9] | HAL_MAC_RX_DESC_FS | 该描述符包含帧首 |
DES0 | [8] | HAL_MAC_RX_DESC_LS | 该描述符包含帧尾 |
DES1 | [15] | HAL_MAC_RX_DESC_END_RING | 该描述符是环的最后一个元素 |
DES1 | [14] | HAL_MAC_RX_DESC_CHAINED | DES3 存下一描述符地址 |
DES1 | [10:0] | HAL_MAC_RX_DESC_BUFFER1_MASK | buffer1 字节长度 |
DES2 | [31:0] | — | buffer1 32 位地址 |
DES3 | [31:0] | — | CHAINED=1 时为下一描述符地址,否则保留 |
说明:HAL 每帧只用一个 TX 描述符,FS 与 LS 同时置位;CHAINED/END_RING 在初始化时按 desc_mode 写入,发送时 HAL 只保留这两位再叠加 FS|LS|IC|OWN;DES1 长度位宽 11 bit,单帧上限 2047 字节;TX 回收时保留 CHAINED|END_RING,清 DES1/DES2,保留 DES3。RX 初始化时 DES0=OWN、DES1=rx_buffer_size & 0x7FF(按 desc_mode 叠加 CHAINED/END_RING)、DES2=buffer 地址、DES3=0 或下一描述符地址;hal_mac_receive() 要求 FS 与 LS 同时置位;释放 RX 描述符给 DMA 时只把 DES0 写回 OWN,DES1/DES2/DES3 保持不变。
扩展描述符 hal_mac_dma_ext_desc_t 在 normal 描述符之后追加 des4(扩展状态/控制)、des5(保留)、des6(timestamp low)、des7(timestamp high),共 32 字节。HAL 当前拒绝 HAL_MAC_DESC_ENHANCED,hal_mac_init() 返回 IM_ERR_UNSUPPORTED;该结构体只作为未来扩展的存储定义保留。
4. 当前实现约束与行为说明
4.1 单实例、时钟与复位
G32S10 当前只暴露一个 GMAC 实例 MAC。hal_mac_handle_init() 内部直接绑定 MAC,ll_mac_is_instance() 只认该地址;不存在端口号、实例表或多句柄并发寻址。
HAL 不写 CPM 寄存器:时钟门控由 BSP 调用 hal_cpm_clk_enable(HAL_CPM_ID_GATE_MAC)/hal_cpm_clk_disable();MAC 软复位由 BSP 调用 ll_cpm_set_mac_soft_reset()/ll_cpm_clear_mac_soft_reset(),MACPHYC.SOFT_RST[3] 由 CPM 驱动管理。CONFIG_DRIVER_MAC 会 select DRIVER_CPM,但 HAL 代码本身不依赖 CPM 调用顺序。未使能时钟或 MAC 仍在复位时,hal_mac_init() 的 DMA 软复位等待会超时并返回 IM_ERR_TIMEOUT,handle 进入 ERROR 状态。
4.2 增强描述符不支持
hal_mac_dma_ext_desc_t 只是结构定义。hal_mac_init() 在 config.desc_type == HAL_MAC_DESC_ENHANCED 时返回 IM_ERR_UNSUPPORTED;hal_mac_tx_clean() 也要求 HAL_MAC_DESC_NORMAL。当前只有 normal 4 字描述符可用。
4.3 描述符与缓冲区对齐和 cache
描述符数组与数据缓冲区必须按
HAL_MAC_DMA_ALIGN(IM_CACHE_MAX_LINE_SIZE_BYTES,当前 64 字节)对齐,hal_mac_config_is_valid()会检查。HAL 内部负责 cache 维护:发送前 clean TX buffer,接收前 invalidate RX buffer,描述符更新后 clean、读取前 invalidate,RX 描述符交还 DMA 前对 buffer 做 clean + invalidate。若调用方在 HAL 之外直接读写同一块 DMA buffer,需要自行处理 cache 一致性。
4.4 SoC IRQ 与 PLIC 由调用方注册
HAL 不调用 im_irq_request()、im_irq_enable()、im_irq_set_priority()。调用方需要:用 IRQ_GMAC(软件 IRQ 号 11)注册 IRQ 入口;在入口中调用 hal_mac_irq_handler(handle);根据返回的事件位图唤醒 worker thread 或做线程级处理。im_net_eth.c 是现有参考实现:start 阶段 im_irq_request(IRQ_GMAC, ...)、im_irq_set_priority()、im_irq_enable(),stop 阶段 im_irq_disable() 与 im_irq_release()。
4.5 调试 helper 双 Kconfig 条件
hal_mac_dump_regs() 与 hal_mac_dump_desc() 的日志代码受两个条件同时控制:
#if (CONFIG_SDK_ENABLE_DEBUG_HELPERS) && (CONFIG_MAC_ENABLE_DEBUG_HELPERS)
任一开关关闭时,函数直接返回 IM_ERR_UNSUPPORTED,不产生日志。
4.6 状态机与错误恢复
hal_mac_init() 失败时不会自动回滚,建议重新执行完整初始化。DMA 软复位超时与 DMA_SR.FBI 会把状态置为 ERROR,此时 hal_mac_start() 返回 IM_ERR_NOT_READY,需要重新执行 hal_mac_init()。hal_mac_stop()/hal_mac_deinit() 不清除 error_code,诊断时需注意错误位可能是历史累积值。
4.7 MACPHYC.MAC_SPEED 只读
CPM 的 MACPHYC 寄存器提供只读 MAC_SPEED[31:30](0=1000 Mbps,2=10 Mbps,3=100 Mbps,复位默认 2)、只读 MAC_PTP_PPS[0] 和 SOFT_RST[3]。HAL 不写 MACPHYC,速率/双工只通过 MAC_CR.PS/FES/DM 配置;MAC_SPEED 的实际值由板级/CPM strap 决定,HAL 不做一致性校验。
5. 典型用法
5.1 HAL 初始化与收发骨架
以下代码只使用真实头文件签名,描述初始化、启动、发送、接收和 IRQ 注册的推荐顺序。描述符与缓冲区必须静态对齐声明。该流程的可运行参考见 code/examples/hal/hal_mac_example.c(入口 example_mac_hal_selftest_run() 与 example_mac_hal_loopback_run())。
#include <string.h>
#include "hal_mac.h"
#include "hal_cpm.h"
#include "ll_cpm.h"
#include "im_irq.h"
#include "im_error.h"
#define APP_MAC_TX_DESC_COUNT 4U
#define APP_MAC_RX_DESC_COUNT 4U
#define APP_MAC_BUFFER_SIZE 1536U
#define APP_MAC_TX_TIMEOUT_MS 100U
static hal_mac_dma_desc_t s_tx_desc[APP_MAC_TX_DESC_COUNT]
__attribute__((aligned(HAL_MAC_DMA_ALIGN)));
static hal_mac_dma_desc_t s_rx_desc[APP_MAC_RX_DESC_COUNT]
__attribute__((aligned(HAL_MAC_DMA_ALIGN)));
static uint8_t s_tx_buffer[APP_MAC_TX_DESC_COUNT * APP_MAC_BUFFER_SIZE]
__attribute__((aligned(HAL_MAC_DMA_ALIGN)));
static uint8_t s_rx_buffer[APP_MAC_RX_DESC_COUNT * APP_MAC_BUFFER_SIZE]
__attribute__((aligned(HAL_MAC_DMA_ALIGN)));
static hal_mac_handle_t s_mac_handle;
static void app_mac_irq_handler(uint32_t irq_id, void *arg);
error_code_t app_mac_init(const uint8_t mac_address[HAL_MAC_ADDRESS_SIZE])
{
error_code_t ret;
/* 1. Board/CPM: enable MAC clock and release MAC soft reset. */
ret = hal_cpm_clk_enable(HAL_CPM_ID_GATE_MAC);
if (ret != IM_OK) {
return ret;
}
ll_cpm_set_mac_soft_reset();
ll_cpm_clear_mac_soft_reset();
/* 2. Bind the single MAC instance; config must be filled after it. */
ret = hal_mac_handle_init(&s_mac_handle);
if (ret != IM_OK) {
return ret;
}
memcpy(s_mac_handle.config.mac_address, mac_address, HAL_MAC_ADDRESS_SIZE);
s_mac_handle.config.mtu = HAL_MAC_DEFAULT_MTU;
s_mac_handle.config.speed = HAL_MAC_SPEED_100M;
s_mac_handle.config.duplex = HAL_MAC_DUPLEX_FULL;
s_mac_handle.config.mdc_clock_range = 0U;
s_mac_handle.config.tx_dma_pbl = 8U;
s_mac_handle.config.rx_dma_pbl = 8U;
s_mac_handle.config.desc_type = HAL_MAC_DESC_NORMAL;
s_mac_handle.config.desc_mode = HAL_MAC_DESC_MODE_RING;
s_mac_handle.config.tx_desc = s_tx_desc;
s_mac_handle.config.rx_desc = s_rx_desc;
s_mac_handle.config.tx_desc_count = APP_MAC_TX_DESC_COUNT;
s_mac_handle.config.rx_desc_count = APP_MAC_RX_DESC_COUNT;
s_mac_handle.config.tx_buffer = s_tx_buffer;
s_mac_handle.config.rx_buffer = s_rx_buffer;
s_mac_handle.config.tx_buffer_size = APP_MAC_BUFFER_SIZE;
s_mac_handle.config.rx_buffer_size = APP_MAC_BUFFER_SIZE;
if (!hal_mac_config_is_valid(&s_mac_handle.config)) {
return IM_ERR_INVALID_PARAM;
}
/* 3. Program DMA/descriptors/MAC core; state becomes READY. */
ret = hal_mac_init(&s_mac_handle);
if (ret != IM_OK) {
return ret;
}
/* 4. Register SoC IRQ; the handler only records events. */
ret = im_irq_request((uint32_t)IRQ_GMAC, app_mac_irq_handler,
"app_mac", &s_mac_handle);
if (ret != IM_OK) {
(void)hal_mac_deinit(&s_mac_handle);
return ret;
}
ret = im_irq_enable((uint32_t)IRQ_GMAC);
if (ret != IM_OK) {
(void)im_irq_release((uint32_t)IRQ_GMAC);
(void)hal_mac_deinit(&s_mac_handle);
return ret;
}
/* 5. Start MAC RX/TX and DMA; state becomes BUSY. */
return hal_mac_start(&s_mac_handle);
}IRQ 入口只做状态处理与事件记录,不拷贝帧、不回收描述符:
static void app_mac_irq_handler(uint32_t irq_id, void *arg)
{
hal_mac_handle_t *handle = (hal_mac_handle_t *)arg;
uint32_t events;
(void)irq_id;
events = hal_mac_irq_handler(handle);
/*
* Record events and wake the application worker thread.
* The worker calls hal_mac_tx_reclaim() after HAL_MAC_EVENT_TX and
* hal_mac_receive() after HAL_MAC_EVENT_RX, in thread context.
*/
(void)events;
}线程上下文的发送、回收与接收:
ret = hal_mac_transmit(&s_mac_handle, frame, frame_len, APP_MAC_TX_TIMEOUT_MS); (void)hal_mac_tx_reclaim(&s_mac_handle); /* thread context, after HAL_MAC_EVENT_TX */ rx_len = sizeof(rx_frame); ret = hal_mac_receive(&s_mac_handle, rx_frame, &rx_len, 0U); /* 0: single poll */
说明:hal_mac_tx_reclaim() 按头文件约定在线程上下文调用,IRQ 中不回收描述符;hal_mac_receive() 使用 timeout_ms = 0 时只检查一次,适合在 worker thread 中循环 drain,直到返回非 IM_OK。结束流程:im_irq_disable(IRQ_GMAC) → im_irq_release(IRQ_GMAC) → hal_mac_stop() → hal_mac_deinit(),最后按板级需要关闭 CPM 时钟。
5.2 LL 寄存器自测示例
参考实现:code/examples/ll/mac_ll_example.c,入口为:
error_code_t example_mac_ll_selftest_run(void); error_code_t example_mac_ll_loopback_run(void);
example_mac_ll_selftest_run() 的返回语义:全部检查通过返回 IM_OK;实例不是 MAC 返回 IM_ERR_INVALID_PARAM;CPM 时钟使能失败原样透传;任一寄存器回读不符返回 IM_ERR_NOT_READY;MDIO 无 PHY 或超时只告警,不影响返回值。
行为概述:
| 阶段 | 内容 |
|---|---|
| 1 | 校验 ll_mac_is_instance(MAC);使能 HAL_CPM_ID_GATE_MAC,CPM MAC 软复位(set → im_time_delay_ms(1) → clear → im_time_delay_ms(1));寄存器测试开始前先停止 DMA |
| 2 | 保存 CR、FFR、FCR、IMR、A0HR/A0LR、A1HR/A1LR、DMA_BMR、DMA_RDLAR/TDLAR、DMA_OMR、DMA_IER 快照;VR/HWFR 只记录(非全 0/全 F 仅告警) |
| 3 | CR set/clear 回读、FFR set/clear、FCR(PT + RFE/TFE)、IMR 整寄存器读写 |
| 4 | A0:写 ADDRHI 后回读,并确认 ll_mac_set_address0_high() 保留 AE;A1:ll_mac_set_address1_high() 整寄存器写入 AE/SA/MBC/ADDRHI 后回读,再写 A1LR |
| 5 | DMA_BMR(PBL=8、RPBL=8,确认 SWR 未置位)、DMA_RDLAR/TDLAR(64 字节对齐静态描述符数组的 32 位地址)、DMA_OMR(TSF|RSF|OSF,确认 ST/SR=0)、DMA_IER(NORMAL/ABNORMAL) |
| 6 | DMA_SR 读→写 MAC_DMA_SR_CLEARABLE_MASK→再读;读取 DMA_MFOCR 观察读清;触发 ll_mac_dma_poll_tx()/poll_rx();读取 CHTDR/CHRDR/CHTBAR/CHRBAR/AHBSR 仅记录 |
| 7 | MDIO 探测:等 GMII_AR.GB 清零后读 PHY 地址 0 的 ID1/ID2(超时只告警);随后恢复快照、停止 DMA、关闭时钟,全部通过时打印通过日志 |
说明:自测只使用 ll_mac.h 与 CPM 时钟/复位接口,不经过 HAL MAC,也不注册 SoC IRQ;快照在 CPM 软复位之后采集,恢复后 MAC 回到复位后状态而不是进入示例前的状态。
5.3 LL 内部 loopback 数据通路示例
example_mac_ll_loopback_run() 通过 MAC_CR.LM 打开 MAC 内部 loopback,用最小描述符环验证 TX→RX 数据通路。返回 IM_OK 表示帧回环与载荷校验通过;IM_ERR_TIMEOUT 表示 DMA 软复位/TX 完成/RX 完成超时;IM_ERR_HARDWARE 表示 TX 描述符 ES;IM_ERR_NOT_READY 表示 RX ES、FS/LS 不完整、长度非 64/68 或载荷不符;实例非法返回 IM_ERR_INVALID_PARAM;时钟失败原样透传。
行为概述:
| 阶段 | 内容 |
|---|---|
| 1 | 使能 MAC 时钟、CPM MAC 软复位(set → delay 1 ms → clear → delay 1 ms)并保存快照 |
| 2 | DMA 软复位;写 A0 单播地址;MAC_FFR=0(只保留 A0 单播过滤) |
| 3 | 配置 2 个 TX + 2 个 RX 描述符(64 字节对齐静态数组,RX 末项置 END_RING、TX 末项仅置 END_RING);1 个 1536 字节 TX buffer + 1 个 1536 字节 RX buffer,两个 RX 描述符指向同一 RX buffer |
| 4 | MAC_CR 置 PS | DM | LM | TE | RE | ACS | CST;DMA_BMR 写 PBL/RPBL=8;写 DMA_RDLAR/TDLAR;DMA_OMR=TSF | RSF | OSF;DMA_IER=0;清 DMA_SR 后置 ST | SR |
| 5 | 构造 64 字节帧(DA=A0 地址、SA=02:00:00:00:00:02、类型 0x88B5、模式载荷)写入 TX buffer 并 clean;写 TX 描述符 OWN|FS|LS、DES1=64、DES2=32 位 buffer 地址,clean 后 poll_tx;轮询 TX OWN 清零或 DMA_SR.TI(超时 EXAMPLE_MAC_TIMEOUT_MS=100),检查 TX ES |
| 6 | 清 TX 状态后 poll_rx,轮询 RX 描述符 OWN 清零;检查 RX ES、FS|LS、DES0[29:16] 长度为 64 或 68(FCS 残留 4 字节),invalidate RX buffer 后校验前 64 字节载荷 |
| 7 | 停 DMA(清 ST|SR)、DMA_IER=0、清 CR.TE|RE|LM、清 DMA_SR;恢复快照、关闭时钟 |
板级前提:
MAC 时钟门控与 MAC 软复位由 BSP/CPM 负责;loopback 前额外执行一次 DMA 软复位。
内部 loopback 仍需 MII TX 时钟,通常由外部 PHY 提供;没有 TX 时钟时发送不会推进并超时。
MAC_CR.FES保持板级复位值,因此 MII TX 时钟必须与默认 link speed 匹配;pinmux、PHY 复位、link 配置由 BSP 负责。示例不注册/不使能任何 SoC 中断,DMA 完成全部通过状态位与描述符轮询。
内部 loopback 依赖 MAC_CR.PS 选择 MII 模式;LM 置位后发送帧在 MAC 内部回送到接收路径,不经过外部 PHY 的线缆侧。
5.4 生产参考
| 参考 | 说明 |
|---|---|
code/middleware/net/net_adapter/ethernet/im_net_eth.c | 现有生产接入:HAL MAC + PHY + lwIP netif、worker thread、IRQ 注册与 RX drain |
code/examples/hal/hal_mac_example.c | HAL MAC bring-up 自测与内部 loopback 收发示例(8.1 流程的可运行版本,轮询方式,不注册 IRQ) |
docs/api/g32s10/middleware/mac_mid_api.md | MAC MID 与 PHY/lwIP 接入、初始化流程、数据流、调试命令与约束 |
code/examples/ll/mac_ll_example.c | 本文档 8.2/8.3 对应的 LL 自测与 loopback 示例 |
test/g32s10/module/gmac/ | GMAC 模块级测试(寄存器、basic、MTU、中断、压力、健壮性) |
6. 推荐关注的错误码
| API/场景 | 推荐关注错误码 | 说明 |
|---|---|---|
hal_mac_handle_init() / hal_mac_config_is_valid() | IM_ERR_INVALID_PARAM / false | handle 为空、实例检查失败,或配置字段越界、指针为空、未按 64 字节对齐 |
hal_mac_init() | IM_ERR_INVALID_PARAM | 未先 hal_mac_handle_init()、未填 config 或 config 非法 |
hal_mac_init() | IM_ERR_UNSUPPORTED | 请求 enhanced 描述符 |
hal_mac_init() | IM_ERR_TIMEOUT | CPM 时钟未使能或 MAC 仍在复位,DMA 软复位无法完成 |
hal_mac_start() | IM_ERR_NOT_READY | 尚未初始化、已启动或处于 ERROR |
hal_mac_transmit() | IM_ERR_INVALID_PARAM | data 为空、len 为 0 或超过 tx_buffer_size |
hal_mac_transmit() | IM_ERR_NOT_READY | 未 hal_mac_start(),或 systick 未初始化且 timeout_ms > 0 |
hal_mac_transmit() | IM_ERR_TIMEOUT | TX 环长时间无可用描述符 |
hal_mac_receive() | IM_ERR_TIMEOUT | 当前无新帧(timeout_ms = 0 时常见) |
hal_mac_receive() | IM_ERR_NOT_READY / IM_ERR_UNSUPPORTED | 未启动、丢弃错误帧,或帧跨多个 RX 描述符 |
hal_mac_receive() | IM_ERR_NO_SPACE | 目的缓冲区不足,*len_inout 返回所需长度 |
hal_mac_tx_reclaim() | IM_ERR_BUSY / IM_ERR_UNSUPPORTED | 暂无完成描述符 / 描述符类型不是 normal |
hal_mac_mdio_read() / hal_mac_mdio_write() | IM_ERR_BUSY | MDIO 上一次事务尚未完成 |
hal_mac_mdio_read() / hal_mac_mdio_write() | IM_ERR_TIMEOUT | MDIO 事务超时或 systick 未初始化 |
hal_mac_set_link_config() | IM_ERR_INVALID_PARAM | speed/duplex 传 UNKNOWN 或越界值 |
hal_mac_set_flow_control() | IM_ERR_INVALID_PARAM | flow_control 含 RX_TX 之外的位 |
hal_mac_set_address() | IM_ERR_INVALID_PARAM | index 不是 0/1 |
hal_mac_dump_regs() / hal_mac_dump_desc() | IM_ERR_UNSUPPORTED / IM_ERR_NOT_READY | 调试 helper 未同时使能两个 Kconfig 开关 / 描述符数量为 0 |
| LL 调用 | 无错误码 | LL 返回 void/寄存器值,参数与状态由调用方保证 |