加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

IAR下關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的三種方法

2021/08/16
830
閱讀需 31 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是在IAR開發(fā)環(huán)境下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的三種方法。

嵌入式項目里應(yīng)用程序代碼正常是放在 Flash 中執(zhí)行的,但有時候也需要將代碼中的一些函數(shù)重定向到RAM中去執(zhí)行,這些函數(shù)包括 Flash 擦寫操作函數(shù)(假定 Flash 本身有 RWW 限制),對執(zhí)行時間要求特別高的中斷響應(yīng)函數(shù)或核心算法函數(shù)(假定 RAM 中代碼執(zhí)行速度超過 Flash)等等,這些被重定向到 RAM 中執(zhí)行的函數(shù)我們通常稱其為關(guān)鍵函數(shù)。

前段時間痞子衡跟一個美國同事正好在關(guān)鍵函數(shù)重定向機制方面交流了一下,那個美國同事用的是 IAR 開發(fā)環(huán)境,我們知道函數(shù)重定向一般是需要借助 IDE 特性的(主要是其中鏈接器),今天痞子衡就和大家聊一聊 IAR 環(huán)境下關(guān)鍵函數(shù)重定向的幾種方法及其實現(xiàn)機制:

Note:閱讀本文前需要對 《IAR鏈接文件(.icf)》、《IAR映射文件(.map)》 這兩種文件有所了解。

一、準(zhǔn)備工作

為了便于描述后面的函數(shù)重定向方法實現(xiàn),我們先做一些準(zhǔn)備工作,選定的硬件平臺是恩智浦 MIMXRT1060-EVK,主芯片內(nèi)部有1MB RAM,外掛了 8MB Flash 和 32MB SDRAM。這些存儲設(shè)備在芯片系統(tǒng)中映射地址空間如下:

NOR Flash: 0x60000000 - 0x607FFFFF (8MB)
ITCM RAM:  0x00000000 - 0x0001FFFF (128KB)
DTCM RAM:  0x20000000 - 0x2001FFFF (128KB)
OCRAM:     0x20200000 - 0x202BFFFF (768KB)
SDRAM:     0x80000000 - 0x81FFFFFF (32MB)

我們隨便選擇一個測試例程:SDK_2.9.1_EVK-MIMXRT1060boardsevkmimxrt1060demo_appsled_blinkyiar,其中 flexspi_nor 工程是最典型的代碼鏈接場景(見 MIMXRT1062xxxxx_flexspi_nor.icf 文件),全部的 readonly 段分配在 0x60000000 - 0x607FFFFF 空間(在 Flash 中),全部的 readwrite 段分配在 0x20000000 - 0x2001FFFF 空間(在 DTCM 中)。鏈接文件精簡如下:

define memory mem with size = 4G;
define region TEXT_region = mem:[from 0x60002000 to 0x607FFFFF];
define region DATA_region = mem:[from 0x20000000 to 0x2001FBFF];
define region CSTACK_region = mem:[from 0x2001FC00 to 0x2001FFFF];

define block CSTACK    with alignment = 8, size = 0x400   { };

initialize by copy { readwrite, section .textrw };
do not initialize  { section .noinit };

place at address mem: 0x60002000    { readonly section .intvec };
place in TEXT_region                { readonly };
place in DATA_region                { readwrite, zi };
place in CSTACK_region              { block CSTACK };

現(xiàn)在我們再創(chuàng)建一個新源文件 critical_code.c 用于示例關(guān)鍵函數(shù),將這個源文件添加進 iled_blinky.ewp 工程里,critical_code.c 文件中只有如下三個測試函數(shù)(它們在 main 函數(shù)里會被調(diào)用):

