日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > ZYNQ
		


本文介紹ZYNQ AXI DMA的簡(jiǎn)單模式使用方法,查詢(xún)模式(poll),不使用中斷,32bit。

1. 有關(guān)DMA的函數(shù)調(diào)用,去參照DMA的官方例程。

所有的外設(shè)都是有ID的,先建立一個(gè)結(jié)構(gòu)體,初始化外設(shè),把外設(shè)的基地址賦值給結(jié)構(gòu)體,對(duì)結(jié)構(gòu)體進(jìn)行賦值就是寫(xiě)相應(yīng)的寄存器,控制DMA工作。所有的外設(shè)都有寄存器手冊(cè),自己去下載,直接看寄存器空間register space就可以了,例如DMA的寄存器手冊(cè)。DMA有兩種方式,我只學(xué)習(xí)了使用簡(jiǎn)單模式,Scatter / Gather Mode可以看其他博主的介紹。下面的代碼是DMA的初始化,非常簡(jiǎn)單,以及開(kāi)啟一次DMA接收,數(shù)據(jù)從axis stream fifo到DMA到DDR3。

Xil_In32 和Xil_Out32直接讀和寫(xiě)寄存器,還有Xil_In8和Xil_Out8,是按byte操作。

void DMA_INIT(void)
{
 //初始化
 int Status;
 XAxiDma_Config *Config;
 XScuGic_Config *IntcConfig;
 Config = XAxiDma_LookupConfig(XPAR_AXIDMA_0_DEVICE_ID);
 Status = XAxiDma_CfgInitialize(&AxiDma, Config);
 //不使能中斷
 XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
 XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
 //初始化DMA,初不初始化無(wú)所謂,跟進(jìn)去就知道本質(zhì)都在寫(xiě)寄存器
 XAxiDma_Reset(&AxiDma); while(!XAxiDma_ResetIsDone(&AxiDma));
 //打印寄存器 
 //0x4040 0000是DMA的基地址,在block design的address中可以看到,后面是偏移地址,
 //window->address editor
 xil_printf("CR is %d\r\n", Xil_In32(0x40400000 + 0x30));
 xil_printf("SR is %d\r\n", Xil_In32(0x40400000 + 0x34));
 xil_printf("LENGTH is %d\r\n", Xil_In32(0x40400000 + 0x58));
 //開(kāi)始傳輸
 Xil_Out32((0x40400000 + 0x30),1); //start dma
 Xil_Out32((0x40400000 + 0x48),base_addr); //set da address
 Xil_Out32((0x40400000 + 0x58),16383); //set length
 //再次打印,看寄存器的狀態(tài)
 xil_printf("CR is %d\r\n", Xil_In32(0x40400000 + 0x30));
 xil_printf("SR is %d\r\n", Xil_In32(0x40400000 + 0x34));
 xil_printf("LENGTH is %d\r\n", Xil_In32(0x40400000 + 0x58));
}

2. Xilinx的例程代碼向來(lái)都是異常復(fù)雜,調(diào)到最后不免都要去看寄存器,翻閱手冊(cè)我們看到DMA簡(jiǎn)單模式如何使用。

要注意,MM2S是說(shuō) memory map to stream,S2MM是stream to memory map,memory map當(dāng)然是指DDR3內(nèi)存了,根據(jù)名稱(chēng)我們就知道MM2S是說(shuō)DMA把數(shù)據(jù)從DDR3搬移到stream FIFO中。從下面手冊(cè)中我們看到了寄存器偏移地址從00h-28h 是mm2s的,30h-58h是s2mm的。30h是CR寄存器,有RS,reset等寄存器位,后面都有說(shuō)明。

3. DMA的使用方法也在手冊(cè)中直接說(shuō)明了。

A. DMA發(fā)送在例程里也有說(shuō)明,xaxidma_example_simple_poll.c,

XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,MAX_PKT_LEN,XAXIDMA_DMA_TO_DEVICE); 注意,XAXIDMA_DMA_TO_DEVICE就是DDR3 到 FIFO,從TxBufferPtr發(fā)送MAX_PKT_LEN個(gè)字節(jié)到FIFO(不一定是FIFO),

判斷DMA發(fā)送的SR寄存器idle是否為1,為1則idle,說(shuō)明發(fā)送結(jié)束。

while (XAxiDma_Busy(&AxiDma,XAXIDMA_DMA_TO_DEVICE))

