天天看點

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

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

  這個關鍵函數重定向到 RAM 中執行系列文章,痞子衡已經寫過 《IAR篇》、《MCUXpresso IDE篇》,今天一鼓作氣把 Keil MDK 篇也寫了,做個全家桶。

  把 Keil MDK 放到最後來寫,其實痞子衡是有用意的。第一篇寫 IAR,我們基本上是要純手工改連結檔案。第二篇寫 MCUXpresso IDE,我們除了手工改連結檔案,也在利用它的連結檔案配置自動生成功能。現在到了 Keil MDK,這個 IDE 其實跟 MCUXpresso IDE 一樣也支援連結檔案配置自動生成,但是具體功能設計上有各有千秋,今天我們就來了解下:

  • Note: 本文使用的 Keil uVision 軟體版本是 v5.31.0.0。

一、準備工作

  為了便于描述後面的函數重定向方法實作,我們先做一些準備工作,標明的硬體平台是恩智浦 MIMXRT1170-EVK,主晶片内部有2MB RAM,外挂了 16MB Flash 和 2 片 32MB SDRAM。這些儲存設備在晶片系統中映射位址空間如下:

NOR Flash: 0x30000000 - 0x30FFFFFF (16MB)     ITCM RAM:  0x00000000 - 0x0003FFFF (256KB)     DTCM RAM:  0x20000000 - 0x2003FFFF (256KB)     OCRAM:     0x20200000 - 0x2037FFFF (1.5MB)     SDRAM:     0x80000000 - 0x83FFFFFF (64MB)           

  我們随便選擇一個測試例程:\SDK_2.10.0_EVK-MIMXRT1170\boards\evkmimxrt1170\demo_apps\hello_world\cm7\mdk,其中 flexspi_nor 工程是最典型的代碼連結場景(見 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 檔案),全部的 readonly 段配置設定在 0x30000000 - 0x30FFFFFF 空間(在 Flash 中),全部的 readwrite 段配置設定在 0x20000000 - 0x2003FFFF 空間(在 DTCM 中)。連結檔案精簡如下:

LR_m_text 0x30002000 0x00FFE000 {       VECTOR_ROM 0x30002000 FIXED 0x00000400 {         * (.isr_vector,+FIRST)       }       ER_m_text 0x30002400 FIXED 0x00FFDC00 {         * (InRoot$$Sections)         .ANY (+RO)       }       RW_m_data 0x20000000 0x0003F800 {         .ANY (+RW +ZI)       }       ARM_LIB_HEAP +0 EMPTY 0x00000400 {       }       ARM_LIB_STACK 0x20040000 EMPTY -0x00000400 {       }     }           

  現在我們再建立一個新源檔案 critical_code.c 用于示例關鍵函數,将這個源檔案添加進 hello_world_demo_cm7.uvprojx 工程裡,critical_code.c 檔案中隻有如下三個測試函數(它們在 main 函數裡會被調用):

void critical_func1(uint32_t n)     {         PRINTF("Arg = %d .\r\n", n);     }     void critical_func2(uint32_t n)     {         PRINTF("Arg * 2 = %d .\r\n", 2 * n);     }     void critical_func3(uint32_t n)     {         PRINTF("Arg * 3 = %d .\r\n", 3 * n);     }           

  編譯連結修改後的工程,然後檢視其映射檔案(hello_world_demo_cm7.map)找到跟 critical_code.c 檔案相關的内容如下,顯然 critical_code.c 中的三個函數都會被鍊在 Flash 空間裡(均在 .text 段裡)。

