介绍
本文描述如何将ROM内容复制到执行它的RAM中。一些IAR编译器可以通过关键字支持实现这一目的,但这里采用一种更普遍适用的方法。最常见的情况是将内容从ROM复制到RAM(在RAM中使用内容),但是更普遍的机制是,它支持将内容链接到一个位置,然后存储到另外一个位置。
复制代码到RAM的基本步骤如下:
1、将代码放置这一个段中
2、使用“-Q”链接选项创建段的初始式
3、将原始的段放置在RAM
4、将段的初始式放置在ROM
5、将初始式段的内容复制到该段
6、调试应用程序
1、将代码放置到段中
将希望在RAM中运行的代码放在它自己的段中。这可以在汇编器、编译器和image中通过不同的方式实现。
使用汇编器:
RSEG RAMCODE:CODE:NOROOT(2)
do_stuff:
.
.
这创建了一个名为RAMCODE,类型为CODE的段,定义入口为do_stuff,非ROOT(指示不能丢弃该段),4字节对齐。
注意:CODE段类型和CODE段名是不同的,虽然一个名为CODE的段也是CODE类型。
使用编译器:
一些IAR编译器提供了提供类似功能的命令行选项(有关任何特定编译器的详细信息,请参阅手册)。IAR编译器提供了一个只能控制下一条语句的段的pragma (#pragma location)。
#pragma location="SPECIAL_CODE"
int do_strange_things(int a, int b)
{
.
.
.
}
这将函数do_strange_things放在SPECIAL_CODE段中,它对文件中上面或下面的声明没有影响。
在代码中使用大量的#pragma语句可能不是你所希望的,特别是如果还需要对其进行修改。#pragma和宏通常不能很好地混在一起,但是有一个IAR扩展关键字_Pragma可以很好应用在这种场景。
#define RAMCODE(x) _Pragma("location=\"RAMCODE\"") x
这个宏会将传入的参数放置在RAMCODE段中,示例:
RAMCODE(int do_strange_things(int a, int b))
{
.
.
.
}
当然,不局限于代码:
#define CONST(x) _Pragma("location=\"CONST_SEG\"") x
例如,将常量定义作为参数传入给宏,放置在CONST_SEG段:
CONST(const int a = 4711;)
CONST(const int arr[3] = { 45, 34, -2};)
使--image_input:
链接器选项--image_input允许一个字节一个字节地导入任何类型的文件,并将其放在一个段中。、
--image_input=image1.raw,image1_start,IMAGE_SEG,0
这将导入image1文件的内容到段IMAGE_SEG中。该段定义了符号image1_start,并且没有对齐。注意:通过--image_input导入的代码应该放置在链接时相同的地址上,除了一些例外,例如位置无关的代码。除非采取特别的措施,否则导入的代码只能在它链接的地址执行。
2、创建初始式段
使用-Q链接器选项创建初始化段,-Q运行在一个地方(本例是RAM)链接,在另外一个地(本例是ROM)存储。
-QRAMCODE=RAMCODE_ID
这将为段RAMCODE创建一个初始式段RAMCODE_ID。
RAMCODE包含标签和调试信息,这是代码实际运行的地方,对RAMCODE的引用都在这里进行。
RAMCODE_ID的大小和RAMCODE一样,仅包含RAMCODE的实际字节,没有标签或调试信息。这里的字节仅用于复制到RAMCODE。将字节用于任何其他目的,比如执行当前驻留的代码(在复制之前),或者将它们复制到另一个地址会导致不明确的行为。
3、将原始段放置在RAM
RAMCODE是一个复制初始化的段,因此应该使用-Z段放置命令来放置,以确保段顺序的准确。
-Z(DATA)RAMCODE=RAM_START-RAM_END
注意,段放置命令的处理顺序与.xcl文件中的顺序相同,而且每次放置都要考虑到以前的放置。可能需要移动位置,以确保某个段被放置在某个特定段之前(或之后)。
4、放置初始式段到ROM
RAMCODE_ID是一个复制初始化器段,因此应该使用Z段放置命令来确保段顺序的准确。
-Z(CONST)RAMCODE_ID=ROM_START-ROM_END
5、将字节从初始化段复制到该段
链接器并没有实际尝试将字节从RAMCODE_ID复制到RAMCODE,程序员必须在运行时进行此操作。可以在执行到达main之前修改启动代码来自动完成此操作,但是只要字节在使用RAMCODE的内容之前完成复制,其他的就无关紧要了。
实际的复制相当简单。调用memcpy就足够了,稍微棘手的部分是获取段的地址,使用IAR扩展(或可能调用汇编写的函数)。下面这段代码已经在具有单个地址空间的处理器上进行了测试。如果处理器对从ROM复制字节到RAM有特殊的要求(不同的地址空间,特殊的指令,特殊的地址要求,等等),所有这些要求都必须通过复制机制来满足。
/* copy all bytes between s (inclusive) and e (exclusive) to d */
void activate(void * s, void * e, void * d)
{
size_t size = (size_t)e - (size_t)s;
memcpy(d,s,size);
}
/* copy the bytes from RAMCODE_ID to RAMCODE */
void activate_RAMCODE(void)
{
#pragma segment="RAMCODE"
#pragma segment="RAMCODE_ID"
activate(__sfb("RAMCODE_ID"), __sfe("RAMCODE_ID"), __sfb("RAMCODE"));
}
#pragma segment=""告诉编译器有一个同名的段,除了允许在该区段上使用剩余__sfb和剩余__sfe外没有其他影响。
__sfb是一个IAR扩展字,返回段的起始地址,该段必须在前面的#pragma中提到过。如果段占用0x500- 0x52f,那么__sfb将返回0x500。
__sfe返回段之后的第一个空闲字节的地址, 如果段占用0x500-0x52F__sfe将返回0x530。
在调用了activate_RAMCODE之后,全部都就绪了。
6、调试
在本例中,复制初始化的代码在字节被复制之后应该具有完全的可调试性。