B.發(fā)送很簡(jiǎn)單,接收有點(diǎn)復(fù)雜,主要是tlast信號(hào)的問(wèn)題,要先了解DMA的接收過(guò)程。

下圖是AXI DMA的配置圖。

  • a.不使能scatter gather和miro DMA,使用簡(jiǎn)單模式,前者可以配置多個(gè)地址,一次開(kāi)啟多次傳輸。width of buffer length register這個(gè)是指接收或發(fā)送的最長(zhǎng)長(zhǎng)度,14位最大就是16384,實(shí)際上在函數(shù)中判斷是16383,該參數(shù)設(shè)置過(guò)長(zhǎng), XAxiDma_SimpleTransfer函數(shù)就會(huì)返回一個(gè)錯(cuò)誤。

  • b.address width是32位,就是對(duì)應(yīng)到DDR3上,一次操作是32bit。read channel 和write channel很不好記,要理解DMA工作時(shí)是獨(dú)立于CPU的,DMA讀寫(xiě)都是指對(duì)DDR3的操作,由于我的應(yīng)用是從底層產(chǎn)生數(shù)據(jù)存到FIFO中,等DMA來(lái)讀取,因此只需要開(kāi)啟寫(xiě)DDR3通道即可。

  • c.max burst size,突發(fā)傳輸長(zhǎng)度。突發(fā)的意思,就是傳送一次地址,取多個(gè)數(shù)據(jù)的長(zhǎng)度。這一點(diǎn)不好理解:當(dāng)CPU通過(guò)AXI lite寫(xiě)DMA寄存器開(kāi)啟DMA接收后,DMA的tready拉高,開(kāi)始傳輸數(shù)據(jù),每接收到突發(fā)長(zhǎng)度后,拉低一次tready,把數(shù)據(jù)寫(xiě)到DDR3中,寫(xiě)完之后再拉高再接收數(shù)據(jù)。

  • d.allow unaligned transfers。地址對(duì)齊,這個(gè)很重要,發(fā)送和接收通道都有,不開(kāi)啟,你發(fā)送和接收都必須要從4byte對(duì)齊的位置開(kāi)始,必須從0x00,0x04,0x08等位置發(fā)送或接收,否則DMA不會(huì)正常工作。

我們?cè)倩厝タ唇邮盏木幊陶f(shuō)明。

  • a.寫(xiě)S2MM_DMACR.RS =1,然后DMASR.Halted = 0 ,DMA表示在運(yùn)行了。

  • b.寫(xiě)目的地地址到DA寄存器中。

  • c.寫(xiě)LENGTH寄存器,必須要最后寫(xiě)。寫(xiě)完這個(gè)寄存器后,DMA就開(kāi)始傳輸了。傳輸完畢后,該寄存器的值是實(shí)際接收到的字節(jié)數(shù)。

  • d.判斷DMASR.idle = 1,后表示接收結(jié)束。

Xil_Out32((0x40400000 + 0x30),1); //start dma
Xil_Out32((0x40400000 + 0x48),base_addr); //set da address
Xil_Out32((0x40400000 + 0x58),16383); //set length

對(duì)照代碼看,是不是很簡(jiǎn)單,看到這里,要是以為你就會(huì)用DMA發(fā)送和接收,扭頭就去寫(xiě)代碼了,小伙子你還是太年輕了。

4. DMA何時(shí)才會(huì)接收結(jié)束?

DMA的讀寫(xiě)接口都是axis stream,是強(qiáng)制有tlast的,就是靠tlast信號(hào)來(lái)判斷接收結(jié)束的,而不是填寫(xiě)的length值。發(fā)送接收值,最后一個(gè)tdata會(huì)同時(shí)伴隨一個(gè)tlast高電平。接收時(shí),設(shè)置接收5000字節(jié),并不是說(shuō)接收滿(mǎn)5000個(gè)字節(jié)就結(jié)束了,而是開(kāi)啟接收后,接收到1個(gè)tlast高電平就結(jié)束了,也就是說(shuō),你可能會(huì)只接收到2000個(gè)字節(jié),手冊(cè)中明確說(shuō)了,當(dāng)設(shè)置的length = 0,或者接收到的數(shù)據(jù)大于設(shè)置的length,就會(huì)引起DMAIntErr置1,接收錯(cuò)誤。

