STM32H7 + 外部 SDRAM - 长度为 3 的 memcpy 崩溃 - 字边界,缓存设置?
STM32H7 + external SDRAM - memcpy with length 3 crashes - word boundaries, cache settings?
我在 STM32H753ieval 板上有一个项目 运行,在外部存储器中有堆,使用 freeRTOS,以 STM32 立方体演示为模型。
目前MPU和缓存未启用。 (据我所知,他们的功能被注释掉了)
这在 main() 函数中有效,其中 a 和 b 在内部 ram 中。
int* aptr;
int* bptr;
int main()
{
// MPU_Config();
// CPU_CACHE_Enable();
int a[100]; int b[100];
memcpy(a, b, 3);
aptr = a;
bptr = b;
...
然而,当 freeRTOS 线程在堆上创建变量时,memcpy 不适用于某些长度值。
static void mymemcpy(char* dst, char* src, int len)
{
for (int i = 0; i < len; i++)
{
dst[i] = src[i];
}
}
void StartThread(void* arg)
{
int a[100]; int b[100];
for (int i = 0; i < 10; i++)
{
memcpy(aptr, bptr, i); //works, using same mem as main
}
for (int i = 0; i < 10; i++)
{
mymemcpy(a, b, i); //works, using external ram mem, but with mymemcpy
}
memcpy(a, b, 4); //works, seems not a overrun issue
for (int i = 0; i < 10; i++)
{
memcpy(a, b, i); //jumps to random memory when i == 3, probably an undefined handler
}
while(1);
}
这是我第一次处理微缓存和外部 ram。
这是缓存问题、内存问题还是库问题?我该如何解决?
注意:我不关心数组是否未初始化。我很高兴复制垃圾。
设备可能会崩溃,因为对初始化内存的读取可能没有设置正确的 ECC 位,当处理器在读取操作期间发现这一点时,它会出现双位错误。
先写入内存再读取或配置链接器
零初始化您的堆区域...这可能需要汇编代码才能获得正确的排序以首先启用 ram,否则零初始化可能会失败
这个问题也让我很伤心。它与未初始化的缓冲区或 ECC 位无关。这是由于访问外部 SDRAM 时数据对齐错误引起的。微读取指令可以访问在 4 字节边界内的任何字节组。它无法执行 跨越 4 字节边界的读取。示例:
Load Register R0 (4-bytes) @ 0xc0000000; // good juju
Load Register R0 (2-bytes) @ 0xc0000002; // good juju
Load Register R0 (1-byte) @ 0xc0000003; // good juju
Load Register R0 (4-bytes) @ 0xc0000004; // good juju
Load Register R0 (4-bytes) @ 0xc0000002; // bad juju
Load Register R0 (2-bytes) @ 0xc0000003; // bad juju
跨 4 字节边界执行读取导致总线异常(我的被硬故障处理程序捕获)。
您的 a 和 b 缓冲区是在堆栈上声明的,因此您不知道它们的起始地址。您的 mymemcpy() 是安全的,因为它一次复制 1 个字节。然而,newlib memcpy 实际上一次复制 4 个字节。该代码可能会尝试跨 4 字节边界读取并引发异常。即使起始地址在 4 字节边界上,结束地址也可能不是。
同样的问题适用于 memset、memcmp 等。但它也会在后台发生。示例:
std::array<uint8_t, 10> a;
std::array<uint8_t, 10> b;
a = b; // potentially bad juju because = operator calls memcpy
为了解决这个问题,我启用了数据缓存并设置了 MPU 区域。 micro 不直接访问外部 SDRAM。相反,数据被加载到没有 4 字节边界限制的缓存中。貌似还可以,但是我的信心不足。
我在 STM32H753ieval 板上有一个项目 运行,在外部存储器中有堆,使用 freeRTOS,以 STM32 立方体演示为模型。
目前MPU和缓存未启用。 (据我所知,他们的功能被注释掉了)
这在 main() 函数中有效,其中 a 和 b 在内部 ram 中。
int* aptr;
int* bptr;
int main()
{
// MPU_Config();
// CPU_CACHE_Enable();
int a[100]; int b[100];
memcpy(a, b, 3);
aptr = a;
bptr = b;
...
然而,当 freeRTOS 线程在堆上创建变量时,memcpy 不适用于某些长度值。
static void mymemcpy(char* dst, char* src, int len)
{
for (int i = 0; i < len; i++)
{
dst[i] = src[i];
}
}
void StartThread(void* arg)
{
int a[100]; int b[100];
for (int i = 0; i < 10; i++)
{
memcpy(aptr, bptr, i); //works, using same mem as main
}
for (int i = 0; i < 10; i++)
{
mymemcpy(a, b, i); //works, using external ram mem, but with mymemcpy
}
memcpy(a, b, 4); //works, seems not a overrun issue
for (int i = 0; i < 10; i++)
{
memcpy(a, b, i); //jumps to random memory when i == 3, probably an undefined handler
}
while(1);
}
这是我第一次处理微缓存和外部 ram。
这是缓存问题、内存问题还是库问题?我该如何解决?
注意:我不关心数组是否未初始化。我很高兴复制垃圾。
设备可能会崩溃,因为对初始化内存的读取可能没有设置正确的 ECC 位,当处理器在读取操作期间发现这一点时,它会出现双位错误。
先写入内存再读取或配置链接器 零初始化您的堆区域...这可能需要汇编代码才能获得正确的排序以首先启用 ram,否则零初始化可能会失败
这个问题也让我很伤心。它与未初始化的缓冲区或 ECC 位无关。这是由于访问外部 SDRAM 时数据对齐错误引起的。微读取指令可以访问在 4 字节边界内的任何字节组。它无法执行 跨越 4 字节边界的读取。示例:
Load Register R0 (4-bytes) @ 0xc0000000; // good juju
Load Register R0 (2-bytes) @ 0xc0000002; // good juju
Load Register R0 (1-byte) @ 0xc0000003; // good juju
Load Register R0 (4-bytes) @ 0xc0000004; // good juju
Load Register R0 (4-bytes) @ 0xc0000002; // bad juju
Load Register R0 (2-bytes) @ 0xc0000003; // bad juju
跨 4 字节边界执行读取导致总线异常(我的被硬故障处理程序捕获)。
您的 a 和 b 缓冲区是在堆栈上声明的,因此您不知道它们的起始地址。您的 mymemcpy() 是安全的,因为它一次复制 1 个字节。然而,newlib memcpy 实际上一次复制 4 个字节。该代码可能会尝试跨 4 字节边界读取并引发异常。即使起始地址在 4 字节边界上,结束地址也可能不是。
同样的问题适用于 memset、memcmp 等。但它也会在后台发生。示例:
std::array<uint8_t, 10> a;
std::array<uint8_t, 10> b;
a = b; // potentially bad juju because = operator calls memcpy
为了解决这个问题,我启用了数据缓存并设置了 MPU 区域。 micro 不直接访问外部 SDRAM。相反,数据被加载到没有 4 字节边界限制的缓存中。貌似还可以,但是我的信心不足。