在Cortex-M系列上如何準確地做us級延時?

来源:http://www.cnblogs.com/pheye/archive/2016/06/30/5630938.html
-Advertisement-
Play Games

前幾天剛好同事問起在Cortex M上延時不准的問題,在網上也沒找到比較滿意的答案,乾脆自己對這個問題做一個總結。 根據我們的經驗,最容易想到的大概通過計算指令周期來解決。該思路在Cortex上並不是很適用:一方面MCU從Flash取指是有延時的,另一方面Cortex的指令集不是固定周期的,特別從M ...


前幾天剛好同事問起在Cortex-M上延時不准的問題,在網上也沒找到比較滿意的答案,乾脆自己對這個問題做一個總結。
根據我們的經驗,最容易想到的大概通過計算指令周期來解決。該思路在Cortex上並不是很適用:一方面MCU從Flash取指是有延時的,另一方面Cortex的指令集不是固定周期的,特別從M3加入分支預測後,分支指令在Cortex-M不同型號上的結果都不相同。因此除了指令周期外,我們需要考慮的東西還有很多,才能得到正確的結果。

不帶分支預測器的情況

仍然先從不帶分支預測器的Cortex-M0開始,通過計算指令周期延時的實現代碼如下:

void delay_us(us) {
     delay_ntimes((us * sysclk - 8) / 4);
}
__asm void delay_ntimes(unsigned int n)
{
L1
               SUBS R0, #1
               BCS L1
               BX LR
}

從這段代碼可發現兩個主要問題:
一、delay_us里的公式是怎麼來的:
假如想延時us微秒,系統時鐘為48MHz,即sysclk=48,那麼周期數period_count滿足以下公式:
period_count = us * sysclk;
然後再delay_ntimes這個函數,又能推出period_count還滿足以下公式(見第二個問題的分析):
period_count = 8 + 4 n
於是:
n = (us
sysclk - 8 ) / 4;
這就解決了第一個問題,需要註意的是:該公式忽略了跳轉到delay_us和(us * sysclk -8 )/4的幾個固定周期。
二、delay_ntimes的周期數怎麼算:
它的周期數滿足以下公式:
period_count = 8 + 4 * n;
這個要根據指令集的周期數來確定,請看下表:

操作 描述 彙編命令 周期
Subtract Lo to Lo SUBS Rd,Rn,Rm 1
3-bit immediate SUBS Rd,Rn,#<imm> 1
8-bit immediate SUBS Rd,Rd,#<imm> 1
Branch Conditional B<cc> <label> 1或3
Unconditional B<label> 3
With link BL<label> 4
With exchange BX Rm 3
With link and exchange BLX Rm 3

先考慮n為0的情況,
SUBS為1周期+BCS為1周期+BX為3周期+外層調用delay_times(相當於BLX指令)的3周期=8周期。
當n不為0時,將再執行n次SUBS和BCS執行,SUBS仍為1周期,BCS有跳轉3周期,所以是4n個周期,因此該函數的執行周期數為:
period_count=8+4
n;

好了,在瞭解了原理之後,是時候到真正的板子上去測試了。
然而在MCU上的實測結果卻不如預期,延時5MS,實測為7.5MS;延時10MS,實測15MS。為什麼會出現這樣的現象?

這個跟MCU的設計有關。一般代碼都放在FLASH上,MCU中Cortex核要從FLASH上先取出指令,然後才能將指令放到指令流水線上執行。而上面的分析忽略了Cortex核從FLASH取出指令的時間,因此實測值與理論值分析不一致。

不同的MCU從FLASH讀取指令的時間消耗各不相同,因此需要根據不同MCU去調整公式,這是一個比較繁瑣的過程,比如這款MCU,將公式修改為(us * sysclk - 8) / 6就得到了正確結果。
另外一個做法是不修改公式,將延時代碼放到RAM中,許多MCU從RAM取出指令沒有等待周期。使用該方法再次測試,延時結果與理論計算一致。
但值得註意的是,不是所有MCU都滿足RAM取值零等待周期的條件,因此一定要做測試。

讀者若對MCU如何從FLASH讀取指令感興趣,參考資料[4]的分析是比較清楚的。

帶分支預測器的情況

將上面的代碼放到Cortex-M3和Cortex-M4的晶元上測試,測試結果是錯誤的,不論在FLASH還是在RAM中,這個是由於Cortex-M3,Cortex-M4上的指令流水線帶有分支預測器引起的。

要瞭解分支預測器,就不得不提指令流水線。Cortex-M3是三級流水線:取指,解碼,執行。但是沒找到CORTEX方面較好的圖,以下討論就基於下圖的4級流水線,該圖多了一步:寫回。這並不影響我們的討論。
流水線

(該圖引用自參考資料[1])
假設一條指令從執行開始到執行結束需要4個時鐘周期,在沒有流水線的情況下,需要等待第一條指令執行結束,才能取第二條指令,這時兩條指令就用了8個周期,效率是很低的。
引入4級流水線將指令拆成4個步驟:取指、解碼、執行、寫回。當第一條指令處於解碼時,同時對第二條指令取指;對第一條指令執行時,同時對第二條指令解碼,對第三條指令取指;對第一條指令寫回時,同時對第二條執行,第三條解碼,第四條取指;如此這般。最終達到的效果就如上圖所示,只有第一條指令需要4個周期,其他後續的指令都只需要1個周期,極大地提高了處理效率。
流水線的高效率是基於指令順序執行的前提,在執行跳轉指令時,流水線將被清空,又回到了上圖中的第一步,跳轉後的第一條指令要執行仍然需要4周期。因此如果程式頻繁跳轉,流水線的作用就大打折扣。
為瞭解決這個問題,就引入了分支預測器:它會提前檢測到跳轉指令,並根據預判結果取指。如果預判結果是不跳轉,就按順序取下一條指令;如果預判結果是跳轉,就從跳轉的目的地址取下一條指令。假如預測對了,那麼流水線就不會被清空,仍然可以一條指令1個周期;如果預測錯了,下一條指令仍然要4周期。從這裡看出,分支預測器對於提高流水線效率是有幫助的。值得一提的是,預判對了能減少指令延遲,但是否是零延遲取決於MCU的設計;預判錯了清空流水線也未必是唯一的做法,同樣取決於MCU的設計。

