基于PL022 SPI 控制器 海思3516系列芯片SPI速率慢問題深入分析與優化( 二 )

spi_write_XXbyte這幾個函數,這幾個函數內都有這樣的代碼
spi_enable();ssp_writew(SSP_DR,spi_data);ret = hi_spi_check_timeout();if(ret != 0){printk("spi_send timeout\n");}spi_disable();在前面的代碼中設置過CS片選信號由spi使能信號控制,也就是說 , 這段代碼每次寫一個字節都要拉一次CS信號 , 效率比較低 , 我將 spi_enable 和 spi_disable 提出來后再次進行測試,速度確實有一定提升,但提升幅度仍然不大,只有大概幾百ns的水平,優化效果仍然不理想 。
這段代碼只剩下兩個函數了,ssp_writew 是寫寄存器,根本不能優化,只能想辦法優化 hi_spi_check_timeout,來看一下這個函數的實現
static int hi_spi_check_timeout(void){unsigned int value =https://www.huyubaike.com/biancheng/0;unsigned int tmp = 0;while (1){ssp_readw(SSP_SR,value);if ((value & SPI_SR_TFE) && (!(value & SPI_SR_BSY))){break;}if (tmp++ > MAX_WAIT){printk("spi transfer wait timeout!\n");return -1;}udelay(1);}return 0;}邏輯非常簡單,不停地讀取發送FIFO空和SPI忙的標志位,延時1us繼續讀,直到發送完成且SPI空閑 ??吹竭@個 udelay(1) 你現在的想法肯定和我當時的想法一樣:第一次讀取發現寄存器沒有置位 , 延時1us,第二次讀取寄存器置位,退出循環 , 現在的發送間隔是1.6us,去掉這個延時盡快讀取寄存器,應該直接能優化到0.6us以內 。
想得美!實際測試去掉這個 udelay(1) 以后優化效果確實挺明顯的,延時直接縮短到 1.2us 左右,但是這個延時還是太長了 。
現在再來看這個函數,就剩一個讀寄存器了,為了保證可靠傳輸,讀標志位肯定不能去掉 , 這已經最簡單了,還能怎么優化呢?我們重新梳理一下:去掉 udelay(1) 后間隔縮短到 1.2us 左右,說明這里循環讀了很多次寄存器,不然怎么還有這么長的延時?那要不就加打印看看這里到底循環讀取了幾次?來來來,競猜一下這里到底循環了幾次?十次以內?百次以內?還是千次以內?答案是一次!沒錯只有一次!
1次這個答案可以說即在意料之外也在意料之中 。說它在意料之外是因為讀寫寄存器這種操作是非??斓?,一般而言幾個cpu時鐘就能完成,但這里讀一個寄存器卻花費了 1.2us 。說它在意料之內是因為這些物理寄存器都是通過硬件置位的,以發送 FIFO 為空標志為例 , 當 SPI 發送完成瞬間它就會被硬件置位,這一點在任何一款單片機上都可以驗證,實際操作一下就會發現類似的寄存器是瞬間被置位的 。pl022 應該是非常成熟的 SPI 控制器,我覺得芯片設計人員不會范發送 FIFO 為空后很長時間才設置標志位這種低級錯誤 。
接下賴重點思考這個問題:為什么 ssp_readw(SSP_SR,value) 這樣一個簡單的讀寄存器操作要 1.2us 之久?(目前我測試 ssp_writew 寫一個寄存器大概在 100ns 以內 , ssp_readw 讀一個寄存器大概在 1us 左右)這個問題無論是百度還是谷歌我找了很久都沒有找到確切答案 , 如果有知道的大佬非常歡迎指導?。。〔話祖沃?,私信發紅包 。下面是我個人的推測,雖然是推測,但我覺得這就是正確答案 , 僅供參考:
linux 不同于單片機裸機程序那樣可以直接訪問寄存器,如果需要讀寫物理寄存器,在內核態使用 ioremap (在用戶態使用 mmap)將一段物理地址映射到內核態(或用戶態)的虛擬地址空間 , 再對映射后的地址進行讀寫 。
來看一下實際讀寫寄存器的這兩個接口,很簡單就是直接讀寫某個地址處的數據(前提是這個地址必須經過映射) 。
#definessp_readw(addr,ret)(ret =(*(volatile unsigned int *)(addr)))#definessp_writew(addr,value)((*(volatile unsigned int *)(addr)) = (value))首先可以明確一點,讀寫寄存器的操作必然會經過MMU 。對于寫寄存器來說,不需要考慮同步、臟數據等問題 , MMU 應該是直接將這個數據寫到物理地址了 。對于讀寄存器來說,有 volatile 關鍵字的存在,這里的代碼不會去優化,每次讀取必須從物理地址進行讀取 , 這里可能需要 cache 回寫等操作導致導致讀取的速度非常慢 。
在 u-boot 下可以直接讀寫物理寄存器,應該不需要這么久,幾個CLK就可以完成吧?這一點我沒有驗證過,有測試過的朋友歡迎分享 。
總之 , 耗時的地方找到了,想辦法優化吧 。我這里有兩種優化思路:

  1. 查閱手冊可知,SPI的內部收發部分各有一個寬度 16bit、深度為 256 的 FIFO 。我可以一次性寫入不超過256字節的數據 , 然后使用 ssp_readw 不停地讀取寄存器,直到發送 FIFO 為空 。在50MHz時鐘的條件下情況下 ssp_readw 的 1.2us 延時相當于發送了8字節左右,理論上來說如果發送的數據量大于8字節 , 這個 1.2us 延時等于沒有 。

    推薦閱讀