编程

在IAR Embedded Workbench for Arm中实现ROPI

  • IAR
  • 16分钟阅读
<span id="hs_cos_wrapper_name" class="hs_cos_wrapper hs_cos_wrapper_meta_field hs_cos_wrapper_type_text" style="" data-hs-cos-general-type="meta_field" data-hs-cos-type="text" >在IAR Embedded Workbench for Arm中实现ROPI</span>

随着汽车智能化程度的提高,集成的ECU(Electronic Control Unit)数量不断增加,OTA(Over-the-Air)技术变得越来越普遍,它允许车辆通过无线网络接收软件更新,从而实现功能升级和性能改进,提高了便利性。

根据硬件的不同特性,OTA有对应的实现策略:[1]

  • 如果硬件支持A/B Swap,对应的应用程序只需要链接一次就可以在两个不同的物理地址运行;
  • 如果硬件不支持A/B Swap,对应的应用程序可以通过ROPI(Read-Only Position-Independent)的方式实现在不同的物理地址运行。

基于ArmMCU在汽车行业中广泛使用,IAR Embedded Workbench for Arm是一套完整的集成开发环境,符合ISO 26262功能安全标准,支持Arm的编译和调试。本文主要以Arm Cortex-M为例介绍如何在IAR Embedded Workbench for Arm中实现ROPI

Arm ROPI介绍

Arm ROPI并没有特别的寄存器来实现,而是通过基于PC的相对地址访问代码和只读数据:

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-1

在IAR Embedded Workbench for Arm中实现ROPI

IAR Embedded Workbench for Arm中实现ROPI非常方便,勾选对应的ROPI编译选项(C/C++ Compiler > Code > Position-independence > Code and read-only data (ropi))

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-2

下面通过一个简单的例子介绍如何在IAR Embedded Workbench for Arm中实现ROPI

假设对应Code Flash的地址区间是:0x00000000 ~ 0x0007FFFF (512KB)。其中Bootloader的地址区间是0x00000000 ~ 0x00007FFF (32KB) ,而Application的地址区间分别为: 0x00008000 ~ 0x00043FFF (240KB) 0x00044000 ~ 0x0007FFFF (240KB)

Application链接的时候使用的地址区间0x00008000 ~ 0x00043FFF

define region IROM_region = mem:[from 0x00008000 to 0x00043fff];
place in IROM_region { readonly };

place at address mem: 0x00008000 { readonly section .intvec };

map文件显示对应Application放到地址区间0x00008000 ~ 0x00043FFF

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-3

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-4

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-5

由于Arm Cortex-M向量表包含的是MSP初始值和对应异常服务函数的地址:

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-6

对应异常服务函数的地址是一个绝对地址,为了让向量表支持ROPI,需要对向量表做对应的处理,其中最简单的方法就是把向量表放到RAM区域:

   SECTION CSTACK:DATA:NOROOT(3)

SECTION .intvec:CODE:ROOT(2)

EXTERN __iar_program_start
EXTERN SystemInit
EXTERN main
PUBLIC __vector_table

DATA

__vector_table

DCD sfe(CSTACK)


; Relocate Vector Table to RAM
SECTION .intvec_ram:DATA:NOROOT(2)
PUBLIC __vector_table_ram

DATA

__vector_table_ram
DCD sfe(CSTACK)

icf文件中把向量表放到RAM指定位置:

place at address mem: 0x1fff8000 { section .intvec_ram };

然后Bootloader在跳转到Application之前需要根据Application实际运行的地址修改对应的向量表:

