簡潔架構 - ch29 整潔的嵌入式架構

作者在他的生涯歷程發現一個現象,許多軟體被化作韌體,而與硬體牢牢地綁在一起。

軟體是可以有很長壽命的東西,但隨著硬體的演進,韌體會變得過時。

儘管軟體不會磨損,但可能因為韌體或硬體上未被管理的依賴性而被破壞。

嵌入式軟體由於硬體依賴性的影響而被剝奪其長壽潛力,並不罕見。

韌體依賴於硬體,隨著硬體的演進,他卻難以改變,所以我們應該考慮到這個現實來建構我們的嵌入式程式碼。

這章節主要想討論如何保持嵌入式軟體架構的整潔。

App 性質的測試

這邊介紹 Kent Beck 的三種軟體建置活動(註解為作者的評論)

  1. 「先讓他可以運作」

    如果他不能運作,我們就會失業

  2. 「然後把他做對」

    重構程式碼,以便你和他人能夠理解他,並且能隨著需求變化或更容易理解而演化。

  3. 「然後讓他變快」

    重構「需要」效能的程式碼

為什麼這麼多潛在的嵌入式軟體變成韌體?因為大多數的開發重點只放在了「讓他可以運作」上。

所以作者在這邊提到程式設計師的「App 性質的測試」,主要是來自於我們「只」關心我們的程式是否可以運作。

僅僅是通過了「App 性質的測試」,並不能說這個應用程式具備一個簡潔的嵌入式架構

目標硬體的瓶頸

嵌入式開發人員需要處理非嵌入式開發人員不用面臨的許多特殊問題,例如:

  1. 有限的記憶體
  2. 即時的限制及最後期限
  3. 有限的 IO
  4. 非傳統的使用者介面
  5. 感測器和現實世界的連接

其中一個特殊的嵌入式問題是「目標硬體的瓶頸」。
當嵌入式程式碼的結構沒有應用整潔架構的原則和實踐時,會經常遇到只能在目標上進行測試的情況,如果目標是你唯一的測試手段,則目標的硬體瓶頸就會讓你放慢開發速度。

簡潔的嵌入式架構是可測試的嵌入式架構

接下來讓我們來看看如何將一些架構的原理應用在嵌入式軟體及韌體,藉此來幫助我們消除目標的瓶頸。

層(Layers)

圖 29.1 三層

比較好的分層範例可以參考像這樣的三層

  • 軟體
  • 韌體
  • 硬體
1
2
3
4
5
6
7
8
9
10
11
agent agent [
Software

----

Firmware

----

Hardware
]

硬體和系統其餘的部分之間的分離式給定的,意思是說,硬體一但被確定下來,就不太會改變了。

1
2
3
4
5
6
7
agent agent [
Firmware

----

Hardware
]
圖 29.2 硬體必須與系統的其他部分分離

軟體和韌體混雜在一起是一種危險的反模式(anti-pattern),面對小的變化我們也可能會需要進行全面的回歸測試。

硬體是細節

1
2
3
4
5
6
7
8
9
10
11
agent agent [
Software

....

Firmware

----

Hardware
]
圖 29.3 軟體和韌體之間的界線比起程式碼與硬體之間的界線要模糊一些

韌體和軟體之間的界線通常不像程式碼與硬體之間那樣明顯。

當你作爲一個嵌入式開發人員,你的工作就是鞏固這條線。軟體和韌體之間的邊界稱之為硬體抽象層(HAL : Hardware Abstractioni Layer)

1
2
3
4
5
6
7
8
9
10
11
12
13
agent agent [
Software

----
HAL
----

Firmware

----

Hardware
]
圖 29.4 硬體抽象層

HAL 是為了給軟體使用而存在,HAL 的 Api 應該為了軟體需求而量身定制,HAL 提供一個服務,但他不會像軟體透露他的細節。

不要像 HAL 的使用者顯示硬體細節

整潔架構上的軟體可以遠離目標硬體去做測試,一個成功的 HAL 提供了軟體及目標硬體上的接縫,讓我們可以在執行測試的時候使用這個些接縫作為代替點。

處理器是細節

一個整潔的嵌入式架構會盡力降低直接使用設備存取暫存器的實作,並將他們完全控制在韌體之中。任何關於這些暫存器的知識都將成為韌體,並因此與晶片綁定在一起。
當我想在取得穩定的硬體之前就需要開發或者是我需要將嵌入式架構移動到新的硬體時,都會對我們造成阻礙。

簡單來說如果硬體有變動,這些綁定的部分造成的依賴會讓我們很難抽身。

如果你使用者樣的微控器,你的韌體可以透過某種形式的處理器抽象層(PAL : processor abstraction layer)來隔離低層級的函式。

作業系統是細節

HAL 是必要的,但並不足夠,當我們今天使用即時系統(RTOS)或是某些嵌入式版本的 Windows 或 Linux ,為了延長我們的嵌入式程式碼的壽命,我們必須要將作業系統視為一個細節,並防止依賴作業系統

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
agent agent [
Software

----

OS

----

Firmware

----

Hardware
]
圖 29.5 加入作業系統

整潔的嵌入式架構透過加入作業系統抽象層(OSAL : operating system abstraction layer)來將軟體與作業系統隔離開來

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
agent agent [
Software

----
OSAL
----

OS

----
HAL
----

Firmware

----

Hardware
]
圖 29.6 作業系統抽象層

OSAL 可以幫忙提供測試點,使軟體層中的程式碼可以擺脫硬體目標及 OS 去做測試。

以介面及替換性來設計程式

除了在每個主要層(軟體、硬體系統、韌體和硬體)內部加入 HAL 以及潛在的 OASL 之外,你還可以導入本書提及過的各種原則,這些原則鼓勵分離關注點,也就是以介面的替換性來設計程式。

分層架構的思維建立在以介面進行程式設計的基礎上,當一個模組透過介面與另外一個模組互動時,可以用一個服務提供者替代另一個。

一個整潔的嵌入式架構在「層內」是可測試的,因為模組之間透過介面去進行互動,而每個介面都提供有助於 off-target 測試的接合處或替代點。

DRY 條件式編譯指令

有一種替換性常常被忽略,坊間的嵌入式程式常見一種趨勢,就是以邏輯判斷去開關程式碼片段,這種類型的程式碼重複違反了 DRY 原則。

但如果這邊有導入了硬體抽象層(HAL)呢?

硬體編譯類型會隱藏在 HAL 的細節之下,如果 HAL 提供了一組介面,而非使用條件是編譯,那麼我們就可以使用鏈結器或某種形式的 runtime 綁定,將軟體連接到硬體。

總結

讓所有程式碼成為韌體不太健康,導入簡潔的嵌入式架構才能有助於使你的產品長期保持健康狀態。