c - STM32F7 - 从 RAM 和闪存执行代码
问题描述
我相信我的链接器脚本有问题,但我不确定这是否是真正的罪魁祸首。
背景:
我在一个项目中使用 STM32F730。uC 有 64K 闪存和 256K 内存。其中,64K 是 TCM,192k 是普通 RAM。该项目将超过 64k 的内部闪存,所以我打算从 RAM 运行额外的代码。板上有外部闪存,因此在启动时,uC 将从板载闪存执行加载程序,通过 SPI 读取板载闪存并将数据存储到 RAM 中的正确位置。这部分有效,但仅在某些时候有效。板外闪存有多种编程方式,但我相信所需的代码就在那里。此外,我的错误发生在从调试器运行时,并且绕过了从片外闪存加载。
所有问题都将涉及从调试器运行。此时没有从片外闪存加载代码。
我打开了很长的函数调用以允许跳转到 RAM,这确实解决了我遇到的一个问题,但不是这个问题。
问题:
有时,当通过调试器加载时,代码可以完美运行。其他时候,代码根本不起作用,并且出现硬故障。我不是嵌入式专家(我主要从事 PCB 设计和 FPGA 工作),但我设法逐步完成了组装并找到了导致问题的奇怪事物,但没有发现问题发生的原因。我当前的项目有一个加载到 RAM 中的 IMU 驱动程序,并且应该通过 USB CDC 将数据发送回。这在完全加载到闪存中时有效,但并不总是在 RAM 中。例如,当我打开调试优化时,它可以工作,但没有优化会导致故障。
我修改了我的链接器如下:
/* Specify the memory areas */
MEMORY
{
TCM_RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
CODE_RAM (xrw) : ORIGIN = 0x20010000, LENGTH = 64K
DATA_RAM (xrw) : ORIGIN = 0x20020000, LENGTH = 112K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* NP 2019-03-15 - RAM Executable code should be linked to RAM */
/* NOTE: With code linked this way it WILL NOT RUN WITHOUT THE DEBUGGER OR BOOTLADED */
/* Any loaded code will be wiped from RAM on a power cycle. */
.coderam :
{
. = ALIGN(4);
__RAM_CODE_SECTION_START = .;
*(.text.imu*)
. = ALIGN(4);
__RAM_CODE_SECTION_END = .;
}>FLASH//CODE_RAM
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
每个 imu 驱动程序函数都以 imu* 开头,所以我尝试使用以下行将所有 imu 函数加载到 RAM 中*(.text.imu*)
,我尝试指定 imu.o,但经过大量搜索和反复试验后无法找出正确的语法.我通过注释位在将imu.*存储在flash和RAM之间切换。注意当我使用flash时,它将它存储在地址0x0,这是为什么?我认为这是一个非常初学者的问题,但我不明白为什么。
问题:我在初始化函数中遇到问题,这是对 RAM 代码段的第一个函数调用。这是 C 代码片段:
uint8_t imu_init(void)
{
uint8_t retry_cnt = 0U;
imu_trans.device_csn_bank = IMU_CSN_BANK;
imu_trans.device_csn_pin = IMU_CSN_PIN;
imu_trans.device = DEVICE_IMU;
imu_trans.speed = SPI_SPEED_6_75MBIT;
imu_trans.rxBuf = imuRxBuf;
**Continues, but hard fault occurs earlier**
通过查看程序集,当从 RAM 运行时,以下代码在我的 init 函数中运行:
imu_init:
20010000: 0x000080b5 imu_init+0 push {r7, lr}
20010002: 0x000082b0 imu_init+2 sub sp, #8
20010004: 0x000000af imu_init+4 add r7, sp, #0
69 uint8_t retry_cnt = 0U;
20010006: 0x00000023 imu_init+6 movs r3, #0
20010008: 0x00000000 imu_init+8 movs r0, r0
71 imu_trans.device_csn_bank = IMU_CSN_BANK;
2001000a: 0x00001d4b imu_init+10 ldr r3, [pc, #116] ; (0x20010080 <imu_init+128>)
2001000c: 0x00000000 imu_init+12 movs r0, r0
2001000e: 0x00000060 imu_init+14 str r0, [r0, #0]
72 imu_trans.device_csn_pin = IMU_CSN_PIN;
20010010: 0x00fb7100 imu_init+16 ; <UNDEFINED> instruction: 0xfb000071
20010014: 0x0000001d imu_init+20 adds r0, r0, #4
20010016: 0x00004a5a imu_init+22 ldrh r2, [r1, r1]
73 imu_trans.device = DEVICE_IMU;
20010018: 0x0000001b imu_init+24 subs r0, r0, r4
2001001a: 0x00004b4f imu_init+26 ldr r7, [pc, #300] ; (0x20010148 <imu_process_vals+4>)
2001001c: 0x0000f400 imu_init+28 lsls r4, r6, #3
74 imu_trans.speed = SPI_SPEED_6_75MBIT;
很明显,硬故障就行了20010010: 0x00fb7100 imu_init+16 ; <UNDEFINED> instruction: 0xfb000071
。有趣的是(这就是我没有得到的)不在十六进制文件中!?!
*snip*
:020000042001D9
:1000000080B582B000AF0023FB711D4B1D4A5A60C2
:100010001B4B4FF480721A80194B01221A72184B35
*snip*
第三行开头是hardfault指令!但内存浏览器显示:
0x0000000020010000 B082B580 2300AF00 4B1D0000 .µ.°.¯.#...K
0x000000002001000C 60000000 0071FB00 5A4A1D00 ...`.ûq...JZ
所以看起来RAM中确实有一条错误的指令,但是为什么呢!?看起来 0x2001000C 也已损坏,因为它与十六进制文件不匹配。我最初认为我的加载过程中的某些东西正在这样做,但是如果我也在重置处理程序处设置了一个断点,就会发生这种情况,所以我不明白原因。我以为这是 SWD 的腐败,但每次都是一样的,所以我也不确定这是一个因素。我最初认为其中一些指令是等待状态(mov r0,r0),但现在我认为它们是损坏的值?
不出所料,加载到闪存时的代码读取与十六进制文件相同:
0x000000000800AEC4 B082B580 2300AF00 4B1D71FB 605A4A1D F44F4B1B 801A7280 .µ.°.¯.#ûq.K.JZ`.KOô.r..
0x000000000800AEDC 22014B19 4B18721A 729A2202 4A184B16 4B1560DA 611A4A17 .K.".r.K.".r.K.JÚ`.K.J.a
所以,TL;DR:1.如果我将所有内容加载到闪存中,它工作正常 2.如果我将 imu 驱动程序加载到 RAM 和闪存中的所有其他内容,它会出现硬故障 3.如果我打开调试优化 -Og,它会再次工作,但可能会将错误移到其他地方。
任何帮助将不胜感激,我花了大约 30 个小时试图让这个看似简单的事情发挥作用(它确实奏效了!然后它停止了),而且我已经没有想法可以尝试了。
谢谢,尼克
编辑
我想我可能已经进一步追踪了这个......我尝试从 ST 链接实用程序加载相同的文件并查看内存,然后尝试加载调试器并查看内存。
果然,调试器加载的代码与 ST Link util 加载的代码不同。ST 链接实用程序加载的代码按预期工作 - USB 枚举并发送数据。
我进行了比较,第二个 RAM 地址之后的所有内容(0x20010008 之后的所有内容)都已损坏。
所以这可能是一个调试器设置问题?我通过 SWD 使用 STlinkV3 - 项目是否需要特殊设置才能从 RAM 运行?同样,有时它可以在 RAM 中正常工作,但有时不能。
解决方案
这最终成为 truestudio 的一个问题。我不确定它是否是我正在使用的特定版本,但是在 truestudio 中使用调试功能时,调试器不会一致地对 RAM 进行编程。
解决方法是使用 STM32CubeProg,但这意味着无需调试。
有趣的是,只需将 ST 链接配置为外部工具就可以正常工作。 https://info.atollic.com/hubfs/AppNotes/st_link_utility_as_ext-tool.pdf
代码现在从闪存/内存运行/执行,并且它们之间的转换没有问题。
推荐阅读
- multithreading - 如何在Queue中实现并发
- python - 需要从基于字符串的文本文件中查找动态变化的整数
- mongodb - 如何检查mongodb数据中是否存在字段
- .htaccess - 从根目录重定向到子目录后如何从子目录运行opencart
- docker - 使用 scp 在两个 docker 容器之间复制文件
- hyperledger-fabric - 在超级账本结构的 chaicode 中执行大量事务时超时到期
- c# - 如何使用 C# 使用 ALSA 在 Linux 中获取和设置音量?
- javascript - Accordion 的 Active Title 属性无法正常工作
- python - 如何将默认值从数据库传递到 wagtail 中的 mapfield 面板
- regex - Perl 正则表达式将多行转换为单行