2020年4月19日 星期日

Real-Time Embedded System 心得(2): delay/sleep 的誤解

所有的 RTOS(或是 non-RTOS) 都提供一個 API or system call,以 uC/OS-II 來說是 OSTimeDly(),FreeRTOS 是 vTaskDelay,功用是讓 task 延遲/睡眠 n 個 tick。

某次我跟同事 con-call,才發現他們對此存在很大的誤解,其中不乏比我資深的同事,這讓我感覺應該不只他們這樣想,可能很多人都這麼想。

對於很多人來說他們並沒有 porting RTOS 的經驗,也不曾鑽研 RTOS 的肌理,因為分工的緣故,對於他們來說就是在 RTOS 上寫純軟體,那就會誕生許多玄談怪論。實在有必要澄清一下。

delay/sleep 遠比你想的不精確

假如你把 RTOS tick 調整成 20ms/tick,也就是把 OS Timer 調整成 20ms 發生一次中斷通知 OS kernel 進行 schedule。

這讓很多人以為 OSTimeDly(10) = 10*20 = delay 200ms,實際上 OSTimeDly(以及其他 RTOS 的相似 API) 是會上下抖動(jitter)甚至延遲的比你認為更長的時間,這個抖動不是只有差 context switch 那幾個 CPU 指令執行時間而已。

這邊同樣搬出 uC/OS-II 作者 Jean J. Labrosse 的權威說明加以解釋,當然筆者除了當文抄公外,也會拿出真實世界的案例解說這個問題的實際影響。

Case 1


上圖取自書中 Page.68 Figure 2.25,由圖中可知, Delayed Task 試圖 delay 1 tick,但卻不是精確的 20ms,而是 19ms, 17ms , 27ms,沒有一個是 20ms。可以看出 Delayed 之所以會產生抖動:
  • Delayed Task 必須等到比他更高 priority 的 task 放棄 CPU
  • 呼叫 OSTimeDly 時與下一次 OS Timer 中斷的時間差。

Case 2


上圖取自原書 Page.69 Figure 2.26,從上圖可以看到這次抖動的比 Case 1 更厲害。由 t1 可以得知,為了保險起見,如果你想要 delay 至少 20ms,最好是 OSTimeDly(2) 而不是 OSTimeDly(1)。

Case 3


上圖取自原書 Page.69 Figure 2.27。這次我們看到 t1 衝到 40ms,如果你的應用 timing 誤差不能超過 20ms,那你的產品就會在某種情況下運作不正常,然後你就準備被客訴跟 debug 到死吧!這極有可能是 All higher priority tasks 中有 task 正在進行耗時的計算。

改善建議

Jean J. Labross 給了幾點建議:
  • 降低 OS Timer 週期
  • 換更高速的 CPU(就筆者經驗來說,光是個 CRC32, MD5 用軟體計算往往就會出現Case2-3 的情形)
  • 重新調整各個 Task 的 priority
  • 避免浮點數運算
  • 使用 Compiler 對 code 進行優化
  • time critical 改用組合語言實做(原來組語還是有存在的價值)
筆者每天工作接觸的專案,它的 tick 是 50ms,那各位讀者就知道這有多吐血了。這中間的原因除了歷史包袱,也包括筆者人微言輕,老闆不知道問題的嚴重性。不禁讓筆者想起洪朝貴老師講的,技術物本身就有政治性

真實世界的影響

德國西門子 PLC S7-300 MPI 與工控控制常見的 Profibus 電氣訊號都是 RS485,聽起來找個 RS485 transceiver 加上所有 SoC 都有 UART 就可以跟這些控制器通訊,但如果你試圖用 task + delay/sleep 實作,你會發現通訊失敗率很高,因為是這些 protocol 對 timing 誤差的容忍度很低,原因就是前面的狀況 1-3。

