大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是在IAR開發環境下將關鍵函數重定向到RAM中執行的三種方法

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

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

一、准備工作

為了便於描述後面的函數重定向方法實現,我們先做一些准備工作,選定的硬件平臺是恩智浦 MIMXRT1060-EVK,主芯片內部有1MB RAM,外掛了 8MB Flash 和 32MB SDRAM。這些存儲設備在芯片系統中映射地址空間如下:

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-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar,其中 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 };

現在我們再創建一個新源文件 critical_code.c 用於示例關鍵函數,將這個源文件添加進 iled_blinky.ewp 工程裏,critical_code.c 文件中只有如下三個測試函數(它們在 main 函數裏會被調用):

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 文件相關的內容如下,顯然 critical_code.c 中的三個函數都會被鏈在 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-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [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中方法

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

2.1 __ramfunc 修飾函數

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

  • Note: __ramfunc 僅重定向被修飾的函數體本身代碼,而該函數中調用的其他函數體本身並不受影響
__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 文件相關的內容如下,此時 critical_func1() 已經被放到了 IAR 內置的 .textrw 段裏,這個段是 IAR 鏈接器專門用來收集重定向到 RAM 裏的函數。

*******************************************************************************
*** 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-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [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,現在變成了 14bytes ro code + 4bytes ro data + 4bytes rw data,因為原本占 4bytes ro code 的 critical_func1() 變成了 4bytes ro data + 4bytes rw data,是的,總消耗空間增大了,因為關鍵函數代碼體本身依然需要占用 4bytes Flash 存儲空間。

2.2 自定義section指定函數

第二種方法是利用 #pragma location 語法,將要指定的關鍵函數放到自定義段裏。比如我們將 critical_func1() 函數放到名為 .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)你會發現效果其實跟第一種方法是一模一樣的,唯一的區別就是一個用 IAR 內置的 .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 針對源文件中全部函數

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

我們現在將 critical_code.c 文件裏全部函數都重定向,只需要在工程鏈接文件 MIMXRT1062xxxxx_flexspi_nor.icf 裏做如下修改:

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

編譯鏈接修改後的 iled_blinky.ewp 工程,然後查看其映射文件(iled_blinky.map)找到跟 critical_code.c 文件相關的內容如下,此時 critical_func1/2/3() 都鏈接在 RAM 裏了,這裏比較有意思的是 critical_code.c 中的函數依舊是在 .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-MIMXRT1060\boards\evkmimxrt1060\demo_apps\led_blinky\iar\flexspi_nor_debug\obj: [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]

三、啟動文件中拷貝過程

三種函數重定向方法都介紹完了,不知道你是否曾有過這樣的疑問,這些關鍵函數機器碼到底是什麼時候怎麼從 Flash 中拷貝到 RAM 裏的?這要從工程啟動文件 startup_MIMXRT1062.s 談起。在複比特函數 Reset_Handler 的最後調用了 IAR 內置函數 __iar_program_start,這個函數中隱藏著玄機,我們可以在 \IAR Systems\Embedded Workbench 8.50.6\arm\src\lib\thumb\cstartup_M.c 文件中找到該函數原型,順著原型你應該可以發現其中的奧秘。

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 內置函數 __iar_program_start 實現細節,我們可以嘗試自己寫初始化代碼來替代 __iar_program_start 中的拷貝過程。

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

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

然後我們在啟動文件複比特函數 Reset_Handler 中調用 __iar_program_start 之前增加一個 init_data_bss() 函數調用:

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 ; 新增初始化函數
BLX R0
CPSIE I
LDR R0, =__iar_program_start
BX R0

在這個 init_data_bss() 函數裏我們來自己完成 .data, .bss, .textrw 段的初始化,示例代碼如下。其中最後的 .textrw 段的初始化就是關鍵函數從 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 段(適用第一種函數重定向方法)
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開發環境下將關鍵函數重定向到RAM中執行的三種方法痞子衡便介紹完畢了,掌聲在哪裏~~~

歡迎訂閱

文章會同時發布到我的 博客園主頁CSDN主頁知乎主頁微信公眾號 平臺上。

微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

痞子衡嵌入式:在IAR開發環境下將關鍵函數重定向到RAM中執行的三種方法的更多相關文章

  1. 痞子衡嵌入式:在IAR開發環境下為工程開啟CRC完整性校驗功能的方法

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家分享的是在IAR開發環境下為工程開啟CRC完整性校驗功能的方法. CRC校驗在嵌入式領域裏的應用非常廣,比如在通信領域,CRC檢驗值可以作為數據 ...

  2. 痞子衡嵌入式:IAR在線調試時設不同複比特類型可能會導致i.MXRT下調試現象不一致(J-Link / CMSIS-DAP)

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家分享的是IAR在線調試時設不同複比特類型可能會導致i.MXRT下調試現象不一致. 做Cortex-M內核MCU嵌入式軟件開發,可用的集成開發環境( ...

  3. python 全棧開發,Day94(Promise,箭頭函數,Django REST framework,生成json數據三種方式,serializers,Postman使用,外部python脚本調用django)

    昨日內容回顧 1. 內容回顧 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放數據 修改數據的唯一方式 异步操作 修改state中數據的步驟: ...

  4. 安卓開發筆記(十八):實現button按鈕事件的三種方法

    Android開發中有三種主要的方式用於設置View的點擊事件,1.創建內部類:2.主類中實現OnClickListener接口:3.使用匿名內部類.這三種方式都用到了OnClickListener接 ...

  5. 痞子衡嵌入式:一次利用IAR自帶CRC完整性校驗功能的實踐(為KBOOT加BCA)

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家分享的是利用IAR自帶CRC完整性校驗功能的一次實踐(為KBOOT加BCA). 痞子衡之前寫過兩篇關於IAR中自帶CRC校驗功能的文章 < ...

  6. 痞子衡嵌入式:探析開啟CRC完整性校驗的IAR工程生成.out和.bin文件先後順序

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家分享的是開啟CRC完整性校驗的IAR工程生成.out和.bin文件先後順序問題. 痞子衡之前寫了一篇 <在IAR開發環境下為工程開啟CRC ...

  7. 痞子衡嵌入式:恩智浦i.MX RT1xxx系列MCU硬件那些事(2.5)- 串行NOR Flash下載算法(IAR EWARM篇)

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家介紹的是IAR開發環境下i.MXRT的串行NOR Flash下載算法設計. 在i.MXRT硬件那些事系列之<在串行NOR Flash XI ...

  8. EFM32在使用IAR開發環境配置ICf文件以及指定程序存儲地址空間

    EFM32在IAR開發環境下指定代碼,數據的存儲空間 為了便於後續的項目昇級,管理,需要對代碼,數據的存儲空間加以設定,也在網上找下相關的資料,筆者水平有限, 如下內容不一定完全正確,如有錯誤之後,還 ...

  9. Java Web開發中用Tomcat部署項目的三種方法

    第一種方法:在tomcat中的conf目錄中,在server.xml中的,<host/>節點中添加: <Context path="/hello" docBase ...

  10. 痞子衡嵌入式:JLink Script文件基礎及其在IAR下調用方法

    大家好,我是痞子衡,是正經搞技術的痞子.今天痞子衡給大家分享的是JLink Script文件基礎及其在IAR下調用方法. JLink可以說是MCU開發者最熟悉的調試工具了,相比於其他調試器(比如DAP ...

隨機推薦

  1. 【iOS】NSNumberFormatter

    介紹 NSNumberFormatter 應該可以滿足你對數據形式的一般需求,值得了解一下. NSNumber *num1 = [NSNumber numberWithDouble:1234567.8 ...

  2. Hash索引和B樹索引

    要知道磁盤結構優化訪問的關鍵在於以block為單比特(比如每次讀取一個頁面) 這兩種索引差別也就在聚集到一個block的標准: B樹聚集到一個block是因為關鍵字在一個範圍內,關鍵字在block內的排 ...

  3. springMvc源碼學習之:spirngMvc的攔截器使用

    SpringMVC 中的Interceptor 攔截器也是相當重要和相當有用的,它的主要作用是攔截用戶的請求並進行相應的處理.比如通過它來進行權限驗證,或者是來判斷用戶是否登陸,或者是像12306 那 ...

  4. JS觸發ASP.NET服務器端控件的方法

    <asp:Button ID="Button_regId" runat="server" Font-Bold="False" OnCl ...

  5. MySQL源碼包編譯安裝

    +++++++++++++++++++++++++++++++++++++++++++標題:MySQL數據庫實力部署時間:2019年3月9日內容:MySQL源碼包進行編譯,然後部署MySQL單實例重點 ...

  6. [UWP 自定義控件]了解模板化控件(2):模仿ContentControl

    ContentControl是最簡單的TemplatedControl,而且它在UWP出場頻率很高.ContentControl和Panel是VisualTree的基礎,可以說幾乎所有VisualTr ...

  7. 基於Linux的智能家居的設計(3)

    2  硬件設計 本課題的硬件設計包含主控制器.傳輸數據設計.數據采集設計.控制驅動設計.顯示設計.門禁設計. 2.1  主控制器 依據方案三選擇S3C6410主控芯片,S3C6410是由Samsung ...

  8. Two ways to invert a string

    package com.itheima_07; import java.util.Scanner; /* * 字符串反轉 * 舉例:鍵盤錄入”abc” * 輸出結果:”cba” * * 分析: * A ...

  9. Nginx負載均衡簡易方法

    做個簡單的測試,一個Nginx, 通過FastCGI協議和另外兩臺服務器上的基於CppCMS開發的web server通信.配置方法很簡單: 首先,必須在nginx.conf文件開頭,server 配 ...

  10. try catch finall 結構裏的 return

    public class ExceptionDemo1 { public static void main(String[] args) { try { ; /b; System.out.printl ...