實現穩健的微控制器到 FPGA SPI 介面:第 2 部分 - 通訊協定定義

本系列貼文的主要目標是開發一種在微控制器(uC)和 FPGA 之間快速可靠地傳輸資料的機制。在本貼文中,我們將描述通訊協定和相關框架的屬性。

設計 uC 到 FPGA 介面的方法有很多。讓我們先建立一套通用的設計要求來框定這個對話:

  • 盡量減少 uC 和 FPGA 之間的線數
  • 允許 uC 和 FPGA 之間有針對性的(可尋址的)資訊交換
  • 容納數千個可尋址的 FPGA 暫存器
  • 優化訊息長度
  • 易於在 uC 中編程,而不會在 FPGA 中過於複雜
  • 高速
  • 提供資料完整性的度量
  • 跨平台相容,例如,不特定於 Xilinx

請注意,FPGA 資源的最小利用率不是設計要求之一。 本系列的第 1 部分 將探討這種明顯遺漏的原因。回想一下,所有 FPGA 模組輸出都是在時脈域內暫存同步的。這種設計規定被認為是一種可接受的權衡,平衡 FPGA 結構的消耗與設計穩定性和減少故障排除時間。雖然本文所描述的 RTL 沒有經過最佳化,但它只消耗了 Digilent Basys 3 的 Xilinx Artix-7 XC7A35T-1CPG236C FPGA 中可用總資源的一小部分。

協定的概念

設計及其需求最好透過硬體和數據協定進行視覺化。這類似於開放系統互連(OSI)模型的第一層和第二層。第一層物理層描述電氣規範。在本設計中,低線數意味著我們可以使用 I2C, SPI,雙SPI,甚至四路 SPI。 從這些選項中,SPI 脫穎而出,因為它允許全雙工通訊,並且很容易從 uC 內部編程。 順便說一句,我們假設 FPGA 可以比 uC 更快處理數據,從而消除了對流量控制機制的需求。

第 2 層數據連結層級要求我們定義一個資料交換的協定。這一層的重點是將資料分成適當大小的區塊,以及適當的標頭和錯誤偵測。請注意,第 2 層和第 1 層是相互獨立的,因為第 2 層不直接與第 1 層指定的電氣介面有關。在未來,這可能允許我們的 SPI 介面被四 SPI 甚至八 SPI 所取代,前提是保留層之間的位元組級介面。

據我所知,目前還沒有使用 SPI 進行資料交換的既定協定。設備特定的實作是規範。 例如,感測器可能有製造商或設備特定的協定來讀取和寫入資料。這將包括涉及單一或區塊寄存器的資料交換規則。 所選的設備可能包含循環冗餘檢查(CRC),允許對資料讀寫操作的資料完整性進行測量。 最後,大多數 SPI 設備都使用半雙工協定。一個典型的 SPI 傳輸從一個包含讀/寫標誌的位元組開始。然後,設備將對後續的時鐘八位元組採取適當的動作。在 MOSI 和 MISO 線上發現同時傳輸的全雙工是很少見的。

從 uC 的角度來看,一個全雙工 SPI 主控是每個實作。這是一個 Arduino 實現,包括晶片選擇訊號內 SPI 傳輸的 。

// prepare buf for SPI transmission
// append CRC

digitalWrite(CS_PIN, LOW);
SPI.transfer(buffer, bufferSize);    // full-duplex: each uC byte in buff is replaced with an FPGA byte
digitalWrite(CS_PIN, HIGH);

// verify and handle CRC
// unpack and handle FPGA data

802.3 框架 SPI 的轉接

在我們繼續之前,有必要檢查一下 802.3 乙太網路框架 ,因為它是 SPI 協定的起點。如圖 1 所示,乙太網路封包以包含前言、MAC 目的地加源和長度欄位的標頭開始。接下來是有效載荷,它可以從 46 到1500 位元組不等。後面是一個 32 位元的 CRC 編碼。

圖1: 簡化的 802.3 乙太網路框架

