汽车电子expert成长之路

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

S32DS 使用Tips之不同版本之间的GNU工具链差异与外设寄存器位域访问问题总结

0
阅读(6210)

S32DS 使用Tips之S32DS for Power不同版本之间的GNU工具链差异与外设寄存器位域访问问题总结

作者按:本文适用于S32DS for Power v1.1、v1.2和 S32 DS for Power v2017.R1以及Qorivva MPC56xx/57xx和S32R系列MCU。


内容提要

引言

1. S32 DS for Power IDE不同版本使用的GNU工具链介绍

1.1 S32 DS for Power V1.1的GNU工具链

1.2 S32 DS for Power V1.2的GNU工具链

1.3 S32 DS for Power v2017.R1的GNU工具链

2. 不同S32 DS for Power代码编译结果的差异

2.1 S32 DS for Power V1.1新建应用工程默认使用其安装目录下的GNU gcc 4.5.2工具链

2.2 S32 DS for Power V1.2新建应用工程默认使用其安装目录下的GNU gcc 4.5.4工具链:

3. 问题分析(the root cause analyze)

4. 解决方案(solutions)

4. 1 方法一:在S32DS for Power v1.2中调用S32DS for Power v1.1的GNU工具链

4.2 方法二、不使用位域操作外设寄存器(推荐)

总结


引言


一个客户使用S32R274开发应用工程,将其工程从S32DS for Power v1.1升级到S32DS for Power v1.2后,遇到程序运行进入CPU内核系统异常IVOR1的问题。同样的程序代码在S32DS for Power 1.1下编译后可以正常工作,而迁移到S32DS for Power v1.2后就不能工作,只要运行以下语句:


STM_0.CR.B.CPS=119;


就会进入到CPU内核系统异常IVOR1,本文将通过这个问题的解决过程,给大家介绍S32 DS for Power V1.1与V1.2以及S32DS_Power_v2017.R1的GNU工具链差异与问题总结,希望对大家有所帮助。


1. S32 DS for Power IDE不同版本使用的GNU工具链介绍


首先,我们从S32DS for Power各版本的release note中可以查到其使用的GNU工具链信息。


Tips:S32DS for Power各版本的release note位于其安装目录\S32DS\Release_Notes文件夹下:

2-1.jpg

在S32DS的realse note中包含了很多重要的关键信息,除了GNU工具链版本信息、支持的MCU型号和调试器以及SDK等(Release Content部分)、还有支持的新part列表(New Device Support)、功能(New Features)以及加上一版本的问题修(Bug Fixes)、当前版本已知的问题和解决方法(Know issues and Workaround)等,建议大家仔细阅读和参考:

3.jpg

1.1 S32 DS for Power V1.1的GNU工具链


4.jpg

5.jpg

1.2 S32 DS for Power V1.2的GNU工具链

2-1.jpg

Tips:虽然在其release note中写的是使用gcc 4.9.2,但实际在其安装目录下为gcc 4.9.4

5.jpg


1.3 S32 DS for Power v2017.R1的GNU工具链

2-1.jpg

3.jpg

Tips:由以上信息,可以看到S32 DS for Power V1.2和S32DS_Power_v2017.R1都使用的是gcc 4.9.4,其编译器差别并不大,但S32 DS for Power V1.1使用的是gcc 4.9.2编译器,存在较大差异。


2. 不同S32 DS for Power代码编译结果的差异


接下来,我们通过将C源文件进行反汇编,看一下不同版本的S32DS for Power IDE的编译结果。


Tips:在S32DS(无论是for ARM还是for Power版本)中,在C源文件中,点击右键-->选择Disassemble即可将该C源文件进行反汇编,查看其编译生成的汇编代码:

9.jpg


2.1 S32 DS for Power V1.1新建应用工程默认使用其安装目录下的GNU gcc 4.5.2工具链


我们将寄存器位域操作的C代码:


STM_0.CR.B.CPS=119;


反汇编之后,可以看到其使用的是通用PowerPC架构指令:architecture: powerpc:common了,相应的汇编指令使用了32-bit的e_stw的字(word)存储指令:

10.jpg

2.2 S32 DS for Power V1.2新建应用工程默认使用其安装目录下的GNU gcc 4.5.4工具链:

11.jpg

我们将寄存器位域操作的C代码:


STM_0.CR.B.CPS=119;


反汇编之后,可以看到其使用的是可变字长(VLE) PowerPC架构指令集:architecture: powerpc:vle,相应的汇编指令使用了16-bit的se_stb的字节(byte)存储指令:

12.jpg

3. 问题分析(the root cause analyze)


虽然在S32R274的参考手册(S32R274 Reference Manual, Rev. 3.1, 07/2017)和MCU头文件(S32R274.h)的寄存器定义中,STM定时器的控制寄存器(STM_CR)确实存在CPS[7:0]的8-bit计数器预分配器设置:

