Zedboard & Zynq 图像采集 视频开发 (三) AXI4总线读写DDR .

上一篇讲到了通过Zynq内部FPGA采集ov7725摄像头的图像数据,并将RAW8视频数据通过双线性插值法恢复为RGB888视频格式,这一篇的内容就是将RBG888视频数据通过PS的HP端口传送到DDR3进行视频缓存,然后再读出,进行VGA视频显示

AMBA协议简介

AMBA 协议是用于连接和管理片上系统 (SoC) 中功能模块的开放标准和片上互连规范。
它有助于首次开发带有大量控制器和外设的多处理器设计。
AMBA 通过使用 AXI、AHB、APB 和 ATB 的规范对 SoC 模块的共同主干进行定义,这有助于设计的重复使用。
AMBA 4 是最新增添到 AMBA 系列中的规范,增加了三个新接口协议:AXI4 有助于最大化性能和能效;AXI4-Lite 和 AXI4-Stream 是 FPGA 中实现的理想选择。
AMBA 4 规范在 AMBA 3 规范的基础上另外新增了三个接口协议。
AXI4
AXI4 协议是对 AXI3 的更新,在用于多个主接口时,可提高互连的性能和利用率。它包括以下增强功能:
对于突发长度,最多支持 256 位
发送服务质量信号
支持多区域接口
AXI4-Lite
AXI4-Lite 是 AXI4 协议的子协议,适用于与组件中更简单且更小的控件寄存器式的接口通信。AXI4-Lite 接口的主要功能如下:
所有事务的突发长度均为 1
所有数据存取的大小均与数据总线的宽度相同
不支持独占访问
AXI4-Stream
AXI4-Stream 协议可用于从主接口到辅助接口的单向数据传输,可显著降低信号路由速率。该协议的主要功能如下:
使用同一组共享线支持单数据流和多数据流
在同一互连内支持多个数据宽度
FPGA 中实现的理想选择

ZYNQ SOC
Zynq内部 PL与PS数据交互,主要使用了AXI4 AXI4-Lite总线协议,AXI4主要进行PL,PS之间批量数据交互,AXI4-Lite总线主要用于控制协议以及小量数据交互。由于FPGA采集到的摄像头视频数据要传送到PS部分的DDR,数据量较大,则可以直接通过AXI4总线将数据传送到PS部分,Xilinx的开发工具提供DMA,VDMA等IP Core可以很方便的完成这类任务,而且功能完善,缺点是如果发生问题,调试不太方便,毕竟对于用户来说就是个黑盒子,本设计没有使用IP,而是写了一个简单的S2MM和MM2S的HDL

BLOCK DESIGN 

PS部分用到了一个GP0口,一个HP0口,一个HP2口。GP口用于控制VDMA,HP口用于视频数据传输。看到这,也许有人会问,
1.为什么要用两个HP口,用一个不也行吗
用一个HP口确实可以,这里面只是为了区分清楚数据流向,所以用了两个HP口,一个只用于写,一个只用于读
2.为什么不用HP0和HP1,而是用HP0和HP2
还是那句话,用HP0和HP1确实也可以,但是在PS内部,HP0和HP1在通往DDR的路上是公用一个端口,HP2和HP3也是公用一个端口,所以也是为了区分清楚数据流向(或者可以说笔者有洁癖)所以才分开使用
3.为什么不直接通过HP端口读写,还要加入interconnect做什么
由于ps部分的HP口是AXI3协议,直接读写也没问题,但是为了规范,则通过interconnect将AXI3转换为AXI4
4.为什么使用axi_apb_bridge
axi_lite总线读写稍微有点儿小麻烦,所以转换成比较简单的apb总线

S2MM VDMA设计

block design中axi_interconnect_1 slave端口设置为WRITE ONLY 模块axi4_s2mm_video_dma通过这个端口对DDR进行写操作。
写操作流程图:

写操作burst时序图:

axi4_s2mm_video_dma模块就是按照时序图来实现的,下面对此模块进行一下讲述

模块端口如下:

