一般Flash为non-XIP时,我们需要在RAM上运行程序。还有一种情况,就是我们不想每次调试都要将程序写入Flash,然后由BootROM进行代码的拷贝和跳转,这样可以减少Flash的烧写次数。本篇文章就来讨论一下如何实现这两种情形的RAM代码运行。本篇文章将non-XIP拷贝到RAM中运行的方案称为方案①,将不经过Flash在RAM烧写并运行程序称为方案②。
- 对于程序放在NAND Flash来说,同属于方案二
首先,如果我们想要代码在RAM中运行,需要更改链接脚本以将程序链接到RAM的地址中。对于不同IDE来说也不太相同,MCUXPresso IDE的编译器调试下载脚本支持通过SWD/JTAG接口将程序直接拷贝到RAM中,然后跳转过去运行。而对于IAR和MDK来说,似乎需要自己手动写函数拷贝代码,当然也可以自己写一个下载算法,Keil和MDK下载算法的工程在其安装目录下。本篇文章就以MCUXPresso IDE为例进行演示。
首先,我们需要将程序链接到RAM中
打开工程属性的C/C++ build
->MCU settings
->Memory details
。对于MCUXPresso IDE来说,第一个Memory如果是NOR Flash,则会保存程序到NOR Flash中,然后读写数据段将默认保存到后面第一个声明的RAM中。如果还想使用其它RAM,则需要在链接脚本中定义相关的memory,然后使用attribute
关键字将变量/函数指定到某个RAM中。
对于方案①来说,无需更改这个顺序,即BOARD_FLASH
放在第一个,RAM放在后面即可。
对于方案②来说,需要把BOARD_FLASH
删掉。此时你会发现编译出来的文件就是最原始的bin文件,没有任何头部,那些定义头部的宏定义都失效了。
接着需要在C/C++ build
->Settings
->Tool Settings
->Managed Linker Script
中勾选Link application to RAM
这个选项不会更改链接脚本,而仅仅是告诉IDE,程序是在RAM中调试的。
现在已经完成了基本的一些步骤,接下来分别讨论一下上面两种方案
一、方案②
到现在,对于方案②来说,如果你的程序链接在系统的SRAM中,就可以直接点调试开始运行程序了。
(1)链接到系统的SRAM中
RT1170中有多个SRAM,这里最好不要使用SRAM_OC1
作为程序链接的内存。那为什么不能把程序链接到SRAM_OC1
?
因为BootROM程序难免要使用到RAM,而它使用的RAM为SRAM_OC1
。BootROM是上电后一定会运行的BootLoader,而在这期间可能会使能一些中断、外设,然后在跳出的时候恢复最早的没有初始化的状态。
比如,BootROM可能初始化了USB或串口中断,然后在Serial Donwloader模式轮询来自USB或串口的消息。此时IDE通过JTAG/SWD将程序拷贝到SRAM_OC1
中运行,会出现一些意想不到的错误,比如一个char *变量初始化为"1234",但是实际上变量可能就是"12x4"。也就是说所有的BootLoader在退出前都需要恢复在进入BootLoader之前的状态。当然你可以在你的程序最开始处关闭所有中断,但是还是不建议将程序链接到SRAM_OC1
。
对于正常由BootROM引导的程序,可以将变量、函数等定义SRAM_OC1
中,这是不受影响的,因为此时已经从BootROM跳出来了。
(2)链接到SDRAM中
如果程序链接在SDRAM中,则需要指定调试器的脚本,实际上也是写一些寄存器来初始化时钟和SEMC接口。不同的调试器(DAP/Jlink)的脚本格式是不一样的,调试器会在开始运行程序前执行这个脚本。
①DAP
对于DAP来说,需要修改这两个地方,一个是指定连接的时候运行我们的初始化SDRAM的脚本,一个是增加libm7_cache
库,否则程序可能由于cache的原因无法打断点。
②JLINK
对于JLINK,指定Script即可
二、方案①
这种情况下,程序的搬移由BootROM来实现,我们需要更改IVT头和Boot Data
相关字段,以保证这些字段中的绝对地址是在我们指定链接到的RAM中的。具体的含义参考文章:RT1170启动详解:Boot配置、Bootable image头的组成
- 可以使用MCUBootUtility来实现这些字段的填充
如果程序链接到SDRAM中,我们还需要在镜像头中填充DCD字段,这样BootROM会通过DCD来初始化SDRAM,然后将程序从Flash拷贝到SDRAM中执行。