我們可以縮小乙太網路框架的尺寸,以更好地適應 uC 到 FPGA 的介面。有了 SPI,就不需要序言和尋址了。 SPI CS_not 控制線和 SPI 的一對一特性使這些欄位冗餘。大小欄位和可變長度的有效載荷是需要保留的特性。這將允許 uC 讀取或寫入單一 FPGA 暫存器。它還將允許傳輸一個數據塊。最後,CRC 將允許對資料進行驗證。

出於我們的目的,字段的大小可能會減少。一個合理的折衷辦法是使用單一位元組來標識有效載荷的長度。這自然將框架的長度限制為 256 個元素,包括標頭和 CRC 的空間。考慮到幀長度的減少,16 位元 CRC 是合理的。

均衡訊息類型要求

回想一下,SPI 提供了一個全雙工通訊通道。如上所示,從 uC 程式設計的角度來看,這並不過於複雜。然而,為了利用這種全雙工模式,我們現在必須確定如何處理 FPGA 內部的特定硬體。

要使用這種方法,FPGA 硬體必須是可尋址的。例如,FPGA 訓練板的 LED 可能與一個基址相關聯-安裝在 Basys 3 上的 16 個 LED 的兩個位元組。電路板的 16 個滑動開關可以與另外兩個位址相關聯。有了這種結構,FPGA 就像 uC 的另一個周邊一樣。uC 透過 SPI 使用框架協定對 FPGA 硬體進行讀寫。

在本文的其餘部分中,我們將把 FPGA 硬體稱為周邊。這反映了 uC 透過 SP 介面有效地使用 FPGA 作為高效能周邊的設計。

設計要求之一是可擴展性,能夠處理幾個到數千個 FPGA 暫存器。考慮到這項要求,一個位元組寬度的位址將我們限制在 256 個項目中過於狹窄。然而,2 位元組寬的介面是可以接受的,因為它允許 uC 在 FPGA 內尋址多達 65536 個專案。

我們可以在這一點上停下來,並為我們的 SPI 框架確定一個標頭。它將包含指示命令類型(讀或寫)的一個位元組、有效載荷長度的一個位元組、位址的兩個位元組、可變長度的有效載荷和 CRC。對應的長度為 4 字節,負載最大可達 250 字節,CRC 為 2 位元組。

這對於許多設計來說可能已經足夠了。然而,這種方法隱含了半雙工的假設。要了解原因,請考慮與 SPI 轉移相關的因果關係。假設控制板的 16 個 LED 燈的暫存器位於基址 0x0200。如果我們要在這個暫存器上啟動 SPI 傳輸,我們將有效地讀取 FPGA 暫存器,然後向其寫入。讀取操作將是毫無意義的,因為它將包含舊資料。在這裡,我們假設在所有 FPGA 暫存器上實現雙緩衝,以防止與多位元組暫存器更新相關的不穩定。

對於全雙工,我們需要新增另一個尋址欄位。假設我們有一個暫存器,它保存位於位址 0x0202 的 FPGA 板的開關狀態。如果我們在框架中新增另一個尋址字段,我們現在可以執行有意義的讀寫操作。

我們現在可以讀取開關狀態,同時更新 LED 燈。

新的標頭長度為 5 個位元組,有效載荷可達 249 個位元組,CRC 為 2 個位元組。注意,命令位元組不再是必需的了。

  • Payload Length:(1位元組)標頭和 Payload 中的 num 位元組
  • To 位址欄位(2位元組)標識要寫入的 FPGA 暫存器
  • 來自位址欄位(2位元組)標識要讀取的 FPGA 硬體
  • Payload:(1 ~ 249 位元組)
  • CRC:(2位元組)

仔細比較這兩個選項,就會發現一個位元組的差異。具有讀/寫命令位元組的半雙工框架需要 8 位元組的傳輸才能寫入 FPGA 板的 16 個 LED。對應的全雙工操作則需要 9 個位元組。然而,該設計現在可以同時讀取開關並寫入 LED。 根據你的看法,我們可以在這個過程中浪費了一個字節,或者節省了八個字節加上與事務相關的時間開銷。

在考慮了這些設計權衡之後,我們將專門關注全雙工選項。uC 開銷和 FPGA 程式開銷是無關緊要的。 小載重傳輸的速度差異是最小的。對於大載荷,這種變化是無關緊要的。

