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

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

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

      痞子衡嵌入式:IVT里的不同entry設置可能會造成i.MXRT1xxx系列啟動App后發生異常跑飛


        大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是IVT里的不同entry設置可能會造成i.MXRT1xxx系列啟動App后發生異常跑飛問題的分析解決經驗

        事情緣起恩智浦官方論壇上的一個疑問帖 《RT1015 dev_cdc_vcom_freertos reset entry failed》,這是客戶QISDA遇到的問題,由痞子衡的同事 - 非常細心負責的Kerry小姐姐將問題整理出來并發了貼,帖子里做了詳盡的問題描述以及各種測試結果。看完長帖后,痞子衡第一猜想就是跟App棧設置有關,最終也確實是這個原因。那么為什么棧設置會出問題呢?且聽痞子衡細聊:

      一、問題描述

        讓我們先來整理一下帖子里的問題現象,客戶在RT1015-EVK上測試了恩智浦官方SDK里的兩個例程,一個是簡單的hello_world,另一個是復雜的dev_cdc_vcom_freertos,這兩個例程在不同IDE、IVT中entry值組合下現象不一致:

      測試App IVT中entry 測試IDE App運行結果
      hello_world 中斷向量表起始地址/
      復位向量函數地址
      IAR EWARM/
      MCUXpresso IDE
      正常
      dev_cdc_vcom_freertos 中斷向量表起始地址 IAR EWARM/
      MCUXpresso IDE
      正常
      dev_cdc_vcom_freertos 復位向量函數地址 IAR EWARM 正常
      dev_cdc_vcom_freertos 復位向量函數地址 MCUXpresso IDE 異常跑飛

        根據上表結果,其實我們很難得出一個有效推論,只能說這個異常結果在特定的App, entry值, MCUXpresso IDE下才能復現。

      二、原因探究

        既然暫時看不出原因,那我們先做一些準備工作吧。我們把三個影響因子(App, entry值, IDE)的差異先整理出來:

      2.1 兩個App的不同鏈接分配

        兩個App都來自SDK,是經過官方詳盡測試的,所以我們不去懷疑App本身的功能異常。它們的差異主要在鏈接分配上。以IAR為例,我們只看flexspi_nor build,在鏈接文件中默認分配的堆、棧大小均為1KB:

      /* Sizes */
      if (isdefinedsymbol(__stack_size__)) {
        define symbol __size_cstack__        = __stack_size__;
      } else {
        define symbol __size_cstack__        = 0x0400;
      }
      
      if (isdefinedsymbol(__heap_size__)) {
        define symbol __size_heap__          = __heap_size__;
      } else {
        define symbol __size_heap__          = 0x0400;
      }
      

        hello_world例程因為比較簡單,所以用直接用了默認的堆棧大小,而dev_cdc_vcom_freertos例程比較復雜,堆棧做了額外調整,棧增大到了8KB。

        此外我們還注意到hello_world例程將其RW, ZI, 堆棧全部放進了32KB的DTCM;而dev_cdc_vcom_freertos例程則將RW, ZI放入了64KB OCRAM,只將堆棧放進了DTCM:

      define symbol m_data_start             = 0x20000000;
      define symbol m_data_end               = 0x20007FFF;
      
      define symbol m_data2_start            = 0x20200000;
      define symbol m_data2_end              = 0x2020FFFF;
      
      define region DATA_region = mem:[from m_data_start to m_data_end-__size_cstack__];
      define region DATA2_region = mem:[from m_data2_start to m_data2_end];
      define region CSTACK_region = mem:[from m_data_end-__size_cstack__+1 to m_data_end];
      
      // 適用hello_world例程
      place in DATA_region                        { block RW };
      place in DATA_region                        { block ZI };
      place in DATA_region                        { last block HEAP };
      place in DATA_region                        { block NCACHE_VAR };
      place in CSTACK_region                      { block CSTACK };
      
      // 適用dev_cdc_vcom_freertos例程
      place in DATA2_region                       { block RW };
      place in DATA2_region                       { block ZI };
      place in DATA_region                        { last block HEAP };
      place in DATA_region                        { block NCACHE_VAR };
      place in CSTACK_region                      { block CSTACK };
      

      2.2 entry值在BootROM中的使用

        再說IVT中的entry,痞子衡在i.MXRT1xxx系列啟動那些事系列文章中的 《Bootable image格式與加載》 的3.2節介紹過IVT結構以及其作用,IVT是關鍵啟動頭,指導了BootROM去搬移App以及加載執行,其中entry成員主要用于跳轉執行。

        為什么這個entry值既可以是中斷向量表(Vector Table)起始地址也可以是復位向量(Reset_Handler)函數地址呢?這取決于BootROM中是怎么利用這個entry值的,下面函數即是BootROM中最終跳轉函數:

      void jump_to_entry(uint32_t entry)
      {
          typedef void (*application_callback_t)(void);
          static application_callback_t s_app_callback;
      
          pu_irom_mpu_disable();
      
          __DMB();
          __DSB();
          __ISB();
      
          // The entry point is the absolute address of the call back function
          if ((uint32_t)entry & 1)
          {
              s_app_callback = (application_callback_t)entry;
          }
          // The entry point is the base address of vector table
          else
          {
              static uint32_t s_stack_pointer;
              // Ensure Core read vector table for destination instead of register
              volatile uint32_t *vector_table = (volatile uint32_t *)entry;
      
              s_stack_pointer = vector_table[0];
              s_app_callback = (application_callback_t)vector_table[1];
      
              // Update Stack pointer
              __set_MSP(s_stack_pointer);
              __set_PSP(s_stack_pointer);
          }
      
          __DSB();
          __ISB();
      
          // Jump to user application in the end
          s_app_callback();
      
          // Should never reach here
          __NOP();
          __NOP();
      }
      

        從上面的跳轉函數jump_to_entry()實現可以看出,entry值如果是復位函數地址(即奇地址),那么BootROM直接跳轉到復位函數執行;如果entry值是中斷向量表首地址(即偶地址),BootROM會先將當前SP重設到App指定的棧頂,然后再跳轉到復位函數。

        好的,現在我們知道了IVT中不同的entry值差異在哪了。

      2.3 不同IDE下startup流程

        因為涉及到兩個不同IDE,即IAR和MCUXpresso IDE,所以我們分別看一下這兩個IDE下的startup實現。我們知道main函數之后的代碼基本是IDE無關的,而startup卻是因編譯器而異。

        痞子衡以i.MXRT1010的SDK2.8.2包里的例程為例,先用IAR打開其中的dev_cdc_vcom_freertos例程,找到工程下的startup_MIMXRT1011.s文件,看它的Reset_Handler實現:

      __vector_table
              DCD     sfe(CSTACK)
              DCD     Reset_Handler
      
              DCD     NMI_Handler                                   ;NMI Handler
              DCD     HardFault_Handler                             ;Hard Fault Handler
              ; ...
      __Vectors_End
      
              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
      

        IAR版本Reset_Handler主要分四步: 重設VTOR、重設SP、執行SystemInit(關看門狗,關Systick,處理Cache)、執行IAR庫函數__iar_program_start(data/bss/ramfunc段初始化,跳轉到main)。

        再用MCUXpresso IDE打開同樣的dev_cdc_vcom_freertos例程,找到工程下的startup_mimxrt1011.c文件,看它的ResetISR實現:

      extern void _vStackTop(void);
      
      __attribute__ ((used, section(".isr_vector")))
      void (* const g_pfnVectors[])(void) = {
          // Core Level - CM7
          &_vStackTop,                       // The initial stack pointer
          ResetISR,                          // The reset handler
          NMI_Handler,                       // The NMI handler
          HardFault_Handler,                 // The hard fault handler
          // ...
      }; /* End of g_pfnVectors */
      
      __attribute__ ((section(".after_vectors.reset")))
      void ResetISR(void) {
          __asm volatile ("cpsid i");
      
          SystemInit();
      
          // Copy the data sections from flash to SRAM.
          unsigned int LoadAddr, ExeAddr, SectionLen;
          unsigned int *SectionTableAddr;
      
          // Load base address of Global Section Table
          SectionTableAddr = &__data_section_table;
      
          // Copy the data sections from flash to SRAM.
          while (SectionTableAddr < &__data_section_table_end) {
              LoadAddr = *SectionTableAddr++;
              ExeAddr = *SectionTableAddr++;
              SectionLen = *SectionTableAddr++;
              data_init(LoadAddr, ExeAddr, SectionLen);
          }
      
          // At this point, SectionTableAddr = &__bss_section_table;
          // Zero fill the bss segment
          while (SectionTableAddr < &__bss_section_table_end) {
              ExeAddr = *SectionTableAddr++;
              SectionLen = *SectionTableAddr++;
              bss_init(ExeAddr, SectionLen);
          }
      
          __asm volatile ("cpsie i");
      
          // Call the Redlib library, which in turn calls main()
          __main();
      
          while (1);
      }
      

        MCUXpresso IDE版本ResetISR主要分三步: 執行SystemInit(重設VTOR,關看門狗,關Systick,處理Cache)、data/bss/ramfunc段初始化、跳轉到main。

        經過上面對比,看出差異沒有?MCUXpresso IDE相比IAR的startup少了一步重設SP的動作。

      2.4 導致異常跑飛的棧錯誤

        有了前面三節的分析基礎,我們基本可以得出dev_cdc_vcom_freertos例程異常跑飛的原因是發生了棧錯誤。為什么會發生棧錯誤?這是由于MCUXpresso下的startup中沒有重設SP操作,所以當IVT中的entry是復位向量時,BootROM跳轉到App后依舊延用BootROM中的棧,根據芯片參考手冊System Boot章節里的信息,BootROM的棧放在了OCRAM空間(0x20200000 - 0x202057FF),但是dev_cdc_vcom_freertos例程又把RW, ZI段也放進了OCRAM中,因此隨著App的運行對棧的利用(函數調用、局部變量定義)有可能與App中的RW, ZI段數據(全局變量)互相破壞,程序發生未知跑飛也在意料之中。

        解決問題的方法是什么?當然是在MCUXpresso IDE的startup流程中加入重設SP操作,保持與IAR startup流程一致。

      __attribute__ ((section(".after_vectors.reset")))
      void ResetISR(void) {
          __asm volatile ("cpsid i");
      
          /* 新增SP重設代碼 */
          __asm volatile ("MSR msp, %0" : : "r" (&_vStackTop) : );
          __asm volatile ("MSR psp, %0" : : "r" (&_vStackTop) : );
      
          SystemInit();
      
          // ...
      }
      

        至此,IVT里的不同entry設置可能會造成i.MXRT1xxx系列啟動App后發生異常跑飛問題的分析解決經驗痞子衡便介紹完畢了,掌聲在哪里~~~

      歡迎訂閱

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

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

      posted @ 2020-09-11 17:25  痞子衡  閱讀(1831)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 美女内射福利大全在线看| 91网站在线看| 精品国产精品午夜福利| 久青草国产综合视频在线| 九九综合va免费看| 欧洲亚洲精品免费二区| 夜夜夜高潮夜夜爽夜夜爰爰| 毛片久久网站小视频| 一二三四中文字幕日韩乱码| 日韩高清国产中文字幕| 鹤庆县| 国产太嫩了在线观看| 国产精品VA尤物在线观看| 中文字幕日韩人妻一区| 亚洲男人第一无码av网站| 天堂…中文在线最新版在线| 国产毛片基地| 麻豆果冻国产剧情av在线播放| 久久人妻精品国产| 国产最大的福利精品自拍| 成人影片一区免费观看| 麻豆一区二区三区蜜桃免费| 亚洲男人AV天堂午夜在| 国产欧美日韩精品丝袜高跟鞋| 久久精品日日躁夜夜躁| 狠狠色综合播放一区二区| 亚洲欧美人成电影在线观看| 国产真人做受视频在线观看 | 无遮挡粉嫩小泬久久久久久久| 无人区码一码二码三码区| 免费无码AV一区二区波多野结衣| 少妇xxxxx性开放| 激情国产一区二区三区四区| 亚洲日本乱码在线观看| 亚洲av乱码一区二区| 国产精品亚洲一区二区三区| 国产成人精品永久免费视频| 亚洲国产精品区一区二区| 国产成人精品日本亚洲| 欧美激情内射喷水高潮| 国产午夜91福利一区二区|