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

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

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

      痞子衡嵌入式:鏈接函數到8字節對齊地址或可進一步提升i.MXRT1xxx內核執行性能


        大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是i.MXRT上進一步提升代碼執行性能的經驗

        今天跟大家聊的這個話題還是跟痞子衡最近這段時間參與的一個基于i.MXRT1170的大項目有關,痞子衡在做其中的開機動畫功能,之前寫過一篇文章 《降低刷新率是定位LCD花屏顯示問題的第一大法》 介紹了開機動畫功能的實現以及LCD顯示注意事項,在此功能上,痞子衡想進一步測試從芯片上電到LCD屏顯示第一幅完整圖像的時間,這個時間我們暫且稱為1st UI時間,該時間的長短對項目有重要意義。

        痞子衡分別測試了代碼在XIP執行下和在TCM里執行下的1st UI時間,得到的結果竟然是XIP執行比TCM執行還要快50ms,這是怎么回事?這完全顛覆了我們的理解,i.MXRT上TCM是與內核同頻的,Flash速度遠低于TCM。如果是XIP執行,即使有I-Cache加速,也最多與TCM執行一樣快,怎么可能做到比TCM執行快這么多。于是痞子衡便開始深挖這個奇怪的現象,然后發現了進一步提升代碼執行性能的秘密。

      一、引出計時差異問題

        痞子衡的開機動畫程序是基于 \SDK_2.x.x_MIMXRT1170-EVK\boards\evkmimxrt1170\jpeg_examples\sd_jpeg 例程的,只是去了SD卡和libjpeg庫相關代碼。工程有兩個build,一個是TCM里執行(即debug),另一個是XIP執行(即flexspi_nor_debug)。

        項目板上的Flash型號是MX25UW51345G,痞子衡將其配成Octal mode, DDR, 166MHz用于啟動。項目板上還有兩個LED燈,痞子衡在LED燈上飛了兩根線,連同POR引腳一起連上示波器,用于精確測量1st UI各部分時間組成。

        示波器通道1連接POR引腳,表明1st UI時間起點;通道2連接LED1 GPIO,表明ROM啟動時間(進入用戶APP的時間點);通道3連接LED2 GPIO,做兩次電平變化,分別是1st圖像幀開始和結束的時間點。翻轉LED GPIO代碼位置如下:

      void light_led(uint32_t ledIdx, uint8_t ledVal);
      
      void SystemInit (void) {
          // 將LED1置1,標示ROM啟動時間
          light_led(1, 1);
      
          SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
      
          // ...
      }
      
      void APP_InitDisplay(void)
      {
          // ...
      
          g_dc.ops->enableLayer(&g_dc, 0);
      
          // 將LED2置1,標示1st圖像幀開始時間點
          light_led(2, 1);
      }
      
      int main(void)
      {
          BOARD_ConfigMPU();
          BOARD_InitBootPins();
          BOARD_BootClockRUN();
          BOARD_ResetDisplayMix();
      
          APP_InitDisplay();
      
          while (1)
      	{
      	    // ...
      	}
      }
      
      static void APP_BufferSwitchOffCallback(void *param, void *switchOffBuffer)
      {
          s_newFrameShown = true;
      
          // 將LED2置0,標示1st圖像幀結束時間點
          light_led(2, 0);
      }
      

        上圖是痞子衡抓到的波形(30Hz,XIP),痞子衡一共做了四次測試,分別是30Hz LCD刷新率下的XIP/TCM以及60Hz LCD刷新率下的XIP/TCM,結果如下表所示。表中的Init Time一欄表示的是開機動畫程序代碼執行時間(從SystemInit()函數開始執行到APP_InitDisplay()函數結束的時間),可以看到TCM執行比XIP執行慢近50ms,這便是奇怪問題所在。

      代碼位置 LCD刷新率 POR Time Boot Time Init Time Launch Time
      XIP 30Hz 3.414ms 10.082ms 34.167ms + 153ms 32.358ms
      TCM 30Hz 3.414ms 10.854ms 33.852ms + 203ms 32.384ms
      XIP 60Hz 3.414ms 9.972ms 18.142ms + 153ms 16.166ms
      TCM 60Hz 3.414ms 10.92ms 17.92ms + 203ms 16.104ms

      二、定位計時差異問題

        對于開機動畫代碼,XIP執行比TCM執行快這個結果,痞子衡是不相信的,于是痞子衡便用二分法逐步查找,發現時間差異是BOARD_InitLcdPanel()函數里的DelayMs()調用引起的,這些人為插入的延時是LCD屏控制器手冊里的要求,總延時時間應該是153ms,但是這個函數的執行在XIP下(153ms)和TCM里(203ms)時間不同。

      static void BOARD_InitLcdPanel(void)
      {
          // ...
      
      #if (DEMO_PANEL ==  DEMO_PANEL_TM103XDKP13)
          // ...
      
          /* Power LCD on */    
          GPIO_PinWrite(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN, 1);
          DelayMs(2);
          GPIO_PinWrite(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN, 0);
          DelayMs(5);
          GPIO_PinWrite(LCD_RESET_GPIO, LCD_RESET_GPIO_PIN, 1);
          DelayMs(6);
          GPIO_PinWrite(LCD_STBYB_GPIO, LCD_STBYB_GPIO_PIN, 1);
          DelayMs(140);
      #endif
          // ...
      }
      

        所以現在的問題就是為何在TCM里執行DelayMs(153)需要203ms,而XIP執行下是精確的。讓我們進一步查看DelayMs()函數的原型,這個函數其實調用的是SDK_DelayAtLeastUs()函數,SDK_DelayAtLeastUs()函數從命名上看就很有意思,AtLeast即保證軟延時一定能滿足用戶設置的時間,但也可能超過這個時間。為何是AtLeast設計,其實這里就涉及到Cortex-M7內核一個很重要的特性 - 指令雙發射,軟件延時的本質是靠CPU執行指令來消耗時間,但是CPU拿指令到底是單發射還是雙發射有一定的不確定性,因此無法做到精確,如果以全雙發射來計算,就能得出最小延時時間。

      #define DelayMs                  VIDEO_DelayMs
      
      #if defined(__ICCARM__)
      static void DelayLoop(uint32_t count)
      {
          __ASM volatile("    MOV    R0, %0" : : "r"(count));
          __ASM volatile(
              "loop:                          \n"
              "    SUBS   R0, R0, #1          \n"
              "    CMP    R0, #0              \n"
              "    BNE    loop                \n");
      }
      #endif
      
      void SDK_DelayAtLeastUs(uint32_t delay_us, uint32_t coreClock_Hz)
      {
          assert(0U != delay_us);
          uint64_t count = USEC_TO_COUNT(delay_us, coreClock_Hz);
          assert(count <= UINT32_MAX);
      
      #if (__CORTEX_M == 7)
          count = count / 3U * 2U;
      #else
          count = count / 4;
      #endif
          DelayLoop(count);
      }
      
      void VIDEO_DelayMs(uint32_t ms)
      {
          SDK_DelayAtLeastUs(ms * 1000U, SystemCoreClock);
      }
      

        分析到現在,問題已經轉化成為何XIP下執行指令雙發射概率比TCM里執行指令雙發射概率更大,關于這個現象并沒有在ARM官方文檔里查找到相關信息,DelayLoop()循環里只是3條指令,XIP下執行肯定是在Cache line里,這跟在TCM里執行并沒有什么區別。讓我們再去看看兩個工程的map文件,找到DelayLoop()函數鏈接地址,這個函數在兩個測試工程下鏈接地址對齊不一樣,這意味著測試條件不完全相同,或許這是一個解決問題的線索。

        XIP執行工程(flexspi_nor_debug),DelayLoop()函數地址8字節對齊:

      *******************************************************************************
      *** ENTRY LIST
      ***
      
      Entry                       Address   Size  Type      Object
      -----                       -------   ----  ----      ------
      DelayLoop               0x3000'3169    0xa  Code  Lc  fsl_common.o [1]
      

        TCM執行工程(debug工程),DelayLoop()函數地址4字節對齊:

      *******************************************************************************
      *** ENTRY LIST
      ***
      
      Entry                       Address   Size  Type      Object
      -----                       -------   ----  ----      ------
      DelayLoop                    0x314d    0xa  Code  Lc  fsl_common.o [1]
      

      三、找到計時差異本質

        前面找到DelayLoop()函數鏈接地址差異是一個線索,那我們就針對這個線索做測試,不再讓鏈接器自動分配DelayLoop()函數地址,改為在鏈接文件里指定地址去鏈接,下面代碼是IAR環境下的示例,我們使用debug工程(即在TCM執行)來做測試。

        C源文件中在DelayLoop()函數定義前加#pragma location = ".myFunc",即將該函數定義為.myFunc的段,然后在鏈接文件icf中用place at語句指定.myFunc段到固定地址m_text_func_start處開始鏈接:

      #if defined(__ICCARM__)
      #pragma location = ".myFunc"
      static void DelayLoop(uint32_t count)
      {
          // ...
      }
      #endif
      
      define symbol m_text_func_start        = 0x00004000;
      
      place at address mem: m_text_func_start     { readonly section .myFunc };
      
      define symbol m_text_start             = 0x00002400;
      define symbol m_text_end               = 0x0003FFFF;
      
      place in TEXT_region                        { readonly };
      

        根據鏈接起始地址m_text_func_start的不同,我們得到了不同的結果,如下表所示。至此真相大白,造成DelayMs()函數執行時間不同的根本原因不是XIP/TCM執行差異,而是鏈接地址對齊差異,8字節對齊的函數更容易觸發CM7指令雙發射,相比4字節對齊的函數在性能上能提升24.8% 。

      m_text_func_start值 鏈接地址對齊 函數調用語句 實際執行時間
      0x00004000 8n字節 DelayMs(100) 100ms
      0x00004002 2字節,未能鏈接 N/A N/A
      0x00004004 4字節 DelayMs(100) 133ms
      0x00004008 8字節 DelayMs(100) 100ms

        現在我們得到了一個有趣的結論,Cortex-M7上將函數鏈接到8字節對齊的地址有利于指令雙發射,這就是進一步提升代碼執行性能的秘密。

        至此,i.MXRT上進一步提升代碼執行性能的經驗痞子衡便介紹完畢了,掌聲在哪里~~~

      歡迎訂閱

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

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

      posted @ 2020-06-10 20:29  痞子衡  閱讀(1230)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 无码人妻人妻经典| 国产精品爽爽久久久久久竹菊| 亚洲国产精品va在线观看麻豆| 国产一区二区不卡在线| 无遮挡粉嫩小泬久久久久久久| 国产精品毛片一区二区| 成人久久精品国产亚洲av| 亚洲人成电影在线天堂色| 久久精品第九区免费观看| 无码伊人66久久大杳蕉网站谷歌| 小嫩批日出水无码视频免费| 性欧美老妇另类xxxx| 精品粉嫩国产一区二区三区| 亚洲av成人无码精品电影在线| 亚洲欧洲美洲无码精品va| 国产精品一区二区传媒蜜臀| 亚洲国产精品成人无码区| 国产露脸无套对白在线播放| 日韩高清福利视频在线观看| 国产影片AV级毛片特别刺激 | 好男人官网资源在线观看| 中文字幕va一区二区三区| 国产精品老熟女一区二区| 成人做受视频试看60秒| 国产成人啪精品视频免费网| 亚洲一区二区无码影院| 日韩精品中文字幕人妻| 亚洲中文字幕国产综合| AV无码不卡一区二区三区| 国产精品 无码专区| 在线播放深夜精品三级| 欧美国产日韩在线三区| 野花社区www视频日本| 四虎国产精品永久入口| 黄色一级片一区二区三区| 亚洲人成网站在线播放2019| 狠狠v日韩v欧美v| 国产精品色三级在线观看| 国产精品毛片大码女人| 国内精品卡一卡二卡三| 欧美精品人人做人人爱视频|