1. 程式人生 > >一個 SPI 轉串列埠驅動的優化

一個 SPI 轉串列埠驅動的優化

一個 SPI 轉串列埠驅動的優化

作者: zjujoe 轉載請註明出處

由於串列埠不夠用,我們選用了 Exar 公司的 xr20m1172 SPI/I2C轉串列埠晶片。考慮到速率問題(I2c較慢只有100k/400k兩種模式),我們採用 SPI介面驅動該晶片。

花了兩週的時間, 該晶片可以和 PC 通訊了。最近應用組同事拿去連線一個加密晶片,發現不能正常工作。 該加密晶片提供的介面為 19200速率,8 位位元組, 奇校驗, 1位停止位,CTS/RTC 流控制。硬體同事仔細檢視示波器,發現原來我們發出 0x94 時, 校驗位時對時錯。

考慮到我們的驅動程式確實也只是剛剛能夠工作,應該有較大的優化空間,

決定對程式碼進行一定優化,以輔助問題解決。

首先寫一個測試程式,該程式以 Cooked 模式, 8 位位元組, 奇校驗, 1位停止位, auto rts/cts 流控設定串列埠,然後寫出一個位元組 (0x55 或者 0x94)。然後恢復串列埠設定,最後退出。

優化1 提高 SPI 介面速率:

通過SPI 介面的時鐘來提高 uart 暫存器速率。 Marvell 平臺的 ssp 介面有一個分頻器,系統有一個暫存器SSCR bit27控制分頻器的輸入是 26M還是 13M 另外同一個暫存器裡bit 8-bit19 控制分頻數。 SPI輸出頻率為 (13M or 26M / SSCR[8-19] + 1

我們使用系統提供的3.25M時鐘來計數,計算各種設定下 SPI 讀寫 uart 暫存器時間:

1)預設設定, SSCR[27] = 0, SSCR[8-19]= 0xf, 測量結果:

計數

time in uS

write

5a

27.69230769

read

52

25.23076923

2SSCR[27] = 0, SSCR[8-19]= 0x0, 測量結果:

計數

time in uS

write

19

7.692307692

read

14

6.153846154

3SSCR[27] = 1, SSCR[8-19]= 0x0, 測量結果:

計數

time in uS

write

17

7.076923077

read

12

5.538461538

效能大概提高了 4-5倍!

遺憾的是發現傳送 0x94 正常,但是傳送0x55 校驗位時好時壞。

優化2 做暫存器 cache, 減少 SPI 讀操作

Uart 暫存器的特殊之處在於它進行了地址複用:同一個地址對應了多個暫存器,到底是哪一個依賴於三個暫存器的內容:lcr, efr, mcr. 比如, lcr[7] == 1, lcr <> 0xbf 時, 地址 0x0 對應了 DLL 暫存器,在 lcr[7] == 1 時, 地址 0x0 對應了RHR/THR暫存器(讀為 RHR, 寫為 THR. 這樣, 我們在進行讀寫暫存器時,需要頻繁訪問 lcr/efr/mcr, 對這三個暫存器進行緩衝可以減少 SPI介面訪問, 提高讀寫效率。

優化前: 整個測試程式 SPI 讀操作為: 103 次, 寫操作為 240次。其中傳送一個字元的 SPI 讀操作為 15 次, 寫操作為 26次。

優化後:整個測試程式 SPI 讀操作為: 24 次, 寫操作為 238次。其中傳送一個字元的 SPI 讀操作為 5 次, 寫操作為 27次。

可見讀操作大大減少了。寫運算元基本不變。

遺憾的是問題依舊。

優化3 暫存器訪問原子化

前面我們提過, 訪問一個uart 暫存器可能需要先設定其它暫存器,這導致操作的原子性很難保證,另外,我們是通過 SPI 介面來讀寫 uart 暫存器的,這導致更加難以保證讀寫暫存器的原子性。 考慮到我們要在中斷處理函式裡訪問 uart 暫存器,我們利用自旋鎖來保證暫存器的原子性。

遺憾的是問題依舊。

優化4 進一步減少暫存器訪問

跟蹤系統暫存器訪問發現, 我們目前不需要使用 IER:bit[4-7], ISR:bit[4/5], FCR :bit[4/5], MCR :bit[2/5/6], 對這些bit 訪問需要更多的限制條件(EFR[4] == 1),事實上我們目前不需要。

所以我們修改這幾個暫存器的訪問限制, 從而節約暫存器訪問。

優化後:整個測試程式 SPI 讀操作為: 21 次, 寫操作為 67次。其中傳送一個字元的 SPI 讀操作為 4 次, 寫操作為 3次。

測試一下,傳送 0x55 , 0x94 50次, 發現全部正確!