上一篇讲到了通过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模块就是按照时序图来实现的,下面对此模块进行一下讲述
模块端口如下:
下面是参数部分:
-
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 : eeprom@yeah.net
-
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
MM2S VDMA设计
AXI读流程图:
读时序图:
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 : eeprom@yeah.net
-
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
当然,读过程也和写过程一样,burst长度是固定的16,所以读数据长度也要能被16整除才可以
注意:在做AXI总线读写的时候,一定要注意大小端的问题。
总结
DMA操作其实就是直接操作AXI总线进行DDR数据搬移,只要满足AXI总线协议就可以了,自己写DMA可以灵活定制功能,减小设计面积,更加可调式。本文中的设计只是很拙劣的设计,就是按照时序拼出来的,如果哪位能给提出些建议,或者有更好的方案,还望不吝赐教
阅读(10271) | 评论(0) | 转发(0) |