基于AXI VDMA的圖像采集系統(tǒng)及VDMA使用總結(jié)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
AXI Stream協(xié)議及視頻流格式
AXI Stream關(guān)鍵的只有兩根信號(hào)線,及tvalid核tready。tvalid是主設(shè)備驅(qū)動(dòng)的信號(hào),表示Stream上的數(shù)據(jù)是有效的,tready由從設(shè)備驅(qū)動(dòng),表示從設(shè)備下一個(gè)時(shí)鐘到來(lái)時(shí)能夠接收數(shù)據(jù)。AXI Stream的特點(diǎn)是這兩個(gè)信號(hào)不存在互相等待的關(guān)系,及數(shù)據(jù)傳輸只發(fā)生再兩者均有效的時(shí)候,從而效率很高,可以認(rèn)為是連續(xù)傳輸,避免了死鎖的情況。
AXI Stream還有一些附加信號(hào),通常是伴隨再數(shù)據(jù)中傳輸,主要包括tuser,tlast,tkeep等。tlast在標(biāo)準(zhǔn)流協(xié)議中表示一個(gè)數(shù)據(jù)包結(jié)束,伴隨最后一個(gè)數(shù)據(jù)傳出,tuser可以很多位,是用戶定義信號(hào),用來(lái)表達(dá)用戶自己需要傳輸?shù)臄?shù)據(jù)。tkeep是字節(jié)修飾符,位數(shù)位數(shù)據(jù)寬度/8,當(dāng)總線數(shù)據(jù)某字節(jié)有效時(shí),tkeep對(duì)應(yīng)位就為高。
Xilinx 所有與視頻有關(guān)的IP均遵循一套AXI Stram視頻流協(xié)議,其中對(duì)tlast與tuser賦予了特殊的含義。視頻流中除了數(shù)據(jù)以外,包好start of frame和end of line信號(hào),其中eol用tlast表示,sof用tuser表示。這兩個(gè)信號(hào)均伴隨像素?cái)?shù)據(jù)傳輸,只保持一次有效傳輸?shù)臅r(shí)間,sof和一幀第一個(gè)像素一起傳出,eol和一行最后一個(gè)像素一起傳輸。視頻流中有時(shí)會(huì)加入tkeep信號(hào),無(wú)特殊情況時(shí)需全部拉高。
VDMA的詳細(xì)講解和使用
vdma IP核配置界面
上面圖片就是在vivado中例化vdma的界面,首先對(duì)參數(shù)做些介紹:
| 基本配置 | 高級(jí)配置 |
|---|---|
| 地址線寬度 | 是否使能異步模式(自動(dòng)) |
| 幀存數(shù)量 | 寫(xiě)通道幀同步 |
| 是否使能讀寫(xiě)通道 | 寫(xiě)通道GenLock模式選擇 |
| 數(shù)據(jù)線寬度 | 寫(xiě)通道是否允許非對(duì)齊傳輸 |
| 觸發(fā)長(zhǎng)度 | 讀通道幀同步 |
| AXI-Stream流數(shù)據(jù)位寬 | 讀通道GenLock模式選擇 |
| Line Buffer深度 | 讀通道是否允許非對(duì)齊傳輸 |
Frame Buffers :選擇vdma緩存幾幀圖像,這里默認(rèn)是寫(xiě)通道和讀通道都設(shè)置相同的緩存幀數(shù),具體設(shè)置多少幀合適一般根據(jù)應(yīng)用來(lái)定,比如讀寫(xiě)帶寬相同,想用ddr作為一個(gè)乒乓buffer,那就可以設(shè)置成2幀,寫(xiě)第一個(gè)地址,讀第二個(gè)地址,寫(xiě)第二個(gè)地址,讀第一個(gè)地址。這里面設(shè)置幾幀,就要在vdma寄存器配置的時(shí)候設(shè)置幾個(gè)幀起始地址。
Memory Map Data Width:代表數(shù)據(jù)到達(dá)AXI4總線上的位寬,比如這里設(shè)置成64,那就代表M_AXI_XX總線上的數(shù)據(jù)位寬是64bit,這時(shí)候如果stream上的數(shù)據(jù)是32bit,那vdma內(nèi)部會(huì)有一個(gè)帶寬轉(zhuǎn)換模塊,把數(shù)據(jù)拼成64bit。
Burst Size :AXI總線上突發(fā)傳輸?shù)拈L(zhǎng)度,一般設(shè)置為16
Stream Data Width:vdma與pl邏輯部分通過(guò)axi stream協(xié)議交互數(shù)據(jù),這里代表stream數(shù)據(jù)位寬
Line Buffer Depth:vdma內(nèi)部會(huì)有一個(gè)行緩存fifo,stream數(shù)據(jù)會(huì)先寫(xiě)入fifo,然后AXI總線邏輯會(huì)讀出到總線上,這個(gè)深度就代表fifo的深度。設(shè)置原則(個(gè)人理解):如果AXI總線數(shù)據(jù)帶寬是stream總線數(shù)據(jù)帶寬的1.5倍以上,這個(gè)fifo深度可以設(shè)置的小一點(diǎn),如果AXI總線帶寬小于1.5倍的stream總線帶寬,那fifo的深度至少要是圖像一個(gè)有效行的一半。
Advanced :這里面只說(shuō)一下Fsync Options,這個(gè)信號(hào)是什么意思呢,就是告訴vdma什么時(shí)候開(kāi)始運(yùn)行,一般s2mm通道選擇tuser,就是說(shuō)在tuser 拉高的時(shí)候開(kāi)始傳輸。mm2s通道,可以選擇none,也可以選擇 mm2s_fsync,這里介紹一下這兩個(gè)的區(qū)別。
none :就是沒(méi)有同步信號(hào),但這并不是說(shuō)沒(méi)有開(kāi)始信號(hào),而是只要mm2s_stream通道tready拉高,就開(kāi)始傳輸,相當(dāng)于free模式 mm2s_fsync:當(dāng)這個(gè)信號(hào)發(fā)生一個(gè)下降沿的時(shí)候開(kāi)始傳輸,如果沒(méi)有這個(gè)下降沿,即使mm2s_stream通道tready拉高也不會(huì)傳輸
詳細(xì)說(shuō)明
對(duì)于S2MM通道:之前在講vdma配置的時(shí)候有一個(gè)Advanced選項(xiàng),里面有Fsync Options選項(xiàng),可選none,s2mm_fsync,s2mm_tuer,三種同步模式。
none就是只要vdma就緒,就立馬準(zhǔn)備接收數(shù)據(jù),不需要同步信號(hào)。
s2mm_fsync,當(dāng)選擇此。模式時(shí),vdma 模塊會(huì)有一個(gè)s2mm_fsync引腳,一般情況下是把視頻幀同步信號(hào)連到這上面,當(dāng)檢測(cè)到s2mm_fsync引腳有一個(gè)下降沿的時(shí)候,vdma正式進(jìn)入傳輸狀態(tài)。
s2mm_tuer,這個(gè)信號(hào)和s2mm_fsync這個(gè)信號(hào)類(lèi)似,但他是在stream協(xié)議里面的,vdma檢測(cè)到s2mm_tuer拉高以后(tuser只在一幀數(shù)據(jù)的第一個(gè)像素位置拉高),正式進(jìn)入傳輸狀態(tài)
對(duì)于MM2S通道,同樣在vdma配置的Advanced選項(xiàng)里面有 none,mm2s_fsync兩種選擇模式。
none不需要同步信號(hào),只要axis_mm2s通道的tready拉高,就開(kāi)始從ddr讀取數(shù)據(jù)進(jìn)行傳輸,選擇這種模式一般主要是把ddr里面的數(shù)據(jù)讀到pl里面進(jìn)行處理,而不是轉(zhuǎn)成視頻
mm2s_fsync,選擇此同步模式,一般是把ddr的數(shù)據(jù)轉(zhuǎn)成視頻數(shù)據(jù),注意,這里重點(diǎn)講這個(gè)同步模式,當(dāng)vdma的讀通道選擇此同步模式的時(shí)候,vdma模塊會(huì)有一個(gè)mm2s_fsync信號(hào),這個(gè)信號(hào)在讀操作中非常重要。當(dāng)vdma寄存器配置完成并開(kāi)啟傳輸,mm2s通道進(jìn)入等待過(guò)程,一直等到mm2s引腳信號(hào)出現(xiàn)一個(gè)下降沿,這時(shí)候vdma啟動(dòng)讀操作,會(huì)從ddr預(yù)讀一些數(shù)據(jù)到內(nèi)部linebuffer,等到axis_mm2s通道的tready信號(hào)拉高,數(shù)據(jù)就開(kāi)始傳輸,進(jìn)入axis2video模塊的fifo,當(dāng)axis2video內(nèi)部fifo滿了以后,會(huì)拉低tready,這時(shí)候就會(huì)反饋到vdma,暫停讀操作,一直等到axis2video模塊的視頻時(shí)序輸入數(shù)據(jù)有效信號(hào),這時(shí)候視頻開(kāi)始輸出,axis2video內(nèi)部fifo數(shù)據(jù)減少,axis_mm2s通道開(kāi)始恢復(fù)傳輸,繼續(xù)從vdma讀出數(shù)據(jù),vdma再通過(guò)axi總線從ddr讀取數(shù)據(jù),如此反復(fù),完成ddr數(shù)據(jù)到video數(shù)據(jù)的轉(zhuǎn)換
下面是接口介紹:
| 接口 | 介紹 |
|---|---|
| M_AXI_XX | axi4總線接口,用來(lái)與ddr交互數(shù)據(jù) |
| M_AXIS_XX , S_AXIS_XX | axi stream接口,用來(lái)與pl交互數(shù)據(jù) |
| S_AXI_LITE | 控制總線,接到ps的gp口或者寫(xiě)一個(gè)axilite master總線去配置 |
其他接口不做介紹 。
為什么要用VDMA
在講解VDMA之前,先來(lái)探討一下為什么要學(xué)習(xí)和使用VDMA,以明確學(xué)習(xí)目的。由于使用VDMA可以方便地實(shí)現(xiàn)雙緩沖和多緩沖機(jī)制,所以本小節(jié)引入了幀緩存和緩沖機(jī)制的概念。另外,VDMA可以很好地契合Zynq內(nèi)部架構(gòu),縮短開(kāi)發(fā)周期。再加上VDMA本身能夠高效地實(shí)現(xiàn)數(shù)據(jù)存取,所以在基于Zynq(也包括其他Xilinx FPGA)圖像、視頻處理系統(tǒng)中,VDMA可謂是必不可少的。
什么是幀緩存
幀緩沖存儲(chǔ)器(Frame Buffer):簡(jiǎn)稱(chēng)幀緩存或顯存,它是屏幕所顯示畫(huà)面的一個(gè)直接映象,又稱(chēng)為位映射圖(Bit Map)或光柵。幀緩存的每一存儲(chǔ)單元對(duì)應(yīng)屏幕上的一個(gè)像素,整個(gè)幀緩存對(duì)應(yīng)一幀圖像。
在開(kāi)發(fā)者看來(lái),F(xiàn)rameBuffer 是一塊顯示緩存,往顯示緩存中寫(xiě)入特定格式的數(shù)據(jù)就意味著向屏幕輸出內(nèi)容。所以說(shuō)FrameBuffer就是一塊畫(huà)布,系統(tǒng)在畫(huà)布上繪制好畫(huà)面之后,就可以通知顯示設(shè)備讀取Frame Buffer進(jìn)行顯示了。
注意,筆者這里所說(shuō)的Frame Buffer和Linux的Frame Buffer不是同一個(gè)概念,這里僅指顯示緩存(畫(huà)布)本身,并不是Linux下的一個(gè)設(shè)備。
雙緩沖機(jī)制
最早解釋多緩沖區(qū)如何工作的方式,是通過(guò)一個(gè)現(xiàn)實(shí)生活中的實(shí)例來(lái)解釋的。在一個(gè)陽(yáng)光明媚的日子,你想將水池里的水打滿,而又找不到水管的時(shí)候,就只能用手邊的木桶來(lái)灌滿水池。水桶滿了之后,關(guān)掉水龍頭,將水提到水池旁邊,倒進(jìn)去,然后走回到水龍頭。重復(fù)上述工作,如此往復(fù)直到將水池灌滿。這就類(lèi)似單緩沖工作過(guò)程,當(dāng)你想將木桶里的水倒出的時(shí)候,你必須關(guān)掉水龍頭。
現(xiàn)在假設(shè)你用兩個(gè)木桶來(lái)做上面的工作。你會(huì)注滿第一個(gè)木桶然后將第二個(gè)木桶換到水龍頭下面,這樣,在第二個(gè)水桶注滿的時(shí)間內(nèi),你就可以將第一個(gè)木桶里面的水倒進(jìn)水池里面,當(dāng)你回來(lái)的時(shí)候,你只需要再將第一個(gè)木桶換下第二個(gè)注滿水木桶,當(dāng)?shù)谝粋€(gè)木桶開(kāi)始注水的時(shí)候你就將第二個(gè)木桶里面的水倒進(jìn)水池里面。重復(fù)這個(gè)過(guò)程直到水池被注滿。很容易看得到用這種技術(shù)注滿水池將會(huì)更快,同時(shí)也節(jié)省了很多等待木桶被注滿的時(shí)間,而這段時(shí)間里你什么也做不了,而水龍頭也就不用等待從木桶被注滿到你回來(lái)的這段時(shí)間了。
當(dāng)你雇傭另外一個(gè)人來(lái)搬運(yùn)一個(gè)被注滿的木桶時(shí),這就有點(diǎn)類(lèi)似于三個(gè)緩沖區(qū)的工作原理。如果將搬運(yùn)木桶的的時(shí)間很長(zhǎng),你可以用更多的木桶,雇傭更多的人,這樣水龍頭就會(huì)一直開(kāi)著注滿木桶了。
在計(jì)算機(jī)圖形學(xué)中,雙緩沖是一種畫(huà)圖技術(shù),使用這種技術(shù)可以使得畫(huà)圖沒(méi)有(至少是減少)閃爍、撕裂等不良效果,并減少等待時(shí)間。
雙緩沖機(jī)制的原理大概是:所有畫(huà)圖操作將它們畫(huà)圖的結(jié)果保存在一塊系統(tǒng)內(nèi)存區(qū)域中,這塊區(qū)域通常被稱(chēng)作“后緩沖區(qū)(back buffer)”,當(dāng)所有的繪圖操作結(jié)束之后,將整塊區(qū)域復(fù)制到顯示內(nèi)存中,這個(gè)復(fù)制操作通常要跟顯示器的光棧束同步,以避免撕裂。雙緩沖機(jī)制必須要求有比單緩沖更多的顯示內(nèi)存和CPU消耗時(shí)間,因?yàn)椤昂缶彌_區(qū)”需要顯示內(nèi)存,而復(fù)制操作和等待同步需要CPU時(shí)間。
基于雙緩沖機(jī)制可以實(shí)現(xiàn)頁(yè)交換,頁(yè)交換初始狀態(tài)如下圖所示:
如上圖所示,此時(shí)由于處于初始狀態(tài),畫(huà)圖操作的結(jié)果都在后緩沖區(qū)中,而屏幕上顯示的則是前緩沖區(qū)中的內(nèi)容。此時(shí)畫(huà)圖操作尚未完成,畫(huà)圖操作完成之后,頁(yè)轉(zhuǎn)換操作開(kāi)始執(zhí)行,示意圖如下圖所示:
如上圖所示,畫(huà)圖操作結(jié)束,下一個(gè)畫(huà)圖操作的結(jié)果保存對(duì)象指向前緩沖區(qū),屏幕的顯示對(duì)象指向后緩沖區(qū),此時(shí)前緩沖區(qū)變成實(shí)際意義上的后緩沖區(qū),后緩沖區(qū)變成實(shí)際意義上的前緩沖去,即實(shí)現(xiàn)“頁(yè)交換”操作。
有時(shí)候也在頁(yè)交換鏈中設(shè)置多個(gè)“后緩沖區(qū)”,這是就需要多緩沖區(qū)機(jī)制的支持。
Zynq硬件架構(gòu)
在Zynq芯片內(nèi)部,PS和PL是共享DDR控制器的。PS訪問(wèn)DDR十分簡(jiǎn)單,只要操作DDR映射的虛擬地址即可。對(duì)于PL而言,要接入DDR,必須通過(guò)AXI_HP端口。
Zynq共有四個(gè)AXI_HP通道,通道數(shù)據(jù)寬度可以配置為32位或64位,這些接口通過(guò)FIFO控制器連接PL到存儲(chǔ)接口上,其中有兩條連接到DDR存儲(chǔ)控制器上,還有一條是連接到雙端口的OCM上的,下圖是AXI_HP訪問(wèn)DDR和OCM的連接圖。
由上圖可以看出,AXI_HP接口也是遵循AXI協(xié)議的,因此利用VDMA可以直接連接HP端口。除了使用VDMA,當(dāng)然也可以自己開(kāi)發(fā)出符合AXI協(xié)議的IP,但是綜合考慮設(shè)計(jì)成本,沒(méi)太有必要自己實(shí)現(xiàn)。此外,自己實(shí)現(xiàn)的IP功能也不見(jiàn)得比VDMA強(qiáng)大。
VDMA的作用
VDMA數(shù)據(jù)接口可以分為讀、寫(xiě)通道,用戶可以通過(guò)寫(xiě)通道將AXI-Stream類(lèi)型的數(shù)據(jù)流寫(xiě)入DDR3,通過(guò)讀通道可以從DDR3讀取數(shù)據(jù),并以AXI-Stream類(lèi)型的格式輸出。由此可知,VDMA本質(zhì)上是一個(gè)數(shù)據(jù)搬運(yùn)IP,為數(shù)據(jù)進(jìn)、出DDR3提供了一種便捷的方案。
將數(shù)據(jù)存入DDR之后,CPU就可以進(jìn)行一些處理(縮放、裁剪等),然后再送至顯示設(shè)備,達(dá)到期望的應(yīng)用目的。當(dāng)然,也可能是簡(jiǎn)單地對(duì)捕獲的視頻進(jìn)行解析,將數(shù)據(jù)存入幀緩存,以供顯示。
VDMA可以控制多達(dá)32個(gè)幀存,并可以自由地進(jìn)行幀存切換,所以就能夠輕松地實(shí)現(xiàn)雙緩沖和多緩沖操作。這也是一個(gè)很重要的特性,在后續(xù)進(jìn)行系統(tǒng)設(shè)計(jì)的時(shí)候,通常是采用多緩沖的方式實(shí)現(xiàn)顯示。
由以上分析可以發(fā)現(xiàn),在基于Zynq的圖像、視頻處理系統(tǒng)中使用VDMA是十分有必要的。
VDMA概述
AXI VDMA是Xilinx提供的軟核IP,用于將AXI Stream格式的數(shù)據(jù)流轉(zhuǎn)換為Memory Map格式或?qū)emory Map格式的數(shù)據(jù)轉(zhuǎn)換為AXI Stream數(shù)據(jù)流,從而實(shí)現(xiàn)與DDR3進(jìn)行通信。
許多視頻類(lèi)應(yīng)用都需要幀緩存來(lái)處理幀率變化或者進(jìn)行圖像的縮放、裁剪等尺寸變換操作。AXI VDMA設(shè)計(jì)的初衷就是用于高效地實(shí)現(xiàn)AXI4-Stream視頻流接口和AXI4接口之間的數(shù)據(jù)傳輸。
VDMA的關(guān)鍵特性&優(yōu)勢(shì)有以下幾點(diǎn):
? 使視頻流能夠高帶寬直接接入內(nèi)存
? 高效的二維DMA操作
? 獨(dú)立的異步讀寫(xiě)通道操作
? Gen-Lock幀存同步機(jī)制
? 最多支持32個(gè)幀存
? 支持視頻格式動(dòng)態(tài)切換
? 猝發(fā)長(zhǎng)度和行緩存深度可調(diào)節(jié)
? 處理器可以控制IP的初始化、狀態(tài)、中斷和管理寄存器
? 基礎(chǔ)AXI流數(shù)據(jù)位寬為8的整數(shù)倍,如8,16,24,32等,最大可達(dá)1024個(gè)位
AXI VDMA框圖如下所示。
主要有以下幾種接口類(lèi)型:
? AXI-lite:PS通過(guò)該接口來(lái)配置VDMA
? AXI Memory Map write:映射到存儲(chǔ)器寫(xiě)
? AXI Memory Map read:映射到存儲(chǔ)器讀
? AXI Stream Write(S2MM):AXI Stream視頻流寫(xiě)入圖像
? AXI Stream Read(MM2S):AXI Stream視頻流讀出圖像
從框圖中可以看出,VDMA主要由控制和狀態(tài)寄存器、數(shù)據(jù)搬運(yùn)模塊、行緩沖這幾部分構(gòu)成。數(shù)據(jù)進(jìn)出DDR要經(jīng)過(guò)行緩沖進(jìn)行緩存,然后由數(shù)據(jù)搬運(yùn)模塊寫(xiě)入或者讀出數(shù)據(jù)。數(shù)據(jù)搬運(yùn)模塊具體如何工作,由相關(guān)寄存器負(fù)責(zé)控制。VDMA的工作狀態(tài)可以通過(guò)讀取狀態(tài)寄存器進(jìn)行獲取。
VDMA詳細(xì)介紹
接口
時(shí)鐘和復(fù)位
各種總線都有自己的時(shí)鐘信號(hào),不用特別說(shuō)明,需要指出的是,這些時(shí)鐘是異步的,并不需要用同一個(gè)時(shí)鐘。但在設(shè)計(jì)過(guò)程中,如無(wú)特別需求,可以使用相同的時(shí)鐘,以降低設(shè)計(jì)難度。
同步復(fù)位信號(hào)axi_resetn,同步時(shí)鐘為s_axi_lite_aclk,低電平有效(至少要保持16個(gè)時(shí)鐘周期的低電平,才能夠生效),有效時(shí)復(fù)位整個(gè)IP核。
AXI總線相關(guān)信號(hào)
-
AXI4-Lite接口(S_AXI_LITE)
-
AXI4讀接口(M_AXI_MM2S)
-
AXI4寫(xiě)接口(M_AXI_S2MM)
-
AXI4-Stream主接口(M_AXI_MM2S)
-
AXI4-Stream從接口(S_AXI_S2MM)
前綴S_、M_分別表示Slave和Master;后綴MM2S、S2MM說(shuō)明數(shù)據(jù)流向是從memory map到stream還是從stream到memory map。
視頻同步接口信號(hào)
| 信號(hào)名稱(chēng) | 方向 | 詳細(xì)描述 |
|---|---|---|
| mm2s_fsync | Frame Sync | MM2S幀同步輸入。使能該信號(hào)后,VDMA操作開(kāi)始于fsync每個(gè)下降沿。該信號(hào)至少要持續(xù)一個(gè)m_axis_mm2s_aclk時(shí)鐘周期 |
| s2mm_fsync | Frame Sync | S2MM幀同步輸入。使能該信號(hào)后,VDMA操作開(kāi)始于fsync每個(gè)下降沿。該信號(hào)至少要持續(xù)一個(gè)s_axis_s2mm_aclk時(shí)鐘周期 |
GenLock相關(guān)信號(hào)
在下一節(jié)將詳細(xì)介紹這些信號(hào)的作用和應(yīng)用場(chǎng)合。
| 信號(hào)名稱(chēng) | 方向 | 詳細(xì)描述 |
|---|---|---|
| mm2s_frame_ptr_in(5:0) | 輸入 | 輸入的幀編號(hào) |
| mm2s_frame_ptr_out(5:0) | 輸出 | 輸出當(dāng)前幀的編號(hào) |
| s2mm_frame_ptr_in(5:0) | 輸入 | 輸入的幀編號(hào) |
| s2mm_frame_ptr_out(5:0) | 輸出 | 輸出當(dāng)前幀的編號(hào) |
VDMA幀存格式
在講述寄存器時(shí),需要設(shè)定和顯示(幀存)相關(guān)的參數(shù),為了方便讀者的理解,這里先介紹VDMA數(shù)據(jù)存放框架,如下圖所示,黑色實(shí)線內(nèi)的區(qū)域?yàn)閷?shí)際存儲(chǔ)畫(huà)面的幀存。
圖中H_STRIDE代表水平方向上的跨度,H_SIZE表示水平方向數(shù)據(jù)總量,V_SIZE表示豎直方向總共有多少行。
至于幀存內(nèi)部數(shù)據(jù)如何組織,就取決于軟件代碼和硬件邏輯如何匹配了,通常來(lái)講,數(shù)據(jù)存放格式為RGB+Alpha或者Alpha+RGB。
讀寫(xiě)通道工作時(shí)序
清晰地理解VDMA讀寫(xiě)通道的工作時(shí)序,對(duì)以后的設(shè)計(jì)有很大的幫助,很多設(shè)計(jì)都是根據(jù)本小節(jié)所示的樣例時(shí)序設(shè)計(jì)出來(lái)的。
讀通道(MM2S)時(shí)序
下圖描述了讀通道的時(shí)序,5行,每行16字節(jié),跨度為32字節(jié)。
從圖中可以看出:在收到mm2s_fsync信號(hào)后,VDMA在m_axi_mm2s_araddr的起始地址處發(fā)出m_axi_mm2s_arvalid信號(hào)。M_axi_mm2s_arvalid總共有效5次,分別獲取一幀的5行數(shù)據(jù)。從MM讀取的數(shù)據(jù)存儲(chǔ)在行緩存里,當(dāng)收到來(lái)自axi-stream端的m_axis_mm2s_tvalid信號(hào)后,將數(shù)據(jù)發(fā)送到axi-stream端。每一行的結(jié)束,axi-stream端會(huì)使m_axis_mm2s_tlast有效。
寫(xiě)通道(S2MM)時(shí)序
下圖描述了寫(xiě)通道的時(shí)序,5行,每行16字節(jié),跨度為32字節(jié)。
從圖中可以看出:在收到s2mm_fsync信號(hào)后,VDMA發(fā)出s2mm_fsync_out和s_axis_s2mm_tready表明已經(jīng)準(zhǔn)備好接收來(lái)自axi-stream端的數(shù)據(jù)。讀取到的數(shù)據(jù)存儲(chǔ)在行緩存里,m_axi_s2mm_awvalid有效后,緊接著有效m_axi_s2mm_wvalid信號(hào),同時(shí)將數(shù)據(jù)放至m_axi_s2mm_wdata。
寄存器
VDMA的寄存器如下表所示。所有寄存器都被映射到非緩存內(nèi)存空間。該內(nèi)存空間必須按照AXI字(32位)進(jìn)行對(duì)齊,換句話說(shuō),寄存器偏移地址至少間隔4個(gè)字節(jié)。
| 寄存器名稱(chēng) | 偏移地址 | 詳細(xì)描述 |
|---|---|---|
| MM2S_VDMACR | 00h | MM2S VDMA控制寄存器 |
| MM2S_VDMASR | 04h | MM2S VDMA狀態(tài)寄存器 |
| 保留 | 08h~10h | N/A |
| MM2S_REG_INDEX | 14h | MM2S寄存器索引 |
| 保留 | 18h~24h | N/A |
| PARK_PRT_REG | 28h | MM2S和S2MM Park指針寄存器 |
| VDMA_VERSION | 2Ch | VDMA版本寄存器 |
| S2MM_VDMACR | 30h | S2MM VDMA控制寄存器 |
| S2MM_VDMASR | 34h | S2MM VDMA狀態(tài)寄存器 |
| 保留 | 38h | N/A |
| S2MM_VDMA_IRQ_MASK | 3Ch | S2MM錯(cuò)誤中斷掩碼寄存器 |
| 保留 | 40h | N/A |
| S2MM_REG_INDEX | 44h | S2MM寄存器索引 |
| 保留 | 48h~4Ch | N/A |
| MM2S_VSIZE | 50h | MM2S垂直方向顯示大小寄存器 |
| MM2S_HSIZE | 54h | MM2S水平方向顯示大小寄存器 |
| MM2S_FRMDLY_STRIDE | 58h | MM2S幀延遲和跨度寄存器 |
| MM2S_START_ADDRESS(1~16) | 5Ch~98h | MM2S幀存起始地址(1~16) |
| 保留 | 9Ch | N/A |
| S2MM_VSIZE | A0h | S2MM垂直方向顯示大小寄存器 |
| S2MM_HSIZE | A4h | S2MM水平方向顯示大小寄存器 |
| S2MM_FRMDLY_STRIDE | A8h | S2MM幀延遲和跨度寄存器 |
| S2MM_START_ADDRESS(1~16) | ACh~E8h | S2MM幀存起始地址(1~16) |
所有寄存器字節(jié)序都是小端格式,如下圖所示。
各個(gè)寄存器的名稱(chēng)和大致作用從上表就可以看出,接下來(lái),筆者會(huì)詳細(xì)介紹重要寄存器的具體bit的作用。明白了每個(gè)bit的作用之后,自然就知道寫(xiě)入什么值能夠達(dá)到自己的控制目的。
從上表可以看出,寄存器可以分為兩組,分別對(duì)應(yīng)MM2S通道和S2MM通道,兩組寄存器的功能是相似的,區(qū)別僅在于偏移地址和所服務(wù)的對(duì)象。因此,在學(xué)習(xí)完MM2S通道的所有寄存器之后,只要大致瀏覽一下S2MM通道對(duì)應(yīng)的寄存器的關(guān)鍵位即可(個(gè)別位不相同),在使用高級(jí)功能時(shí),再仔細(xì)查閱VDMA用戶手冊(cè)。
MM2S VDMA 控制寄存器(00h)
顧名思義,該寄存器用于控制VDMA,具體可以實(shí)現(xiàn)復(fù)位、使能鎖相同步、設(shè)定幀存切換模式、啟動(dòng)VDMA讀寫(xiě)通道等操作。每一位作用如下圖所示,低4位是最重要的,接下來(lái)會(huì)詳細(xì)介紹。