這種全雙工協議有一個缺點,必須加以辨識。事實上,現在每一次資料傳輸都涉及到一次寫入操作。有時這樣做會很不方便。 我們將保留從 0xFF00 到 0xFFFF 位置的 FPGA 位址的空/非功能塊,而不是混淆寫入操作。當需要進行唯讀記憶體操作時,uC 將「寫」到這個空位置。透過這種簡單的修改,全雙工操作可以作為半雙工讀操作來操作。

向 FPGA 寫入和讀取

全雙工 uC 到 FPGA 協定的命令與回應幀如圖 2 所示。標頭內容在框架之間密切相關。我們看到讀地址和長度標頭資訊在回應中得到呼應。請注意,有一個與回應相關的因果關係問題。由於字段是同時進行逐字節傳輸的流,因此回應將被延遲。例如,作為發送的第一個位元組的長度字段不能作為回應中的第一個字段,因為當 FPGA 鎖定要發送的第一個位元組時,它尚未被接收。這不是一個問題,因為寫地址對回應幀並不重要。相反,FPGA 可以發送程式特定的標誌,而 uC 正在串流傳輸第一個位元組的長度。

框架字段的其餘部分是類似的。 FPGA 回應將從基讀位址開始串流資料。同時,FPGA 將同時將 uC 資料移至臨時緩衝區。計算出相應的CRC並附加到框架中。

圖2:指令框架和回應框架之間的關係。

命令框架的資料完整性

CRC 提供了資料完整性的量測。回想一下,CRC 是應用於框架內容的。讓我們從 uC 的角度來考慮這個三步驟流程。uC 將首先準備一個帶有相關資料的幀緩衝區。然後,它將呼叫一個函數來計算並將 CRC 追加到緩衝區。最後,uC 將呼叫函數透過 SPI 傳輸資料。

FPGA 將把流資料放入緩衝區,同時計算指令框架 CRC。當框架完成後,FPGA 將比較接收到的和計算得到的命令框架 CRC。如果它們匹配,FPGA 將快速地將資料從緩衝區傳輸到相關的硬體暫存器。 這是一個相對快速的操作,需要大約 5 個 uS 來完成一個完整的負載。如果接收到的和計算的 CRC 不一致,FPGA 將丟棄框架。它還會觸發框架錯誤機制。

包含一個框架錯誤計數器用於故障排除,如圖 2 響應框架所示。也可以增加從 FPGA 到 uC 的物理線的規定。這將允許 uC 快速檢測並響應框架錯誤。

FPGA 臨時框架緩衝器的重要性怎麼強調都不為過。在 CRC 驗證之前,FPGA 不會對新資料採取行動。這個重要的主題將在另一期文章中討論。

回應框架的資料完整性

響應框架的完整性需要在 uC 中仔細編程。我們必須先認識到,在命令框架被驗證之前,FPGA 將立即回應 uC 讀取請求。因此,由 uC 來驗證 FPGA 開發資料的完整性以及 FPGA 對指令的理解。這是透過回應框架中的框架長度和基讀位址的迴聲來完成的。uC 可以透過比較回應框架和命令框架中的資料來識別錯誤。

例如,假設從 uC 到 FPGA 的命令訊框的讀取位址欄位中有一個位元錯誤。在 uC 到 FPGA 的寫入操作中有一個很好的完整性措施,因為錯誤有非常高的可能性被 FPGA 的 CRC 機制偵測到。FPGA 將忽略該框架。同時,我們必須考慮與 FPGA 並行結構相關的因果關係。

FPGA 將在它知道命令框架損壞之前很久串流讀取的資料。事實上,它會盡職盡責地從錯誤的讀取位址開始串流資料。它甚至會向框架追加一個有效的 CRC。

當傳輸完成時,uC 將擁有回應框架的完整副本。在採取行動之前,它必須驗證數據。這是一個兩步驟的過程:

  • uC 必須先驗證傳送和接收欄位的長度和讀取位址是否相符。
  • 然後,它必須驗證回應框架的 CRC。

當這兩個操作完成後,uC 可以採取適當的措施來處理通訊錯誤。

第 3 部分已發布

歡迎您的評論和建議。 特別歡迎進一步討論高階 RTL 系統設計方法。