`timescale 1ps/1ps
/*
模块完成16bit的YC数据的64bit的数据对齐,然后写入FIFO,
有帧写入状态机完成64bit数据写入ddr2
*/
module vin_frame_buffer_ctrl
#(
parameter MEM_DATA_BITS = 64
)
(
input rst_n, /*复位 */
input vin_clk, /*视频输入时钟 */
input vin_vs, /*视频输入场同步 */
input vin_de, /*视频输入数据有效 */
input[23:0] vin_data, /*视频输入数据YC */
input[11:0] vin_width, /*视频输入宽度*/
input[11:0] vin_height, /*视频输入高度*/
input mem_clk, /*存储器接口:时钟*/
output reg wr_burst_req, /*存储器接口:写请求*/
output reg[9:0] wr_burst_len, /*存储器接口:写长度*/
output reg[23:0] wr_burst_addr, /*存储器接口:写首地址 */
input wr_burst_data_req, /*存储器接口:写数据数据读指示 */
output[MEM_DATA_BITS - 1:0] wr_burst_data, /*存储器接口:写数据*/
input burst_finish /*存储器接口:本次写完成 */
);
localparam BURST_LEN = 10'd128; /*一次写操作数据长度 */
localparam BURST_IDLE = 3'd0; /*状态机状态:空闲 */
localparam BURST_ONE_LINE_START = 3'd1; /*状态机状态:视频数据一行写开始 */
localparam BURSTING = 3'd2; /*状态机状态:正在处理一次ddr2写操作 */
localparam BURST_END = 3'd3; /*状态机状态:一次ddr2写操作完成*/
localparam BURST_ONE_LINE_END = 3'd4; /*状态机状态:视频数据一行写完成*/
reg[2:0] burst_state = 3'd0; /*状态机状态:当前状态 */
reg[2:0] burst_state_next = 3'd0; /*状态机状态:下一个状态*/
reg[11:0] burst_line = 12'd0; /*已经写入ddr2的行计数*/
reg[11:0] remain_len = 12'd0; /*当前视频一行数据的剩余数据个数*/
reg vin_vs_mem_clk_d0 = 1'b0;
reg vin_vs_mem_clk_d1 = 1'b0;
reg frame_flag = 1'b0;
wire[11:0] rdusedw;
fifo_4096_24 fifo_4096_24_m0(
.aclr(frame_flag), // FRAME清除标志
.data(vin_data), // 写入数据
.rdclk(mem_clk), // DDR内存读时钟
.rdreq(wr_burst_data_req), // DDR需要的读请求信号
.wrclk(vin_clk), // 外部写入时钟
.wrreq(vin_de), // 外部写入请求
.q(wr_burst_data[23:0]), // DDR读出的数据
.rdempty(),
.rdusedw(rdusedw), // DDR读出的长度
.wrfull(),
.wrusedw()
);
/*突发写首地址的产生*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
wr_burst_addr <= 24'd0;
else if(burst_state_next == BURST_ONE_LINE_START)
wr_burst_addr <= {3'd0,burst_line[9:0],11'd0};//24bit ddr addr
else if(burst_state_next == BURST_END && burst_state != BURST_END) // 当前不是BURST_END,下一个才是BURST_END
wr_burst_addr <= wr_burst_addr + BURST_LEN[7:0]; // 指针指向下一闪的突发写地址
else
wr_burst_addr <= wr_burst_addr; // 保持
end
/*产生帧标志*/
/*产生帧开始标志,只是一个mem_clk周期才为高,其余时间为低*/
always@(posedge mem_clk)
begin
vin_vs_mem_clk_d0 <= vin_vs;
vin_vs_mem_clk_d1 <= vin_vs_mem_clk_d0;
frame_flag <= vin_vs_mem_clk_d0 && ~vin_vs_mem_clk_d1;
end
/*每一帧都将状态机强行进入BURST_IDLE状态*/
/*frame_flag在为1时,第一次为IDLE,然后为0时,是置为burst_state_next,
注意这里:在为0时,mem_clk为敏感信号,burst_state和burst_state_next是连接的二个状态
*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
burst_state <= BURST_IDLE;
else if(frame_flag)
burst_state <= BURST_IDLE;
else
burst_state <= burst_state_next;
end
/*下面的操作都应是在frame_flag为0时, 二个状态字,
burst_state_next是在burst_state的下一状态,根据当前的burst_state来指定
burst_state_next的值,由于mem_clk都为二者的敏感信号,用非阻塞赋值,相当于
以下的结构,
always@(posedge mem_clk or negedge rst_n)
begin
...
burst_state_next <= BURST_END;
burst_state <= burst_state_next;
...
end
这样的话,burst_state_next是burst_state的下一状态
*/
always@(*)
begin
case(burst_state)
BURST_IDLE:/*如果FIFO有足够的数据则完成一行第一次写操作*/
if(rdusedw > BURST_LEN[7:0])
burst_state_next <= BURST_ONE_LINE_START;
else
burst_state_next <= BURST_IDLE;
BURST_ONE_LINE_START:/*一行的写操作开始*/
burst_state_next <= BURSTING;
BURSTING:/*写操作*/
if(burst_finish)
burst_state_next <= BURST_END;
else
burst_state_next <= BURSTING;
BURST_END:/*写操作完成时判断一行数据是否已经完全写入ddr2,如果完成则进入空闲状态,等待第二行数据*/
if(remain_len == 12'd0)
burst_state_next <= BURST_ONE_LINE_END;
else if(rdusedw >= BURST_LEN[7:0])// || (remain_len <= BURST_LEN && rdusedw == remain_len - 10'd1))//һ��ͻ������,��һ������һ��ͻ��
burst_state_next <= BURSTING;
else
burst_state_next <= BURST_END;
BURST_ONE_LINE_END:
burst_state_next <= BURST_IDLE;
default:
burst_state_next <= BURST_IDLE;
endcase
end
/*burst_line产生*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
burst_line <= 12'd0;
else if(frame_flag)
burst_line <= 12'd0;
else if(burst_state == BURST_ONE_LINE_END)//每次一行写完burst_line加1
burst_line <= burst_line + 12'd1;
else
burst_line <= burst_line;
end
/*remain_len产生,每一行写开始时等于byte_per_line,如果一行数据小于一次写的最大长度,
一次写完,则remain_len = 0,否则减去最大写长度*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
remain_len <= 12'd0;
else if(burst_state_next == BURST_ONE_LINE_START)
remain_len <= vin_width;
else if(burst_state_next == BURST_END && burst_state != BURST_END)
if(remain_len < BURST_LEN)
remain_len <= 12'd0;
else
remain_len <= remain_len - BURST_LEN;
else
remain_len <= remain_len;
end
/*突发长度产生,如果一行的剩余数据大于最大写长度,则突发长度是BURST_LEN,否则就等于剩余数据长度*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
wr_burst_len <= 10'd0;
else if(burst_state_next == BURSTING && burst_state != BURSTING)
if(remain_len > BURST_LEN)
wr_burst_len <= BURST_LEN;
else
wr_burst_len <= remain_len;
else
wr_burst_len <= wr_burst_len;
end
/*ddr2写请求信号的产生于撤销*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
wr_burst_req <= 1'd0;
else if(burst_state_next == BURSTING && burst_state != BURSTING)
wr_burst_req <= 1'b1;
else if(burst_finish || wr_burst_data_req || burst_state == BURST_IDLE)
wr_burst_req <= 1'b0;
else
wr_burst_req <= wr_burst_req;
end
endmodule
其中关于remain_len 长度的问题,有以下分析:
/*remain_len产生,每一行写开始时等于byte_per_line,如果一行数据小于一次写的最大长度,
一次写完,则remain_len = 0,否则减去最大写长度*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
remain_len <= 12'd0;
else if(burst_state_next == BURST_ONE_LINE_START)
remain_len <= vin_width;
else if(burst_state_next == BURST_END && burst_state != BURST_END) // 正在BURSTING中
if(remain_len < BURST_LEN)
remain_len <= 12'd0; // 如果小于128的话,则表示下一次没有余留数据,直接为0
else
remain_len <= remain_len - BURST_LEN; // 大于BURST_LEN 128长度的话,减去128
else
remain_len <= remain_len;
end
/*突发长度产生,如果一行的剩余数据大于最大写长度,则突发长度是BURST_LEN,否则就等于剩余数据长度*/
always@(posedge mem_clk or negedge rst_n)
begin
if(!rst_n)
wr_burst_len <= 10'd0;
else if(burst_state_next == BURSTING && burst_state != BURSTING)
if(remain_len > BURST_LEN)
wr_burst_len <= BURST_LEN;
else
wr_burst_len <= remain_len; // 突发写的长度不足够128
else
wr_burst_len <= wr_burst_len;
end
remain_len <= 12'd0; 为0时,是在最后的不足128个字节时为0,注意状态机表示当前在BURSTING的状态。