/*-----------------------------------------------------------------------
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
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