PCIE開發筆記(七)DLL介紹(上)
來自專欄 PCI-E協議9 人贊了文章
在之前的文章中,我們已經知道PCIE協議可以劃分為三個層,每個層的作用與分工不同。在接下來的幾篇文章中,我將詳細介紹PCIE協議中三個層的詳細作用。
一個良好的結構分層可以使整個系統功能簡明。明確每個部分的工作是什麼,使得每個層只專註自己應該做的工作,同時也使得當問題出現之後能夠馬上定位出問題出現的位置。
如果你了解TCP/IP網路協議,那麼你就不難發現它好像是PCIE的一個升級版,增加了更多的層次。同時PCIE又好像是TCP/IP協議的一個濃縮版,保留了達到目的的必要層次。如果你仔細思考這些層的劃分,你就不難想到, 其實他們的相似並非偶然,而是進行一個功能比較完善的通信所必須的,同時又因為兩者的目的的不同,而產生一些必然的差異。
- TL層(Transaction Layer):TL層著眼於數據包在整個拓撲結構中的傳輸,完成數據包的路由工作,同時進行一些數據包傳輸優先順序的控制,從而滿足一些特定要求的數據包的傳輸。
- DLL層(Data Link Layer):DLL層主要作用是確保一個數據包能夠正確地從一個鏈路的發送端傳輸到鏈路的接收端。
- PL層(Physical Layer):PL層的主要作用是探測一個鏈路的兩端是否連接能夠工作的設備。在探測到鏈路兩端存在設備之後,對鏈路進行訓練,使得鏈路能夠在正確的頻率和位寬下工作。
那DLL層如何確保一個數據包正確的傳輸?
首先,我們必須對「正確的傳輸」有如下定義:
- 接收方接收的數據包與發送方的數據包完全一致的,沒有任何改變。
- 在圖1中,接收方DLL層向上傳遞給TL層的數據包的順序必須和發送方發送的順序嚴格一致(至於什麼原因後面會講到)。
DLL層使用ACK/NAK協議保證數據包的正確傳輸。如圖2所示,發送方(Transmit)發送一個數據包之後,接收方(Receiver)會返回一個ACK/NAK DLLP(Data Link Layer Packet),並且在DLLP中包含一個序列號(Sequence Number)信息,來具體確認那一個TLP傳輸是否成功。ACK(Acknowledgement)表示成功,NAK(Negative Acknowledgment)表示失敗。
圖3展示了DLL層中實現ACK/NAK協議的更多細節。對於每一個通過Link由Device A發送到Device B的TLP數據包,Device B會通過TLP的LCRC(Link Cyclic Redundancy Check)來判斷TLP數據包是否存在錯誤。在檢查錯誤之後,Device B通過返回一個ACK或NAK DLLP數據包來告知Device A是否接受成功。Device A接收一個ACK DLLP則確認Device B正確地接受一個或者多個數據包,接收一個NAK DLLP則確認Device B錯誤地接受一個數據包(同時它也隱形的表示一些TLP發送成功,這個在下一篇中說明)。當Device A接收到NAK DLLP之後,Device A設備將會再次發送相關數據包。
下面我們就比照圖4詳細介紹ACK/NAK協議如何實現整個過程的。
首先介紹原理圖裡面幾個部分:
- Replay Buffer:Replay Buffer:主要用來存儲TLP數據包,其中包括序列號(Sequence Number)和LCRC。所有的TLP按照發送的順序儲存,後進入的TLP的序列號總是大於先進入的序列號。 當Device A接收到ACK DLLP之後表明Device B接收TLP成功,Device A就會從Replay Buffer中去掉相應的TLP。如果收到一個NAK數據包,則再次發送整個Buffer。
- NEX_TRANSMIT_SEQ Counter:這個計數器主要用來產生Sequence Number,這個值將會賦給下一個傳輸的TLP。這是一個32_bit計數器,當計數器到達最大值4095後回滾到0。
- LCRC Generator:LCRC產生器,用來為TLP產生一個32位的LCRC。Device B使用它來判斷接受的TLP是否有問題。
- REPLAY_NUM Count: 這是一個2_bit的計數器,主要是用來統計Device A收到NAK和REPLAY_TIMER 時間溢出次數(time out)的計數器。當該計數器溢出時,表明這個物理鏈路可能出現問題,DLL層將會觸發PL層進行一個物理鏈路重訓練(Physical Layer Link-retrain)。整個DLL層不再進行數據包的傳輸,直到重訓練完成之後。REPLAY_NUM在系統複位或者DLL層處於不活躍狀態時初始化為00b。當Device A再次發送Buffer之後,其中一些TLP被確認被接收(通過ACK或者NAK DLLP)時,則複位為00b。
- REPLAY_TIMER Counter:REPLAY_TIMER只要是用來測量從Device A發送任意TLP之後到它接收到一個與之對應的ACK/NAK的數據包之間的時間。這個計數器從發送的TLP的最後一刻開始計數。當且僅當Device A接收到一個ACK並且與之對應的TLP在Replay Buffer 中還存在時,這個計數器歸0,如果此時Buffer中還有其他未經NAK/ACK確認的TLP,則計數器重新開始計時,如果不存在未發送的TLP,就保持0。當Device A接收到NAK時,計數器歸零並保持,直到DLL再次發送數據包。當PL進行鏈路訓練時,不計數。該計數器的作用主要是用來決定什麼時候進行數據重發包,使保證這個DLL層向前運行,而不是卡在某個環節(例如當TLP已經發送出去,但是始終接收不到返回的ACK/NAK DLLP,或者始終接收到錯誤的ACK/NAK DLLP)。
- ACKD_SEQ Count:一個12_bit的計數器,用來保存上一次接受到的ACK/NAK DLLP中的Sequence Number。當接受到新的ACK/NAK DLLP,就使用其中的AckNak_Seq_Num欄位更新這個計數器。ACKD_SEQ可以和NEXT_TRANSMIT_SEQ一起來判斷整個Buffer是否滿。當滿足:
(NEXT_TRANSMIT_SEQ - ACKD_SEQ)mod 4096>2048
表明兩個計數器之間的間距過大,DLL層將會拒絕接收TL層傳輸的新的TLP數據包,同時一個致命的錯誤將會報告。
- DLLP CRC Check: Device A接收到一個返回的DLLP之後,會對它進行CRC檢查,如果是一個沒有錯誤的DLLP,那個將會接收它。如果是錯誤的,將什麼措施都不採取,將這個DLLP丟棄。整個流程如圖5所示。
- Receive Buffer:該緩衝區用來短暫的存儲帶有TLP CRC和Sequence Number 的TLP數據包,然後傳遞給上游進行檢查,如果沒有錯誤,那麼TLP將傳遞給TL層。如果存在錯誤,那麼Device B將會丟棄這個TLP,並計劃準備發送一個NAK DLLP數據包。
- LCRC Error Check:用來檢查接收到的TLP是否存在錯誤。
- NEXT_RCV_SEQ Count:一個12_bit的計數器,用來保存下一個期望接收到的數據包的Sequence Number。這個計數器在Device B複位或者DLL層處於休眠狀態時初始化為0。當Device A接收到一個期望的TLP之後,並且傳遞給TL層之後,該寄存器進行加1。當計數器溢出後返回0。當接收到錯誤TLP之後,寄存器不遞增。
- Sequence Number Check:在CRC檢查之後,將會對比接收到的TLP的Sequence Number位是否和NEXT_RCV_SEQ一致。
當TLP的Sequence Number等於NEXT_RCV_SEQ時,這個TLP將會被接收,並傳遞給TL層進行後續處理。同時NEXT_RCV_SEQ計數器進行加一。Device B將繼續接收進入的TLP,不返回ACK DLLP直到ACKNAK_LATENCY_TIMER溢出或者是超出預設值。
當TLP的Sequence Number小於NEXT_RCV_SEQ時,並且兩者之間的差距小於2048,說明這個TLP數據包之前已經接收過,那麼Device B將會丟棄這個TLP,並計劃返回一個ACK DLLP。
當TLP的Sequence Number大於NEXT_RCV_SEQ時,那麼這個TLP將會被丟棄,同時計劃返回一個NAK DLLP。
- NAK_SCHEDULED Flag:當Device B計劃返回一個NAK DLLP時,這個位將會被置1。當Device B接收到再次發送的與返回的NAK DLLP有關的第一個TLP時,該位將清零。當該位被置1時,Device B對後續的TLP不做處理,直接丟棄。
- ACKNAK_LATENCY_TIMER:ACKNAK_LATENCY_TIMER是用來記錄第一個ACK DLLP計劃發出到當前時間的長度。Device B當該值溢出或者超出預設值時,實際發出返回一個ACK DLLP。
這裡我們要區別一下計劃發出與實際發出之間的區別,計劃發出是表明Device將要發出一個但是實際上還沒有發出,實際發出就是真正有一個DLLP從鏈路發出。使用ACKNAK_LATENCY_TIMER的原因主要是想要減輕ACK DLLP佔用鏈路帶寬,具體方法後面介紹。
- ACK/NAK DLLP Generator: 這一部分主要是用來產生ACK/NAK DLLP。其中產生的DLLP中的ACKNAK_RCV_SEQ=NEXT_RCV_SEQ-1。
在詳細介紹ACK/NAK原理圖中的幾個部分之後,下面介紹這個工作流程。分為發送端(Device A)和接收端(Device B)。
- 發送端工作流程
圖4中,在Device A接收到來自TL層傳遞的TLP之後,會首先在TLP數據包的前段添加Sequence Number,在數據包後添加CRC,之後將數據包放入Buffer中。於此同時會進行兩個判斷,
- (NEXT_TRANSMIT_SEQ - ACKD_SEQ)mod 4096>2048
- 判斷Buffer是否已經寫滿
這兩個判斷B將說明DLL接收來自TLP數據包是否過多,如果過多將會阻止TL再傳遞數據包。然後Device A發送Buffer中的數據包。等待Device B返回的ACK/NAK DLLP。
當接收到Device B返回的DLLP數據包之後,會首先按照圖5對DLLP進行錯誤檢查。檢查無誤之後按照圖6流程進行相應的流程。對於接收到的DLLP,不管是ACK/NAK,都將進行圖6紅框內操作。
除了圖6所示的方式引起Replay Buffer的再次發送,另外一種引起再次發送的方式就是REPLAY_TIMER 計數器到達預設值。
- 接收端工作流程
圖7中顯示Device B接收數據包之後的工作流程,首先會進行褐色方框內的檢查,方框中工作主要是物理層進行的工作,檢查給數據包接收是否出現問題,以及這個數據包是否是一個被丟棄的數據包。這裡不做過多講述。重點介紹紅框裡面的內容。
在完成褐色框的檢查之後,數據包會首先進行CRC錯誤檢查,如果檢查不通過,說明接收的TLP數據包存在錯誤,那麼將會判斷NAK_SCHEDULED是否置位,如果未置位,那個將該位置1,同時立即返回一個NAK DLLP,然後丟棄這個TLP。(這裡NAK_SCHEDULED的作用體現出來了,你可以結合前面的內容想想它的作用)
如果檢查通過之後,將判斷Sequence Number是否等於NEXT_RCV_SEQ,如果兩者相等那就將接收到的TLP進行處理,傳遞給TL層,同時將NEXT_RCV_SEQ加一,清除NAK_SCHEDULED標誌位。如果不等,將判斷
(NEXT_RCV_SEQ - Sequence Number)mod 4096<2048(判斷兩者大小)是否成立。
如果成立,說明該TLP Device B之前已經接收過,是一個重複發送的TLP,那麼Device A計劃發送一個ACK DLLP。如果不成立,說明Sequence Number大於NEXT_RCV_SEQ ,則說明TLP有部分TLP數據包丟失,應該立即返回NAK DLLP,同時置位NAK_SCHEDULED。
如果本篇看完後還是一頭霧水,請看下篇。
Spring:PCIE開發筆記(八)DLL介紹(下)以上內容讀者如果覺得有錯誤之處,請您私信我,我將及時改正。
歡迎轉發,如果有疑惑之處,歡迎評論,我們一起探討。
請勿轉載
推薦閱讀:
TAG:PCIE | 現場可編輯邏輯門陣列FPGA | 數字電路 |