痞子衡嵌入式:在IAR開發環境下RT-Thread工程自定義函數段重定向失效分析



  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是在IAR開發環境下RT-Thread工程函數重定向失效分析

  痞子衡舊文 《在IAR下將關鍵函數重定向到RAM中執行的方法》 里介紹了三種關鍵函數重定向方法,不過這三種方法只是寫法形式不同,本質上沒啥區別,都是利用 IAR 鏈接器特性將函數重定向到工程數據段(RW)所在 RAM 里。

  對於 i.MXRT 這種擁有多塊地址非連續的 RAM 的芯片,其實我們也可以單獨將這些重定向函數放到一個指定的 RAM 里,不一定非得跟數據段放在同一個 RAM 里。具體實現也很簡單,只需要在鏈接文件里額外加一句 place in 語句處理即可,恩智浦官方 SDK 包里就是這么做的。

  然而痞子衡最近在移植一個 i.MXRT1170 RT-Thread 工程時發現,在 IAR 鏈接文件里用自定義段來單獨指定重定向函數到 ITCM 竟然失效了,這是怎么回事?今天我們一起來看一下:

一、回顧SDK里函數重定向做法

  我們以最經典的 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar 例程來看,工程 Build 選擇 flexspi_nor_sdram_debug(僅該 build 預編譯宏里有 XIP_BOOT_HEADER_DCD_ENABLE=1),即代碼段放在 Flash 里(0x30000000 - ),數據段放在 SDRAM 里(0x80000000 - )。

  在時鍾初始化函數 BOARD_BootClockRUN() 里會調用如下 UpdateSemcClock() 函數,這個函數需要重定向到 RAM 里執行,在代碼里先將它放到自定義 CodeQuickAccess 段里。

#define AT_QUICKACCESS_SECTION_CODE(func) func @"CodeQuickAccess"

#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(XIP_BOOT_HEADER_DCD_ENABLE) && (XIP_BOOT_HEADER_DCD_ENABLE == 1)
AT_QUICKACCESS_SECTION_CODE(void UpdateSemcClock(void));
void UpdateSemcClock(void)
{
    SEMC->IPCMD = 0xA55A000D;
    while ((SEMC->INTR & 0x3) == 0);
    SEMC->INTR                                = 0x3;
    SEMC->DCCR                                = 0x0B;
    CCM->CLOCK_ROOT[kCLOCK_Root_Semc].CONTROL = 0x602;
}
#endif
#endif

  然后在工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor_sdram.icf 里(僅摘錄部分),再將 CodeQuickAccess 段單獨放在 ITCM 里(0x00000000 - ),這就是官方 SDK 里的做法。

define symbol m_data3_start            = 0x80000000;
define symbol m_data3_end              = 0x82FFFFFF;
define symbol m_qacode_start           = 0x00000000;
define symbol m_qacode_end             = 0x0003FFFF;
define region DATA3_region  = mem:[from m_data3_start to m_data3_end-__size_cstack__];
define region QACODE_region = mem:[from m_qacode_start to m_qacode_end];
define block RW            { first readwrite, section m_usb_dma_init_data };
define block QACCESS_CODE  { section CodeQuickAccess };
initialize by copy         { readwrite, section .textrw, section CodeQuickAccess };
place in DATA3_region                  { block RW };
place in QACODE_region                 { block QACCESS_CODE };

  編譯鏈接 hello_world_demo_cm7.ewp 工程,然后查看其映射文件(hello_world_demo_cm7.map)找到跟 UpdateSemcClock() 函數相關的內容如下,顯然這是符合預期的。這里特別注意一下,CodeQuickAccess 的類別顯示的是 inited,表明其不是常見的 ro code,而是經過重定向的,而且 UpdateSemcClock() 函數所在 clock_config.o 里包含了 60個字節的 rw code。

*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P7":  place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };

  Section              Kind         Address      Size  Object
  -------              ----         -------      ----  ------
"P7":                                            0x3c
  QACCESS_CODE                          0x0      0x3c  <Block>
    QACCESS_CODE-1                      0x0      0x3c  <Init block>
      CodeQuickAccess  inited           0x0      0x3c  clock_config.o [1]
                                     - 0x3c      0x3c

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  rw code  ro data  rw data
    ------                              -------  -------  -------  -------
    clock_config.o                        2'644       60      844

*******************************************************************************
*** ENTRY LIST
***
    Entry                       Address   Size  Type      Object
    ----                       -------   ----  ----      ------
    UpdateSemcClock                 0x1   0x3c  Code  Gb  clock_config.o [1]

二、引出RT-Thread下函數重定向失效問題

  現在來看 RT-Thread 工程,也是一個簡單的 hello world(具體工程略去不表),其中 i.MXRT1170 芯片 BSP 部分直接來自於官方 SDK,鏈接文件也與 SDK 里一致,但是編譯鏈接工程后查看其映射文件,發現跟 UpdateSemcClock() 函數相關的內容如下,CodeQuickAccess 的類別顯示的是 ro code, UpdateSemcClock() 函數所在 clock_config.o 里干脆連 rw code 都沒有。顯然函數重定向失效了,鏈接文件里 initialize by copy { section CodeQuickAccess }; 語句沒起作用,這顯然就是一個分散鏈接而已。