===============================================================================     Image Symbol Table         Global Symbols         Symbol Name                              Value     Ov Type        Size  Object(Section)         critical_func1                           0x30005429   Thumb Code    28  critical_code.o(.text.critical_func1)         critical_func2                           0x30005449   Thumb Code    32  critical_code.o(.text.critical_func2)         critical_func3                           0x30005469   Thumb Code    36  critical_code.o(.text.critical_func3)     ===============================================================================     Memory Map of the image         Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b68, Max: 0x00fbdc00, ABSOLUTE, FIXED)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x30005428   0x30005428   0x0000001c   Code   RO           17    .text.critical_func1  critical_code.o         0x30005444   0x30005444   0x00000004   PAD         0x30005448   0x30005448   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o         0x30005468   0x30005468   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o         0x3000548c   0x3000548c   0x00000004   PAD     ===============================================================================     Image component sizes           Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name             96         56          0          0          0        903   critical_code.o           

二、重定向到RAM中方法

  我們現在要做的事就是将 critical_code.c 檔案中的函數重定向到 RAM 裡執行,原連結檔案 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 中指定的是 DTCM 來存放 readwrite 段,那我們就嘗試将關鍵函數放到 DTCM 裡(如需改到 ITCM、OCRAM、SDRAM,方法類似)。

2.1 自定義section指定函數 - 針對單個函數

  第一種方法是用 __attribute__((section("UserSectionName"))) 文法來修飾函數定義,将其放到自定義程式段裡。這種方法主要适用重定向單個關鍵函數,比如我們将 critical_func1() 函數放到名為 .criticalFunc 的自定義段裡:

__attribute__((section(".criticalFunc"))) void critical_func1(uint32_t n)     {         PRINTF("Arg = %d .\r\n", n);     }     void critical_func2(uint32_t n)     {         PRINTF("Arg * 2 = %d .\r\n", 2 * n);     }     void critical_func3(uint32_t n)     {         PRINTF("Arg * 3 = %d .\r\n", 3 * n);     }           

  然後在工程連結檔案 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 裡将這個自定義的 section .criticalFunc 也放進 RW_m_data 執行域中:

LR_m_text 0x30002000 0x00FFE000 {       ; ...       RW_m_data 0x20000000 0x0003F800 {         .ANY (+RW +ZI)         * (.criticalFunc)  ;添加 .criticalFunc 段           ; 第二種寫法:*.o (.criticalFunc)       }       ; ...     }           

  編譯連結修改後的工程,然後檢視其映射檔案(hello_world_demo_cm7.map)找到跟 critical_code.c 檔案相關的内容如下,此時 critical_func1() 已經被放到自定義段 .criticalFunc 裡,并且這個段被 MDK 底層連結器連結到了 RAM 裡(RW_m_data 執行域空間)。

===============================================================================     Image Symbol Table         Global Symbols         Symbol Name                              Value     Ov Type        Size  Object(Section)         critical_func1                           0x20000001   Thumb Code    28  critical_code.o(.criticalFunc)         critical_func2                           0x30005429   Thumb Code    32  critical_code.o(.text.critical_func2)         critical_func3                           0x30005449   Thumb Code    36  critical_code.o(.text.critical_func3)     ===============================================================================     Memory Map of the image         Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f60, Size: 0x00000078, Max: 0x0003f800, ABSOLUTE)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x20000000   0x30005f60   0x0000001c   Code   RO           17    .criticalFunc       critical_code.o         Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b60, Max: 0x00fbdc00, ABSOLUTE, FIXED)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x30005428   0x30005428   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o         0x30005448   0x30005448   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o         0x3000546c   0x3000546c   0x00000004   PAD     ===============================================================================     Image component sizes           Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name             96         56          0          0          0        903   critical_code.o           

2.2 自定義section指定函數 - 針對同一檔案裡的多個函數

  第二種方法是利用 #pragma 文法來修飾函數定義(注意 AC5 編譯器 Armcc 和 AC6 編譯器 Armclang 文法不太一樣),将同一源檔案裡緊挨在一起的多個關鍵函數放到自定義段裡。比如我們将 critical_func1() 和 critical_func2() 函數放到名為 .criticalFunc 的自定義段裡:

  • Note: 這種方法一般情況下不太推薦,代碼可移植性較差。
痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法
#pragma clang section text = ".criticalFunc"  // 适用 AC6 編譯器(範圍開始)     //#pragma arm section code = ".criticalFunc"  // 适用 AC5 編譯器(範圍開始)     void critical_func1(uint32_t n)     {         PRINTF("Arg = %d .\r\n", n);     }     void critical_func2(uint32_t n)     {         PRINTF("Arg * 2 = %d .\r\n", 2 * n);     }     #pragma clang section text = ""   // 适用 AC6 編譯器(範圍結束)     //#pragma arm section code        // 适用 AC5 編譯器(範圍結束)     void critical_func3(uint32_t n)     {         PRINTF("Arg * 3 = %d .\r\n", 3 * n);     }           

  然後也是同樣在工程連結檔案 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 裡将這個自定義的 section .criticalFunc 也放進 RW_m_data 執行域中:

LR_m_text 0x30002000 0x00FFE000 {       ; ...       RW_m_data 0x20000000 0x0003F800 {         .ANY (+RW +ZI)         * (.criticalFunc)  ;添加 .criticalFunc 段       }       ; ...     }           

  編譯連結修改後的工程,然後檢視其映射檔案(hello_world_demo_cm7.map)找到跟 critical_code.c 檔案相關的内容如下,此時 critical_func1/2() 均已經被放到自定義段 .criticalFunc 裡,并且這個段被 MDK 底層連結器連結到了 RAM 裡(RW_m_data 執行域空間)。

===============================================================================     Image Symbol Table         Global Symbols         Symbol Name                              Value     Ov Type        Size  Object(Section)         critical_func1                           0x20000001   Thumb Code    28  critical_code.o(.criticalFunc)         critical_func2                           0x20000021   Thumb Code    32  critical_code.o(.criticalFunc)         critical_func3                           0x30005429   Thumb Code    36  critical_code.o(.text.critical_func3)     ===============================================================================     Memory Map of the image         Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f50, Size: 0x0000009c, Max: 0x0003f800, ABSOLUTE)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x20000000   0x30005f50   0x00000040   Code   RO           17    .criticalFunc       critical_code.o         Execution Region ER_m_text (Exec base: 0x30002400, Load base: 0x30002400, Size: 0x00003b4c, Max: 0x00fbdc00, ABSOLUTE, FIXED)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x30005428   0x30005428   0x00000024   Code   RO           19    .text.critical_func3  critical_code.o         0x3000544c   0x3000544c   0x00000004   PAD     ===============================================================================     Image component sizes           Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name            100         60          0          0          0        887   critical_code.o           

2.3 針對源檔案中全部函數

  前兩種重定向方法都是針對具體函數的(如果是多個關鍵函數分散在多個檔案裡,按方法逐一添加修飾當然也行),但如果某個庫源檔案特别多,并且我們希望将這些源檔案裡函數全部重定向到 RAM 裡,有沒有更便捷的方法呢?當然有!

  我們現在将 critical_code.c 檔案裡全部函數都重定向,隻需要在工程連結檔案 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 裡做如下修改:

LR_m_text 0x30002000 0x00FFE000 {       ; ...       RW_m_data 0x20000000 0x0003F800 {         .ANY (+RW +ZI)         critical_code.o (+RO +RW +ZI)  ;添加 critical_code.o 全部目标       }       ; ...     }           

  編譯連結修改後的工程,然後檢視其映射檔案(hello_world_demo_cm7.map)找到跟 critical_code.c 檔案相關的内容如下,此時 critical_func1/2/3() 都連結在 RAM 裡了。

