2018年10月14日 星期日

HMI 回憶錄 (6)

軟體篇 PART2

這篇繼續談編譯,您不要以為編譯只處理圖形而已,在編譯的過程中也會處理韌體(我這邊不用「編譯韌體」這種說法,與程式語言的編譯有所區別)。


回顧前篇的專案檔示意圖:


上圖中的"通訊設定"設定頁面如下(取材自台達 DOPSoft):



所謂的"通訊設定",就是上圖 COM1-COM3 baud rate, parity, stop-bits, data-bits, IP address, Port Number ...。

注意,專案檔並沒有包含這些 PLC protocol driver。那這些 protocol driver 在哪呢?首先,預先燒好放在 HMI 上是不可能的,各位看了前篇就知道以 HMI 這種鳥價錢,怎麼可能給你很大的 Flash  ROM,那還要賺錢嗎?

所以在編譯的過程中,會從"通訊設定"中挑選用到的 protocol driver,然後從 PC 打包上傳到 HMI(透過 USB or Ethernet),這些 PLC protocol driver 可能是 .so(Linux) or .dll(Windows Embedded)。

各位看到這裡會覺得這哪有技術性可言!當然對於 Linux or Windows Embedded 這類天生內建 file system 的 OS 來說是小菜一碟(就只是單純讀寫檔案)。可是你要知道第一代 HMI 是沒有 file system 的喔!

如果你不知道困難在哪,以上圖為例,3x COM Port + 1 Ethernet Port,共有 4 種不同的 driver 可選,如果全部可以用的 PLC protocol driver(s) 有 100 個(這很常見),你就必須事先編譯好 3921225 個韌體放在 PC:


可以想見這種作法根本不可行。那如果把 COM1-3, Ethernet PLC protocol driver 固定在某些記憶體位址呢?

註:下圖 OSTaskCreate 是 uC/OS-II API,用他舉例只是因為筆者對他很熟...

這樣做還是無法徹底解決問題:
  1. 當 PLC protocol driver or OS code/data size 成長時必須重新重新調整位址,重新編譯所有的韌體。
  2. 為了避免第一點提到的問題,預留過多空間又會造成浪費。
  3. 如果 HMI 有 5 種機型,5 種機型記憶體 protocol driver 因為記憶體位址不同: 5x100 = 500 protocol drivers。
我們希望修正成:
  1. 某 PLC protocol driver 或 OS code/data size 成長時不需要檢查/重新編譯 所有的 firmware。
  2. ROM/RAM 空間使用最佳化,不會跑出上圖中的 gap(間隙)
  3. 如果 5 種機型都使用相同的 SoC/CPU、相同的 OS,5 種機型可以共用相同的 PLC protocol drivers and OS。

Linker and Loader

要達成這三點目的,其實跟 Linker & Loader 有關(簡稱 L&L 好了),講到 L&L,很多人會覺得很高大上(據說亞洲只有 5 個編譯器團隊)。事實上要達成上面這三點,不用完整實做 L&L,甚至連 1/2 L&L、1/4 L&L 可能都沒有。

你只需要搞懂 3 個關鍵:
  1. 搞懂 ELF(Executable and Linkable) format
  2. relocation
  3. 略懂該 CPU 組合語言
還記得 1x 年前為了弄懂 1,2,筆者把 ARM object format 列印出來,看了兩個星期才弄懂。現在不用那麼辛苦,您把下面這兩本書找來,大概就可以弄懂 87% 了:


後來想想,唸書時修系統程式時早就寫過類似的作業了(下圖是筆者當初上課用的課本,不知道現在學校還用嗎?),好在當初有認真寫作業,不然大概就不只 k 兩個星期了。


進一步思考,那這些與 OS 分開編譯的 task,要怎麼呼叫 OS API 呢?有兩種作法可以達成:
  • software interrupt: 這是 Linux system call 常用的方式
  • 符號解析: 您的軟體在「組合」f/w 時,把這些未定義的符號連結到正確的位址。
這兩種作法市面上可以找到不少參考資料(上面那兩本也有提及),這邊就不囉唆了。如果您覺得還是很難,以現在可以取得的資源來說,說不定可以在不違反 GPL 的情形下直接套用 GNU Embedded Toolchain for Arm 的 linker(ld)。

泓x那顆號稱可以使用 Turbo/Borland C++ 開發的 PAC,個人猜測也用上了類似的技術。筆者 1x 年前在 PC 上做"把 uC/OS-II 放到一張磁片上開機執行"的實驗,使用該磁片開機後,uC/OS-II 會載入 Turbo/Borland C++ 編譯出的 MS-DOS .exe 成一個 task 執行。當初為了做這個實驗,還回頭複習 x86 組合語言,現在早就忘光光了,各位看官也別寫信來問我,參考資料都在上面(不過應該全部絕版了)。

(大概只剩這本了)

n 年前曾經被大名鼎鼎的 CIH 作者面試,我跟他說我搞過 ELF loader,他說這不是 readelf 就可以作到了嗎?我猜他應該沒搞懂我的意思...雖然最後有上那間公司,不過實在沒辦法接受在地下室工作就放棄了 ><

前鎮子看到有人在寫什麼"C 語言運行 main 之前函數的祕密"之類的文章,甚至還開了課程。過了 1x 年,筆者都有點不好意思再提這些往事了,筆者已經不覺得這有什麼技術可言,起碼稱不上什麼上乘武功。

現在 Linux 大行其道,這招恐怕已經用不到了。不過筆者猜想也許可以在 FreeRTOS + STM32 上借屍還魂,也許哪天筆者再來寫個教學,不過最近工作繁忙加上背痛,短時間內是看不到了。

3 則留言:

  1. 感覺我們的學習之路很類似, 我用上述的觀念做了以下無用的東西。
    https://descent-incoming.blogspot.com/search/label/load%20object

    你提到的書我通通都有, 不過系統程式教科書我覺得離真實世界太遠。

    回覆刪除
    回覆
    1. 感謝回應~

      也不能說沒用,只是過了那個時代了,就好像計算尺在計算機出現後就沒用了,不過就算是1x年前,跟老闆解釋這個東西有用請投資我,大概才是最難的事XDDD

      會有這些書的原因除了收集癖外,最近碰到的例子是根本拿不到source code,或是必須從 binary 逆向回去 debug。

      系統程式的教科書有個問題就是不肯用真實世界的CPU當例子...這點不知道是在堅持什麼

      刪除
  2. 我也這麼覺得, 明明可以用 elf 當例子, 用真實世界的 cpu 來教學, 卻偏偏用什麼 SIC, 和真實世界連不起來。

    就算用真實世界的東西來說明, 還是可以簡化不少東西, 也是很適合拿來教學的。

    回覆刪除