汽车电子expert成长之路

本博客发布的个人原创精品----嵌入式系统技术文章,欢迎大家参考学习,并转发分享!

CodeWarrior IDE使用tips之prm链接文件详解

0
阅读(4741)

CodeWarrior IDE使用tips之prm链接文件详解(自定义存储器分区以及自定义RAM数据初始化与在RAM中运行函数)

引言

1. 链接文件的作用和特点

2. CodeWarrior 5.x IDE工程的prm链接文件详解

3. CodeWarrior 5.x IDE工程的prm链接文件高级应用

        3.1 自定义全局变量、常数和函数代码指定到指定的存储器空间

        3.2 自定义全局变量、常数和函数代码指定到指定的存储器空间的注意事项

总结


引言


        在嵌入式MCU应用程序的开发过程中,工程师将源代码(汇编或者C/C++语言)编辑好,到最终生成可以下载到MCU被CPU执行的二进制可执行文件,需要经过预编译、编译、汇编和链接四个过程,其分别有集成开发环境IDE中的预编译器(pre-compiler)、C/C++编译器(compiler)、汇编器(assemler)和链接器(linker)完成。

1. 链接文件的作用和特点

        在CodeWarrior 5.x IDE中,处理头文件中各种宏定义以及inline函数的预编译器集成在C/C++编译器中,其完成对C/C++源文件(.c/.cpp和.h)的编译输出相应的.obj目标文件,而汇编器完成对汇编源代码输入文件(.s和.inc)的编译并生成相应的.obj目标文件。接下来由链接器根据链接文件对obj目标文件进行链接,生成最终的abs/elf二进制可执行文件以及Flash下载编程S19文件(其与HEX和BIN文件作用相同,都包含编译结果中需要下载的代码/数据以及存储地址信息,可以相互转换(convert))。

2.jpg

        链接文件(Linker File)作为链接器的输入,其作用是告诉链接器如何将obj文件中所包含的函数/程序代码和用户数据链接到目标MCU的存储器资源中----具体的各种存储器的大小、起始地址和结束地址、属性(只读(RO)还是可读可写(R/W))、系统分区和用户分区信息、堆栈大小设置等。


链接文件具有如下特点:


        a. 不同的链接器(或者IDE)链接文件的格式不同。比如CodeWarrior 6.x(S08系列MCU工程)、CodeWarrior 4.x/5.x(S12(X)系列MCU工程)和CodeWarrior10.x(S08系列和MagniV S12Z系列MCU工程)的链接文件为prm文件;而CodeWarrior10.x(Kinetis/KEA 系列和Qorivva MPC56xx系列MCU工程)、CodeWarrior 2.x(Qorivva MPC56xx系列MCU工程)、S32DS for ARM/Power 使用的GNU工具链的工程链接文件则为ld文件; Keil/MDK工程的链接文件为sct分散加载文件; IAR工程的链接文件为icf文件。


        b.链接文件中存储器资源的种类和大小以及地址范围必须严格与目标MCU硬件存储器资源一致,才能保证工程链接结果在目标MCU上可以正常下载和运行(PS: 存储器的大小可以小于目标MCU真实的大小,但决不能大于MCU实际的大小,否则如果是Flash/EEPROM下载时(擦除和编程)会报地址溢出错误,若果是RAM,下载不会报错,但运行时会导致非法地址访问引起内核CPU异常或者直接非法地址复位,程序跑飞等);


        c.一个工程可以有多个链接文件对于不同的链接目标(target),但每一次链接(make)仅使用一个链接目标(不同的目标可以编译不同的文件和库)、通过工程属性-->l链接器配置进行设置选择。


2. CodeWarrior 5.x IDE工程的prm链接文件详解


        这里以一个S12G128MCU的CodeWarrior 5.x IDE工程为例,来介绍CodeWarrior IDE的prm链接文件。

在CodeWarrior 5.1 IDE中工程的链接文件就是新建工程时默认根据所选part number添加的prm文件,其位于CodeWarrior工程的工程视窗-->Files-->Project Settings-->Linker Files目录下,prm文件可以使用任意文本编辑器打开并修改:


        首先我们来看看一个S12G128默认工程的prm文件:


        其中蓝色高亮的部分为prm文件保留关键词和默认系统段名:

        在SEGMENTS...END中对MCU存储器资源进行分区时,一般格式如下:


        分区名    = 存储属性   访问属性  开始地址  TO 结束地址;


        其中“= ”和“TO ”为关键词,每一个分区以分号“;”结束。其余部分解释如下:


分区名:需要给每一个分区取一个名字,可以用户自定义,只要方便理解即可;


存储属性:用于指示存储器的存储属性

访问属性:包括使用near指针(16-bit地址)访问:DATA_NEAR, IBCC_NEAR(代码访问,只适用于只读存储器,如EEPROM和Flash)和使用far指针(24-bit地址)访问:DATA_FAR, IBCC_FAR(代码访问,只适用于只读存储器,如EEPROM和Flash);


