--------------------- 已经征得作者同意
作者:同年纪_
来源:优快云
原文:https://blog.youkuaiyun.com/q774318039a/article/details/88377135
版权声明:本文为博主原创文章,转载请附上博文链接!
---------------------
其他介绍部分省略,摘抄部分内容如下:(要了解更多详情,请参考原文链接。)
本人问题:在实际接收中,数据从AD产生经过fifo到达AXI DMA。然后不论是linux还是sdk调试,axidma的接收都会出错。出错时,接收状态寄存器都是同一值,读0x58为0x5011。经过调试,以及搜索发现的有用的帖子摘抄如下
转载帖子摘抄如下(引用是作者原文,未改动)成果待验证。
4. DMA何时才会接收结束?
DMA的读写接口都是axis stream,是强制有tlast的,就是靠tlast信号来判断接收结束的,而不是填写的length值。发送接收值,最后一个tdata会同时伴随一个tlast高电平。接收时,设置接收5000字节,并不是说接收满5000个字节就结束了,而是开启接收后,接收到1个tlast高电平就结束了,也就是说,你可能会只接收到2000个字节,手册中明确说了,当设置的length = 0,或者接收到的数据大于设置的length,就会引起DMAIntErr置1,接收错误。
所以,当设置每次接收5000字节时,tlast就应该每隔<5000字节时插入。我的应用中,DMA前是一个AXIS DATA FIFO,写入是自定义的时序,但接收时还是报错,接收得太多了。为什么呢?因为DMA是自己拉高tready,而fifo的valid也为高就开始计数了,跟FIFO随后的valid 无关(我瞎猜的),DMA读的速度远高于我写入速度,我还没写到tlast,DMA就计数到16383了,DMA就报错,怎么办呢,开启fifo的packet模式。然后DMA接收就ok了。
5. 实际应用
DMA的使用上我遇到的坑,实际上就是地址对齐,tlast的给定上,因为我每次接收完后都会reset下,导致看不出来dma哪儿有问题,但数据总是不对。
DMA的发送一般没有什么问题,有个小技巧,可以用DMA的reset_out引脚去初始化fifo,我有一个应用就2个dma交替发送数据到pl的2个fifo,fifo的存取上老有问题,写768个字节,读768个字节,fifo有时还会有留存数据,导致下一次取数据多了一个,也没细想过问题,DMA发送前先复位一下,顺便把fifo复位一下,就解决这个问题。
另一个应用是pl采集spi的数据,但数据不知道什么时候来,每一包也不知道有多少个字节。我在ps中开了一个定时器,1ms一次,开定时器前,先开启DMA接收,在定时器中断中查询是否接收完成(idle),idle后就把接收地址base_addr加上1个length,再开启下一次传输。而pl端,每隔4096个字节就插一个tlast。又有一个问题,要是有5000个字节呢?剩下的4个怎么办?我又加了个定时器,超过2s,没有数据来,且fifo的data_count不等于0,我就加入aa aa aa数据,并在最后一个aa时拉高一次tlast,表示一次传输完成。在ps端,开启freertos多线程,设置一个send_addr和base_addr初始值相同,当send_addr小于base_addr时,用udp把数据发送出去。又有一个问题,base_addr不能一直往上上加啊,加到大于某个值的时候,且send_addr = base_addr,把数据拷贝到初始值去,重置这2个指针。
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及时刷新。这个刷新速度很慢的,要注意控制刷新的长度。
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) //超时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;
坑有无数,愿你抗住!