下面是参数部分:

  1. parameter [31:0]  APB_BASE_ADDR  = 32'h70000000,  
  2. parameter [31:0]  DMA_DEST_ADDR0 = 32'h1c000000,  
  3. parameter [31:0]  DMA_DEST_ADDR1 = 32'h1c200000,  
  4. parameter [31:0]  DMA_DEST_ADDR2 = 32'h1c400000,  
  5. parameter [8:0]   C_DATA_WIDTH = 9'd32,  
  6. parameter [12:0]  STRIDE       = 13'd1920,  
  7. parameter [12:0]  WIDTH        = 13'd1920,  
  8. parameter [12:0]  HEIGHT       = 13'd1080  
   parameter [31:0]  APB_BASE_ADDR  = 32'h70000000,
   parameter [31:0]  DMA_DEST_ADDR0 = 32'h1c000000,
   parameter [31:0]  DMA_DEST_ADDR1 = 32'h1c200000,
   parameter [31:0]  DMA_DEST_ADDR2 = 32'h1c400000,
   parameter [8:0]   C_DATA_WIDTH = 9'd32,
   parameter [12:0]  STRIDE       = 13'd1920,
   parameter [12:0]  WIDTH        = 13'd1920,
   parameter [12:0]  HEIGHT       = 13'd1080
APB_BASE_ADDR : apb slave地址
DMA_DEST_ADDR0 : 传送目标地址0
DMA_DEST_ADDR1 : 传送目标地址1
DMA_DEST_ADDR2 : 传送目标地址2

端口包括 AXI4总线信号,APB总线信号,video信号
APB总线的使用:通过简单的修改代码,可以让ARM控制VDMA进行一帧图像的传输,传输完成以后自动停止,并在IRQ_F2P_pin引脚产生一个中断信号,此信号可以连接到PS的中断控制器,PS在接收到中断以后,向中断寄存器写1就可清除中断标志。
但Zedboard ov7725工程中并没有使用单帧传输模式,而是连续传输,如
  1. reg [1:0] DMA_WRITE_STATUS;  
  2. assign IRQ_F2P_pin = DMA_WRITE_STATUS[1];  
  3. // write dma busy status configuration  
  4. always @(posedge M_APB_PCLK_pin)  
  5. begin  
  6.    if (!M_APB_PRESETN_pin) begin  
  7.       DMA_WRITE_STATUS[0] <= 1'b0;  
  8.    end  
  9.    else if(dma_frame_write_end) begin  
  10.       DMA_WRITE_STATUS[0] <= 1'b0;  
  11.    end  
  12.    else if(M_APB_PSEL_pin && M_APB_PWRITE_pin && M_APB_PENABLE_pin) begin  
  13.       if (M_APB_PADDR_pin==APB_BASE_ADDR && M_APB_PWDATA_pin[0] && !DMA_WRITE_STATUS[0]) begin  
  14.          DMA_WRITE_STATUS[0] <= 1'b1;  
  15.       end  
  16.    end  
  17. end  
reg [1:0] DMA_WRITE_STATUS;
assign IRQ_F2P_pin = DMA_WRITE_STATUS[1];
// write dma busy status configuration
always @(posedge M_APB_PCLK_pin)
begin
   if (!M_APB_PRESETN_pin) begin
      DMA_WRITE_STATUS[0] <= 1'b0;
   end
   else if(dma_frame_write_end) begin
      DMA_WRITE_STATUS[0] <= 1'b0;
   end
   else if(M_APB_PSEL_pin && M_APB_PWRITE_pin && M_APB_PENABLE_pin) begin
      if (M_APB_PADDR_pin==APB_BASE_ADDR && M_APB_PWDATA_pin[0] && !DMA_WRITE_STATUS[0]) begin
         DMA_WRITE_STATUS[0] <= 1'b1;
      end
   end
end


  1. wire dma_write_start;  
  2. reg process_start_pre;  
  3. always @(posedge v_video_clk)     
  4. begin  
  5.     if(!axi_rstn)  
  6.     begin  
  7.         v_video_data_r <= 24'd0;  
  8.         v_timing_vsync_r <= 1'b0;  
  9.         v_timing_hsync_r <= 1'b0;  
  10.         v_timing_active_video_r <= 1'b0;  
  11.         process_start <= 1'b0;  
  12.         //process_start_pre <= 1'b0;  
  13.         process_start_pre <= 1'b1;  
  14.     end  
  15.     else  
  16.     begin  
  17.         v_video_data_r <= v_video_data_i;  
  18.         v_timing_vsync_r <= v_timing_vsync_i;  
  19.         v_timing_hsync_r <= v_timing_hsync_i;  
  20.         v_timing_active_video_r <= v_timing_active_video_i;  
  21.         if(dma_write_start)  
  22.             process_start_pre <= 1'b1;  
  23.         if((~v_timing_vsync_r) && v_timing_vsync_i && process_start_pre)begin  
  24.             process_start <= 1'b1;  
  25.             //process_start_pre <= 1'b0;  
  26.         end  
  27.         else if(dma_frame_write_end)  
  28.             process_start <= 1'b0;  
  29.     end  
  30. end  