void critical_func1(uint32_t n)
{
    SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{
    SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{
    SysTick_DelayTicks(n*3);
}

 

編譯鏈接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相關(guān)的內(nèi)容如下,顯然 critical_code.c 中的三個函數(shù)都會被鏈在 Flash 空間里(均在 .text 段里,總大小為 18bytes)。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P1":                                        0x1f9a
  .text                ro code  0x6000'3da4    0x12  critical_code.o [1]

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  ro data  rw data
    ------                              -------  -------  -------
D:SDK_2.9.1_EVK-MIMXRT1060boardsevkmimxrt1060demo_appsled_blinkyiarflexspi_nor_debugobj: [1]
    critical_code.o                          18

*******************************************************************************
*** ENTRY LIST
***
 Entry                      Address   Size  Type      Object
 -----                      -------   ----  ----      ------
critical_func1          0x6000'3da5    0x4  Code  Gb  critical_code.o [1]
critical_func2          0x6000'3da9    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x6000'3daf    0x8  Code  Gb  critical_code.o [1]

二、重定向到RAM中方法

我們現(xiàn)在要做的事就是將 critical_code.c 文件中的函數(shù)重定向到 RAM 里執(zhí)行,原鏈接文件 MIMXRT1062xxxxx_flexspi_nor.icf 中指定的是 DTCM 來存放 readwrite 段,那我們就嘗試將關(guān)鍵函數(shù)放到 DTCM 里(如需改到 ITCM、OCRAM、SDRAM,方法類似)。

2.1 __ramfunc 修飾函數(shù)

第一種方法是利用 __ramfunc 修飾符,這個修飾符是 IAR 鏈接器能特殊識別的,主要適用重定向單個關(guān)鍵函數(shù)。比如我們用它來修飾 critical_func1() 函數(shù):

Note: __ramfunc 僅重定向被修飾的函數(shù)體本身代碼,而該函數(shù)中調(diào)用的其他函數(shù)體本身并不受影響

 

__ramfunc void critical_func1(uint32_t n)
{
    SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{
    SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{
    SysTick_DelayTicks(n*3);
}

編譯鏈接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相關(guān)的內(nèi)容如下,此時 critical_func1() 已經(jīng)被放到了 IAR 內(nèi)置的 .textrw 段里,這個段是 IAR 鏈接器專門用來收集重定向到 RAM 里的函數(shù)。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10
  RW                            0x2000'0000    0x10  <Block>
    RW-1                        0x2000'0000    0x10  <Init block>
      .textrw          inited   0x2000'000c     0x4  critical_code.o [1]    // 變化處1
"P1":                                        0x1faa
  .text                ro code  0x6000'3dac     0xe  critical_code.o [1]

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  ro data  rw data
    ------                              -------  -------  -------
D:SDK_2.9.1_EVK-MIMXRT1060boardsevkmimxrt1060demo_appsled_blinkyiarflexspi_nor_debugobj: [1]
    critical_code.o                          14        4        4           // 變化處2

*******************************************************************************
*** ENTRY LIST
***
 Entry                      Address   Size  Type      Object
 -----                      -------   ----  ----      ------
critical_func1          0x2000'000d    0x4  Code  Gb  critical_code.o [1]   // 變化處3
critical_func2          0x6000'3dad    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x6000'3db3    0x8  Code  Gb  critical_code.o [1]

在 MODULE SUMMARY 里,原本 critical_code.o 只占 18bytes 的 ro code,現(xiàn)在變成了 14bytes ro code + 4bytes ro data + 4bytes rw data,因為原本占 4bytes ro code 的 critical_func1() 變成了 4bytes ro data + 4bytes rw data,是的,總消耗空間增大了,因為關(guān)鍵函數(shù)代碼體本身依然需要占用 4bytes Flash 存儲空間。

2.2 自定義section指定函數(shù)

第二種方法是利用 #pragma location 語法,將要指定的關(guān)鍵函數(shù)放到自定義段里。比如我們將 critical_func1() 函數(shù)放到名為 .criticalFunc 的自定義段里:

#pragma location = ".criticalFunc"
void critical_func1(uint32_t n)
{
    SysTick_DelayTicks(n*1);
}
void critical_func2(uint32_t n)
{
    SysTick_DelayTicks(n*2);
}
void critical_func3(uint32_t n)
{
    SysTick_DelayTicks(n*3);
}

然后在工程鏈接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里將這個自定義的 section .criticalFunc 也放進 initialize by copy 語句中:

initialize by copy { readwrite, section .textrw, 
                     section .criticalFunc };  // 添加 .criticalFunc 段

編譯鏈接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)你會發(fā)現(xiàn)效果其實跟第一種方法是一模一樣的,唯一的區(qū)別就是一個用 IAR 內(nèi)置的 .textrw 段名,一個是用自定義段名 .criticalFunc 而已。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x6000'2000 to 0x607f'ffff] { ro };
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10
  RW                            0x2000'0000    0x10  <Block>
    RW-1                        0x2000'0000    0x10  <Init block>
      .criticalFunc    inited   0x2000'000c     0x4  critical_code.o [1]    // 變化處
"P1":                                        0x1faa
  .text                ro code  0x6000'3dac     0xe  critical_code.o [1]

2.3 針對源文件中全部函數(shù)

前兩種重定向方法都適用單個關(guān)鍵函數(shù)(如果是多個關(guān)鍵函數(shù),按方法逐一添加修飾當(dāng)然也行),但如果某個源文件里函數(shù)特別多,并且我們希望將這個源文件里函數(shù)全部重定向到 RAM 里,有沒有更便捷的方法呢?當(dāng)然有!

我們現(xiàn)在將 critical_code.c 文件里全部函數(shù)都重定向,只需要在工程鏈接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里做如下修改:

initialize by copy { readwrite, section .textrw, 
                     object critical_code.o, };  // 添加 critical_code.o 全部目標(biāo)

編譯鏈接修改后的 iled_blinky.ewp 工程,然后查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相關(guān)的內(nèi)容如下,此時 critical_func1/2/3() 都鏈接在 RAM 里了,這里比較有意思的是 critical_code.c 中的函數(shù)依舊是在 .text 段里,不過這部分 .text 段的屬性從 RO 換到了 RW。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P2":  place in [from 0x2000'0000 to 0x2001'fbff] { rw };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P2-P3|P5", part 1 of 2:                       0x10
  RW                            0x2000'0000    0x10  <Block>
    RW-1                        0x2000'0000    0x10  <Init block>
      .text            inited   0x2000'000c    0x12  critical_code.o [1]    // 變化處1

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  ro data  rw data
    ------                              -------  -------  -------
D:SDK_2.9.1_EVK-MIMXRT1060boardsevkmimxrt1060demo_appsled_blinkyiarflexspi_nor_debugobj: [1]
    critical_code.o                                   18       18           // 變化處2

*******************************************************************************
*** ENTRY LIST
***
 Entry                      Address   Size  Type      Object
 -----                      -------   ----  ----      ------
critical_func1          0x2000'000d    0x4  Code  Gb  critical_code.o [1]   // 變化處3
critical_func2          0x2000'0011    0x6  Code  Gb  critical_code.o [1]
critical_func3          0x2000'0017    0x8  Code  Gb  critical_code.o [1]

三、啟動文件中拷貝過程

三種函數(shù)重定向方法都介紹完了,不知道你是否曾有過這樣的疑問,這些關(guān)鍵函數(shù)機器碼到底是什么時候怎么從 Flash 中拷貝到 RAM 里的?這要從工程啟動文件 startup_MIMXRT1062.s 談起。在復(fù)位函數(shù) Reset_Handler 的最后調(diào)用了 IAR 內(nèi)置函數(shù) __iar_program_start,這個函數(shù)中隱藏著玄機,我們可以在 IAR SystemsEmbedded Workbench 8.50.6armsrclibthumbcstartup_M.c 文件中找到該函數(shù)原型,順著原型你應(yīng)該可以發(fā)現(xiàn)其中的奧秘。

Reset_Handler
        CPSID   I
        LDR     R0, =0xE000ED08
        LDR     R1, =__vector_table
        STR     R1, [R0]
        LDR     R2, [R1]
        MSR     MSP, R2
        LDR     R0, =SystemInit
        BLX     R0
        CPSIE   I
        LDR     R0, =__iar_program_start
        BX      R0

不過痞子衡并不打算過多介紹 IAR 內(nèi)置函數(shù) __iar_program_start 實現(xiàn)細節(jié),我們可以嘗試自己寫初始化代碼來替代 __iar_program_start 中的拷貝過程。

先在工程鏈接文件 MIMXRT1062xxxxx_flexspi_nor.icf 里做如下修改,即關(guān)掉  __iar_program_start 中的拷貝動作。

// initialize by copy { readwrite, section .textrw };
initialize manually { readwrite, section .textrw };

然后我們在啟動文件復(fù)位函數(shù) Reset_Handler 中調(diào)用 __iar_program_start 之前增加一個 init_data_bss() 函數(shù)調(diào)用:

Reset_Handler
        CPSID   I
        LDR     R0, =0xE000ED08
        LDR     R1, =__vector_table
        STR     R1, [R0]
        LDR     R2, [R1]
        MSR     MSP, R2
        LDR     R0, =SystemInit
        BLX     R0
        LDR     R0, =init_data_bss   ; 新增初始化函數(shù)
        BLX     R0
        CPSIE   I
        LDR     R0, =__iar_program_start
        BX      R0

在這個 init_data_bss() 函數(shù)里我們來自己完成 .data, .bss, .textrw 段的初始化,示例代碼如下。其中最后的 .textrw 段的初始化就是關(guān)鍵函數(shù)從 Flash 到 RAM 的拷貝過程:

#pragma section = ".data"
#pragma section = ".data_init"
#pragma section = ".bss"
#pragma section = ".textrw"
#pragma section = ".textrw_init"

void init_data_bss(void)
{
    uint32_t n;

    // 拷貝 .data 段
    uint8_t *data_ram     = __section_begin(".data");
    uint8_t *data_rom     = __section_begin(".data_init");
    uint8_t *data_rom_end = __section_end(".data_init");
    n = data_rom_end - data_rom;
    if (data_ram != data_rom)
    {
        while (n)
        {
            *data_ram++ = *data_rom++;
            n--;
        }
    }

    // 清零 .bss 段
    uint8_t *bss_start = __section_begin(".bss");
    uint8_t *bss_end   = __section_end(".bss");
    n = bss_end - bss_start;
    while (n)
    {
        *bss_start++ = 0;
        n--;
    }

    // 拷貝 .textrw 段(適用第一種函數(shù)重定向方法)
    uint8_t *code_relocate_ram     = __section_begin(".textrw");
    uint8_t *code_relocate_rom     = __section_begin(".textrw_init");
    uint8_t *code_relocate_rom_end = __section_end(".textrw_init");
    n = code_relocate_rom_end - code_relocate_rom;
    while (n)
    {
        *code_relocate_ram++ = *code_relocate_rom++;
        n--;
    }
}

至此,在IAR開發(fā)環(huán)境下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的三種方法痞子衡便介紹完畢了,掌聲在哪里~~~

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門,擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會定期分享嵌入式相關(guān)文章