回到Cortex-M3的延時問題,網路上找到的資料提到分支預測器將延遲減小到1個周期,沒有找到更詳細的說明。那麼理論上計算公式就應該調整為(us * sysclk - 8) / 3,在兩款Cortex-M3和兩款Cortex-M4上測試,測試結果與理論值一致。

微秒級精確延時的其他方法

對於Cortex而微秒級延時最通用的方法,大概便是通過比較SysTick的SYST_CVR寄存器來做延時,理論誤差在1us內(基於48MHz主頻)。以下為實現代碼:

/*
* 使用SysTick的CVR實現微秒級精確延時,一般SysTick周期設置為10MS,因此該方法適用於10MS以內的延時
*/
void delay_us(int us) {
    unsigned t1, t2, count, delta, sysclk;
    sysclk = 48;//假設為48MHz主頻

    t1 = SYST_CVR;
    while (1) {
        t2 = SYST_CVR;
        delta = t2 < t1 ? (t1 - t2) : (SYST_RVR - t2 + t1) ;
        if (delta >= us * sysclk)
            break;
    }
}

其他補充點

  1. 本文假設在延時過程中沒有產生任何中斷,如果有中斷產生,將影響延時精確性。
  2. 這部分的內容屬於電腦體繫結構。
  3. 以上測試時間範圍在[0,10MS),該範圍之外未詳細測試,建議採用其他方法。
  4. 覆蓋測試的MCU:1款Cortex-M0,2款Cortex-M3,2款Cortex-M4。
  5. 在我測試的兩款Cortex-M3 MCU上,將代碼都放RAM上,測試結果比放在FLASH差,而在Cortex-M4 MCU上,測試結果都一樣,目前沒有找到合理的解釋。

參考資料

  1. 淺談分支預測、流水線與條件轉移
  2. Cortex-M0指令集
  3. CPU性能衡量參數-主頻,MIPS,CPI,時鐘周期,機器周期,指令周期
  4. Cortex-M3的周期判斷的依據是什麼
  5. 電腦體繫結構——流水線中的相關——延遲分支方法

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文轉自http://www.cnblogs.com/yunf/archive/2011/04/12/2013448.html,在此感謝作者yfProgramer。 對於我這種剛學mysql的,還是十分有用的。 雖然現在mysql已經可以利用workbench直接創建資料庫了,但是開頭的對庫和表的增 ...
  • 好久不寫文,最近得空寫一點。Oracle資料庫國內用戶量主要在企業上,其中有一種byte的存儲稱為Blob,並不能直接看。 有時候為了調試需要,可以通過: 這種sql去轉為字元串查看,但是不方便,一次最多轉出2000個位元組。需要通過index拼成完整的文本。 另外一種情況下,如果存儲的是圖片、wor ...
  • 安裝好MySQL以後,系統給了個預設的的密碼,然後說如果忘記了預設的密碼。。。。。。我複製了預設密碼就走過了只一步,這一步就是我漫長旅程的開始。他給的密碼太複雜了,當然我得換一個,而且我還要假裝我不記得密碼了,就這樣我走上了不歸路。。。。。。 這個過程是心酸的,網上的資料多如狗,關鍵是各有各的錯法, ...
  • 當我談論索引時,大家經常會問我在複合非聚集索引里,列的順序是否重要?簡單來說:“看情況”。我們來具體看下為啥“看情況”…… 單例查找(Singleton Lookups) 當在你的表上有進行單例查找的查詢時,在複合非聚集索引里列的順序真的不重要。假設下列查詢: 現在你可以在StateProvince ...
  • 聲明:以下的代碼成果,是參考了網上的injso技術,文章最後會給出地址。 另外一個,injso文章中的代碼實際上不能夠運行起來的,後面出現的代碼都是經過我個人修改和檢測的。 最近因為在學習一些調試的技術,但是很少有提到如何在函數運行時實現函數替換的。 為什麼會想到這一點?因為在學習調試時,難免會看到 ...
  • call和jmp都是跳轉指令,但是call的同時會把pc地址壓入堆棧,並且這兩種方式都有遠和近跳轉。下麵的分析不全,因為沒有在網上找到足夠的資料,個人創造這個情景還是有些困難。 1.例子中的call的機器碼為0xe8。 0x400204ba <+30>: e8 41 b6 05 00 call 0x ...
  • STM32除TIM6和TIM7外都可以產生PWM輸出。高級定時器TIM1和TIM8可以同時產生7路PWM,通用定時器可以產生4路PWM輸出。 1.TIM1 CH1輸出PWM配置步驟 ①開啟TIM1時鐘,配置PA8為復用輸出 APB2外設時鐘使能寄存器(RCC_APB2ENR) APB1外設複位寄存器 ...
  • 1.什麼是kqueue和IO復用 kueue是在UNIX上比較高效的IO復用技術。 所謂的IO復用,就是同時等待多個文件描述符就緒,以系統調用的形式提供。如果所有文件描述符都沒有就緒的話,該系統調用阻塞,否則調用返回,允許用戶進行後續的操作。 常見的IO復用技術有select, poll, epol ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...