一個 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 |
2)SSCR[27] = 0, SSCR[8-19]= 0x0, 測量結果:
計數 |
time in uS |
|
write |
19 |
7.692307692 |
read |
14 |
6.153846154 |
3)SSCR[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次, 發現全部正確!