/* Copy vector table from ROM to RAM and add corresponding offset for ROPI !!!*/
#pragma section = ".intvec"
static void CopyVectorTable(void)
{
    const uint32_t u32NrOfVectors = (uint32_t) __section_size(".intvec") / 4U;
    uint32_t * const pu32RamTable = (uint32_t *) (0x1FFF8000U);
     uint32_t * const pu32RomTable = (uint32_t *) (0x00044000U);

     /* The 1st element is MSP which does not add offset for ROPI !!! */
    pu32RamTable[0] = pu32RomTable[0];

    /* Following elements need add corresponding offset for ROPI !!! */
    for(uint32_t u32Index = 1U; u32Index < u32NrOfVectors; u32Index++)
    {
    pu32RamTable[u32Index] = pu32RomTable[u32Index] + 0x0003C000U;
    }
}

 

下面启动代码里面的Reset_Handler实现不支持ROPI

    THUMB
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER:NOROOT(2)
Reset_Handler

LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0

需要将对应向量表中的Reset_Handler改成main函数:

   SECTION CSTACK:DATA:NOROOT(3) 

SECTION .intvec:CODE:ROOT(2)

EXTERN __iar_program_start
EXTERN SystemInit
EXTERN main
PUBLIC __vector_table

DATA

__vector_table
DCD sfe(CSTACK)
DCD main ;main


; Relocate Vector Table to RAM
SECTION .intvec_ram:DATA:NOROOT(2)
PUBLIC __vector_table_ram

DATA

__vector_table_ram
DCD sfe(CSTACK)
DCD main ;main

同时在main函数里面调用SystemInit__iar_data_init3函数:

void __iar_data_init3(void);


int main(void)
{
SystemInit();
__iar_data_init3();

对应program entry配置为main函数:

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-7

Bootloader工程中下载调试来验证对应Application是否支持ROPI

Bootloader调试选项(Debugger > Images > Download extra image)中添加对应Application.out文件并配置对应的Offset (0x00044000 - 0x00008000 = 0x0003C000):

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-8

Bootloader在跳转到Application之前根据Application实际运行的地址修改对应的向量表 (对应向量表包含的地址需要是Application实际运行的地址):

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-9

然后Bootloader跳转到向量表中指定的Application的入口 (main函数):

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-10

Application中的异常服务函数可以正常运行:

china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-11

说明对应Application成功实现ROPI(因为Application链接到地址区间0x00008000 ~ 0x00043FFF,同样可以在地址区间0x00044000 ~ 0x0007FFFF正常运行)

注意事项

  • 当前IAR Embedded Workbench for ArmROPI实现有如下相关限制:
china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-12
  • 如果ROPI程序中有需要跳转到其他非ROPI程序中使用绝对地址运行的函数(比如对应函数运行在RAM的绝对地址),那么在ROPI程序中需要使用__absolute关键字声明对应函数:
china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-13
  • 调试ROPI程序的时候,如果ROPI程序实际运行地址区间跟ROPI程序链接的地址区间不一样的时候,需要配置对应的Offset(对应Offset的值是程序实际运行地址减去程序链接的地址)。另外Debug info only选项表示调试器只加载对应调试信息,而不下载对应程序:
china-learn-Implementing-ROPI-in-IAR-Embedded-Workbench-14

 

总结

本文主要以Arm Cortex-M为例介绍了如何在IAR Embedded Workbench for Arm中实现ROPI,更多关于在IAR Embedded Workbench for Arm中实现ROPI的信息,欢迎联系IAR中国。

 

参考文献:

  1. https://www.vector.com/us/en/products/application-areas/embedded-software/embedded-trends/ota-update-approaches/
  2. IAR Embedded Workbench for Arm C/C++ Development Guide
  3. Arm Cortex™-M4 Devices Generic User Guide
  4. NXP AN5163 Load Position-Independent Code (PIC) on a Kinetis Platform Using the IAR EWARM Compiler
  5. https://mypages.iar.com/s/article/Execute-in-RAM-after-copying-from-Flash-or-ROM?language=en_US
  6. https://www.iar.com/knowledge/webinars-and-events/all-you-need-to-know-about-position-independent-code-and-data
  7. https://mypages.iar.com/s/article/Position-independent-code-and-data-ROPI-and-RWPI
  8. IAR Embedded Workbench for Arm C-SPY Debugging Guide