在之前章節中,筆者就IP層之分片包的整合處理進行了概念介紹,以及代碼編寫和仿真,在整體代碼調試環節,筆者發現了一個問題,在本文中,筆者將就這個BUG進行說明,以及進行修復,講解代碼實現思路,驗證代碼邏輯正確性
注意看圖片中的框選文字,這里說明,如何確定這是一個單幀包,這里的確定單幀包邏輯是不存在問題,但是直接將UDP報文輸出是存在問題的,因為即使確定了當前輸入進來的數據報文不是分片包,但該報文的的上一幀報文,可能是分片包,而由于分片包要進行巨型幀組合后輸出,其報文長度可能為9000字節以上(假設),那么當前幀報文傳輸來時,巨型幀報文可能尚未傳輸完成,此時的輸出數據總線是被巨型幀報文占用的。而根據輸出賦值的優先級,會出現,巨型幀輸出不完全,被新一幀數據報文頂替輸出,以及當前報文優先級低,無法獲得總線使用權,報文丟失,所謂的優先級,即是在一個時序控制邏輯內,else if的上下級,當兩個else if語句滿足時,總是先執行最上面的else if語句,單純的文字解釋這個問題,可能會給讀者的理解造成混亂,筆者接下來就流程示意框圖、代碼仿真波形兩方面展現這類情況,使得讀者更加清晰的理解這些邏輯
上圖所示,便是一種情況,在RAM中整合了三包分片數據包之后,進行數據幀輸出,此時接連來了兩包單包
此時,這兩個單包按照之前的邏輯是會頂替當前包輸出的,造成數據混亂,代碼仿真如下圖所示
圖片一模擬了流程圖中的情況,而圖片二中則顯示了這樣的數據流動出現的問題,接下來將就這種現象進行解決方案的思考。
顯然,為了避免這種情況,需要判斷此時的數據輸出總線是否被占用,如果數據輸出總線被占用,則應該緩存本幀數據,待到數據輸出總線空閑,再進行數據的讀取,同時,若是數據到來時,數據輸出總線是空閑的,則不需要緩存數據,直接進行輸出,減少不必要的時間浪費。
那么還有一個問題,數據輸出總線被占用期間,可能有多幀數據到來,都需要被緩存,那么數據長度同樣應該被緩存,在本處理模塊中不涉及數據類型,因為其下一級在本設計中一定屬于UDP報文,所以不考慮數據類型的緩存,當涉及TCP/UDP兩種報文時,會就IP_RX模塊中的輸出信號進行添加,這在之后講解的TCP協議棧實現中,會進行具體介紹,大家可以點個關注,后續會有更多文章分享。
對于這些單幀數據包,使用FIFO進行數據緩存,使得邏輯處理簡單,同時還需要注意一個問題,每次讀取指定長度后,還需要暫緩下一幀數據的輸出,避免背靠背傳輸,對數據總線處理的壓力。
代碼的主要難點是以下幾方面
- 緩存分片數據包,并在緩存完成后進行輸出
- 在分片數據包輸出過程中,多個單包數據包到來,緩存至FIFO
- 在單包數據包輸出過程中,再次到來單包數據包(因為分片包導致的數據堆積)緩存至FIFO
- 兩個單包從FIFO中讀出時,輸出間隔問題,保證輸出間隔大于10個周期,減輕后續處理壓力,以及背靠背傳輸導致數據包輸出長度計算錯誤問題
- 評定各種情況優先級,合理規劃緩存以及讀取
- 針對以上難點,應該多進行代碼仿真,找出時序存在問題中,進行對應修改,多仿真是寫出好代碼的關鍵
整體仿真測試時序:
- 首先發送3472字節的巨型幀,分三次發送1480–1480–512
- 之后發送512字節單包,多次128字節單包–在巨型幀尚未緩存完畢以及巨型幀尚未輸出完成
- 之后發送128字節單包–在輸出總線輸出單包數據時
- 最后發送128字節單包-在輸出總線空閑時候
initial begin#200/*ip frag*/@(posedge i_udp_clk)ip_frag_send(0,'d1480,1,0);#400@(posedge i_udp_clk)ip_frag_send('d0,'d1480,1,185);#400@(posedge i_udp_clk)ip_frag_send('d0,'d512,0,370);#400@(posedge i_udp_clk)ip_frag_send('d0,'d512,0,0);#400@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);#400@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);#400@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);#400@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);#20000@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);#20000@(posedge i_udp_clk)ip_frag_send('d0,'d128,0,0);
end
情況一:對應仿真情況如下,可以看出分片包成功組合,數據長度3472字節,輸出時序正確。
情況二:對應仿真情況如下,可以看出在巨型幀緩存輸出完成后,被緩存的單幀數據包成功進行輸出。
情況三:對應仿真情況如下,可以看出在輸出單包數據完成后,在單包數據輸出期間被緩存的單幀數據包成功進行輸出。
情況四:對應仿真情況如下,可以看出在總線空閑期間到的數據包,被直接輸出,不需經過緩存。
經過上述仿真模擬各種可能出現的情況,經驗證后,輸出時序正確,不會出現數據錯誤、覆蓋、丟失等現象。
關于本次代碼的實現、調試思路。大家有什么問題歡迎在評論區中進行討論,有哪些考慮不到的地方,也請大家批評指正,可以關注下作者,后續會進行更多技術文章分享。
在下一章節中,筆者會將之前介紹的模塊進行整合,組合成最終的UDP協議棧,當然,還不包括PHY層的處理,關于PHY層的處理,筆者也會在之后的章節進行介紹,以及UDP的上層及應用層協議,如IEEE1588,筆者也會進行介紹。