===============================================================================     Image Symbol Table         Global Symbols         Symbol Name                              Value     Ov Type        Size  Object(Section)         critical_func1                           0x20000001   Thumb Code    28  critical_code.o(.text.critical_func1)         critical_func2                           0x20000021   Thumb Code    32  critical_code.o(.text.critical_func2)         critical_func3                           0x20000041   Thumb Code    36  critical_code.o(.text.critical_func3)     ===============================================================================     Memory Map of the image         Execution Region RW_m_data (Exec base: 0x20000000, Load base: 0x30005f30, Size: 0x000000c0, Max: 0x0003f800, ABSOLUTE)         Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object         0x20000000   0x30005f30   0x0000001c   Code   RO           17    .text.critical_func1  critical_code.o         0x2000001c   0x30005f4c   0x00000004   PAD         0x20000020   0x30005f50   0x00000020   Code   RO           19    .text.critical_func2  critical_code.o         0x20000040   0x30005f70   0x00000024   Code   RO           21    .text.critical_func3  critical_code.o     ===============================================================================     Image component sizes           Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name             96         56          0          0          0        903   critical_code.o           

2.4 被淘汰的 __ram 關鍵字

  IAR 和 MCUXpresso IDE 下都内置了 .ramfunc 段,使用關鍵字 __ramfunc 或者 __RAMFUNC() 來修飾函數就可以直接将該函數放進内置的 .ramfunc 段裡,不需要使用者再去手工改連結檔案。

  早期的 Keil native compiler(CARM) 确實是支援類似的特性的,就是用 __ram 來修飾函數,但是到了 AC5/AC6 這個特性被拿掉了,使用者一律需要修改自己連結檔案來完成。為了代碼相容,在 MDK 下可以自己定義一個宏,然後連結檔案裡将 .ramfunc 放進 RW_m_data 執行域裡。

#define __ramfunc __attribute__((section(".ramfunc")))           

三、連結檔案自動生成功能

  第二節裡介紹的方法都是基于使用者自己提供的連結檔案,如果想啟動 MDK 的連結檔案自動生成功能,需要在工程 Option / Linker 裡将 Use Memory Layout from Target Dialog 選項勾選上,這時候使用者提供的 MIMXRT1176xxxxx_cm7_flexspi_nor.scf 檔案就失效了,MDK 會自動生成一個名為 hello_world_demo_cm7.sct 的連結檔案。

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

  自動生成的 hello_world_demo_cm7.sct 連結檔案配置非常簡單,在工程 Option / Target 裡有 Read/Only Memory Areas 和 Read/Write Memory Areas 指定,這裡僅簡單提供了 RO 和 RW 段空間指定設定,沒有關于使用者自定義段的單獨設定。

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

  不過比較特色的是,在 MDK 裡可以單獨為某個檔案指定 Memory Assignment,這樣我們也能跟 2.3 節裡的方法一樣實作整個檔案裡的函數重定向。

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

四、啟動檔案中拷貝過程

  三種函數重定向方法都介紹完了,不知道你是否曾有過這樣的疑問,這些關鍵函數機器碼到底是什麼時候怎麼從 Flash 中拷貝到 RAM 裡的?這要從工程啟動檔案 startup_MIMXRT1176_cm7.S 談起。在複位函數 Reset_Handler 的最後調用了 MDK 内置函數 __main,這個函數中隐藏着玄機,我們可以在 ARM CMSIS 庫中找到該函數原型,順着原型你應該可以發現其中的奧秘。

Reset_Handler:         cpsid   i         .equ    VTOR, 0xE000ED08         ldr     r0, =VTOR         ldr     r1, =__Vectors         str     r1, [r0]         ldr     r2, [r1]         msr     msp, r2         ldr   r0,=SystemInit         blx   r0         cpsie   i         ldr   r0,=__main         bx    r0           

  至此,在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

文章會同時釋出到我的 部落格園首頁、CSDN首頁、知乎首頁、微信公衆号 平台上。

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

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

  最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。

痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法
痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法
痞子衡嵌入式:在MDK開發環境下将關鍵函數重定向到RAM中執行的幾種方法

  衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。

  專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/

  與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]

  可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。

  關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。

  痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。