文章目录
本章节主要将hdmi输入的1080p视频通过ddr3缓存,然后通过hdmi输出口输出到显示屏上显示
一, 突发读写命令
设置读写突发长度为64
//parameter defineparameter WRITE_LENGTH = 64;parameter READ_LENGTH = 64;parameter IDLE = 3'd0; //空闲状态parameter WRITE = 3'd1; //写状态parameter WRITE_DONE = 3'd2; //读到写过度等待parameter READ = 3'd3; //读状态parameter READ_DONE = 3'd4;
1.突发写命令
当写fifo计数器rd_data_count计数大于62,执行一次写突发命令,拉高wr_len_en
//突发写使能always@(posedge ui_clk or negedge i_rst_n)beginif(!i_rst_n)wr_len_en <= 1'd0; else if(ddr_wr_end)wr_len_en <= 1'd0; else if(rd_len_en)wr_len_en <= 1'd0; else if(wr_len_done)wr_len_en <= 1'd0; else if(rd_data_count >= 9'd62)wr_len_en <= 1'd1; end
2.突发读命令
当读fifo计数器wr_data_count小于等于64执行一次读突发命令,拉高rd_len_en
//突发读使能always@(posedge ui_clk or negedge i_rst_n)beginif(!i_rst_n)rd_len_en <= 1'd0;else if(ddr_rd_end)rd_len_en <= 1'd0; else if(wr_len_en)rd_len_en <= 1'd0; else if(rd_len_done)rd_len_en <= 1'd0; else if(ddr_rd_en && wr_data_count <= 9'd64)rd_len_en <= 1'd1; end
从上面的读突发命令可以看出来,读突发命令rd_len_en拉高,需要fifo计数器wr_data_count小于等于64和ddr_rd_en为高才行。因读突发设置的fifo读计数器wr_data_count小于等于64才触发,开发板上电后,ddr初始化后ddr里这时并没有数据,但如果直接设置读fifo小于等于64触发,那么这时进行的突发读将会读到的是ddr里面的随机值,因为此时突发读是先开始执行的,所以加入ddr_rd_en这个信号,当ddr_rd_en拉高才能使能突发读,ddr_rd_en信号需要输入的hdmi的场信号vs计数为3开始拉高ddr_rd_en这个信号,也就是ddr写入三个帧存才开始ddr突发读。
3.突发读使能信号
将hdmi输入的vs_in场信号打拍,每次上升沿计数,当计数到3时开始拉高start_en信号
reg vs_r;
reg [2:0] count;
always@(posedge clk or negedge rst)beginif(!rst) vs_r <= 1'b0;elsevs_r <= vs_in;
end always@(posedge clk or negedge rst)beginif(!rst) count <= 3'd0;else if(count == 3'd3)count <= count; else if(vs_in && !vs_r)count <= count + 1'b1;
endassign start_en = (count == 3'd3)? 1'b1:1'b0;
可以看到start_en信号直接连接ddr_rd_en
ddr_test ddr_test_inst (// Memory interface ports.ddr3_addr (ddr3_addr), // output [13:0] ddr3_addr.ddr3_ba (ddr3_ba), // output [2:0] ddr3_ba.ddr3_cas_n (ddr3_cas_n), // output ddr3_cas_n.ddr3_ck_n (ddr3_ck_n), // output [0:0] ddr3_ck_n.ddr3_ck_p (ddr3_ck_p), // output [0:0] ddr3_ck_p.ddr3_cke (ddr3_cke), // output [0:0] ddr3_cke.ddr3_ras_n (ddr3_ras_n), // output ddr3_ras_n.ddr3_reset_n (ddr3_reset_n), // output ddr3_reset_n.ddr3_we_n (ddr3_we_n), // output ddr3_we_n.ddr3_dq (ddr3_dq), // inout [31:0] ddr3_dq.ddr3_dqs_n (ddr3_dqs_n), // inout [3:0] ddr3_dqs_n.ddr3_dqs_p (ddr3_dqs_p), // inout [3:0] ddr3_dqs_p.ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n.ddr3_dm (ddr3_dm), // output [3:0] ddr3_dm.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt.init_calib_complete (init_calib_complete),// System Clock Ports.sys_clk_i (clk_out2),.sys_rst (rst_n), // input sys_rst//fifo.wr_clk (vin_clk),.rd_clk (video_clk),.write_fifo_wr_en (vin_de),.write_fifo_din ({8'd0,vin_data[23:0]}),.read_fifo_rd_en (read_en),.read_fifo_dout (read_data),.read_fifo_empty (read_fifo_empty),.read_valid (read_valid),.ddr_rd_en (start_en),.vin_vs (vin_vs),.vout_vs (read_req),.rd_reset (read_req_ack) );
二,fifo读写复位信号
1.写fifo复位信号
hdmi输入的场信号vin_vs检测到上升沿,拉高wr_reset信号用于写fifo复位
always@(posedge ui_clk or negedge rst_n)beginif(!rst_n)beginwr_load_r0 <= 1'd0;wr_load_r1 <= 1'd0;endelse beginwr_load_r0 <= vin_vs;wr_load_r1 <= wr_load_r0;end
end always@(posedge ui_clk or negedge rst_n)beginif(!rst_n)wr_reset <= 1'd0;else if(wr_load_r0 && !wr_load_r1)wr_reset <= 1'd1;else if(app_addr_wr == 0 && !(wr_load_r0 && !wr_load_r1)) wr_reset <= 1'd0;
end
2.读fifo复位信号
hdmi输出的场信号vout_vs检测到上升沿,拉高rd_reset信号用于读fifo复位
always@(posedge ui_clk or negedge rst_n)beginif(!rst_n)beginrd_load_r0 <= 1'd0;rd_load_r1 <= 1'd0;endelse begin rd_load_r0 <= vout_vs;rd_load_r1 <= rd_load_r0;end
endalways@(posedge ui_clk or negedge rst_n)beginif(!rst_n)rd_reset <= 1'd0;else if(rd_load_r0 && !rd_load_r1)rd_reset <= 1'd1;else if(app_addr_rd == 0 && !(rd_load_r0 && !rd_load_r1)) rd_reset <= 1'd0;
end
三,帧缓存切换
从下面的ddr手册可以看出,ddr一共有8个bank,也就是这个ddr一共可以缓存8页
而且地址是[14:0],注意这里在mig选ddr型号的时候一定要注意ddr的地址务必要选[14:0]或者以上,不然就只能操作一个帧缓存,我前面的工程都是[13:0],即使写了多帧,也只能操作一帧,目前这个工程是[14:0],按照ddr手册可以最大操作8个帧存 ,当然具体可以操作多少个帧存也和mig控制器有关,如果ddr最大帧存有8个,那我们操作8个帧存或者8个以下的帧存都是可以,当然mig也必须配置相应的帧存个数,如果我们的帧存为4个那么只能操作4个或者以下的,即使你的mig设置为8个帧存,那么能操作的最大的也只能是4个,帧存说白了就是ddr的bank数。
1.mig控制器帧设置
bank数量设置,ddr最大的bank数为8,我们这里设置为4表示最大能操作4个帧存,也就是0,1,2,3这四页
2.ddr写帧缓存切换
将hdmi输入的场信号vin_vs的上升沿做为计数信号,每次检测到上升沿就直接加1,因wr_page位宽为[1:0],也就是两个位宽,所以ddr写帧存在0,1,2,3之间切换
always@(posedge ui_clk or negedge rst_n)beginif(!rst_n)beginwr_load_r0 <= 1'd0;wr_load_r1 <= 1'd0;endelse beginwr_load_r0 <= vin_vs;wr_load_r1 <= wr_load_r0;end
end always @(posedge ui_clk or negedge rst_n) beginif(!rst_n)wr_page <= 2'd0;else if(wr_load_r0 && !wr_load_r1)wr_page <= wr_page + 1'b1;
end
3.ddr读帧缓存切换
将hdmi输出的场信号的上升沿做为计数信号,每次检测到上升沿直接将当前读ddr帧存减1,这样做的目的,就是为了让读和写不在同一个帧存,避免图像有撕裂现象(一般显示撕裂也不明显,单帧也是可以的,但为了显示效果建议还是不要在同一个帧存进行)
always@(posedge ui_clk or negedge rst_n)beginif(!rst_n)beginrd_load_r0 <= 1'd0;rd_load_r1 <= 1'd0;endelse begin rd_load_r0 <= vout_vs;rd_load_r1 <= rd_load_r0;end
endalways @(posedge ui_clk or negedge rst_n) beginif(!rst_n)rd_page <= 2'd0;else if(rd_load_r0 && !rd_load_r1)rd_page <= wr_page - 1'b1;
end
从ddr手册看,行地址宽度是[14:0],列地址宽度为[9:0],所以ddr的寻址空间为[24:0],所以帧存控制管脚BA0,BA1,BA2,为ddr地址里25,26,27这三个管脚,2^3=8所以最大寻址控制为0~7一共8个帧存,又因我们上面mig控制器设置的是4,所以将ddr寻址的27这个管脚设置为0,直接使用25,26两个管脚来进行帧存切换,一共有0,1,2,3这样四种帧存切换,如下图所示。
hdmi输入和输出显示工程截图如下所示
工程看起来也不是很多,将黑金的hdmi输入和输出工程的axi的ddr控制全部去掉,然后把ddr读写这部分放进去,两个hdmi输入和输出的iic配置都是使用黑金的代码,ddr缓存使用前面的工程文件
四,硬件连接
hdmi输入接机箱,hdmi输出接显示屏
五,显示效果
手机不行,只能拍出这种效果了,实际显示还是很清晰的
工程下载链接:链接:链接:https://pan.baidu.com/s/1_3o0YiaL7cE_9mjd9fy6GQ 提取码:4clr
如若转载,请注明出处