所以,當(dāng)設(shè)置每次接收5000字節(jié)時(shí),tlast就應(yīng)該每隔<5000字節(jié)時(shí)插入。我的應(yīng)用中,DMA前是一個(gè)AXIS DATA FIFO,寫(xiě)入是自定義的時(shí)序,但接收時(shí)還是報(bào)錯(cuò),接收得太多了。為什么呢?因?yàn)镈MA是自己拉高tready,而fifo的valid也為高就開(kāi)始計(jì)數(shù)了,跟FIFO隨后的valid 無(wú)關(guān)(我瞎猜的),DMA讀的速度遠(yuǎn)高于我寫(xiě)入速度,我還沒(méi)寫(xiě)到tlast,DMA就計(jì)數(shù)到16383了,DMA就報(bào)錯(cuò),怎么辦呢,開(kāi)啟fifo的packet模式。然后DMA接收就ok了。

5. 實(shí)際應(yīng)用

DMA的使用上我遇到的坑,實(shí)際上就是地址對(duì)齊,tlast的給定上,因?yàn)槲颐看谓邮胀旰蠖紩?huì)reset下,導(dǎo)致看不出來(lái)dma哪兒有問(wèn)題,但數(shù)據(jù)總是不對(duì)。

DMA的發(fā)送一般沒(méi)有什么問(wèn)題,有個(gè)小技巧,可以用DMA的reset_out引腳去初始化fifo,我有一個(gè)應(yīng)用就2個(gè)dma交替發(fā)送數(shù)據(jù)到pl的2個(gè)fifo,fifo的存取上老有問(wèn)題,寫(xiě)768個(gè)字節(jié),讀768個(gè)字節(jié),fifo有時(shí)還會(huì)有留存數(shù)據(jù),導(dǎo)致下一次取數(shù)據(jù)多了一個(gè),也沒(méi)細(xì)想過(guò)問(wèn)題,DMA發(fā)送前先復(fù)位一下,順便把fifo復(fù)位一下,就解決這個(gè)問(wèn)題。

另一個(gè)應(yīng)用是pl采集spi的數(shù)據(jù),但數(shù)據(jù)不知道什么時(shí)候來(lái),每一包也不知道有多少個(gè)字節(jié)。我在ps中開(kāi)了一個(gè)定時(shí)器,1ms一次,開(kāi)定時(shí)器前,先開(kāi)啟DMA接收,在定時(shí)器中斷中查詢(xún)是否接收完成(idle),idle后就把接收地址base_addr加上1個(gè)length,再開(kāi)啟下一次傳輸。而pl端,每隔4096個(gè)字節(jié)就插一個(gè)tlast。又有一個(gè)問(wèn)題,要是有5000個(gè)字節(jié)呢?剩下的4個(gè)怎么辦?我又加了個(gè)定時(shí)器,超過(guò)2s,沒(méi)有數(shù)據(jù)來(lái),且fifo的data_count不等于0,我就加入aa aa aa數(shù)據(jù),并在最后一個(gè)aa時(shí)拉高一次tlast,表示一次傳輸完成。在ps端,開(kāi)啟freertos多線程,設(shè)置一個(gè)send_addr和base_addr初始值相同,當(dāng)send_addr小于base_addr時(shí),用udp把數(shù)據(jù)發(fā)送出去。又有一個(gè)問(wèn)題,base_addr不能一直往上上加啊,加到大于某個(gè)值的時(shí)候,且send_addr = base_addr,把數(shù)據(jù)拷貝到初始值去,重置這2個(gè)指針。

void Timer_ISR(void *CallBackRef)
{
 lock = 1; if(Xil_In32(0x40400000 + 0x34) & 0x02) //idle = 10
 {
 Xil_DCacheFlushRange(base_addr,Xil_In32(0x40400000 + 0x58));
 // xil_printf("length is %d\r\n",Xil_In32(0x40400000 + 0x58));
 unsigned int len = Xil_In32(0x40400000 + 0x58); if((send_addr == base_addr) &&(send_addr != 0x1000000) && (base_addr >= 0x1F400000))
 {
 memcpy(0x1000000,base_addr,len);// dest,src,len
 base_addr = 0x1000000;
 send_addr = 0x1000000;
 base_addr = base_addr + len;
 Xil_Out32((0x40400000 + 0x48),base_addr); //set da address
 Xil_Out32((0x40400000 + 0x58),8192); //set da address
 } else {
 base_addr = base_addr + len;
 Xil_Out32((0x40400000 + 0x48),base_addr); //set da address
 Xil_Out32((0x40400000 + 0x58),8192); //set da address
 }
 
 }
 lock = 0;
// xil_printf("count is %d\r\n",XGpio_DiscreteRead(&COUNT,1));
// xil_printf("CR is %d\r\n", Xil_In32(0x40400000 + 0x30));
// xil_printf("SR is %d\r\n", Xil_In32(0x40400000 + 0x34));
// xil_printf("LENGTH is %d\r\n", Xil_In32(0x40400000 + 0x58));
}

