问题记录:华大 HC32F460 内存边界禁止非对齐访问

文章更新(2023-11-14)

经过排查,本文所述异常现象并非由「非对齐访问」所致,而是因为 SRAM3 开启 ECC 校验时仅支持以字为单位进行访问。

现象描述

  • 程序无法正常启动
  • 有时会进入 hard fault 中断
  • 有时候会出现以下异常:
Call Stack + Locals                                                                    ✘
Name Location/Value Type
__scatterload_zeroinit 0x00074C56 function
__scatterload 0x00018DFE function

基础信息

芯片资源

  • RAM 192KB/0x30000
  • ROM 512KB/0x80000(其中通过分散加载给应用程序分配的空间为 416KB/0x68000)

链接数据

==============================================================================

Total RO Size (Code + RO Data) 419844 ( 410.00kB)
Total RW Size (RW Data + ZI Data) 163952 ( 160.11kB)
Total ROM Size (Code + RO Data + RW Data) 421056 ( 411.19kB)

==============================================================================

分散加载

LR_IROM1 0x00018000 0x00068000 {

; 中断向量
ER_IROM1 0x00018000 0x00068000 {
*.o (RESET, +First)
}

; 程序信息
ER_IROM2 + 0 {
*.o (SECTION_INFO_APP, +First)
}

; 程序代码
ER_IROM3 + 0 {
*(InRoot$$Sections)
.ANY (+RO)
}

; 内存空间
RW_IRAM1 0x1FFF8000 0x00030000 {
.ANY (+RW +ZI)
}

}

原因分析

由于代码量比较临界,起初我以为是 flash 的问题。但是 map 文件中显示 ROM 只占用了 411.19kB 的空间,并没有超过 416KB 的总量。那 RAM 呢?RAM 只用了 160.11KB 也没有超过 192KB 的总量,邪了门儿了真是!哎,等等!这款芯片的 RAM 我记得好像是分成了两块,怎么说的来着,瞅一眼《参考手册》:

果然,两块 RAM 间不支持非对齐访问,再一看 SRAMH 的大小:32KB!破案了!现在 RAM 的用量不正好到达 160KB(192KB - 32KB = 160KB)这个临界点了么!

超过 160KB 确实会出现异常,但不是因达到 SRAMH 所致(SRAMH 地址最小,始终会用到)而是因达到 SRAM3 且使能 ECC 校验所致。注意看红线上面的那句话:「在允许 RAM ECC 校验错误产生 NMI 中断和复位的情况下,必须对所用 RAM 空间以字(16bit)为单位进行访问」。

按照现有分散加载文件的写法,两块内存会被认为是一个整体,那么就必然存在「在内存边界处进行非对齐访问」的可能。

规避方法

非对齐访问的风险可以在分散加载文件中将两块内存断开:

LR_IROM1 0x00018000 0x00068000 {

; 中断向量
ER_IROM1 0x00018000 0x00068000 {
*.o (RESET, +First)
}

; 程序信息
ER_IROM2 + 0 {
*.o (SECTION_INFO_APP, +First)
}

; 程序代码
ER_IROM3 + 0 {
*(InRoot$$Sections)
.ANY (+RO)
}

; 内存空间
RW_IRAM1 0x1FFF8000 0x00007FF0 { ; 空出若干字节不用,以避免非对齐访问。
.ANY (+RW +ZI)
}

; 内存空间
RW_IRAM2 0x20000000 0x00028000 {
.ANY (+RW +ZI)
}

}

SRAM3 开启 ECC 后仅支持以字为单位进行访问的问题可以通过禁用 ECC 功能来解决。当然如果你一定要用 ECC 的话,那这块空间肯定不能用来让编译器自动分配内存,而是要自己来手动管理。