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

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

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

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

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

S2MM 幀存起始地址(0xAC~0xE8)
有最多32個寄存器用于存放幀存起始地址,其分別存在于兩個寄存器bank上:bank0和bank1,每個bank上有16個寄存器。這兩個bank上有相同的起始偏移地址(0x5C),選擇這兩個bank可以通過S2MM_REG_INDEX的值進(jìn)行選擇。假如想訪問第1個寄存器,則給S2MM_REG_INDEX賦值為0,并設(shè)定偏移地址為0x5C;如果想訪問第17個寄存器,需要將MM2S_REG_INDEX設(shè)為1,并設(shè)定初始偏移地址為0x5C。
S2MM_FRMDLY_STRIDE S2MM幀延遲和跨度(A8h)
該寄存器有兩個作用,第一是bit24~bit28指定幀延遲,僅用于Genlock從模式,指定從接口比主接口至少要延遲多少個幀;第二是低16位指定水平方向的跨度,同樣以字節(jié)為單位。所謂跨度是指每兩行第一個像素之間間隔的數(shù)據(jù)個數(shù),具體請參考22.3.2小節(jié),VDMA幀存格式。
S2MM_HSIZE S2MM水平方向尺寸(A4h)
該寄存器的低16位用于指定每一行有多少字節(jié)的數(shù)據(jù)需要傳輸。例如顯示分辨率為640480,每個像素4個字節(jié)(RGB+Alpha),該值應(yīng)該設(shè)定為6404。
S2MM_VSIZE S2MM垂直方向尺寸(A0h)
該寄存器有兩個作用,第一是用低13位指定總共有多少行;第二是啟動S2MM的傳輸。當(dāng)S2MM_VDMACR.RS=1,對該寄存器的寫操作會將所有設(shè)定參數(shù)傳遞給VDMA內(nèi)部寄存器模塊,用于VDMA控制。對某個通道進(jìn)行配置時,必須在最后一步設(shè)置該寄存器。
幀同步選項(xiàng)
VDMA支持以下三種幀同步源:
? 基于AXI4-Stream的幀同步(使用tuser(0)信號)
? 讀通道使用m_axis_mm2s_tuser(0)作為幀起始信號
? 寫通道使用s_axis_s2mm_tuser(0)作為幀起始信號
? S2MM幀同步(s2mm_fsync)
? MM2S幀同步(mm2s_fsync)
Genlock同步機(jī)制
什么是Genlock?
Genlock,同步鎖相,可以使一套或多套系統(tǒng)與同一同步源實(shí)現(xiàn)同步。能夠使視頻的刷新和外部視頻源保持一致。當(dāng)提供了一個適當(dāng)?shù)男盘柡?,系統(tǒng)就會把它的顯示刷新率和這個信號進(jìn)行鎖定 。
在許多視頻應(yīng)用中,輸入端產(chǎn)生數(shù)據(jù)的速率往往不同于輸出端數(shù)據(jù)速率,為了避免由速率不一致導(dǎo)致的潛在錯誤,幀緩沖的使用是很有必要的。幀緩沖機(jī)制開辟多個緩沖頁,用于保存數(shù)據(jù),輸入和輸出端分別操作不同的幀存,從而避免了沖突。
VDMA的鎖相同步特性正是用于阻止讀、寫通道同時操作同一個幀存。VDMA的每個通道都可以選擇自己的操作類型(同步鎖相主/從或者動態(tài)同步鎖相主/從),利用該特性,禁止主從接口同時訪問同一緩存,從而保持同步。
VDMA支持四種模式的鎖相同步,分別為:
? Genlock Master(鎖相同步主端)
? Genlock Slave(鎖相同步從端)
? Dynamic Genlock Master(動態(tài)鎖相同步主端)
? Dynamic Genlock Slave(動態(tài)鎖相同步從端)
Genlock Master
讀通道(MM2S):當(dāng)配置為Genlock Master時,該通道不會跳過或者重復(fù)任一幀數(shù)據(jù),并把當(dāng)前幀的編號輸出在mm2s_frame_ptr_out端口。通道不會檢測mm2s_frame_ptr_in端口提供的幀編號。Genlock Slave通道應(yīng)跟隨Genlock Master通道變化,但有一定的延遲。延遲大小預(yù)定義在寄存器中(*frmdly_stride[28:24])。
寫通道(S2MM):當(dāng)配置為Genlock Master時,該通道不會跳過或者重復(fù)任一幀數(shù)據(jù),并把當(dāng)前幀的編號輸出到s2mm_frame_ptr_out端口。通道不會檢測s2mm_frame_ptr_in端口提供的幀編號。Genlock Slave通道應(yīng)跟隨Genlock Master通道變化,但有一定的延遲。延遲大小預(yù)定義在寄存器中(*frmdly_stride[28:24])。
Genlock Slave
讀通道(MM2S):當(dāng)配置為Genlock Slave時,該通道會通過跳過或者重復(fù)一些幀的方式,嘗試與Genlock Master同步。通道會對mm2s_frame_ptr_in端口進(jìn)行采樣,獲取Genlock Master的幀編號。為了實(shí)現(xiàn)狀態(tài)反饋,通道會把當(dāng)前幀的編號輸出到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中同時使能讀、寫通道,該位默認(rèn)置位。當(dāng)GenlockSRC=1時,VDMA默認(rèn)支持內(nèi)部同步鎖相總線。這樣一來就沒有必要在外部對幀指針端口(frame_ptr_out和_frame_ptr_in)進(jìn)行連接了。
? 根據(jù)主從通道的幀率,使用mm2s_frmdly_stride[28:24]設(shè)定合適的延遲時間。
寫通道(S2MM):當(dāng)配置為Genlock Slave時,該通道會通過跳過或者重復(fù)一些幀的方式,嘗試與Genlock Master同步。通道會對s2mm_frame_ptr_in端口進(jìn)行采樣,獲取Genlock Master的幀編號。為了實(shí)現(xiàn)狀態(tài)反饋,通道會把當(dāng)前幀的編號輸出到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中同時使能讀、寫通道,該位默認(rèn)置位。當(dāng)GenlockSRC=1時,VDMA默認(rèn)支持內(nèi)部同步鎖相總線。這樣一來就沒有必要在外部對幀指針端口(frame_ptr_out和_frame_ptr_in)進(jìn)行連接了。
? 根據(jù)主從通道的幀率,使用mm2s_frmdly_stride[28:24]設(shè)定合適的延遲時間。
Dynamic Genlock Master
動態(tài)Genlock Master與Genlock Master的區(qū)別在于,主通道會跳過從通道正在操作的幀。舉例而言,對于三幀存而言,動態(tài)Genlock Master會按照0,1,2,0,1,2的順序循環(huán)使用幀存,一旦檢測到Master即將操作Slave正在操作的幀,就會跳過該幀繼續(xù)循環(huán)。因此,如果Slave通道一直在操作幀存1,那么Master通道就會在幀0和幀2之間來回切換。
Dynamic Genlock Slave
Dynamic Genlock Slave通道會操作Dynamic Genlock Master通道上一周期操作的幀。
下圖描述了一種簡單的Genlock操作時序。在這個示例中,S2MM通道是Genlock Master,MM2S通道是Genlock Slave,并且寫通道幀率高于讀通道幀率。
由于讀通道幀率慢于寫通道,所以讀通道僅處理幀2和幀0,跳過幀1不做處理。
軟件控制流程
以下步驟是最簡單的VDMA控制初始化操作。
? 寫VDMACR寄存器,將VDMACR.RS設(shè)為1,啟動VDMA通道。
? 設(shè)定有效的幀緩存起始地址。
? 設(shè)定幀延遲(僅針對Genlock從模式)以及跨度到FRMDLY_STRIDE寄存器。
? 設(shè)定水平方向字節(jié)數(shù)到HSIZE寄存器。
? 設(shè)定豎直方向行數(shù)到VSIZE寄存器。啟動通道的數(shù)據(jù)傳輸。
在VDMA運(yùn)行過程中,可以動態(tài)的進(jìn)行顯示參數(shù)配置,但是需要注意的是,想要使參數(shù)生效,必須在設(shè)置的最后一步,對VSIZE寄存器進(jìn)行寫操作。
最后,給出一段通過VDMA對DDR讀寫傳輸?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個字節(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)行 - 啟動VDMA操作,在運(yùn)行VDMA時,其狀態(tài)寄存器中的停止位賦值為0 第一位:循環(huán)模式 -通過連續(xù)循環(huán)幀緩沖 XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x0, 0x03); XAxiVdma_WriteReg(XPAR_AXIVDMA_0_BASEADDR, 0x50, 480);//480 /*********** 寫入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)相比非常類似。實(shí)際上他們都是屬于DMA系統(tǒng),只是VDMA在配置完成后,可以無需依賴CPU可以獨(dú)立運(yùn)行,有點(diǎn)類似顯卡的功能了。
構(gòu)BLOCK模塊化設(shè)計(jì)方案圖
PS部分
main函數(shù)
本課程提供了二種方式啟動VDMA,第一種是通過庫函數(shù)版本,第二種是通過寄存器版本。寄存器版本主要是驗(yàn)證我們對VDMA的寄存器掌握情況。庫函數(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寫數(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 庫函數(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寫通道設(shè)置,主要設(shè)置分辨率,延遲參數(shù),開啟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,否則圖像會有卡頓,開啟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 啟動VDMA讀寫通道
表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; }測試結(jié)果
![]()