要注意DMA操作DDR3,CPU是不知道的,要用dcacheflush及時(shí)刷新。這個(gè)刷新速度很慢的,要注意控制刷新的長(zhǎng)度。

while(1)
{ if(lock == 0)
 { if(send_addr < base_addr)
 {
 unsigned int len = base_addr - send_addr; if(len > 16384)
 len = 16384;
 msg_udp_send((UINTPTR)send_addr,len);
 send_addr = send_addr + len;
 }

 }
 vTaskDelay(10);
}
always@(posedge clk) if((!en) ||(delay_over == 32'd400_000_000))
 byte_cnt <= 1'd0; else if(tvalid)
 begin if(byte_cnt == 32'd4095)
 byte_cnt <= 1'd0; else byte_cnt <= byte_cnt + 1'd1; 
 end
 
 assign m_axis_tlast = (tvalid && (byte_cnt == 32'd4095 || i == 8'd13))?1'd1:1'd0;
 
 
 reg [31:0] delay_over = 1'd0;
 assign mode = reg_mode;
 always@(posedge clk) if(!en)
 begin
 i <= 1'd0;
 reg_mode <= 1'd0;
 tvalid <= 1'd0;
 tdata <= 1'd0;
 end else case(i)
 0: if(cs_fall_edge && m_axis_tready&&(fifo_count <= 32'd36700) ) //fifo ready && cs fall
 begin
 i <= i + 1'd1;
 delay_over = 1'd0;
 end
 else if(fifo_count != 1'd0)
 begin if(delay_over == 32'd400_000_000) //超時(shí)2s 
 begin
 delay_over = 1'd0;
 i <= 8'd10;
 end
 else
 delay_over = delay_over + 1'd1;
 end
 1: if(data_cs)
 i <= 1'd0;
 else if(valid)
 begin
 case (data)
 8'h03,8'h0b:
 reg_mode <= 4'd0; 
 8'h05,8'h9f:
 reg_mode <= 4'd1; 
 default:
 reg_mode <= 4'd1; 
 endcase
 i <= i + 1'd1;
 end
 else
 i <= i;
 2:
 begin
 tvalid<= 1'd1;
 tdata <= 8'hee;
 if(m_axis_tready&&(fifo_count <= 32'd36700))
 i <= i + 1'd1;
 else
 i <= 8'd7;
 end
 3:
 begin
 tvalid<= 1'd1;
 tdata <= 8'h55; if(m_axis_tready&&(fifo_count <= 32'd36700))
 i <= i + 1'd1; else i <= 8'd7;
 end
 4:
 begin
 tvalid<= 1'd1;
 tdata <= data; if(m_axis_tready&&(fifo_count <= 32'd36700))
 i <= i + 1'd1; else i <= 8'd7;
 end
 5:
 begin
 tvalid<= 1'd0;
 i <= i + 1'd1;
 end
 6:
 if(data_cs)
 i <= i + 1'd1; else if(valid)
 begin
 tvalid<= 1'd1;
 tdata <= data;
 if(m_axis_tready&&(fifo_count <= 32'd36700))
 i <= 8'd5;
 else
 i <= 8'd7;
 end else i <= i;
 7:
 begin
 tvalid<= 1'd1;
 tdata <= 8'haa;
 i <= i + 1'd1;
 end
 8:
 begin
 tvalid<= 1'd0;
 i <= i + 1'd1;
 if(isr_en)
 isr_start <= 1'd1;
 end
 9:
 begin
 i <= 1'd0;
 isr_start <= 1'd0;
 end
 10,11,12:
 begin
 tvalid<= 1'd1;
 tdata <= 8'haa;
 i <= i + 1'd1;
 end
 13:
 begin
 tvalid<= 1'd0;
 i <= 1'd0;
 end
 default: 
 i <= 1'd0;
 endcase
 
 
 assign m_axis_tvalid =  tvalid;
 assign m_axis_tdata  =  tdata;
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