那你說我把 tick 調到 < 1ms 行不行?這種搞法就會讓 OS context switch 的 overhead 過高,而且對於某些 low-cost CPU 來說也不可行。而且你得把 task 調成最高 priority,整個系統設計上會被大幅限制。

Jean J. Labross 在書中直接說:Regardless of what you do, jitter will always occur.

所以筆者見過的設計都是乾脆把 code 放到 ISR 裡還比較省事,筆者之前做 HMI(Linux)時,也是進到 kernel mode 處理。有一個 Raspberry Pi Profibus 專案,他 RS485 的部份就是外掛一顆 MCU,因為 Profibus baudrate 最高可達 12Mbps,不如直接用一顆單晶片用傳統作法跟他對著幹比較簡單。而且就筆者的經驗,即使放到 kernel mode 也會吃掉很多 CPU,因為會一直收到中斷(這跟Profibus 通訊協議有關)。

(這個 open source 專案似乎收掉了 ,只剩 Google 截圖)

另一個專案看似不需要進入 kernel mode,但卻用上了 FPGA,Serial Port 只有 19200bps(USB to RS485 不行),根據筆者的實戰經驗,19200bps 實用價值很低,這個專案就請有興趣的讀者自行研究。不過要是這麼簡單,那 Profibus ASIC 就不知道要賣給誰了。

所以西門子會賣一個看起來像是騙錢的東西讓你在 PC 上可以接 PLC,這種東西在外行人來看不就是 USB 轉 RS485 嗎?幾百元一個怎麼西門子賣那麼貴?不是他們硬要賺你這個錢,是即使 PC 如此高速,要他們用軟體硬做仍然有很大的困難(除非是像筆者在另一篇文章提過的的,可以獨占 x86 中某個核心的特殊 RTOS,或者改用 Linux 可能機會大點),所以筆者猜測這不只是單純的 USB 轉 RS485,內部是有 MCU 寫了不少軟體在裡面。


寫到這裡不禁又想起 10 多年前碰過的那個 8 核心 FxxA,他們在這個問題上其實已經找到破口(硬體 delay 指令保證精確性),但他們顯然沒有好好運用這方面的優勢,從多年後退化為單核心可見一般,可惜了!


3 則留言:

  1. 沒辦法,現在MCU 的效能太好了,好到讓大家都忽略了,再好效能的MCU,

    也是都是有Latency 問題。反而以前MCU 效能差,寫程式就得要好好先做一些

    效能評估,譬如:那怕一個小小的副程式,也都會事先分析與模擬

    用掉多少 Stack 深度,動到那些 Registers 。甚至還要換算出副程式執行的

    Cycle time(包含所有Branch 條件)。

    沒有這些基礎觀念,要調好系統的穩定度,是很辛苦的啦。

    回覆刪除
    回覆
    1. 電腦科學大師Dijkstra認為程式要有嚴格的數學論證證明正確才算數,但這樣以現在程式的規模,大概也不知道寫到幾年去了,您老的建議也是一樣

      其實現在都有一些自動化檢查的工具(如同那些EDA tool)可以避開基本錯誤,只是說看要不要做而已,願不願意花點小錢投資那些工具+投資時間

      我昨天才被唸如果要花超過一天分析就別做了,所以並不是工程師偷懶,如果高層不在意,我多做了也沒人感謝,何苦呢?唉~

      刪除
    2. 是啊。你都是幹主管了,經驗也告訴你問題可能出在哪了?

      那你就叫小弟去做就好了啊(雖然你這時可能也還沒找到小弟...)

      所以啦~我常開玩笑說:世界會變成這樣子,不是沒有道理的。

      台灣科技界為什麼會成這樣子,也不是沒有道理的。

      你認真,你就輸了。所以啦~為什麼說:幹工程師,為什麼不能在一家

      公司安安穩穩的做一輩子呢?到底是要為了自己的那一份堅持呢?

      還是要隨波逐流...辛苦啊。加油!

      刪除