上一篇讲到了通过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 中实现的理想选择
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模块就是按照时序图来实现的,下面对此模块进行一下讲述
模块端口如下:
下面是参数部分:
- 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工程中并没有使用单帧传输模式,而是连续传输,如
- 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
- 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
- cross_clk cross_clk1
- (
- .clkin (axi_clk),
- .din (frame_last),
- .clkout (v_video_clk),
- .dout (dma_frame_write_end)
- );
- 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时序罢了,直接上代码
- /*-----------------------------------------------------------------------
- CONFIDENTIAL IN CONFIDENCE
- This confidential and proprietary software may be only used as authorized
- by a licensing agreement from EEPROM .
- In the event of publication, the following notice is applicable:
- Copyright (C) 2013-20xx EEPROM Corporation
- The entire notice above must be reproduced on all authorized copies.
- Author : EEPROM
- Technology blogs : http://blog.csdn.net/zhangyu_eeprom
- Email Address : [email protected]
- Filename : axi4_s2mm_video_dma.v
- Data : 2014-05-20
- Description : axi4_s2mm_video_dma.
- Modification History :
- Data By Version Change Description
- =========================================================================
- -------------------------------------------------------------------------
- ------ ------ ------| ------| /-------\
- | | | | | | / \ /-\ /-\
- |------ |------ |-----| |-----| / \ / \ / \
- | | | | \ \ / / \_/ \
- |------ |------ | | \ \ / / \
- | | \ \-------/
- -----------------------------------------------------------------------*/
- module axi4_s2mm_video_dma #(
- 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
- )
- (
- input axi_clk,
- input axi_rstn,
- output IRQ_F2P_pin,
- input M_APB_PCLK_pin,
- input M_APB_PRESETN_pin,
- input [31:0] M_APB_PADDR_pin,
- input M_APB_PSEL_pin,
- input M_APB_PENABLE_pin,
- input M_APB_PWRITE_pin,
- input [31:0] M_APB_PWDATA_pin,
- output M_APB_PREADY_pin,
- output [31:0] M_APB_PRDATA_pin,
- output M_APB_PSLVERR_pin,
- input v_video_clk,
- input v_timing_hsync_i,
- input v_timing_vsync_i,
- input v_timing_hblank_i,
- input v_timing_vblank_i,
- input v_timing_active_video_i,
- input [23:0] v_video_data_i,
- input S_AXI_AWREADY_pin,
- input S_AXI_BVALID_pin,
- input S_AXI_WREADY_pin,
- input [1:0] S_AXI_BRESP_pin,
- input [5:0] S_AXI_BID_pin,
- output reg S_AXI_AWVALID_pin,
- output S_AXI_BREADY_pin,
- output S_AXI_WLAST_pin,
- output reg S_AXI_WVALID_pin,
- output [1:0] S_AXI_AWBURST_pin,
- output [1:0] S_AXI_AWLOCK_pin,
- output [2:0] S_AXI_AWSIZE_pin,
- output [2:0] S_AXI_AWPROT_pin,
- output reg [31:0] S_AXI_AWADDR_pin,
- output reg [C_DATA_WIDTH-1:0] S_AXI_WDATA_pin,
- output [3:0] S_AXI_AWCACHE_pin,
- output [3:0] S_AXI_AWLEN_pin,
- output [3:0] S_AXI_AWQOS_pin,
- output [7:0] S_AXI_WSTRB_pin,
- output [5:0] S_AXI_AWID_pin,
- output [5:0] S_AXI_WID_pin
- );
- /******************************************/
- reg [23:0] v_video_data_r;
- reg v_timing_vsync_r;
- reg v_timing_active_video_r;
- reg process_start; // first vsync rising
- reg [1:0]frame_num;
- wire line_last;
- wire frame_last;
- reg frame_last_r;
- reg [12:0]hcount;
- reg [12:0]vcount;
- reg [4:0]axi_write_num;
- reg axi_write_ok;
- reg axi_addr_ok;
- reg [31:0]prev_line_start;
- wire stall;
- reg [4:0]read_fifo_num;
- reg fifo_rd_ok;
- wire dma_frame_write_end;
- wire dma_line_write_end;
- reg v_timing_hsync_r;
- reg [12:0]write_fifo_num;
- /************fifo**************/
- reg fifo_wr_en;
- wire fifo_rd_en;
- wire fifo_full;
- wire fifo_empty;
- wire fifo_valid;
- wire [9:0] fifo_rd_data_count;
- wire [9:0] fifo_wr_data_count;
- wire [31:0] fifo_dout;
- /******************************/
- /******************************/
- /*
- * APB bus
- */
- 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
- // write dma interrupt configuration, write '1' to clear interrupt
- always @(posedge M_APB_PCLK_pin)
- begin
- if (!M_APB_PRESETN_pin) begin
- DMA_WRITE_STATUS[1] <= 1'b0;
- end
- else if(dma_frame_write_end) begin
- DMA_WRITE_STATUS[1] <= 1'b1;
- 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[1] && DMA_WRITE_STATUS[1]) begin
- DMA_WRITE_STATUS[1] <= 1'b0;
- end
- end
- end
- //------------------------------------------------------
- // APB output
- //------------------------------------------------------
- assign M_APB_PRDATA_pin = {28'h0000,frame_num,DMA_WRITE_STATUS};
- assign M_APB_PREADY_pin = 1'b1;
- assign M_APB_PSLVERR_pin = 1'b0;
- /*******************************************/
- 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
- cross_clk cross_clk1
- (
- .clkin (axi_clk),
- .din (frame_last),
- .clkout (v_video_clk),
- .dout (dma_frame_write_end)
- );
- cross_clk cross_clk2
- (
- .clkin (M_APB_PCLK_pin),
- .din (DMA_WRITE_STATUS[0]),
- .clkout (v_video_clk),
- .dout (dma_write_start)
- );
- always @(posedge v_video_clk) begin
- if(!axi_rstn)
- write_fifo_num <= 13'h0;
- else if(fifo_wr_en && (write_fifo_num != (WIDTH - 1)))
- write_fifo_num <= write_fifo_num + 1;
- else if(fifo_wr_en && (write_fifo_num == (WIDTH - 1)))
- write_fifo_num <= 13'h0;
- end
- always @(posedge v_video_clk)
- begin
- if(!axi_rstn)begin
- fifo_wr_en = 1'b0;
- end
- else begin
- if((fifo_wr_en == 1'b0) && process_start && v_timing_active_video_i && (write_fifo_num != (WIDTH - 1)))
- fifo_wr_en = 1'b1;
- else if(write_fifo_num == (WIDTH - 1))
- fifo_wr_en = 1'b0;
- else if(!process_start)
- fifo_wr_en = 1'b0;
- end
- end
- fifo_axi32bit u_fifo_axi32bit (
- .wr_clk(v_video_clk),
- .rd_clk(axi_clk),
- .din({v_video_data_r,8'h00}),
- .wr_en(fifo_wr_en),
- .rd_en(fifo_rd_en),
- .dout(fifo_dout),
- .full(fifo_full),
- .empty(fifo_empty),
- .valid(fifo_valid),
- .rd_data_count(fifo_rd_data_count),
- .wr_data_count(fifo_wr_data_count)
- );
- //------------------------------------------------------
- // AXI bus
- //------------------------------------------------------
- assign S_AXI_AWID_pin = 6'b000000;
- assign S_AXI_AWLEN_pin = 4'hf; //burst length: 16
- assign S_AXI_AWSIZE_pin = 3'b010; //size: 4byte
- assign S_AXI_AWBURST_pin = 2'b01; //incr
- assign S_AXI_AWLOCK_pin = 2'b00;
- assign S_AXI_AWCACHE_pin = 4'b0011; ////
- assign S_AXI_AWPROT_pin = 3'b000;
- assign S_AXI_AWQOS_pin = 4'b0000;
- assign S_AXI_BREADY_pin = 1'b1;
- assign S_AXI_WSTRB_pin = 8'b11111111;
- assign S_AXI_WID_pin = 0;
- parameter IDLE = 4'b0000;
- parameter CHECK_FIFO = 4'b0001;
- parameter AXI_WRITE = 4'b0011;
- parameter NEXT_WRITE = 4'b0111;
- parameter NEXT_LINE = 4'b1111;
- parameter NEXT_FRAME = 4'b1110;
- parameter FINISH = 4'b1100;
- reg [3:0]w_cs;
- reg [3:0]w_ns;
- always @(posedge axi_clk)
- begin
- if(!axi_rstn)
- begin
- w_cs <= IDLE;
- end
- else
- begin
- w_cs <= w_ns;
- end
- end
- always @(*) begin
- case(w_cs)
- IDLE: begin
- if(process_start)
- w_ns = CHECK_FIFO;
- else
- w_ns = w_cs;
- end
- CHECK_FIFO: begin
- if(fifo_rd_data_count >= 10'h10)
- w_ns = AXI_WRITE;
- else
- w_ns = w_cs;
- end
- AXI_WRITE: begin
- if(axi_addr_ok && axi_write_ok && ~line_last)
- w_ns = NEXT_WRITE;
- else if(axi_addr_ok && axi_write_ok && line_last && ~frame_last)
- w_ns = NEXT_LINE;
- else if(axi_addr_ok && axi_write_ok && line_last && frame_last)
- w_ns = NEXT_FRAME;
- else
- w_ns = w_cs;
- end
- NEXT_WRITE: begin
- w_ns = CHECK_FIFO;
- end
- NEXT_LINE: begin
- w_ns = CHECK_FIFO;
- end
- NEXT_FRAME: begin // pro
- if(frame_num < 32'h11)
- w_ns = CHECK_FIFO;
- else
- w_ns = FINISH;
- end
- FINISH: begin
- w_ns = FINISH;
- end
- default: begin
- w_ns = w_cs;
- end
- endcase
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn) begin
- read_fifo_num <= 5'h0;
- fifo_rd_ok <= 1'b0;
- end
- else if(w_ns != AXI_WRITE) begin
- read_fifo_num <= 5'h0;
- fifo_rd_ok <= 1'b0;
- end
- else if(fifo_rd_en && (read_fifo_num != 5'hf)) begin
- read_fifo_num <= read_fifo_num + 1;
- fifo_rd_ok <= 1'b0;
- end
- else if(fifo_rd_en && (read_fifo_num == 5'hf)) begin
- read_fifo_num <= 5'h0;
- fifo_rd_ok <= 1'b1;
- end
- end
- assign fifo_rd_en = (w_ns == AXI_WRITE && ~stall && !fifo_rd_ok)? 1'b1 : 1'b0;
- assign stall = (w_cs == AXI_WRITE && ~axi_write_ok && S_AXI_WVALID_pin && ~S_AXI_WREADY_pin)? 1'b1 : 1'b0;
- always @(posedge axi_clk) begin
- if(!axi_rstn) begin
- S_AXI_AWVALID_pin <= 1'b0;
- axi_addr_ok <= 1'b0;
- end
- else if(w_ns == AXI_WRITE && w_cs != AXI_WRITE) begin
- S_AXI_AWVALID_pin <= 1'b1;
- axi_addr_ok <= 1'b0;
- end
- else if(S_AXI_AWREADY_pin) begin
- S_AXI_AWVALID_pin <= 1'b0;
- axi_addr_ok <= 1'b1;
- end
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- S_AXI_WVALID_pin <= 1'b0;
- else if((S_AXI_WREADY_pin & S_AXI_WVALID_pin) && (axi_write_num == 5'hf))
- S_AXI_WVALID_pin <= 1'b0;
- else if(w_cs == AXI_WRITE && ~axi_write_ok)
- S_AXI_WVALID_pin <= 1'b1;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- S_AXI_WDATA_pin <= 32'h0;
- else if(!stall)
- S_AXI_WDATA_pin <= {fifo_dout[7:0],fifo_dout[15:8],fifo_dout[23:16],fifo_dout[31:24]};//{8'h33,8'h22,8'h11,8'h00};
- end
- wire [31:0] dma_tran_addr;
- assign dma_tran_addr = (frame_num == 2'b00)? DMA_DEST_ADDR0 : (frame_num == 2'b01)? DMA_DEST_ADDR1 : DMA_DEST_ADDR2;
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- S_AXI_AWADDR_pin <= 32'h0;
- else if(w_cs == IDLE && w_ns == CHECK_FIFO)
- S_AXI_AWADDR_pin <= dma_tran_addr;
- else if(w_cs == NEXT_WRITE && w_ns == CHECK_FIFO)
- S_AXI_AWADDR_pin <= S_AXI_AWADDR_pin + 8'h40; //16 * 1byte
- else if(w_cs == NEXT_LINE && w_ns == CHECK_FIFO)
- S_AXI_AWADDR_pin <= prev_line_start + STRIDE;
- else if(w_cs == NEXT_FRAME && w_ns == CHECK_FIFO)
- S_AXI_AWADDR_pin <= dma_tran_addr;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- prev_line_start <= 32'h0;
- else if(w_cs == IDLE && w_ns == CHECK_FIFO)
- prev_line_start <= dma_tran_addr;
- else if(w_cs == NEXT_LINE && w_ns == CHECK_FIFO)
- prev_line_start <= prev_line_start + STRIDE;
- else if(w_cs == NEXT_FRAME && w_ns == CHECK_FIFO)
- prev_line_start <= dma_tran_addr;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn) begin
- axi_write_num <= 5'h0;
- axi_write_ok <= 1'b0;
- end
- else if(w_ns != AXI_WRITE) begin
- axi_write_num <= 5'h0;
- axi_write_ok <= 1'b0;
- end
- else if((S_AXI_WREADY_pin & S_AXI_WVALID_pin) && (axi_write_num != 5'hf)) begin
- axi_write_num <= axi_write_num + 1;
- axi_write_ok <= 1'b0;
- end
- else if((S_AXI_WREADY_pin & S_AXI_WVALID_pin) && (axi_write_num == 5'hf)) begin
- axi_write_num <= 5'h0;
- axi_write_ok <= 1'b1;
- end
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- frame_num <= 2'b00;
- else if(process_start && frame_last && ~frame_last_r)
- begin
- if(frame_num != 2'b10)
- frame_num <= frame_num + 1;
- else
- frame_num <= 2'b00;
- end
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- hcount <= 13'h0;
- else if(w_ns == NEXT_LINE)
- hcount <= 13'h0;
- else if(w_ns == NEXT_FRAME)
- hcount <= 13'h0;
- else if(w_ns == NEXT_WRITE)
- hcount <= hcount + 16;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- vcount <= 13'h0;
- else if(w_ns == NEXT_FRAME)
- vcount <= 13'h0;
- else if(w_ns == NEXT_LINE)
- vcount <= vcount + 1;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- frame_last_r <= 1'b0;
- else
- frame_last_r <= frame_last;
- end
- assign line_last = (hcount == (WIDTH - 16) )? 1'b1 : 1'b0;
- assign frame_last = (vcount == (HEIGHT - 1) )? 1'b1 : 1'b0;
- assign S_AXI_WLAST_pin = (axi_write_num == 5'hf)? 1'b1 : 1'b0;
- endmodule
读时序图:
AXI读数据就比较简单了,只要在AXI和video之间加入个fifo进行一下缓存就可以了,只要时序是对的,直接上代码
- /*-----------------------------------------------------------------------
- CONFIDENTIAL IN CONFIDENCE
- This confidential and proprietary software may be only used as authorized
- by a licensing agreement from EEPROM .
- In the event of publication, the following notice is applicable:
- Copyright (C) 2013-20xx EEPROM Corporation
- The entire notice above must be reproduced on all authorized copies.
- Author : EEPROM
- Technology blogs : http://blog.csdn.net/zhangyu_eeprom
- Email Address : [email protected]
- Filename : axi4_mm2s_video_dma.v
- Data : 2014-05-20
- Description : axi4_mm2s_video_dma.
- Modification History :
- Data By Version Change Description
- =========================================================================
- -------------------------------------------------------------------------
- ------ ------ ------| ------| /-------\
- | | | | | | / \ /-\ /-\
- |------ |------ |-----| |-----| / \ / \ / \
- | | | | \ \ / / \_/ \
- |------ |------ | | \ \ / / \
- | | \ \-------/
- -----------------------------------------------------------------------*/
- module axi4_mm2s_video_dma #(
- 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 = 12'd1920,
- parameter [12:0] WIDTH = 12'd1920,
- parameter [12:0] HEIGHT = 12'd1080
- )
- (
- input axi_clk,
- input axi_rstn,
- input v_video_clk,
- input v_timing_hsync_i,
- input v_timing_vsync_i,
- input v_timing_hblank_i,
- input v_timing_vblank_i,
- input v_timing_active_video_i,
- output reg v_timing_hsync_o,
- output reg v_timing_vsync_o,
- output reg v_timing_hblank_o,
- output reg v_timing_vblank_o,
- output reg v_timing_active_video_o,
- output [23:0] v_video_o,
- input S_AXI_ARREADY_pin,
- input S_AXI_RLAST_pin,
- input S_AXI_RVALID_pin,
- input [1:0] S_AXI_RRESP_pin,
- input [C_DATA_WIDTH-1:0] S_AXI_RDATA_pin,
- input [5:0] S_AXI_RID_pin,
- output S_AXI_ARVALID_pin,
- output S_AXI_RREADY_pin,
- output [1:0] S_AXI_ARBURST_pin,
- output [1:0] S_AXI_ARLOCK_pin,
- output [2:0] S_AXI_ARSIZE_pin,
- output [2:0] S_AXI_ARPROT_pin,
- output reg [31:0] S_AXI_ARADDR_pin,
- output [3:0] S_AXI_ARCACHE_pin,
- output [3:0] S_AXI_ARLEN_pin,
- output [3:0] S_AXI_ARQOS_pin,
- output [5:0] S_AXI_ARID_pin
- );
- /******************************/
- wire frame_last;
- wire line_last;
- reg process_start = 1'b0;
- wire [9:0] space;
- reg [31:0]prev_line_start;
- reg [1:0]frame_num;
- reg [12:0]hcount;
- reg [12:0]vcount;
- reg frame_last_r;
- wire [31:0] dma_tran_addr;
- wire dma_frame_read_end;
- reg v_timing_hsync_r;
- reg v_timing_vsync_r;
- reg v_timing_hblank_r;
- reg v_timing_vblank_r;
- reg v_timing_active_video_r;
- reg v_timing_active_video_d1;
- reg v_timing_active_video_d2;
- /************fifo**************/
- wire fifo_wr_en;
- reg fifo_rd_en;
- wire fifo_full;
- wire fifo_empty;
- wire fifo_valid;
- wire [6:0] fifo_rd_data_count;
- wire [6:0] fifo_wr_data_count;
- wire [31:0] fifo_dout;
- /******************************/
- assign fifo_wr_en = S_AXI_RVALID_pin & S_AXI_RREADY_pin;
- //assign fifo_rd_en = process_start & v_timing_active_video_r;
- assign v_video_o = fifo_dout[31:8];
- fifo_axi32bit u_fifo_axi32bit (
- .wr_clk(axi_clk),
- .rd_clk(v_video_clk),
- .din({S_AXI_RDATA_pin[7:0],S_AXI_RDATA_pin[15:8],S_AXI_RDATA_pin[23:16],S_AXI_RDATA_pin[31:24]}),
- .wr_en(fifo_wr_en),
- .rd_en(fifo_rd_en),
- .dout(fifo_dout),
- .full(fifo_full),
- .empty(fifo_empty),
- .valid(fifo_valid),
- .rd_data_count(fifo_rd_data_count),
- .wr_data_count(fifo_wr_data_count)
- );
- always @(posedge v_video_clk)
- begin
- v_timing_hsync_r <= v_timing_hsync_i;
- v_timing_vsync_r <= v_timing_vsync_i;
- v_timing_hblank_r <= v_timing_hblank_i;
- v_timing_vblank_r <= v_timing_vblank_i;
- v_timing_active_video_r <= v_timing_active_video_i;
- v_timing_hsync_o <= v_timing_hsync_r;
- v_timing_vsync_o <= v_timing_vsync_r;
- v_timing_hblank_o <= v_timing_hblank_r;
- v_timing_vblank_o <= v_timing_vblank_r;
- v_timing_active_video_o <= v_timing_active_video_r;
- v_timing_active_video_d1 <= v_timing_active_video_r;
- v_timing_active_video_d2 <= v_timing_active_video_d1;
- end
- reg [1:0] process_start_r;
- always @(posedge v_video_clk)
- begin
- if(!axi_rstn)begin
- process_start <= 1'b0;
- end
- else if(~v_timing_vsync_r && v_timing_vsync_i)
- begin
- process_start <= 1'b1;
- end
- else if(dma_frame_read_end)
- process_start <= 1'b0;
- end
- cross_clk cross_clk1
- (
- .clkin (axi_clk),
- .din (frame_last),
- .clkout (v_video_clk),
- .dout (dma_frame_read_end)
- );
- reg pre;
- reg [12:0] invalid_cnt;
- reg [12:0] invalid_cnt_debug;
- reg [12:0] fifo_read_num;
- always @(posedge v_video_clk)
- begin
- if(!axi_rstn)
- begin
- end
- else if(v_timing_active_video_r == 1'b0 || fifo_read_num == (WIDTH - 2))
- begin
- fifo_rd_en <= 1'b0;
- end
- else if(v_timing_active_video_r && fifo_read_num < (WIDTH - 2))
- fifo_rd_en <= 1'b1;
- end
- always @(posedge v_video_clk)
- begin
- if(!axi_rstn)
- begin
- fifo_read_num <= 13'd0;
- end
- else if(v_timing_active_video_d2 && fifo_read_num != (WIDTH - 1))
- fifo_read_num <= fifo_read_num + 1;
- else if(!v_timing_active_video_d2 && fifo_read_num == (WIDTH - 1))
- fifo_read_num <= 13'd0;
- end
- //------------------------------------------------------
- // AXI bus
- //------------------------------------------------------
- assign S_AXI_ARID_pin = 6'b000000;
- assign S_AXI_ARLEN_pin = 4'hf; //burst length: 16
- assign S_AXI_ARSIZE_pin = 3'b010; //size: 4byte
- assign S_AXI_ARBURST_pin = 2'b01; //incr
- assign S_AXI_ARLOCK_pin = 2'b00;
- assign S_AXI_ARCACHE_pin = 4'b0011; /////
- assign S_AXI_ARPROT_pin = 3'b000;
- assign S_AXI_ARQOS_pin = 4'b0000;
- parameter IDLE = 4'b0000;
- parameter SEND_ADDR = 4'b0001;
- parameter WAIT_DATA = 4'b0011;
- parameter WAIT_FIFO = 4'b0111;
- parameter NEXT_READ = 4'b1111;
- parameter NEXT_LINE = 4'b1110;
- parameter NEXT_FRAME = 4'b1100;
- reg [3:0]r_cs;
- reg [3:0]r_ns;
- assign space = 127 - fifo_wr_data_count[6:0];
- always @(posedge axi_clk)
- begin
- if(!axi_rstn)
- begin
- r_cs <= IDLE;
- end
- else
- begin
- r_cs <= r_ns;
- end
- end
- always @(*) begin
- case(r_cs)
- IDLE: begin
- if(process_start)
- r_ns = SEND_ADDR;
- else
- r_ns = r_cs;
- end
- SEND_ADDR: begin
- if(S_AXI_ARREADY_pin & S_AXI_ARVALID_pin)
- r_ns = WAIT_DATA;
- else
- r_ns = r_cs;
- end
- WAIT_DATA: begin
- if(S_AXI_RREADY_pin & S_AXI_RVALID_pin & S_AXI_RLAST_pin & (space < 10'h12))
- r_ns = WAIT_FIFO;
- else if(S_AXI_RREADY_pin & S_AXI_RVALID_pin & S_AXI_RLAST_pin & (~line_last))
- r_ns = NEXT_READ;
- else if(S_AXI_RREADY_pin & S_AXI_RVALID_pin & S_AXI_RLAST_pin & ( line_last) & (~frame_last))
- r_ns = NEXT_LINE;
- else if(S_AXI_RREADY_pin & S_AXI_RVALID_pin & S_AXI_RLAST_pin & ( line_last) & ( frame_last))
- r_ns = NEXT_FRAME;
- else
- r_ns = r_cs;
- end
- WAIT_FIFO: begin
- if((space >= 10'h11) & (~line_last))
- r_ns = NEXT_READ;
- else if((space >= 10'h11) & ( line_last) & (~frame_last))
- r_ns = NEXT_LINE;
- else if((space >= 10'h11) & ( line_last) & ( frame_last))
- r_ns = NEXT_FRAME;
- else
- r_ns = r_cs;
- end
- NEXT_READ: begin
- r_ns = SEND_ADDR;
- end
- NEXT_LINE: begin
- r_ns = SEND_ADDR;
- end
- NEXT_FRAME: begin
- r_ns = IDLE;//SEND_ADDR
- end
- default: begin
- r_ns = r_cs;
- end
- endcase
- end
- /************test***************/
- /***************************/
- assign dma_tran_addr = (frame_num == 2'b00)? DMA_DEST_ADDR0 : (frame_num == 2'b01)? DMA_DEST_ADDR1 : DMA_DEST_ADDR2;
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- prev_line_start <= 32'h0;
- else if(r_cs == IDLE && r_ns == SEND_ADDR)
- prev_line_start <= dma_tran_addr;
- else if(r_cs == NEXT_LINE && r_ns == SEND_ADDR)
- prev_line_start <= prev_line_start + STRIDE;
- else if(r_cs == NEXT_FRAME && r_ns == IDLE)//SEND_ADDR
- prev_line_start <= dma_tran_addr;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- S_AXI_ARADDR_pin <= 32'h0;
- else if(r_cs == IDLE && r_ns == SEND_ADDR)
- S_AXI_ARADDR_pin <= dma_tran_addr;
- else if(r_cs == NEXT_READ && r_ns == SEND_ADDR)
- S_AXI_ARADDR_pin <= S_AXI_ARADDR_pin + 8'h40;
- else if(r_cs == NEXT_LINE && r_ns == SEND_ADDR)
- S_AXI_ARADDR_pin <= prev_line_start + STRIDE;
- else if(r_cs == NEXT_FRAME && r_ns == IDLE)//SEND_ADDR
- S_AXI_ARADDR_pin <= dma_tran_addr;
- end
- assign S_AXI_ARVALID_pin = (r_cs == SEND_ADDR)? 1'b1 : 1'b0;
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- hcount <= 13'h0;
- else if((r_cs == IDLE || r_cs == NEXT_LINE || r_cs == NEXT_FRAME) && (r_ns == SEND_ADDR))//SEND_ADDR
- hcount <= 13'h0;
- else if(r_cs == NEXT_READ && r_ns == SEND_ADDR)
- hcount <= hcount + 16;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- vcount <= 13'h0;
- else if(r_cs == NEXT_FRAME && r_ns == IDLE)//SEND_ADDR
- vcount <= 13'h0;
- else if(r_cs == NEXT_LINE && r_ns == SEND_ADDR)
- vcount <= vcount + 1;
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- begin
- frame_num <= 2'b00;
- end
- else if(/*process_start && */frame_last && ~frame_last_r)
- begin
- if(frame_num != 2'b10)
- frame_num <= frame_num + 1;
- else
- frame_num <= 2'b00;
- end
- end
- always @(posedge axi_clk) begin
- if(!axi_rstn)
- frame_last_r <= 1'b0;
- else
- frame_last_r <= frame_last;
- end
- assign line_last = (hcount == (WIDTH - 16) )? 1'b1 : 1'b0;
- assign frame_last = (vcount == (HEIGHT - 1) )? 1'b1 : 1'b0;
- assign S_AXI_RREADY_pin = 1'b1;
- endmodule
DMA操作其实就是直接操作AXI总线进行DDR数据搬移,只要满足AXI总线协议就可以了,自己写DMA可以灵活定制功能,减小设计面积,更加可调式。本文中的设计只是很拙劣的设计,就是按照时序拼出来的,如果哪位能给提出些建议,或者有更好的方案,还望不吝赐教