wire dma_write_start;
reg process_start_pre;
always @(posedge v_video_clk)   
begin
	if(!axi_rstn)
	begin
		v_video_data_r <= 24'd0;
		v_timing_vsync_r <= 1'b0;
		v_timing_hsync_r <= 1'b0;
		v_timing_active_video_r <= 1'b0;
		process_start <= 1'b0;
		//process_start_pre <= 1'b0;
		process_start_pre <= 1'b1;
	end
	else
	begin
		v_video_data_r <= v_video_data_i;
		v_timing_vsync_r <= v_timing_vsync_i;
		v_timing_hsync_r <= v_timing_hsync_i;
		v_timing_active_video_r <= v_timing_active_video_i;
		if(dma_write_start)
			process_start_pre <= 1'b1;
		if((~v_timing_vsync_r) && v_timing_vsync_i && process_start_pre)begin
			process_start <= 1'b1;
			//process_start_pre <= 1'b0;
		end
		else if(dma_frame_write_end)
			process_start <= 1'b0;
	end
end
  1. cross_clk cross_clk1  
  2.    (  
  3.       .clkin   (axi_clk),  
  4.       .din     (frame_last),  
  5.       .clkout  (v_video_clk),  
  6.       .dout    (dma_frame_write_end)  
  7.    );  
cross_clk cross_clk1
   (
      .clkin   (axi_clk),
      .din     (frame_last),
      .clkout  (v_video_clk),
      .dout    (dma_frame_write_end)
   );

  1. cross_clk cross_clk2  
  2.    (  
  3.       .clkin   (M_APB_PCLK_pin),  
  4.       .din     (DMA_WRITE_STATUS[0]),  
  5.       .clkout  (v_video_clk),  
  6.       .dout    (dma_write_start)  
  7.    );  
cross_clk cross_clk2
   (
      .clkin   (M_APB_PCLK_pin),
      .din     (DMA_WRITE_STATUS[0]),
      .clkout  (v_video_clk),
      .dout    (dma_write_start)
   );

如果想使用单帧模式,可以把上面代码中注释的部分process_start_pre信号稍加修改即可。

在VIDEO信号中,真正起作用的只有3个信号
v_timing_vsync_i 此信号的上升沿代表一帧数据开始,这个信号可以用来做AXI写操作开始信号
v_timing_active_video_i 视频数据有效信号,只有有效的视频信号才会被传输
v_video_data_i 24bit RGB888视频信号

在视频信号与AXI信号之间加入一个位宽32bit,深度128的fifo进行数据缓存,视频数据写fifo,AXI总线读fifo。在fifo的应用上面,有一点是需要注意的,那就是rd_en读使能信号,rd_en拉高,并且在读时钟的下一个上升沿fifo内的数据才会出现在dout信号线上。根据AXI协议规范,为了防止产生死锁,WVALID不能等待WREADY,而WREADY可以等待WVALID,这样便会在写操作的时候出现一个问题,就是WVALID拉高,但是此时WREADY不一定是拉高的,而WREADY又控制着fifo rd_en何时拉高,笔者的解决办法就是,在WVALID拉高以后,rd_en拉高一个时钟周期然后拉低,这样在下一个读时钟的上升沿,fifo内的数据就出现在dout信号线上了,然后等待着WREADY,具体实现可以看我的代码,信号stall就是做这个用处的。

还有一个需要注意的地方,由于这个是个人写的简单VDMA,burst长度是固定的为16,所以传输数据的长度必须是16的整数倍,而且传输位宽是32bit。

别的就不说了,就是用状态机来实现AXI时序罢了,直接上代码
  • 8
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值