*******************************************************************************
*** PLACEMENT SUMMARY
***
define block QACCESS_CODE { section CodeQuickAccess };
"P7":  place in [from 0x0 to 0x3'ffff] { block QACCESS_CODE };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P7":                                          0x3c
  QACCESS_CODE                          0x0    0x3c  <Block>
    CodeQuickAccess    ro code          0x0    0x3c  clock_config.o [4]
                                     - 0x3c    0x3c

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  ro data  rw data
    ------                              -------  -------  -------
    clock_config.o                        2'768      784

*******************************************************************************
*** ENTRY LIST
***
    Entry                       Address   Size  Type      Object
    ----                       -------   ----  ----      ------
    UpdateSemcClock                 0x1    0x3c  Code  Gb  clock_config.o [4]

三、RT-Thread下函數重定向失效分析

  第一節里 SDK 裸機環境下函數重定向做法不會失效,RT-Thread 環境下同樣的做法就失效了,難道 IAR 對 RTOS 支持不友好?但是痞子衡在 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\rtos_examples\freertos_hello 下做了相同實驗,FreeRTOS 下這種函數重定向方式也是沒有問題的(FreeRTOS 內核啟動是在 main() 里),所以這個問題主要跟 RT-Thread 內核代碼結構設計有關。

  經過裸機工程、RT-Thread 工程、FreeRTOS 工程三者對比,痞子衡找到了問題所在。RT-Thread 內核啟動是在 /src/components.c 文件中的 __low_level_init() 函數里,而這個 __low_level_init() 函數本應是 IAR 入口函數 __iar_program_start() 中的一部分(IAR 系統庫里有一個內置 PUBWEAK 版本),但是 RT-Thread 重實現了這個 __low_level_init() 函數,很不幸的是 IAR 鏈接器對於自定義段的函數重定向認定與原內置 __low_level_init() 函數設計有某種內在關聯。

  當痞子衡將內核啟動函數 rtthread_startup() 放到 main() 里,而將 components.c 文件里的 __low_level_init() 函數臨時刪掉時,函數重定向失效問題就解決了,不過這只是驗證分析,並不是真正的解決方案。

四、RT-Thread下函數重定向失效解決方案

  經過痞子衡的一番嘗試(嘗試結果見下表),在 RT-Thread 重寫 __low_level_init() 函數的情況下,IAR 僅僅是無法正常處理自定義段的重定向函數代碼體,而如果將那些需要重定向的函數用 __ramfunc 修飾,統一放到 IAR 內置默認的 .textrw 段里,IAR 是可以正常處理的(感覺更像是 IAR 的一個缺陷)。

源文件處理 鏈接文件處理 IAR 最終鏈接結果
放入自定義程序段 CodeQuickAccess 僅 initialize by copy { section CodeQuickAccess }; 函數直接鏈在代碼段所在 Flash 區,無效重定向
放入自定義程序段 CodeQuickAccess initialize by copy { section CodeQuickAccess };
place in ITCM_region { section CodeQuickAccess };
函數直接鏈在 ITCM 里,無效重定向
__ramfunc 修飾放入默認段 .textrw 僅 initialize by copy { section .textrw }; 函數體保存在代碼段所在區,但鏈接在數據段所在 RAM 區,有效重定向
__ramfunc 修飾放入默認段 .textrw initialize by copy { section .textrw };
place in ITCM_region { section .textrw };
函數體保存在代碼段所在區,但鏈接在 ITCM 里,有效重定向

  分析到這里,解決方案清晰了,首先是棄用 AT_QUICKACCESS_SECTION_CODE 宏,而改用 __ramfunc 來修飾 UpdateSemcClock() 函數:

#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(XIP_BOOT_HEADER_DCD_ENABLE) && (XIP_BOOT_HEADER_DCD_ENABLE == 1)
__ramfunc void UpdateSemcClock(void)
{
    SEMC->IPCMD = 0xA55A000D;
    while ((SEMC->INTR & 0x3) == 0);
    SEMC->INTR                                = 0x3;
    SEMC->DCCR                                = 0x0B;
    CCM->CLOCK_ROOT[kCLOCK_Root_Semc].CONTROL = 0x602;
}
#endif
#endif

  然后在工程鏈接文件 MIMXRT1176xxxxx_cm7_flexspi_nor_sdram.icf 里直接將 section .textrw 放到 ITCM 里:


define symbol m_qacode_start           = 0x00000000;
define symbol m_qacode_end             = 0x0003FFFF;
define region QACODE_region = mem:[from m_qacode_start to m_qacode_end];
initialize by copy          { readwrite, section .textrw };
place in QACODE_region                 { section .textrw };

  這時候再編譯鏈接工程查看映射文件,函數重定向結果就符合預期了。.textrw 的類別顯示的是 inited,而且 UpdateSemcClock() 函數所在 clock_config.o 里也包含了 60個字節的 rw code。

*******************************************************************************
*** PLACEMENT SUMMARY
***
"P7":  place in [from 0x0 to 0x3'ffff] { section .textrw };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P7":                                          0x3c
  P7                                    0x0    0x3c  <Init block>
    .textrw            inited           0x0    0x3c  clock_config.o [4]
                                     - 0x3c    0x3c

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  rw code  ro data  rw data
    ------                              -------  -------  -------  -------
    clock_config.o                        2'708       60      846

*******************************************************************************
*** ENTRY LIST
***
    Entry                       Address   Size  Type      Object
    ----                       -------   ----  ----      ------
    UpdateSemcClock                 0x1    0x3c  Code  Gb  clock_config.o [4]

  至此,在IAR開發環境下RT-Thread工程函數重定向失效分析痞子衡便介紹完畢了,掌聲在哪里~~~

歡迎訂閱

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM