适用于ARM Cortex-M7内核(STM32H7、F7等系列)
ARM在高端的Cortex-M7上新增了一些内存保护的功能(RAMECC、MPU(Memory Protect Unit)等), 导致我在使用这个高级平台时碰壁好多次 , 简单记录一下我踩的坑和解决方式。
1. 现象
移植LwIP时已将phy层适配好, 接入网络时发现无法获取DHCP地址(其实是无法正常收发数据)。在ETH的Rx、Tx中断中打断点发现无法进入。
2. 第一个问题
2.1 问题描述
引脚连接正常、LwIP层正常、phy芯片时钟正常、能正常执行到ETH_Tx等代码。 但是执行后就没反应了。发现是ETH中为Rx、Tx设置了描述符内存地址和Rx缓冲区地址, 但是 链接脚本 中并没有为这些数组分配内存地址, 启动后将这几个数组的地址打印出来, 果然是0x20000xxx, 没有被放在0x30000000处。
Danger
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma location=0x30000000
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
#pragma location=0x30000080
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
#elif defined ( __CC_ARM ) /* MDK ARM Compiler */
__attribute__((at(0x30000000))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
__attribute__((at(0x30000080))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
#elif defined ( __GNUC__ ) /* GNU Compiler */
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((section(".RxDecripSection"))); /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".TxDecripSection"))); /* Ethernet Tx DMA Descriptors */
#endif
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma location = 0x30000100
extern u8_t memp_memory_RX_POOL_base[];
#elif defined ( __CC_ARM ) /* MDK ARM Compiler */
__attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
#elif defined ( __GNUC__ ) /* GNU */
__attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
#endif
2.2 解决方式: 修改链接脚本
打开链接脚本, 在SECTIONS块中添加如下代码:
/* 以太网接收描述符 */
.RxDecripSection 0x30000000 (NOLOAD) :
{
KEEP(*(.RxDecripSection))
} >RAM_D2
/* 以太网发送描述符 */
.TxDecripSection 0x30000080 (NOLOAD) :
{
KEEP(*(.TxDecripSection))
} >RAM_D2
/* 以太网接收缓冲池 (LwIP Zero-Copy) */
.Rx_PoolSection 0x30000100 (NOLOAD) :
{
KEEP(*(.Rx_PoolSection))
} >RAM_D2Info
关于链接脚本(.ld文件)的语法等, 详见: GNU ld linker script 介绍
这里涉及到比较深层的编译器知识, 不必深入了解, 并且由于不同具体情况下所需的链接脚本代码也大相径庭, 所以只需在需要时将你的链接脚本和代码丢给AI并清楚描述需求即可。
重新加载CMake并编译即可将数据放到内存对应位置
3. 第二个问题
3.1 问题
按照如上方法更改之后, 发现依然不正常, 报错如下:
DHCP Success! IP: 0.0.Got Sem, Reading...
psr: 0xa1000000
r00: 0x20007a98
r01: 0x20007a9c
r02: 0xe000ed00
r03: 0xfffffff8
r04: 0xdeadbeef
r05: 0xdeadbeef
r06: 0xdeadbeef
r07: 0x20006008
r08: 0xdeadbeef
r09: 0xdeadbeef
r10: 0xdeadbeef
r11: 0xdeadbeef
r12: 0x00000000
lr: 0x08001249
pc: 0x0801a480
hard fault on thread: eth_rx
这里询问AI, 考虑到了使用DMA发送、接收, 而STM32的MPU默认启用时, 会阻止CPU在非特权态访问DMA的Buffer(0x30000000), 一旦访问就会触发HardFault。
3.2 解决方法: 更改权限
为了不丢失缓存带来的性能提升, 为DMA的buffer单开一个MPU Region
- 更改权限为可读写
- 开启Shareable, 关闭Cacheable和Bufferable
- 将TEX field level设为1

这样即可。