13.jpg

但所有的STM定时器寄存器都必须按照32-bit(word)访问,否则将产生总线错误(bus error):

14.jpg

从S32R274的参考手册(S32R274 Reference Manual, Rev. 3.1, 07/2017)中关于内核的异常定义可知,总线错误(bus error)将触发e200z4201n3或者e200z7260n3的IVOR1(Machine Check--机器检查)异常:

15.jpg

16.jpg

因此,如果按照S32 DS for Power V1.2默认的GNU GCC 4.9.4编译配置C代码--STM_0.CR.B.CPS=119; 将使用se_stb指令按照字节(byte)对STM_CR寄存器进行访问,从而触发总线错误(bus error),进入CPU内核系统异常IVOR1。

17.jpg

4. 解决方案(solutions)


4. 1 方法一:在S32DS for Power v1.2中调用S32DS for Power v1.1的GNU工具链


由以上分析可知,改变应用工程属性GNU工具链的路径(C/C++ Build-->Settings-->Cross Settings-->Path),将其设置为S32DS for Power v1.1的工具链所在目录(这里我的S32DS for Power v1.1安装目录为D:\Freescale\S32_Power_v1.1\Cross_Tools\powerpc-eabivle-4_9\bin,请根据实际安装目录进行设置)之后,即可在S32DS for Power v1.2应用工程中调用S32DS for Power v1.1的GNU工具链了:

18.jpg

此时,我们再将寄存器位域操作的C代码:


STM_0.CR.B.CPS=119;


反汇编之后,可以看到其使用的指令集变为与S32DS for Power v1.1一样的:architecture: powerpc:common了,相应的汇编指令使用了32-bit的e_stw的字(word)存储指令:

19.jpg

4.2 方法二、不使用位域操作外设寄存器(推荐)


使用S32DS for Power v1.2默认的GNU工具链,但为了避免其编译生成按字节操作的指令,使用对整个寄存器进行操作,而不是头文件中定义的为控制域,比如将本例中的操作语句改为如下形式:


STM_0.CR.R |= (uint32_t)(119<<8);


则编译结果使用se_stw指令按照word(32-bit)进行操作,从而避免了非字对其引起的总线错误和系统异常:

20.jpg

Tips:一方面,Qorivva MPC56xx/57xx系列MCU和S32R系列MCU除了STM定时器模块,SWT定时器模块的所有控制和状态寄存器也必须按照字(word=4bytes)对齐进行访问:


21.jpg

另一方面,Qorivva MPC56xx/57xx系列MCU和S32R系列MCU虽然其手册和头文件定义中寄存器定义存在功能位或者位域(bitfield),但其使用的PowerPC e200系列CPU内核并不真的具有位操作指令,因此使用C语言操作这样的寄存器位/位域,将被编译成对该寄存器的读改写操作,从而造成本例中的非对齐访问总线错误(bus error),从而触发系统异常。同时,很多外设的中断标志寄存器中存在多个写1清除的中断标志位,如果按照位操作进行清除,则会引起其他中断标志位被意外清除,从而丢失中断的情况:

比如下面的FlexCAN的中断标志寄存器:

22.jpg

因此,强烈建议大家在对外设寄存器进行读写操作时不要使用位/位域,而使用对整个寄存器的读写操作,这样不但可以避免上述问题,还可以保证在不同编译器之间代码移植的一致性和通用性。


总结


通过查看github上关于GNU工具链中GCC编译器功能定义的cpu-powerpc.c,可以发现,虽然编译架构power:common和power:vle都支持Power架构的VLE指令集,但其对字长(word)的处理存在差异,前者字长为32-bit,而后者为16-bit,因此出现对相同代码的不同编译结果:


23.jpg

24.jpg


虽然前缀“se_”开头的指令(比如se_stw)和前缀”e_“开头的指令(比如e_stw)都是VLE属于指令集,但前者为16-bit指令,而后者为32-bit指令。


使用gcc 4.9.2的S32DS for Power 1.1只能编译仅生成前缀“e_”开头的32-bit VLE指令;

使用gcc 4.9.4的S32DS for Power 1.1则可以编译生成前缀“e_”开头的32-bit VLE指令和前缀”se_“开头的16-bit指令。


因此,同样的C语言程序代码,使用S32DS for Power v1.2编译效率更高--占用的空间更小。

25.jpg

比如C源代码STM_0.CR.R |= (uint32_t)(119<<8); S32DS for Power v1.1编译结果占用28个字节(0x14~0x2F),而S32DS for Power v1.2编译结果则只占用20个字节(0x14~0x28),占用存储器空间小了28.6%(2/7)。

综上所述,我给大家的建议是:尽量使用新版本的S32DS编译器,同时使用对整个寄存器操作的方式访问外设寄存器,从而保证在不同编译器版本之间移植/升级时的代码一致性和通用性。