开始地址和结束地址:该存储器分区的起始地址和结束地址,这里只能使用存储器的本地地址和逻辑地址/分页地址,不能直接使用全局地址/物理地址;


        在PLACEMENT...END中,将系统和用户数据/代码段放入具体分区的格式为:


        段名1,段名2...    INTO   分区名;


        其中“INTO”为关键词,表述放入,可以将一个或多个段(以不同的段名表示)放置在一个分区(以分区名区别)中,关于段名和分区名解释如下:


段名:可以是系统保留段和用户自定义段,用于表示不同的数据或代码段,可以将多个不同的段放在同一分区内:

分区名: 在SEGMENTS...END中定义好的分区;

注意:系统保留段必须放置在未分页区(non-paged)中。


3. CodeWarrior 5.x IDE工程的prm链接文件高级应用


        基于以上介绍,我们通过编辑prm链接文件可以完成以下高级应用:


3.1 自定义全局变量、常数和函数代码指定到指定的存储器空间


        具体步骤如下:


        第一步,在prm文件SEGMENTS..END中对片上RAM和Flash进行分区如下:

        并在PLACEMENT..END中给要需要自定义存储的全局变量(USR_DATA_SECTION)、常数(USR_CONST_SECTION)和函数代码(USR_CODE_SECTION)定义一个段,并分配带特定的分区中,完整的工程prm文件如下:

9.jpg

        第二步、在C语言中,使用"#pragma CODE_SEG/DATA_SEG/CONST_SEG/STRINGS_SEG 段名",将具体的用户数据和代码指定到相应的段(segment);

具体语法为:

#pragma XX_SEG (<Modif>  <Name>|DEFAULT)


"#pragma"为关键词;

XX_SEG(必选)以是以下四种之一:

10.jpg

<Modif>(可选)为该段的访问属性,可以是以下几种之一:

        __DIRECT_SEG (compatibility alias: DIRECT)

        __NEAR_SEG   (compatibility alias: NEAR)

        __CODE_SEG   (compatibility alias: CODE)

        __FAR_SEG    (compatibility alias: FAR)


<Name>|DEFAULT:用户自定义段名,或者DEFAULT(恢复默认段,将数据和代码放到DEFAULT_RAM或者DEFAULT_ROM默认段中)或者其他系统保留段,比如NON_BANK;


具体如下:

11.jpg

3.2 自定义全局变量、常数和函数代码指定到指定的存储器空间的注意事项


a. 用户新建工程后,在C文件中数据定义都默认放到DEFAULT_RAM区,函数代码都放到DEFAULT_ROM区;当时用“#pragma XX_SEG (<Modif>  <Name>”自定义一个数据、代码段之后需要使用“#pragma XX_SEG  DEFAULT”将其恢复默认段;

头文件声明和C文件定义时也需要使用“#pragma XX_SEG(<Modif>  <Name>|DEFAULT)”保持其一致性;

b. 用户自定义的全局变量数据段分区属性若为READ_WRITE,则MCUstartup过程中会对该段进行初始化,比如prm分区如下:

2.jpg

则相应链接生成的map文件如下:

2.jpg

如果用户不想让其在startup过程中被coypdown RAM初始化时置零或者赋初始化值,则需要在定义分区时将其属性该位NO_INIT,比如:

2.jpg

这样链接后map文件中_Startup启动结构体中ZeroOut区数量变为了1,不会对0x3000开始的My_Data_RAM进行初始化:

2.jpg

c. 用户定义代码段如果使用关键词RELOCATE_TO使重定向到RAM中,则可以实现将用户函数运行时地址重定向到RAM中的作用:

16.jpg

相应的工程链接结果map文件如下:

从工程链接的结果可以看到, 用户自定义函数My_Function的存储地址为0xF000而运行时地址为重定向之后的RAM地址0x3800;


注意:运行这类重定向到RAM中的函数之前,必须手动将其拷贝搬移到重定向的RAM地址才能正常运行,MCU startup启动过程中并不会帮用户做这个代码拷贝的工作:

18.jpg

总结


本文介绍的prm文件规则和使用技巧,同样适用于S08系列和MagniV S12Z系列MCU的CodeWarrior IDE工程。掌握prm文件的规则之后,我们能够实现对MCU存储器资源的使用做个性化的定制,以满足特定需求,比如对存储器进行分区、某些变量的保护和将函数代码链接到RAM中执行,这些知识对开发BootLoader十分有用,因为BootLoader中需要对Flash进行编程,运行Flash擦除和编程驱动函数时,必须将其拷贝到RAM执行,以避免Read-while-write带来的总线冲突和程序崩溃。


另外,我将本文中介绍prm文件所使用的完整CodeWarrior 5.x工程(Project_S12G128_UserCustomize_Prm_DemoPrj.mcp),分享到百度云盘,供大家参考学习:

链接:http://pan.baidu.com/s/1dESeY0D

密码:jvkj

胡恩伟

NXP汽车电子FAE

若对本文观点有任何意见和建议欢迎留言指出。

如果喜欢,可以关注本人公众号,阅读更多精彩内容。