MM2S VDMA 狀態(tài)寄存器(04h)
該寄存器用于獲取VDMA工作狀態(tài)。每一位作用如下圖所示,低4位是最重要的,接下來(lái)會(huì)詳細(xì)介紹。

PARK_PTR_REG停留指針寄存器(28h)
該寄存器用于管理讀、寫(xiě)通道的數(shù)據(jù)傳輸。

學(xué)習(xí)了這個(gè)寄存器之后,就可以發(fā)現(xiàn):當(dāng)VDMA工作在Parked模式下,通過(guò)操作該寄存器,就能夠?qū)崿F(xiàn)幀緩存的切換,建立自己想要的緩存切換機(jī)制。
MM2S 幀存起始地址(0x5C~0x98)
有最多32個(gè)寄存器用于存放幀存起始地址,其分別存在于兩個(gè)寄存器bank上:bank0和bank1,每個(gè)bank上有16個(gè)寄存器。這兩個(gè)bank上有相同的起始偏移地址(0x5C),選擇這兩個(gè)bank可以通過(guò)MM2S_REG_INDEX的值進(jìn)行選擇。假如想訪問(wèn)第1個(gè)寄存器,則給MM2S_REG_INDEX賦值為0,并設(shè)定偏移地址為0x5C;如果想訪問(wèn)第17個(gè)寄存器,需要將MM2S_REG_INDEX設(shè)為1,并設(shè)定初始偏移地址為0x5C。
MM2S_FRMDLY_STRIDE MM2S幀延遲和跨度(58h)
該寄存器有兩個(gè)作用,第一是bit24~bit28指定幀延遲,僅用于Genlock從模式,指定從接口比主接口至少要延遲多少個(gè)幀;第二是低16位指定水平方向的跨度,同樣以字節(jié)為單位。所謂跨度是指每?jī)尚械谝粋€(gè)像素之間間隔的數(shù)據(jù)個(gè)數(shù),具體請(qǐng)參考22.3.2小節(jié),VDMA幀存格式。
MM2S_HSIZE MM2S水平方向尺寸(54h)
該寄存器的低16位用于指定每一行有多少字節(jié)的數(shù)據(jù)需要傳輸。例如顯示分辨率為640480,每個(gè)像素4個(gè)字節(jié)(RGB+Alpha),該值應(yīng)該設(shè)定為6404。
MM2S_VSIZE MM2S垂直方向尺寸(50h)
該寄存器有兩個(gè)作用,第一是用低13位指定總共有多少行;第二是啟動(dòng)MM2S的傳輸。當(dāng)MM2S_VDMACR.RS=1,對(duì)該寄存器的寫(xiě)操作會(huì)將所有設(shè)定參數(shù)傳遞給VDMA內(nèi)部寄存器模塊,用于VDMA控制。對(duì)某個(gè)通道進(jìn)行配置時(shí),必須在最后一步設(shè)置該寄存器。
S2MM VDMA 控制寄存器(30h)
顧名思義,該寄存器用于控制VDMA S2MM通道,具體可以實(shí)現(xiàn)復(fù)位、使能鎖相同步、設(shè)定幀存切換模式、啟動(dòng)VDMA讀寫(xiě)通道等操作。每一位作用如下圖所示,低4位是最重要的,接下來(lái)會(huì)詳細(xì)介紹。

