DDR的終極方案:AXI Datamover原理與實(shí)戰(zhàn))
判斷一個(gè)Zynq工程師是不是真把數(shù)據(jù)通路吃透了最簡(jiǎn)單的問(wèn)題就是你的PL邏輯打算怎么往DDR里寫(xiě)數(shù)據(jù)很多人習(xí)慣直接在AXI總線上掛一個(gè)寄存器堆或者BRAM轉(zhuǎn)FIFO讓PS用CPU去搬結(jié)果帶寬和實(shí)時(shí)性雙雙拉胯。真正干這活的其實(shí)是AXI Datamover這個(gè)IP。這篇就把PS、PL、DDR三者之間最核心的數(shù)據(jù)搬運(yùn)邏輯講透重點(diǎn)說(shuō)清楚Datamover到底能干什么、怎么干、以及它和AXI DMA那類(lèi)看起來(lái)差不多的IP到底差在哪。本文適合手里有Zynq板子、準(zhǔn)備讓PL側(cè)邏輯直接讀寫(xiě)DDR做數(shù)據(jù)采集或算法加速的人。你要是從來(lái)沒(méi)碰過(guò)AXI協(xié)議光聽(tīng)過(guò)valid/ready握手那也問(wèn)題不大我會(huì)把鏈接關(guān)系、命令字格式、狀態(tài)返回邏輯一條條拆開(kāi)講爭(zhēng)取讓剛?cè)腴T(mén)的同學(xué)能對(duì)著這篇把最小系統(tǒng)搭出來(lái)讓已經(jīng)被Datamover折磨過(guò)的人也能對(duì)號(hào)入座找到坑。1. 為什么PL要讀寫(xiě)DDR以及為什么得靠Datamover1.1 Zynq里PS、PL、DDR之間的真實(shí)拓?fù)湎壤砬逡粋€(gè)容易被初始概念帶偏的問(wèn)題Zynq里的DDR物理上是掛在PS側(cè)的。DDR控制器的硬核位于PS內(nèi)部DDR顆粒通過(guò)專(zhuān)用的物理接口連到PSPL這邊并沒(méi)有獨(dú)立的內(nèi)存控制器。也就是說(shuō)PL想要訪問(wèn)DDR唯一的路徑就是通過(guò)AXI互聯(lián)矩陣把請(qǐng)求送到PS側(cè)的高性能端口比如S_AXI_HP0/1/2/3再由HP端口接到DDR控制器后端。這個(gè)拓?fù)錄Q定了三件事第一PL訪問(wèn)DDR必須發(fā)起AXI內(nèi)存映射Memory Map類(lèi)型的事務(wù)第二訪問(wèn)路徑上有一個(gè)跨時(shí)鐘域的橋接過(guò)程吞吐量和延遲都會(huì)受AXI互聯(lián)配置影響第三PL側(cè)如果只是做數(shù)據(jù)采集那么數(shù)據(jù)最終必須落到DDR里否則后面的算法和軟件處理無(wú)從談起。這么說(shuō)吧整個(gè)Zynq的數(shù)據(jù)通路就像一個(gè)倉(cāng)庫(kù)DDR搬運(yùn)通道AXI互聯(lián)被安排在倉(cāng)庫(kù)旁邊而Datamover就是一個(gè)專(zhuān)業(yè)的叉車(chē)司機(jī)。你寫(xiě)RTL時(shí)如果想親自實(shí)現(xiàn)AXI讀突發(fā)、寫(xiě)突發(fā)、突發(fā)長(zhǎng)度控制、地址遞增那復(fù)雜度會(huì)迅速爆炸而且很容易在握手時(shí)序上出錯(cuò)。Datamover存在的意義就是把這些底層的AXI事務(wù)細(xì)節(jié)全部封裝掉對(duì)外只提供一套簡(jiǎn)潔的AXI4-Stream接口。1.2 用CPU搬運(yùn)數(shù)據(jù)的三個(gè)痛點(diǎn)也有人問(wèn)那我直接在PL里掛一個(gè)AXI接口讓PS通過(guò)CPU循環(huán)讀寫(xiě)不行嗎行但會(huì)有三個(gè)明顯的坑。第一個(gè)坑是帶寬。PS的CPU通過(guò)M_AXI_GP口訪問(wèn)PL時(shí)數(shù)據(jù)位寬一般只有32位而且每次訪問(wèn)都需要穿越整個(gè)AXI互聯(lián)CPU還要執(zhí)行l(wèi)oad/store指令。做大數(shù)據(jù)量搬運(yùn)時(shí)這種方式的帶寬根本跑不滿(mǎn)DDR控制器只能眼睜睜看著CPU占用率拉滿(mǎn)、DDR帶寬閑著。第二個(gè)坑是實(shí)時(shí)性。CPU搬運(yùn)本質(zhì)是請(qǐng)求-響應(yīng)模型兩次讀之間CPU還要做循環(huán)控制、地址更新、結(jié)束判斷這就引入了不可控延遲抖動(dòng)。在高速數(shù)據(jù)采集場(chǎng)景下ADC往FIFO里灌數(shù)據(jù)的速度可不管你的CPU忙不忙只要上游FIFO滿(mǎn)一次采樣就丟點(diǎn)。第三個(gè)坑是CPU被完全占死。如果搬數(shù)據(jù)的活全讓PS側(cè)核心干了那PS就無(wú)法同時(shí)跑Linux、做協(xié)議棧處理、響應(yīng)中斷。對(duì)很多應(yīng)用來(lái)說(shuō)CPU應(yīng)該去干更復(fù)雜的事情機(jī)械的搬數(shù)據(jù)工作應(yīng)該交給專(zhuān)用硬件。所以PL直接讀寫(xiě)DDR的標(biāo)準(zhǔn)方案就是在PL里例化一個(gè)Datamover讓這個(gè)IP替你去執(zhí)行AXI內(nèi)存映射端的讀寫(xiě)事務(wù)數(shù)據(jù)的生產(chǎn)和消費(fèi)則通過(guò)AXI4-Stream接口對(duì)接。這樣CPU只需要下發(fā)一條命令剩下的搬運(yùn)全部由硬件完成。2. 先弄清Datamover的三個(gè)通道和控制模型2.1 指令通道把傳輸需求寫(xiě)成一條命令字Datamover最核心的思想是它自己不產(chǎn)生數(shù)據(jù)只是一個(gè)執(zhí)行引擎。你要它干什么得通過(guò)一條命令告訴它。這條命令走的是S_AXIS_MM2S_CMD讀側(cè)或者S_AXIS_S2MM_CMD寫(xiě)側(cè)本質(zhì)上就是一個(gè)AXI4-Stream接口。命令字的位寬默認(rèn)是72位跟具體IP版本和配置有關(guān)核心字段就兩個(gè)一個(gè)是目標(biāo)地址就是你想從DDR哪里讀、或者往DDR哪里寫(xiě)另一個(gè)是傳輸字節(jié)數(shù)BTTBytes to Transfer告訴它要搬多少數(shù)據(jù)。有些配置里還有tag字段方便你在狀態(tài)返回時(shí)區(qū)分是哪一條命令完成了。正經(jīng)的命令流格式不同Vivado版本略有差異但大邏輯是一樣的。以讀傳輸為例你往S_AXIS_MM2S_CMD通道扔一條命令字其中包含起始地址、長(zhǎng)度Datamover就會(huì)自動(dòng)把這次傳輸拆成一筆或者多筆AXI讀突發(fā)從DDR把數(shù)據(jù)讀出來(lái)然后從M_AXIS_MM2S端口流式輸出。傳輸完成后它會(huì)從M_AXIS_MM2S_STS端口回一條狀態(tài)。這里有個(gè)細(xì)節(jié)特別重要命令通道的握手是標(biāo)準(zhǔn)的AXI4-Stream valid/ready機(jī)制??刂七壿嫴荒芟氘?dāng)然地認(rèn)為我發(fā)出命令就完成了必須等到valid和ready同時(shí)拉高的那個(gè)時(shí)鐘上升沿才算命令真正被接收。很多初學(xué)者卡死在這里后面專(zhuān)門(mén)講。2.2 狀態(tài)通道和數(shù)據(jù)通道怎么配合Datamover本質(zhì)上幫用戶(hù)省掉了三類(lèi)復(fù)雜工作AXI突發(fā)拆分、地址遞增、DDR控制器時(shí)序管理的適配。所有這些對(duì)用戶(hù)來(lái)說(shuō)都是透明的黑盒用戶(hù)只需要盯著一組Stream接口。整個(gè)系統(tǒng)有兩種工作方向?qū)?yīng)兩個(gè)完全對(duì)稱(chēng)的通道。MM2S方向是Memory Map to StreamS2MM方向是Stream to Memory Map。MM2S把DDR數(shù)據(jù)讀出來(lái)送到PL內(nèi)部S2MM把PL內(nèi)部的數(shù)據(jù)寫(xiě)入DDR。每個(gè)方向都有獨(dú)立的命令通道、數(shù)據(jù)通道和狀態(tài)通道。狀態(tài)通道返回的狀態(tài)字通常是8位或者32位。狀態(tài)字里除了OK標(biāo)志還有錯(cuò)誤標(biāo)志位。一旦你在狀態(tài)字里看到錯(cuò)誤位置1基本就是下面這幾種情況之一地址越界訪問(wèn)到了DDR地址空間之外、命令格式非法比如BTT配置超過(guò)支持范圍、S2MM通道數(shù)據(jù)沒(méi)跟上上游數(shù)據(jù)斷流導(dǎo)致內(nèi)部FIFO上溢/下溢。調(diào)試時(shí)把狀態(tài)通道拉進(jìn)ILA比瞎猜快得多。數(shù)據(jù)通道則是對(duì)外提供的數(shù)據(jù)接口。MM2S的數(shù)據(jù)通道是輸出方向S2MM的數(shù)據(jù)通道是輸入方向。這兩個(gè)通道都有valid/ready握手并且在數(shù)據(jù)流之前還有一個(gè)tlast信號(hào)表示一筆傳輸?shù)淖詈笠淮瓮话l(fā)結(jié)束。如果數(shù)據(jù)流里有tlast你要把它和有效的最后一個(gè)數(shù)據(jù)對(duì)齊好不然接收端可能在Done判斷上出錯(cuò)。2.3 它和AXI DMA、AXI CDMA差在哪這三個(gè)IP經(jīng)常被放在一起比較功能也確實(shí)高度重疊但定位有明顯不同。AXI DMA是一個(gè)自帶寄存器管理功能的IP。它內(nèi)部有一組用戶(hù)可配置的寄存器PS側(cè)的軟件可以通過(guò)AXI4-Lite接口讀寫(xiě)這些寄存器從而發(fā)起DMA傳輸。這種方式對(duì)軟件工程師非常友好因?yàn)樵贚inux里直接操作寄存器就能做DMA搬運(yùn)。但代價(jià)是它的控制模型相對(duì)固定你很難把它嵌入到很復(fù)雜的PL自定義狀態(tài)機(jī)里。AXI CDMA是Central DMA它最大的特點(diǎn)是同一個(gè)引擎既能讀也能寫(xiě)也就是先從一個(gè)地址讀、再直接寫(xiě)到另一個(gè)地址不需要數(shù)據(jù)通過(guò)外部邏輯過(guò)一次手。這在做DDR內(nèi)部數(shù)據(jù)拷貝時(shí)特別高效。Datamover跟它倆不一樣。Datamover沒(méi)有用戶(hù)寄存器你全靠往命令通道塞命令字來(lái)驅(qū)動(dòng)它。這種設(shè)計(jì)在一開(kāi)始確實(shí)讓人不適應(yīng)但它的優(yōu)勢(shì)在于兩點(diǎn)第一它是純Stream接口的引擎很容易被PL里的自定義邏輯直接控制根本不需要PS參與第二它能同時(shí)支持MM2S和S2MM兩個(gè)方向并發(fā)工作流式吞吐能力很強(qiáng)。所以當(dāng)你需要在PL側(cè)實(shí)現(xiàn)一個(gè)完全由硬件自主控制的搬運(yùn)鏈時(shí)Datamover往往比AXI DMA順手。3. IP配置里真正值得摳的選項(xiàng)3.1 模式選擇與數(shù)據(jù)寬度、突發(fā)長(zhǎng)度Vivado里例化AXI Datamover時(shí)首先會(huì)問(wèn)你Enable MM2S還是Enable S2MM或者兩個(gè)都要。這個(gè)選擇不是越多越好。如果只需要讀數(shù)就只開(kāi)MM2S可以節(jié)省不少BRAM和LUT資源。如果要做環(huán)回測(cè)試或轉(zhuǎn)發(fā)系統(tǒng)那當(dāng)然兩個(gè)都開(kāi)。接下來(lái)是Stream Data Width和Memory Map Data Width。這兩個(gè)值得單獨(dú)說(shuō)。Memory Map側(cè)的位寬應(yīng)該跟實(shí)際DDR控制器端口位寬匹配常見(jiàn)的有64位和128位。Stream側(cè)的位寬則可以相對(duì)獨(dú)立Datamover內(nèi)部會(huì)做位寬轉(zhuǎn)換。但位寬轉(zhuǎn)換是有代價(jià)的每跨一次位寬就需要額外的FIFO做緩沖資源占用和延遲都會(huì)上去。如果你的上游邏輯本身就是64位建議兩邊都設(shè)成64位減少無(wú)謂轉(zhuǎn)換。Max Burst Size決定了Datamover單次AXI突發(fā)能跨多少拍。AXI4協(xié)議規(guī)定INCR突發(fā)最大不能超過(guò)256筆。這個(gè)值設(shè)大了對(duì)DDR帶寬利用有好處因?yàn)橥话l(fā)越長(zhǎng)AXI總線上地址階段占用的開(kāi)銷(xiāo)比例越小。但如果你的下游邏輯比如接一個(gè)窄FIFO跟不上數(shù)據(jù)速率突發(fā)太大反而容易導(dǎo)致Stall。我實(shí)測(cè)下來(lái)64位總線上設(shè)16或者32是比較保守穩(wěn)定的選擇128位總線可以試試64或者128。另外有一個(gè)Address Width設(shè)置。這個(gè)值決定了命令字里地址字段占據(jù)多少位一般跟處理器的地址空間匹配32位或40位。千萬(wàn)不要想當(dāng)然地認(rèn)為地址位寬越大越好因?yàn)槊钭值奈粚捠枪潭ǖ牡刂纷侄握级嗔肆艚o其他字段的位置就少了。3.2 這些配置對(duì)應(yīng)到哪些端口配置完之后你會(huì)在IP例化界面看到一堆端口。很多人一看到S_AXIS_MM2S_CMD、M_AXIS_MM2S_STS、M_AXIS_MM2S、S_AXIS_S2MM_CMD、M_AXIS_S2MM_STS、S_AXIS_S2MM這些名字就開(kāi)始頭大其實(shí)它們一一對(duì)應(yīng)著指定的功能。MM2S側(cè)的三個(gè)口分別是命令入口、數(shù)據(jù)出口或者叫狀態(tài)出口、以及數(shù)據(jù)出口。如果方向記混了后面寫(xiě)RTL時(shí)大概率會(huì)接錯(cuò)線。有一個(gè)端口需要特別注意主接口側(cè)的M_AXI_MM2S和M_AXI_S2MM。這兩個(gè)才是真正連接內(nèi)存映射世界的通道必須接到AXI互聯(lián)上最終指向HP端口或者ACP端口。如果這倆沒(méi)接對(duì)Datamover再?gòu)?qiáng)大也使不上勁。同時(shí)建議把復(fù)位和時(shí)鐘端口都檢查一遍。Datamover有獨(dú)立的aresetn它要求復(fù)位信號(hào)至少保持幾個(gè)時(shí)鐘周期的低電平才能安全重新開(kāi)始。別把復(fù)位和系統(tǒng)復(fù)位直接群聊式地接在一起最好是復(fù)位釋放后至少等100個(gè)時(shí)鐘周期再下發(fā)命令讓IP內(nèi)部邏輯充分初始化。4. 搭一個(gè)最小讀寫(xiě)鏈路從地址映射到控制狀態(tài)機(jī)4.1 地址映射和HP端口選型假設(shè)我們要在PL里例化一個(gè)Datamover把DDR里地址0x10000000開(kāi)始的4KB數(shù)據(jù)讀出來(lái)經(jīng)過(guò)MM2S口發(fā)到一個(gè)FIFO中同時(shí)用S2MM口把另一個(gè)數(shù)據(jù)源寫(xiě)入DDR另一段地址。第一步是搞清楚DDR的基地址。Zynq-7000系列DDR默認(rèn)被映射在0x00100000起始的地址空間具體看板卡的memory map一般在地址0x0010_0000到0x3FFFFFFF范圍內(nèi)MPSOC平臺(tái)又有不同映射。一定去查硬件手冊(cè)別拿網(wǎng)上的默認(rèn)值直接套。了解基地址后把DDR的起始位置1GB或2GB空間分配好PL側(cè)的M_AXI_MM2S/S2MM接到S_AXI_HP口然后做地址翻譯。地址翻譯這一步也很關(guān)鍵。Zynq里HP端口的地址空間和物理DDR地址是有映射關(guān)系的有時(shí)你從PL側(cè)發(fā)地址0x10000000對(duì)應(yīng)的DDR物理地址實(shí)際上是0x00100000 0x10000000。具體換算要看Vivado里的地址編輯器別在地址上栽跟頭。我的習(xí)慣做法是在Vivado Block Design里把Datamover的M_AXI接到一個(gè)AXI SmartConnect或者AXI Interconnect然后再接到S_AXI_HP0同時(shí)在Address Editor里給這條路徑分配地址范圍保證PS側(cè)和PL側(cè)看到的是同一個(gè)DDR物理區(qū)間。4.2 一次MM2S讀傳輸?shù)耐暾麜r(shí)序我把讀傳輸?shù)牧鞒滩鸪晌鍌€(gè)狀態(tài)做成一個(gè)極簡(jiǎn)狀態(tài)機(jī)各位可以直接參考。第一步IDLE狀態(tài)下等待外部觸發(fā)。觸發(fā)信號(hào)可以是PS通過(guò)AXI GPIO拉高的也可以是PL內(nèi)部的事件計(jì)數(shù)器產(chǎn)生的。第二步SEND_CMD。在這個(gè)狀態(tài)下把要讀的起始地址、傳輸字節(jié)數(shù)拼成一個(gè)命令字放到S_AXIS_MM2S_CMD的TDATA上然后拉高TVALID。這時(shí)要等TREADY信號(hào)為高并且TVALID為高兩者同時(shí)滿(mǎn)足的那個(gè)時(shí)鐘上升沿命令才算發(fā)出。命令發(fā)完后TVALID拉低狀態(tài)機(jī)跳到WAIT_DATA。第三步WAIT_DATA。在這一步DataMover內(nèi)部會(huì)發(fā)起一系列AXI讀事務(wù)然后數(shù)據(jù)流從M_AXIS_MM2S送出來(lái)。你的接收邏輯只需要用valid/ready握手接收數(shù)據(jù)每收一個(gè)數(shù)據(jù)計(jì)數(shù)器加一直到收到TLAST拉高的那一拍說(shuō)明這一次命令對(duì)應(yīng)的數(shù)據(jù)全部到期。第四步WAIT_STS。數(shù)據(jù)傳輸完后Datamover會(huì)從M_AXIS_MM2S_STS回狀態(tài)。要繼續(xù)讀取狀態(tài)通道握手方式跟命令通道一致直到狀態(tài)通道上出現(xiàn)valid??梢詸z查狀態(tài)字里的錯(cuò)誤位。第五步回到IDLE等待觸發(fā)下一次搬運(yùn)。整個(gè)過(guò)程中CPU完全不需要介入??雌饋?lái)很簡(jiǎn)單對(duì)吧但實(shí)際操作里很多人會(huì)在WAIT_DATA和WAIT_STS之間漏掉一個(gè)關(guān)鍵點(diǎn)Datamover內(nèi)部是有命令FIFO的如果你連續(xù)下發(fā)多條命令不用等上一條完全結(jié)束就可以在數(shù)據(jù)通道還忙著的時(shí)候發(fā)下一條。這對(duì)提高吞吐量很關(guān)鍵但也要求你的狀態(tài)機(jī)不能死板地一讀一停否則吞吐量會(huì)被狀態(tài)機(jī)拖垮。4.3 AXI4-Stream握手與背壓怎么處理AXI4-Stream的握手是所有Stream邏輯的基礎(chǔ)規(guī)則其實(shí)就三條第一源端在數(shù)據(jù)穩(wěn)定后拉高TVALID第二目的端在可以接收時(shí)拉高TREADY第三只有在同一個(gè)時(shí)鐘上升沿TVALID和TREADY同時(shí)為高這一筆數(shù)據(jù)傳輸才完成。這里有個(gè)比較容易寫(xiě)反的依賴(lài)關(guān)系TVALID不能等TREADY拉高后才拉高否則就形成了循環(huán)等待死鎖。正確寫(xiě)法是只要源端有數(shù)據(jù)TVALID就可以拉高目的端如果沒(méi)有準(zhǔn)備好接收通過(guò)TREADY拉低來(lái)背壓也就是告訴源端先別發(fā)至少這一拍不發(fā)。源端收到TREADY為低時(shí)必須保持當(dāng)前數(shù)據(jù)和TVALID不變直到握手成功。當(dāng)我第一次把Datamover接進(jìn)一個(gè)實(shí)時(shí)數(shù)據(jù)采集系統(tǒng)時(shí)上游FIFO偶爾讀空下游FIFO偶爾寫(xiě)滿(mǎn)如果背壓邏輯設(shè)計(jì)不當(dāng)整個(gè)數(shù)據(jù)鏈路就會(huì)卡死。所以我建議所有接收來(lái)自Datamover數(shù)據(jù)的FIFO都用First-Word-Fall-ThroughFWFT模式這樣tvalid可以提前有效更容易滿(mǎn)足Datamover數(shù)據(jù)通道的時(shí)序要求也不容易在握手初始化階段出現(xiàn)莫名的氣泡。S2MM方向同理當(dāng)你的數(shù)據(jù)源頻繁刷新時(shí)在Datamover接受數(shù)據(jù)之前數(shù)據(jù)源要一直保持?jǐn)?shù)據(jù)和TVALID不變。這時(shí)候如果上游是連續(xù)產(chǎn)生的有效信號(hào)你就要為它配一個(gè)足夠深的FIFO讓背壓能被緩沖掉不至于把前一級(jí)數(shù)據(jù)源直接逼停。5. 調(diào)試實(shí)錄最容易卡住的三類(lèi)問(wèn)題5.1 死鎖valid/ready的依賴(lài)關(guān)系寫(xiě)反了我調(diào)試時(shí)踩過(guò)最經(jīng)典的坑就是命令通道的握手死鎖。最初寫(xiě)控制狀態(tài)機(jī)時(shí)我很自然地寫(xiě)成先判斷TREADY是否拉高如果拉高我再把TVALID拉高。表面上看邏輯沒(méi)有錯(cuò)但實(shí)際上在仿真里這兩個(gè)信號(hào)互相等永遠(yuǎn)等不到握手完成那一刻。原因很簡(jiǎn)單目的端的TREADY可能也在等你的TVALID。這個(gè)問(wèn)題的根源是違反了AXI協(xié)議的基本法則valid不能依賴(lài)readyready可以等待valid。正確寫(xiě)法是TVALID必須由數(shù)據(jù)是否準(zhǔn)備好決定而TREADY才是由我能否接收決定。你只需要保證自信地拉高TVALID然后等待兩者同時(shí)為高即可。如果你用的是Vivado的仿真環(huán)境建議直接在波形窗口里看s_axis_mm2s_cmd_tvalid和s_axis_mm2s_cmd_tready。如果看到tvalid和tready有一個(gè)一直為低另一個(gè)一直為高互相僵持那八九不離十就是依賴(lài)關(guān)系寫(xiě)反了。再檢查一下?tīng)顟B(tài)機(jī)的默認(rèn)分支確認(rèn)每個(gè)狀態(tài)在無(wú)效條件下都有明確的跳轉(zhuǎn)別因?yàn)槟硞€(gè)信號(hào)缺省賦值為0導(dǎo)致?tīng)顟B(tài)機(jī)徹底歇菜。5.2 未對(duì)齊地址導(dǎo)致的數(shù)據(jù)錯(cuò)位另一個(gè)讓我折騰了整整一個(gè)晚上的問(wèn)題是數(shù)據(jù)錯(cuò)位。當(dāng)時(shí)系統(tǒng)是從DDR讀取數(shù)據(jù)結(jié)果收到的數(shù)據(jù)整體平移了幾個(gè)字節(jié)數(shù)據(jù)本身的數(shù)值都對(duì)但位置全亂了。排查到最后才發(fā)現(xiàn)數(shù)據(jù)位寬是64位時(shí)DDR地址必須是8字節(jié)對(duì)齊的。我的命令里給了一個(gè)末位不是0的地址比如0x10000004。Datamover實(shí)際AXI訪問(wèn)時(shí)會(huì)把這個(gè)地址處理成8字節(jié)對(duì)齊的邊界然后把多讀出來(lái)的數(shù)據(jù)調(diào)整到正確的Stream輸出位置上。本身這個(gè)調(diào)整機(jī)制是IP內(nèi)部完成的理應(yīng)正確但問(wèn)題出在我后續(xù)處理邏輯沒(méi)有把首地址未對(duì)齊導(dǎo)致的數(shù)據(jù)偏移考慮進(jìn)去。對(duì)策其實(shí)有兩種一種是在應(yīng)用層強(qiáng)制所有地址對(duì)齊到數(shù)據(jù)寬度對(duì)應(yīng)的字節(jié)數(shù)另一種是在配置Datamover時(shí)開(kāi)啟未對(duì)齊支持并仔細(xì)閱讀它的輸出在未對(duì)齊情況下的行為說(shuō)明。我個(gè)人建議除非你的數(shù)據(jù)流協(xié)議本質(zhì)上就必須支持任意偏移否則一律在軟件和邏輯層約定對(duì)齊訪問(wèn)。不光是地址要對(duì)齊傳輸長(zhǎng)度也盡量湊成突發(fā)長(zhǎng)度的整數(shù)倍這樣能在調(diào)試階段省下大量時(shí)間。5.3 突發(fā)長(zhǎng)度與BTT不一致還有一個(gè)很容易被忽略的問(wèn)題命令字里的BTT值和實(shí)際配置的Max Burst Size之間如果不匹配會(huì)讓Datamover拆成多個(gè)突發(fā)去傳輸。本身這不會(huì)出錯(cuò)但如果你的接收端邏輯里用了一個(gè)固定的突發(fā)計(jì)數(shù)認(rèn)為一條命令就是一次突發(fā)、一次突發(fā)就是固定的若干拍那麻煩就來(lái)了。比如你發(fā)了一條BTT1024字節(jié)的命令數(shù)據(jù)寬度64位總共需要128拍。如果Max Burst Size配置為16那么Datamover會(huì)拆成8次AXI突發(fā)每次突發(fā)16拍。在M_AXIS_MM2S側(cè)這些突發(fā)會(huì)以TLAST作為每次突發(fā)結(jié)束的標(biāo)記。那么你接收數(shù)據(jù)時(shí)就必須以TLAST為邊界而不是以固定的拍數(shù)來(lái)計(jì)數(shù)。更隱蔽的是如果你的上游數(shù)據(jù)源和Datamover的S2MM數(shù)據(jù)處理不好TLAST那么在最后一個(gè)突發(fā)結(jié)束后可能會(huì)少一個(gè)TLAST最終導(dǎo)致寫(xiě)入DDR的長(zhǎng)度比預(yù)期少一筆數(shù)據(jù)。S2MM方向有一個(gè)內(nèi)部字節(jié)計(jì)數(shù)邏輯它要求收到的數(shù)據(jù)字節(jié)數(shù)必須和BTT完全一致。數(shù)據(jù)多了、少了或者TLAST時(shí)機(jī)不對(duì)都會(huì)在狀態(tài)通道里上報(bào)錯(cuò)誤。調(diào)試這類(lèi)問(wèn)題時(shí)我強(qiáng)烈建議在ILA里同時(shí)抓三個(gè)信號(hào)命令通道、數(shù)據(jù)通道、以及狀態(tài)通道。這樣你才能把我發(fā)了多少命令、我給了多少數(shù)據(jù)、Datamover最終認(rèn)為完成了多少三者對(duì)上。凡是看到數(shù)據(jù)計(jì)數(shù)和狀態(tài)字完成的字節(jié)計(jì)數(shù)對(duì)不上優(yōu)先懷疑TLAST的生成邏輯。Datamover這個(gè)IP說(shuō)到底就是一個(gè)把AXI內(nèi)存映射事務(wù)翻譯成AXI4-Stream數(shù)據(jù)流的專(zhuān)職搬運(yùn)工。它不負(fù)責(zé)產(chǎn)生數(shù)據(jù)也不負(fù)責(zé)理解數(shù)據(jù)只負(fù)責(zé)準(zhǔn)確無(wú)誤地把數(shù)據(jù)從一個(gè)世界搬到另一個(gè)世界。你要做的就是學(xué)會(huì)用命令字驅(qū)動(dòng)它、用握手協(xié)議伺候它、用狀態(tài)通道監(jiān)控它。把這幾個(gè)點(diǎn)想透了PL讀寫(xiě)DDR這件事基本就打開(kāi)了思路剩下的就是按照你的實(shí)際數(shù)據(jù)流需求把它接到對(duì)應(yīng)的邏輯上去。