Zynq SDK 驅(qū)動(dòng)探求——數(shù)據(jù)從軟件來(lái)到邏輯
掃描二維碼
隨時(shí)隨地手機(jī)看文章
對(duì)于一個(gè)希望能夠達(dá)到軟件定義,硬件加速的協(xié)議棧來(lái)說(shuō),打通軟硬之間的任督二脈是最為重要的一環(huán)。本文通過(guò)搭建一個(gè) AXI DMA 子系統(tǒng),將 PS 中網(wǎng)卡驅(qū)動(dòng)軟件接收到的數(shù)據(jù)包發(fā)送給邏輯部分,建立起 PS 到 PL 的數(shù)據(jù)流。
搭建邏輯 DMA 子系統(tǒng)
在 Vivado 的 Block Design 功能中通過(guò)拖動(dòng) IP 的方式建立一個(gè) DMA傳輸子系統(tǒng)。DMA 子系統(tǒng)包括一個(gè) AXI-DMA 和一個(gè) AXI-FIFO。本文中的實(shí)驗(yàn)因?yàn)槭前褦?shù)據(jù)從 PS 端傳輸?shù)?PL 端,所以只會(huì)用到接收 FIFO(但為了回環(huán)測(cè)試,所以又將接收 FIFO 的輸出端連接到了 DMA 發(fā)送端(S2MM 對(duì)應(yīng)于邏輯部分的發(fā)送端))。DMA IP 有多條總線通道,分別用于和 FIFO 以及 PS 連接,下圖表示了 DMA 系統(tǒng)各部分的連接情況。
在 IP 連接圖上會(huì)看到很多的總線矩陣( interconnect ),DMA 和 PS 之間通過(guò)兩個(gè)總線矩陣連接,不考慮這些總線矩陣,本質(zhì)上的總線連接就如上圖,藍(lán)色的是邏輯部分,紅色的是 PS 部分。紅藍(lán)之間共有 3 條總線與一條信號(hào)線連接。系統(tǒng)中真正使用的為接收 FIFO,發(fā)送 FIFO 暫未使用,用虛線表示。
信號(hào)線是中斷控制線,DMA 產(chǎn)生的中斷會(huì)由 PS 進(jìn)行中斷響應(yīng)。AXI-Lite 是一條配置總線,PS 端對(duì) DMA 的配置控制都會(huì)通過(guò)這條總線進(jìn)行。剩下的是一對(duì) AXI 總線,這對(duì)總線分別對(duì)應(yīng)著發(fā)送接收的數(shù)據(jù)流通道。圖中的發(fā)送是指 DMA 到 PS 的數(shù)據(jù)流向,在本實(shí)驗(yàn)中沒(méi)有用到,使用的是接收是 PS 到 DMA 的數(shù)據(jù)流向。
DMA 的工作可以理解為將數(shù)據(jù)從 AXI 總線的形式轉(zhuǎn)為 AXIS 的形式。AXI 是指 Memory Mapped 的數(shù)據(jù),即數(shù)據(jù)有對(duì)應(yīng)的內(nèi)存地址,數(shù)據(jù)存儲(chǔ)于存儲(chǔ)介質(zhì)(如 DDR,BRAM)或者寄存器(比如網(wǎng)絡(luò) MAC 的數(shù)據(jù)緩沖寄存器)中。在本實(shí)驗(yàn)中 MM 指的是從 PS 的 MAC 數(shù)據(jù)緩沖寄存器中搬用的網(wǎng)絡(luò)數(shù)據(jù)包。
AXIS 總線對(duì)應(yīng)的一般是對(duì)數(shù)據(jù)進(jìn)行處理的邏輯部分,AXIS 總線在本實(shí)驗(yàn)中指的是接收 FIFO,此時(shí)的數(shù)據(jù)沒(méi)有地址的概念,以數(shù)據(jù)流的形式出現(xiàn),簡(jiǎn)單來(lái)說(shuō)當(dāng) valid 信號(hào)線為 1 時(shí), data 上的數(shù)據(jù)有效。FIFO 作為從機(jī) (slave)接收數(shù)據(jù),所以整個(gè)從 PS 到 PL 的傳輸過(guò)程稱為 MM2S (Memory Mapped to Slave),PL 到 PS 的數(shù)據(jù)流則稱為 S2MM 。
本實(shí)驗(yàn)中的 DMA 配置為簡(jiǎn)單模式,數(shù)據(jù)傳輸?shù)?AXI 總線連接到 PS 的 HP_AXI 接口,控制用 AXI-Lite 連接到 PS 的 GP_AXI 接口。我們將在后續(xù)的文章中討論 DMA 以及 PS 上的多種 AXI 接口。
AXI-DMA 外設(shè)驅(qū)動(dòng)
AXI-DMA 屬于我們?cè)谇拔闹姓f(shuō)的,由 Xilinx 官方提供的邏輯 IP 外設(shè)。區(qū)別于 PS 系統(tǒng)中包括的 ps7_dma,DMA 在 Vivado 中是一個(gè)標(biāo)準(zhǔn)的 IP 核,所以官方有提供標(biāo)準(zhǔn)的外設(shè)驅(qū)動(dòng)以及使用示例。
這里我們同樣化用 example 中的代碼,封裝成我們自己的功能函數(shù)。
我們使用的是簡(jiǎn)單模式,使用中斷的例程 axi_dma_loop_test_bsp_xaxidma_example_simple_intr,首先看下例程的結(jié)構(gòu)。例程中主要的函數(shù)分別是
main(void)
主函數(shù)中完成了對(duì) DMA 的初始化,準(zhǔn)備一份發(fā)送數(shù)據(jù),放在數(shù)組緩沖區(qū)中,調(diào)用 DMA 的 XAxiDma_SimpleTransfer 函數(shù)發(fā)送,同時(shí)接收一份數(shù)據(jù)放到接收緩沖區(qū)中。因?yàn)橛布Y(jié)構(gòu)上為回環(huán)連接,發(fā)送的數(shù)據(jù)會(huì)被 DMA 接收,主函數(shù)會(huì)對(duì)發(fā)送接收的數(shù)據(jù)進(jìn)行比較。
void TxIntrHandler(void *Callback)/void RxIntrHandler(void *Callback)
發(fā)送接收中斷函數(shù)在 DMA 發(fā)送/接收后被調(diào)用,對(duì)當(dāng)前置起的中斷進(jìn)行處理,如果沒(méi)有錯(cuò)誤發(fā)生,那么置起發(fā)送完成標(biāo)志。這是一個(gè)全局變量,主程序在發(fā)送/接收中斷完成后,對(duì)發(fā)送接收的數(shù)據(jù)進(jìn)行比較。在正常的使用中,發(fā)送接收中斷完成標(biāo)志表示程序可以進(jìn)行下一次發(fā)送/接收操作。
封裝應(yīng)用驅(qū)動(dòng)函數(shù)
為了方便使用,我們以 example 的代碼為基礎(chǔ),封裝成自己的應(yīng)用驅(qū)動(dòng)
初始化 DMA 函數(shù),在主函數(shù)初始化外設(shè)時(shí)調(diào)用。
int axidma_init(XAxiDma*dma_inst,XScuGic*intc_inst) { intStatus; XAxiDma_Config*Config; xil_printf("\r\n--- Init AXI_DMA Begin--- \r\n"); Config=XAxiDma_LookupConfig(DMA_DEV_ID); if(!Config){ xil_printf("No config found for %d\r\n",DMA_DEV_ID); returnXST_FAILURE; } Status=XAxiDma_CfgInitialize(dma_inst,Config); if(Status!=XST_SUCCESS){ xil_printf("Initialization failed %d\r\n",Status); returnXST_FAILURE; } if(XAxiDma_HasSg(dma_inst)){ xil_printf("Device configured as SG mode \r\n"); returnXST_FAILURE; } /* Set up Interrupt system */ Status=SetupIntrSystem(intc_inst,dma_inst,TX_INTR_ID,RX_INTR_ID); if(Status!=XST_SUCCESS){ xil_printf("Failed intr setup\r\n"); returnXST_FAILURE; } /* Disable all interrupts before setup */ XAxiDma_IntrDisable(dma_inst,XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrDisable(dma_inst,XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA); /* Enable all interrupts */ XAxiDma_IntrEnable(dma_inst,XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE); XAxiDma_IntrEnable(dma_inst,XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA); xil_printf("\r\n--- Init AXI_DMA Scuess--- \r\n"); returnXST_SUCCESS; }
DMA 發(fā)送函數(shù),封裝了 XAxiDma_SimpleTransfer 函數(shù)。
int axidma_send_data(XAxiDma*dma_inst,u32*send_buff,u32data_len) { intStatus; Xil_DCacheFlushRange((UINTPTR)send_buff,data_len); Status=XAxiDma_SimpleTransfer(dma_inst,(UINTPTR)send_buff, data_len,XAXIDMA_DMA_TO_DEVICE); if(Status!=XST_SUCCESS){ returnXST_FAILURE; } returnXST_SUCCESS; }
在 LwIP 中使用 DMA 驅(qū)動(dòng)
在本文實(shí)驗(yàn)中,將 LwIP 和 DMA 子系統(tǒng)結(jié)合起來(lái),將網(wǎng)卡驅(qū)動(dòng)接收到的數(shù)據(jù)傳輸給邏輯部分。在 xilinx sdk 的 LwIP 例程的基礎(chǔ)上,添加對(duì)于 AXI DMA 驅(qū)動(dòng)的支持。
首先在主函數(shù) main 中初始化平臺(tái)時(shí),初始化 DMA 。
接下來(lái)修改網(wǎng)卡接收中斷函數(shù) emacps_recv_handler ,加入 DMA 發(fā)送函數(shù) axidma_send_data 。在將數(shù)據(jù)包 pbuf 交付上層軟件之前,先啟動(dòng) DMA 傳輸,傳輸長(zhǎng)度為數(shù)據(jù)包的長(zhǎng)度。我們這里只傳輸數(shù)據(jù)包的 payload 部分,所以傳入指向 payload 的指針。
數(shù)據(jù)來(lái)了
DMA 開(kāi)始傳輸后,數(shù)據(jù)就開(kāi)始從 axis 接口進(jìn)入接收 FIFO??梢允褂?vivado 的 debug 功能設(shè)置觸發(fā)電,觀察到數(shù)據(jù)開(kāi)始到來(lái)。我們可以把邏輯收到的數(shù)據(jù)和軟件中 payload 指針指向的地址上的數(shù)據(jù)相比較,可以發(fā)現(xiàn)數(shù)據(jù)顯然是一致的。但注意到此時(shí)傳輸給邏輯部分的數(shù)據(jù)也是按照主機(jī)字節(jié)序組織的。

對(duì)數(shù)據(jù)做些什么
目前初步的計(jì)劃是在邏輯中進(jìn)行計(jì)算數(shù)據(jù)的校驗(yàn)和,解析協(xié)議等操作,在 PS 需要這些操作的結(jié)果時(shí),不需要在 PS 端再進(jìn)行計(jì)算,只需要從寄存器中讀取由邏輯部分計(jì)算完成的信息即可,以此實(shí)現(xiàn)減少 PS 端軟件運(yùn)算需求的目的。
結(jié)語(yǔ)
本文中簡(jiǎn)單介紹了 DMA 子系統(tǒng)的硬件搭建,DMA example 的介紹以及在 LwIP 網(wǎng)卡接收中斷函數(shù)中添加 DMA 傳輸函數(shù)。介紹地比較簡(jiǎn)單,有興趣的讀者可以來(lái)進(jìn)一步討論。