S2MM VDMA 狀態(tài)寄存器(34h)
該寄存器用于獲取S2MM工作狀態(tài)。每一位作用如下圖所示,低4位是最重要的,接下來(lái)會(huì)詳細(xì)介紹。

S2MM 幀存起始地址(0xAC~0xE8)
有最多32個(gè)寄存器用于存放幀存起始地址,其分別存在于兩個(gè)寄存器bank上:bank0和bank1,每個(gè)bank上有16個(gè)寄存器。這兩個(gè)bank上有相同的起始偏移地址(0x5C),選擇這兩個(gè)bank可以通過(guò)S2MM_REG_INDEX的值進(jìn)行選擇。假如想訪問(wèn)第1個(gè)寄存器,則給S2MM_REG_INDEX賦值為0,并設(shè)定偏移地址為0x5C;如果想訪問(wèn)第17個(gè)寄存器,需要將MM2S_REG_INDEX設(shè)為1,并設(shè)定初始偏移地址為0x5C。
S2MM_FRMDLY_STRIDE S2MM幀延遲和跨度(A8h)
該寄存器有兩個(gè)作用,第一是bit24~bit28指定幀延遲,僅用于Genlock從模式,指定從接口比主接口至少要延遲多少個(gè)幀;第二是低16位指定水平方向的跨度,同樣以字節(jié)為單位。所謂跨度是指每?jī)尚械谝粋€(gè)像素之間間隔的數(shù)據(jù)個(gè)數(shù),具體請(qǐng)參考22.3.2小節(jié),VDMA幀存格式。
S2MM_HSIZE S2MM水平方向尺寸(A4h)
該寄存器的低16位用于指定每一行有多少字節(jié)的數(shù)據(jù)需要傳輸。例如顯示分辨率為640480,每個(gè)像素4個(gè)字節(jié)(RGB+Alpha),該值應(yīng)該設(shè)定為6404。
S2MM_VSIZE S2MM垂直方向尺寸(A0h)
該寄存器有兩個(gè)作用,第一是用低13位指定總共有多少行;第二是啟動(dòng)S2MM的傳輸。當(dāng)S2MM_VDMACR.RS=1,對(duì)該寄存器的寫(xiě)操作會(huì)將所有設(shè)定參數(shù)傳遞給VDMA內(nèi)部寄存器模塊,用于VDMA控制。對(duì)某個(gè)通道進(jìn)行配置時(shí),必須在最后一步設(shè)置該寄存器。
幀同步選項(xiàng)
VDMA支持以下三種幀同步源:
? 基于AXI4-Stream的幀同步(使用tuser(0)信號(hào))
? 讀通道使用m_axis_mm2s_tuser(0)作為幀起始信號(hào)
? 寫(xiě)通道使用s_axis_s2mm_tuser(0)作為幀起始信號(hào)
? S2MM幀同步(s2mm_fsync)
? MM2S幀同步(mm2s_fsync)
Genlock同步機(jī)制
什么是Genlock?
Genlock,同步鎖相,可以使一套或多套系統(tǒng)與同一同步源實(shí)現(xiàn)同步。能夠使視頻的刷新和外部視頻源保持一致。當(dāng)提供了一個(gè)適當(dāng)?shù)男盘?hào)后,系統(tǒng)就會(huì)把它的顯示刷新率和這個(gè)信號(hào)進(jìn)行鎖定 。
在許多視頻應(yīng)用中,輸入端產(chǎn)生數(shù)據(jù)的速率往往不同于輸出端數(shù)據(jù)速率,為了避免由速率不一致導(dǎo)致的潛在錯(cuò)誤,幀緩沖的使用是很有必要的。幀緩沖機(jī)制開(kāi)辟多個(gè)緩沖頁(yè),用于保存數(shù)據(jù),輸入和輸出端分別操作不同的幀存,從而避免了沖突。
VDMA的鎖相同步特性正是用于阻止讀、寫(xiě)通道同時(shí)操作同一個(gè)幀存。VDMA的每個(gè)通道都可以選擇自己的操作類(lèi)型(同步鎖相主/從或者動(dòng)態(tài)同步鎖相主/從),利用該特性,禁止主從接口同時(shí)訪問(wèn)同一緩存,從而保持同步。
VDMA支持四種模式的鎖相同步,分別為:
? Genlock Master(鎖相同步主端)
? Genlock Slave(鎖相同步從端)
? Dynamic Genlock Master(動(dòng)態(tài)鎖相同步主端)
? Dynamic Genlock Slave(動(dòng)態(tài)鎖相同步從端)
Genlock Master
讀通道(MM2S):當(dāng)配置為Genlock Master時(shí),該通道不會(huì)跳過(guò)或者重復(fù)任一幀數(shù)據(jù),并把當(dāng)前幀的編號(hào)輸出在mm2s_frame_ptr_out端口。通道不會(huì)檢測(cè)mm2s_frame_ptr_in端口提供的幀編號(hào)。Genlock Slave通道應(yīng)跟隨Genlock Master通道變化,但有一定的延遲。延遲大小預(yù)定義在寄存器中(*frmdly_stride[28:24])。
寫(xiě)通道(S2MM):當(dāng)配置為Genlock Master時(shí),該通道不會(huì)跳過(guò)或者重復(fù)任一幀數(shù)據(jù),并把當(dāng)前幀的編號(hào)輸出到s2mm_frame_ptr_out端口。通道不會(huì)檢測(cè)s2mm_frame_ptr_in端口提供的幀編號(hào)。Genlock Slave通道應(yīng)跟隨Genlock Master通道變化,但有一定的延遲。延遲大小預(yù)定義在寄存器中(*frmdly_stride[28:24])。
Genlock Slave
讀通道(MM2S):當(dāng)配置為Genlock Slave時(shí),該通道會(huì)通過(guò)跳過(guò)或者重復(fù)一些幀的方式,嘗試與Genlock Master同步。通道會(huì)對(duì)mm2s_frame_ptr_in端口進(jìn)行采樣,獲取Genlock Master的幀編號(hào)。為了實(shí)現(xiàn)狀態(tài)反饋,通道會(huì)把當(dāng)前幀的編號(hào)輸出到mm2s_frame_ptr_out端口。
指定通道工作在Genlock Slave模式,必須進(jìn)行如下操作。
? 將GenlockEn置1(MM2S_VDMACR[3]=1),使能主、從通道之間的Genlock同步。
? 將GenlockSrc置1(MM2S_VDMACR[7]=1),使能內(nèi)部Genlock模式。如果在Vivado IDE中同時(shí)使能讀、寫(xiě)通道,該位默認(rèn)置位。當(dāng)GenlockSRC=1時(shí),VDMA默認(rèn)支持內(nèi)部同步鎖相總線。這樣一來(lái)就沒(méi)有必要在外部對(duì)幀指針端口(frame_ptr_out和_frame_ptr_in)進(jìn)行連接了。
? 根據(jù)主從通道的幀率,使用mm2s_frmdly_stride[28:24]設(shè)定合適的延遲時(shí)間。
寫(xiě)通道(S2MM):當(dāng)配置為Genlock Slave時(shí),該通道會(huì)通過(guò)跳過(guò)或者重復(fù)一些幀的方式,嘗試與Genlock Master同步。通道會(huì)對(duì)s2mm_frame_ptr_in端口進(jìn)行采樣,獲取Genlock Master的幀編號(hào)。為了實(shí)現(xiàn)狀態(tài)反饋,通道會(huì)把當(dāng)前幀的編號(hào)輸出到s2mm_frame_ptr_out端口。
指定通道工作在Genlock Slave模式,必須進(jìn)行如下操作。
? 將GenlockEn置1(S2MM_VDMACR[3]=1),使能主、從通道之間的Genlock同步。
? 將GenlockSrc置1(S2MM_VDMACR[7]=1),使能內(nèi)部Genlock模式。如果在Vivado IDE中同時(shí)使能讀、寫(xiě)通道,該位默認(rèn)置位。當(dāng)GenlockSRC=1時(shí),VDMA默認(rèn)支持內(nèi)部同步鎖相總線。這樣一來(lái)就沒(méi)有必要在外部對(duì)幀指針端口(frame_ptr_out和_frame_ptr_in)進(jìn)行連接了。
? 根據(jù)主從通道的幀率,使用mm2s_frmdly_stride[28:24]設(shè)定合適的延遲時(shí)間。
Dynamic Genlock Master
動(dòng)態(tài)Genlock Master與Genlock Master的區(qū)別在于,主通道會(huì)跳過(guò)從通道正在操作的幀。舉例而言,對(duì)于三幀存而言,動(dòng)態(tài)Genlock Master會(huì)按照0,1,2,0,1,2的順序循環(huán)使用幀存,一旦檢測(cè)到Master即將操作Slave正在操作的幀,就會(huì)跳過(guò)該幀繼續(xù)循環(huán)。因此,如果Slave通道一直在操作幀存1,那么Master通道就會(huì)在幀0和幀2之間來(lái)回切換。
Dynamic Genlock Slave
Dynamic Genlock Slave通道會(huì)操作Dynamic Genlock Master通道上一周期操作的幀。
下圖描述了一種簡(jiǎn)單的Genlock操作時(shí)序。在這個(gè)示例中,S2MM通道是Genlock Master,MM2S通道是Genlock Slave,并且寫(xiě)通道幀率高于讀通道幀率。
由于讀通道幀率慢于寫(xiě)通道,所以讀通道僅處理幀2和幀0,跳過(guò)幀1不做處理。
軟件控制流程
以下步驟是最簡(jiǎn)單的VDMA控制初始化操作。
? 寫(xiě)VDMACR寄存器,將VDMACR.RS設(shè)為1,啟動(dòng)VDMA通道。
? 設(shè)定有效的幀緩存起始地址。
? 設(shè)定幀延遲(僅針對(duì)Genlock從模式)以及跨度到FRMDLY_STRIDE寄存器。
? 設(shè)定水平方向字節(jié)數(shù)到HSIZE寄存器。
? 設(shè)定豎直方向行數(shù)到VSIZE寄存器。啟動(dòng)通道的數(shù)據(jù)傳輸。
在VDMA運(yùn)行過(guò)程中,可以動(dòng)態(tài)的進(jìn)行顯示參數(shù)配置,但是需要注意的是,想要使參數(shù)生效,必須在設(shè)置的最后一步,對(duì)VSIZE寄存器進(jìn)行寫(xiě)操作。
最后,給出一段通過(guò)VDMA對(duì)DDR讀寫(xiě)傳輸?shù)倪M(jìn)行初始化的示例代碼:
//VDMA configurateAXI VDMA0 /*****************從DDR讀數(shù)據(jù)設(shè)置**********************/ XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x0, 0x4); //reset XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x0, 0x8); //gen-lock XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x5C, 0x08000000); // AXI4 Data Width為32位,是4個(gè)字節(jié)數(shù) // 0x0A000000 0x0015F900 XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x5C+4, 0x0A000000); // 0x09000000 0x002BF200 XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x5C+8, 0x09000000); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x54, 640);// 640 XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x58, 0x01000280); // 第0位: 運(yùn)行 - 啟動(dòng)VDMA操作,在運(yùn)行VDMA時(shí),其狀態(tài)寄存器中的停止位賦值為0 第一位:循環(huán)模式 -通過(guò)連續(xù)循環(huán)幀緩沖 XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x0, 0x03); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x50, 480);//480 /*********** 寫(xiě)入DDR設(shè)置*************************/ XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x30, 0x4); //reset XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x30, 0x8); //genlock XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xAC, 0x08000000); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xAC+4, 0x0A000000); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xAC+8, 0x09000000); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xA4, 640); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xA8, 0x01000280); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x30, 0x03); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0xA0, 480);
搭建VDMA圖像系統(tǒng)
構(gòu)架方案圖
可以看到VMDA的圖像系統(tǒng)和前面介紹的DMA系統(tǒng)相比非常類(lèi)似。實(shí)際上他們都是屬于DMA系統(tǒng),只是VDMA在配置完成后,可以無(wú)需依賴(lài)CPU可以獨(dú)立運(yùn)行,有點(diǎn)類(lèi)似顯卡的功能了。
構(gòu)BLOCK模塊化設(shè)計(jì)方案圖
PS部分
main函數(shù)
本課程提供了二種方式啟動(dòng)VDMA,第一種是通過(guò)庫(kù)函數(shù)版本,第二種是通過(guò)寄存器版本。寄存器版本主要是驗(yàn)證我們對(duì)VDMA的寄存器掌握情況。庫(kù)函數(shù)具備更強(qiáng)的功能,和可維護(hù)性。
#include "sys_intr.h" #include "xaxivdma.h" #include "xaxivdma_i.h" #define VTC_BASEADDR XPAR_MIZ702_VTG_VGA_0_BASEADDR #define DDR_BASEADDR 0x00000000 //#define UART_BASEADDR 0xe0001000 #define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR #define H_STRIDE 640 #define H_ACTIVE 640 #define V_ACTIVE 480 #define pi 3.14159265358 #define COUNTS_PER_SECOND (XPAR_CPU_CORTEXA9_CORE_CLOCK_FREQ_HZ)/64 #define VIDEO_LENGTH (H_STRIDE*V_ACTIVE) #define VIDEO_BASEADDR0 DDR_BASEADDR + 0x2000000 #define VIDEO_BASEADDR1 DDR_BASEADDR + 0x3000000 #define VIDEO_BASEADDR2 DDR_BASEADDR + 0x4000000 u32 *BufferPtr[3]; unsigned int srcBuffer = (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000); int run_triple_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize, int vsize, int buf_base_addr, int number_frame_count, int enable_frm_cnt_intr); int main(void) { u32 Status; Miz702_EMIO_init(); ov7725_init_rgb(); XAxiVdma InstancePtr; xil_printf("Starting the first VDMA \n\r"); Status = run_triple_frame_buffer(&InstancePtr, 0, 640, 480, srcBuffer, 2, 0); if (Status != XST_SUCCESS) { xil_printf("Transfer of frames failed with error = %d\r\n",Status); return XST_FAILURE; } else { xil_printf("Transfer of frames started \r\n"); } print("TEST PASS\r\n"); //VDMA configurateAXI VDMA0 /****************往DDR寫(xiě)數(shù)據(jù)設(shè)置**********************/ /*Xil_Out32((VDMA_BASEADDR + 0x030), 0x00000003);// enable circular mode Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1); // start address Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2); // start address Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*4)); // h offset (640 * 4) bytes Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*4)); // h size (640 * 4) bytes Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE);*/ // v size (480) /*****************從DDR讀數(shù)據(jù)設(shè)置**********************/ /*Xil_Out32((VDMA_BASEADDR + 0x000), 0x00000003); // enable circular mode Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); // start address Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); // start address Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*4)); // h offset (640 * 4) bytes Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*4)); // h size (640 * 4) bytes Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); // v size (480) */ while (1) ; return XST_SUCCESS; }
vdma_api.c函數(shù)
XAxiVdma_LookupConfig函數(shù)是XILINX 庫(kù)函數(shù)的標(biāo)準(zhǔn)調(diào)用方式,可以獲取到硬件的默認(rèn)配置參數(shù)。默認(rèn)的配置參數(shù)保存在 參數(shù)表XAxiVdma_ConfigTable 中。
表6-6-2-1 XAxiVdma_LookupConfig /*****************************************************************************/ /** * Look up the hardware configuration for a device instance * * @param DeviceId is the unique device ID of the device to lookup for * * @return * The configuration structure for the device. If the device ID is not found, * a NULL pointer is returned. * ******************************************************************************/ XAxiVdma_Config *XAxiVdma_LookupConfig(u16 DeviceId) { extern XAxiVdma_Config XAxiVdma_ConfigTable[]; XAxiVdma_Config *CfgPtr = NULL; int i; for (i = 0; i < XPAR_XAXIVDMA_NUM_INSTANCES; i++) { if (XAxiVdma_ConfigTable[i].DeviceId == DeviceId) { CfgPtr = &XAxiVdma_ConfigTable[i]; break; } } return CfgPtr; } 表6-6-2-2 XAxiVdma_ConfigTable參數(shù)表 XAxiVdma_Config XAxiVdma_ConfigTable[] = { { XPAR_AXI_VDMA_0_DEVICE_ID, XPAR_AXI_VDMA_0_BASEADDR, XPAR_AXI_VDMA_0_NUM_FSTORES, XPAR_AXI_VDMA_0_INCLUDE_MM2S, XPAR_AXI_VDMA_0_INCLUDE_MM2S_DRE, XPAR_AXI_VDMA_0_M_AXI_MM2S_DATA_WIDTH, XPAR_AXI_VDMA_0_INCLUDE_S2MM, XPAR_AXI_VDMA_0_INCLUDE_S2MM_DRE, XPAR_AXI_VDMA_0_M_AXI_S2MM_DATA_WIDTH, XPAR_AXI_VDMA_0_INCLUDE_SG, XPAR_AXI_VDMA_0_ENABLE_VIDPRMTR_READS, XPAR_AXI_VDMA_0_USE_FSYNC, XPAR_AXI_VDMA_0_FLUSH_ON_FSYNC, XPAR_AXI_VDMA_0_MM2S_LINEBUFFER_DEPTH, XPAR_AXI_VDMA_0_S2MM_LINEBUFFER_DEPTH, XPAR_AXI_VDMA_0_MM2S_GENLOCK_MODE, XPAR_AXI_VDMA_0_S2MM_GENLOCK_MODE, XPAR_AXI_VDMA_0_INCLUDE_INTERNAL_GENLOCK, XPAR_AXI_VDMA_0_S2MM_SOF_ENABLE, XPAR_AXI_VDMA_0_M_AXIS_MM2S_TDATA_WIDTH, XPAR_AXI_VDMA_0_S_AXIS_S2MM_TDATA_WIDTH, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_1, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_5, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_6, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_7, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_9, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_13, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_14, XPAR_AXI_VDMA_0_ENABLE_DEBUG_INFO_15, XPAR_AXI_VDMA_0_ENABLE_DEBUG_ALL, XPAR_AXI_VDMA_0_ADDR_WIDTH } };
WriteSetup VDMA寫(xiě)通道設(shè)置,主要設(shè)置分辨率,延遲參數(shù),開(kāi)啟CircularBuf 模式,使能Gen-Lock。更底層的分析讀者可以順藤摸瓜下去。
表6-6-2-3 WriteSetup /*****************************************************************************/ /** * * This function sets up the write channel * * @param dma_context is the context pointer to the VDMA engine.. * * @return XST_SUCCESS if the setup is successful, XST_FAILURE otherwise. * * @note None. * ******************************************************************************/ static int WriteSetup(vdma_handle *vdma_context) { int Index; u32 Addr; int Status; vdma_context->WriteCfg.VertSizeInput = vdma_context->vsize; vdma_context->WriteCfg.HoriSizeInput = vdma_context->hsize; vdma_context->WriteCfg.Stride = vdma_context->hsize; vdma_context->WriteCfg.FrameDelay = 0; /* This example does not test frame delay */ vdma_context->WriteCfg.EnableCircularBuf = 1; vdma_context->WriteCfg.EnableSync = 1; /* Gen-Lock */ vdma_context->WriteCfg.PointNum = 0; vdma_context->WriteCfg.EnableFrameCounter = 0; /* Endless transfers */ vdma_context->WriteCfg.FixedFrameStoreAddr = 0; /* We are not doing parking */ /* Configure the VDMA is per fixed configuration, This configuration * is being used by majority of customers. Expert users can play around * with this if they have different configurations */ Status = XAxiVdma_DmaConfig(vdma_context->InstancePtr, XAXIVDMA_WRITE, &vdma_context->WriteCfg); if (Status != XST_SUCCESS) { xil_printf( "Write channel config failed %d\r\n", Status); return Status; } /* Initialize buffer addresses * * Use physical addresses */ Addr = vdma_context->buffer_address; /* If Debug mode is enabled write frame is shifted 3 Frames * store ahead to compare read and write frames */ #if DEBUG_MODE Addr = Addr + vdma_context->InstancePtr->MaxNumFrames * \ (vdma_context->WriteCfg.Stride * vdma_context->vsize); #endif for(Index = 0; Index < vdma_context->InstancePtr->MaxNumFrames; Index++) { vdma_context->WriteCfg.FrameStoreStartAddr[Index] = Addr; #if DEBUG_MODE xil_printf("Write Buffer %d address: 0x%x \r\n",Index,Addr); #endif Addr += (vdma_context->hsize * vdma_context->vsize); } /* Set the buffer addresses for transfer in the DMA engine */ Status = XAxiVdma_DmaSetBufferAddr(vdma_context->InstancePtr, XAXIVDMA_WRITE, vdma_context->WriteCfg.FrameStoreStartAddr); if (Status != XST_SUCCESS) { xil_printf("Write channel set buffer address failed %d\r\n", Status); return XST_FAILURE; } /* Clear data buffer */ #if DEBUG_MODE memset((void *)vdma_context->buffer_address, 0, vdma_context->ReadCfg.Stride * vdma_context->ReadCfg.VertSizeInput * vdma_context->InstancePtr->MaxNumFrames); #endif return XST_SUCCESS; }
ReadSetup VDMA讀通道設(shè)置,主要設(shè)置分辨率,這里的延遲參數(shù)1,否則圖像會(huì)有卡頓,開(kāi)啟CircularBuf 模式,使能Gen-Lock。更底層的分析讀者可以順藤摸瓜下去。
表6-6-2-4 ReadSetup /*****************************************************************************/ /** * * This function sets up the read channel * * @param vdma_context is the context pointer to the VDMA engine. * * @return XST_SUCCESS if the setup is successful, XST_FAILURE otherwise. * * @note None. * ******************************************************************************/ static int ReadSetup(vdma_handle *vdma_context) { int Index; u32 Addr; int Status; vdma_context->ReadCfg.VertSizeInput = vdma_context->vsize; vdma_context->ReadCfg.HoriSizeInput = vdma_context->hsize; vdma_context->ReadCfg.Stride = vdma_context->hsize; vdma_context->ReadCfg.FrameDelay = 0; /* This example does not test frame delay */ vdma_context->ReadCfg.EnableCircularBuf = 1; vdma_context->ReadCfg.EnableSync = 1; /* Gen-Lock */ vdma_context->ReadCfg.PointNum = 0; vdma_context->ReadCfg.EnableFrameCounter = 0; /* Endless transfers */ vdma_context->ReadCfg.FixedFrameStoreAddr = 0; /* We are not doing parking */ /* Configure the VDMA is per fixed configuration, This configuration is being used by majority * of customer. Expert users can play around with this if they have different configurations */ Status = XAxiVdma_DmaConfig(vdma_context->InstancePtr, XAXIVDMA_READ, &vdma_context->ReadCfg); if (Status != XST_SUCCESS) { xil_printf("Read channel config failed %d\r\n", Status); return XST_FAILURE; } /* Initialize buffer addresses * * These addresses are physical addresses */ Addr = vdma_context->buffer_address; for(Index = 0; Index < vdma_context->InstancePtr->MaxNumFrames; Index++) { vdma_context->ReadCfg.FrameStoreStartAddr[Index] = Addr; /* Initializing the buffer in case of Debug mode */ #if DEBUG_MODE { u32 i; u8 *src; u32 total_pixel = vdma_context->ReadCfg.Stride * vdma_context->vsize; src = (unsigned char *)Addr; xil_printf("Read Buffer %d address: 0x%x \r\n",Index,Addr); for(i=0;i#endif Addr += vdma_context->hsize * vdma_context->vsize; } /* Set the buffer addresses for transfer in the DMA engine * The buffer addresses are physical addresses */ Status = XAxiVdma_DmaSetBufferAddr(vdma_context->InstancePtr, XAXIVDMA_READ, vdma_context->ReadCfg.FrameStoreStartAddr); if (Status != XST_SUCCESS) { xil_printf( "Read channel set buffer address failed %d\r\n", Status); return XST_FAILURE; } return XST_SUCCESS; } StartTransfer 啟動(dòng)VDMA讀寫(xiě)通道
表6-6-2-5 StartTransfer /*****************************************************************************/ /** * * This function starts the DMA transfers. Since the DMA engine is operating * in circular buffer mode, video frames will be transferred continuously. * * @param InstancePtr points to the DMA engine instance * * @return * - XST_SUCCESS if both read and write start successfully * - XST_FAILURE if one or both directions cannot be started * * @note None. * ******************************************************************************/ static int StartTransfer(XAxiVdma *InstancePtr) { int Status; /* Start the write channel of VDMA */ Status = XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_WRITE); if (Status != XST_SUCCESS) { xil_printf("Start Write transfer failed %d\r\n", Status); return XST_FAILURE; } /* Start the Read channel of VDMA */ Status = XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_READ); if (Status != XST_SUCCESS) { xil_printf("Start read transfer failed %d\r\n", Status); return XST_FAILURE; } return XST_SUCCESS; }測(cè)試結(jié)果
![]()





