<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      痞子衡嵌入式:深扒IAR啟動函數流程及其__low_level_init設計對函數重定向的影響


        大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是IAR啟動函數流程及其__low_level_init設計對函數重定向的影響

        上一篇文章 《IAR下RT-Thread工程自定義函數段重定向失效分析》 里我們找出了影響 IAR 鏈接器處理自定義程序段重定向的原因,主要跟 __low_level_init() 函數有關,這個函數屬于 IAR 底層設計,它在 IAR 啟動函數 __iar_program_start() 中會被自動調用。

        __iar_program_start() 是 IAR 標準啟動函數,也屬于 IDE 底層設計。在任何一個 Cortex-M 廠商芯片的啟動文件里(startup_xxDevice.s)都能看到它的身影,它是復位函數 Reset_Handler() 和 主函數 main() 之間的橋梁,今天我們就仔細說說這個啟動函數以及其中 __low_level_init 設計:

      一、通用芯片上電啟動流程

        在深入挖掘 IAR 啟動函數源代碼之前,有必要先整體了解一下通用的芯片上電啟動流程,即進入用戶 main 函數之前內核必須要做的事情,注意這里并不包含芯片底層外設的初始化(這是因芯片而異的)。

        通用啟動流程簡單來說分為如下四步:第一步是從 ROM 區域中斷向量表里獲取入口函數開始執行,設置好初始棧指針,有了正確的棧,內核就具備函數跳轉執行的能力了。第二步和第三步是全局變量的初始化(將全局變量初值從 ROM 區域拷貝到變量所鏈接的 RAM 區域),全局變量初始化完成,應用程序就有了正確的初始態,最后一步就是跳轉到 main 函數。

      二、從源代碼角度看啟動流程

        在上一節通用啟動流程的指導下,我們還需要增加一些 MCU 外設相關的初始化便形成了完整的芯片啟動流程,現在我們從源代碼角度再來看一下具體實現。

      2.1 典型的 Cortex-M 復位函數

        我們知道復位函數 Reset_Handler() 是芯片上電啟動執行的第一個函數(有時又叫入口函數),它完成了進入用戶 main() 函數之前的全部動作。隨便下載一家 Cortex-M 廠商芯片 SDK 包,找到 IAR 版啟動文件,其復位函數流程都差不多,這是 Cortex-M 內核架構決定的。

        如下是典型的復位函數代碼。復位函數里的操作包括關全局中斷、設置中斷向量表首地址、設置棧頂、系統初始化、開全局中斷、進啟動函數。其中系統初始化 SystemInit() 函數是因芯片而異的,各廠商 SDK 里會有具體源代碼實現(一般在 system_xxDevice.c 文件里),這里面主要做芯片硬件層面的初始化,比如關看門狗、Cache 初步設置等,保證內核不受硬件模塊狀態影響,能正常執行指令。

              THUMB
      
              PUBWEAK Reset_Handler
              SECTION .text:CODE:REORDER:NOROOT(2)
      Reset_Handler
              CPSID   I               ; Mask interrupts
              LDR     R0, =0xE000ED08
              LDR     R1, =__vector_table
              STR     R1, [R0]
              LDR     R2, [R1]
              MSR     MSP, R2
              LDR     R0, =SystemInit
              BLX     R0
              CPSIE   I               ; Unmask interrupts
              LDR     R0, =__iar_program_start
              BX      R0
      

      2.2 __iar_program_start() 到底干了啥?

        上一小節里我們知道復位函數里的最后一個動作就是跳轉到啟動函數,將內核執行權交給 __iar_program_start(),這個啟動函數源代碼并不在廠商 SDK 包里,而在 IAR 安裝目錄下,因為它是 IAR 的通用底層設計。

        為了找到 __iar_program_start() 的源代碼,我們可以隨便編譯一個 SDK 例程(痞子衡選擇的是 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar),查看其對應映射文件(.map),發現啟動函數來自于 cstartup_M.o,然后我們在 IAR 安裝目錄下搜索 cstartup_M.c/.s 文件,最終我們在如下路徑找到了啟動函數相關的全部源文件。

      \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\thumb\cstartup_M.s
      \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\thumb\cmain.s
      \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\runtime\low_level_init.c
      \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\data_init.c
      

        結合啟動函數相關源文件里的代碼,我們終于搞清了啟動函數全部流程,也找到了我們最關心的 __low_level_init() 函數調用位置,它在 .data/.bss/.textrw 段初始化之前被執行,所以它的功能應該跟 SystemInit() 差不多。默認 __low_level_init() 函數是空的,返回值是 1(返回值 0/1 決定后面的 __iar_data_init3() 要不要執行,1 是要執行),如果你想激活這個函數,需要在自己的源文件里重新定義實現,IAR 編譯時會優先引用重新定義的版本。

      __iar_program_start() -> 
      __cmain() -> 
      __low_level_init() ->          // 底層初始化,默認是個空函數
      __iar_data_init3() ->          // .data, .bss, .textrw 段初始化
      main()
      

      2.3 __low_level_init() 設計注意事項

        在 EWARM_DevelopmentGuide.ENU 手冊里搜索 __low_level_init,我們可以找到這個函數的設計初衷,官方說法是為了給應用程序一個早期初始化的機會,本質上就是跟 SystemInit() 一樣的作用,但是因為這個 __low_level_init 函數只在 IAR 環境下適用,如果用了它,應該程序代碼就不具備跨 IDE 的通用性,因此在各廠商 SDK 包里選擇了統一定義的 SystemInit() 來完成早期初始化工作。

      IAR 開發手冊: \IAR Systems\Embedded Workbench 9.10.2\arm\doc\EWARM_DevelopmentGuide.ENU
      

        EWARM_DevelopmentGuide.ENU 手冊里還特別提了幾點跟 __low_level_init 相關的注意事項,均跟 IAR 鏈接器所識別的 initialize by copy 鏈接語法有關,概括來說就是因為 __low_level_init 是在 .data/.bss/.textrw 段初始化之前被執行的,所以其代碼本身及其調用的全部代碼都不受 initialize by copy 作用,也就是這些代碼都不應是 RAMFUNC 型。

      • Note: 更準確地說 initialize by copy 作用范圍其實是 __iar_data_init3() 之后的代碼

      三、一個 __low_level_init() 相關的重定向實驗

        最后我們再做個 __low_level_init() 相關的小實驗,在 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar 例程基礎上(flexspi_nor_debug build),創建一個包含如下內容的 ramfunc_test.c 源文件,并將其添加進工程編譯。

        ramfunc_test1/3() 函數放入自定義程序段 CodeQuickAccess,ramfunc_test2/4() 函數放到默認 .textrw 段,然后重寫 __low_level_init() 函數,在 __low_level_init() 函數里分別調用 ramfunc_test1/2/3/4(),其中 ramfunc_test1/2() 函數的調用在 __iar_data_init3() 前面,ramfunc_test1/2() 函數的調用在 __iar_data_init3() 后面。

      void ramfunc_test1(void) @"CodeQuickAccess"
      {
          __NOP();
      }
      
      __ramfunc void ramfunc_test2(void)
      {
          __NOP();
      }
      
      void ramfunc_test3(void) @"CodeQuickAccess"
      {
          __NOP();
      }
      
      __ramfunc void ramfunc_test4(void)
      {
          __NOP();
      }
      
      // 重定義此函數,讓 IAR 編譯器使用這個版本,而不是默認版本
      int __low_level_init(void)
      {
          extern void __iar_data_init3(void);
      
          ramfunc_test1();
          ramfunc_test2();
          
          // 這里增加 .data/.bss/.textrw 的初始化調用,
          //  便于區分 ramfunc_test1/2 和 ramfunc_test3/4 位置
          __iar_data_init3();
          
          ramfunc_test3();
          ramfunc_test4();
          
          return 0;
      }
      
      

        編譯鏈接修改后的測試工程,查看其映射文件,以及在板子上實測,得到如下結果:

      • 結論1:放入自定義程序段的函數,無論其調用位置在 __iar_data_init3() 之前還是之后,一律被 initialize by copy 忽略,函數直接鏈接在目標 RAM 區,函數重定向無效;
      • 結論2:放入默認 .textrw 段的函數,如果其調用位置在 __iar_data_init3() 之后,能夠被 initialize by copy 作用,函數重定向生效;
      • 結論3:放入默認 .textrw 段的函數,如果其調用位置在 __iar_data_init3() 之前,從映射文件里看其能夠被 initialize by copy 作用,但在板子上實測,發現執行到該函數時返回會產生總線錯誤,因此函數重定向也是無效的;
      *******************************************************************************
      *** PLACEMENT SUMMARY
      ***
      "P1":  place in [from 0x3000'2000 to 0x30fb'ffff] { ro };
      "P2":  place in [from 0x2000'0000 to 0x2003'fbff] { rw };
      "P8":  place in [from 0x0 to 0x3'ffff] { section CodeQuickAccess };
      initialize by copy { rw, section .textrw, section CodeQuickAccess };
      
        Section              Kind         Address    Size  Object
        -------              ----         -------    ----  ------
      "P8":                                           0x8
          CodeQuickAccess    ro code          0x0     0x8  ramfunc_test.o [6]
      
      "P2-P3|P5|P9", part 1 of 2:                     0xc
        RW                            0x2000'0000     0xc  <Block>
            .textrw          inited   0x2000'0004     0x8  ramfunc_test.o [6]
      
      "P1":                                        0x443a
        .text                ro code  0x3000'63d0    0x1a  ramfunc_test.o [6]
      
      *******************************************************************************
      *** MODULE SUMMARY
      ***
          Module                              ro code  rw code  ro data  rw data
          ------                              -------  -------  -------  -------
          ramfunc_test.o                           34        8        8
      
      *******************************************************************************
      *** ENTRY LIST
      ***
          Entry                       Address   Size  Type      Object
          ----                       -------   ----  ----      ------
          ramfunc_test1                   0x1    0x4  Code  Gb  ramfunc_test.o [6]
          ramfunc_test2           0x2000'0005    0x4  Code  Gb  ramfunc_test.o [6]
          ramfunc_test3                   0x5    0x4  Code  Gb  ramfunc_test.o [6]
          ramfunc_test4           0x2000'0009    0x4  Code  Gb  ramfunc_test.o [6]
      

        至此,IAR啟動函數流程及其__low_level_init設計對函數重定向的影響痞子衡便介紹完畢了,掌聲在哪里~~~

      歡迎訂閱

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

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

      posted @ 2021-11-17 21:13  痞子衡  閱讀(2413)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 999精品色在线播放| 精品久久久久久无码中文字幕| 人人澡人人妻人人爽人人蜜桃 | 色噜噜在线视频免费观看| 国产偷窥熟女精品视频大全| 日本午夜精品一区二区三区电影| 精品无码人妻| 亚洲天堂在线免费| 日本黄页网站免费观看| 国产精品福利午夜久久香蕉| 年轻女教师hd中字3| 亚洲 成人 无码 在线观看| 久久国内精品自在自线91| 国产WW久久久久久久久久| 鲁丝片一区二区三区免费| 欧美日韩国产亚洲沙发| 亚洲欧洲精品国产二码| 亚洲理论在线A中文字幕| 久久热这里只有精品最新| 最新午夜男女福利片视频| 亚洲日韩国产二区无码| 日韩一区在线中文字幕| 漳浦县| 99久久亚洲综合精品成人| 亚洲精品日韩精品久久| 久久精品国产中文字幕| 国精品无码一区二区三区在线看| 国产999久久高清免费观看| 99国精品午夜福利视频不卡99| 成人无码视频| 欧美国产精品啪啪| 中文字幕乱码熟妇五十中出| 国产精品无码a∨麻豆| 国产精品久久久久久爽爽爽| 中文国产人精品久久蜜桃| 日韩精品一区二区三区影院| 新绛县| 国产日韩精品一区在线不卡| 无码AV动漫精品一区二区免费| 一区二区中文字幕av| 一色桃子中出欲求不满人妻|