基于Modelsim的视频捕获模拟仿真
一、前言
针对牟新刚编著的《基于FPGA的数字图像处理原理及应用》中第五章系统仿真中关于视频捕获模拟的例子进行补充和仿真验证,简言之,吊书袋子。
2020-02-27 21:09:05
二、视频捕获原理
假定输入视频分辨率为640*512*24Bit RGB数据,传输数据位宽为8位,扫描频率为60Hz,那么每一帧的像素数为pixel_total = 3*640*512*60。设h_total = 2000,
v_total = 600;那么1s之内的数据总量为pixel_total = 60 * h_total * v_total = 72,000,000,即像素时钟最少需要72MHz。根据需求,需将三个RGB通道分别解出,也
就是捕获后的数据位宽为24Bit,则很容易地计算出本地时钟所需的最小频率:CLKlocal ≥ CLKpixel /3 = 24MHz。因此,选择25MHz作为本地处理逻辑的时钟。
本地时钟不宜选得过大,否则,会造成带宽匹配困难:可能需要一块很大的缓存来进行带宽匹配。一般情况下,输出带宽略大于输入带宽即可。如下图所示为视
频捕获电路示意图;
确定了本地时钟之后,接下来便是如何捕获的问题。首先必须进行跨时钟域转换,即将输入视频同步到本地时钟域。其次由于输入带宽略小于输出带宽,如何进行输入/
输出带宽的匹配也是需要首要考虑的问题。输入是8位的像素数据,需要将8位的像素数据转换为3个同时输出的RGB通道,位宽转换电路也是必不可少的。
1.位宽转换电路
位宽转换电路相对比较简单,我们的目的是将8位的DV数据转换为24位的RGB数据,因此需要将输入数据缓存两拍与当前数据合并即可。
2.跨时钟域转换电路
我们通常会用一个异步fifo来完成异步时钟转换。异步fifo就是读写时钟分开的fifo。
3.带宽匹配
根据我们的设计目标,本地所能提供的最大带宽要大于输入视频流的带宽,这时就必须进行带宽匹配。异步fifo是解决带宽匹配问题的关键部件,如何控制此fifo的读写时机
是匹配的核心问题:不能在fifo空时去读fifo,也不能在fifo满时写数据。
由于本地带宽略大,本地逻辑负责从异步fifo中读取数据,因此我们很容易地知道,读速度要大于写速度,从而读操作必然要延迟于写操作,这样不至于出现“读空”。同时,
读时机也不能太迟,因为“写满”也是我们所不能接受的。
我们不会在fifo中只要一有数据就去读,这样会出现数据断流。实际上,合理的设计是每次读取一整行像素数据后停止,再等待一段固定间隔后读取下一行。这样可以保证
输出数据为连续的像素流和行消隐,同时可以对图像进行行计数,保证我们可以有效地掌握视频流。
这个“固定的间隔”的设计非常重要,它保证了整个输出视频流的连续性。如果这个间隔很小,读取速率频繁,那么行消隐时间就比较窄,可能出现的问题是“读空”。如果间隔
设计比较大,那么行消隐时间就比较长,这个情况下,增加fifo的深度有可能避免“写满”的问题,但是过长的消隐时间可能会“侵占”下一帧的处理时间,并且极大地浪费了带宽,因
此不合适。
输入带宽、输出带宽、trig_value与图像分辨率等决定了fifo深度。实际工作时,可以根据需要来进行测试,获得最好的匹配。
4.命令行缓存与读取电路
对输出图像进行行计数,有效图像结束后的那一行记为命令行,将此命令行写入一个同步fifo,等待外部读出请求读出即可。需要注意的是,必须在下一帧的命令行来之前读走,在此之前需要对这个fifo进行复位操作。
三、代码注释与解析
代码由三部分组成,视频流生成image_src.v,视频捕获video_cap.v及仿真测试文件video_cap_tb.v。书中例程是对24位的RGB图像进行采集,因此上一章节中的image_src.v中的视频参数要稍做调整。video_cap.v中的视频缓存img_fifo及cmd_buf分别位异步fifo和同步fifo。但是书中给出的却是两种不同平台(cmd_buf为Xilinx平台下的同步FIFO,img_fifo为Altera平台下的异步fifo)下的模块例化示例,实际过程中都采用统一平台下的fifo就可以。
(1)image_src.v代码如下:
/*
***********************************************************************************************************
** Input file: None
** Component name: image_src.v
** Author: zhengXiaoliang
** Company: WHUT
** Description: to simulate dvd stream
***********************************************************************************************************
*/ `timescale 1ns/1ns `define SEEK_SET
`define SEEK_CUR
`define SEEK_END module image_src(
reset_l, //全局复位
clk, //同步时钟
src_sel, //数据源通道选择
test_vsync, //场同步输出
test_dvalid, //像素有效输出
test_data, //像素数据输出
clk_out //像素时钟输出
); parameter iw = ; //默认视频宽度
parameter ih = ; //默认视频高度
parameter dw = ; //默认像素数据位宽 parameter h_total = ; //行总数
parameter v_total = ; //垂直总数 parameter sync_b = ; //场前肩
parameter sync_e = ; //场同步脉冲
parameter vld_b = ; //场后肩 //port decleared
input reset_l,clk;
input [:] src_sel; //to select the input file
output test_vsync, test_dvalid,clk_out;
output [dw-:] test_data; //variable decleared
reg [dw-:] test_data_reg;
reg test_vsync_temp;
reg test_dvalid_tmp;
reg [:] test_dvalid_r; reg [:] h_cnt;
reg [:] v_cnt; integer fp_r;
integer cnt = ; //输出像素时钟
assign clk_out = clk; //output the dv clk //输出像素数据
assign test_data = test_data_reg; //test data output //当行同步有效时,从文件读取像素数据输出到数据线上
always@(posedge clk or posedge test_vsync_temp)begin
if(((~(test_vsync_temp))) == 'b0) //场同步清零文件指针
cnt <= ; //clear file pointer when a new frame comes
else begin
if(test_dvalid_tmp == 'b1)begin //行同步有效,说明当前时钟数据有效
case(src_sel) //选择不同的数据源
'b0000: fp_r = $fopen("E:/Modelsim/video_cap/sim/lena_rgb_3.txt","r");
'b0001: fp_r = $fopen("txt_source/test_scr1.txt","r");
'b0010: fp_r = $fopen("txt_source/test_scr2.txt","r");
'b0011: fp_r = $fopen("txt_source/test_scr3.txt","r");
'b0100: fp_r = $fopen("txt_source/test_scr4.txt","r");
'b0101: fp_r = $fopen("txt_source/test_scr5.txt","r");
'b0110: fp_r = $fopen("txt_source/test_scr6.txt","r");
'b0111: fp_r = $fopen("txt_source/test_scr7.txt","r");
'b1000: fp_r = $fopen("txt_source/test_scr8.txt","r");
'b1001: fp_r = $fopen("txt_source/test_scr9.txt","r");
'b1010: fp_r = $fopen("txt_source/test_scr10.txt","r");
'b1011: fp_r = $fopen("txt_source/test_scr11.txt","r");
'b1100: fp_r = $fopen("txt_source/test_scr12.txt","r");
'b1101: fp_r = $fopen("txt_source/test_scr13.txt","r");
'b1110: fp_r = $fopen("txt_source/test_scr14.txt","r");
'b1111: fp_r = $fopen("txt_source/test_scr15.txt","r");
default: fp_r = $fopen("txt_source/test_src3.txt","r");
endcase $fseek(fp_r,cnt,); //查找当前需要读取的文件位置
$fscanf(fp_r,"%02x\n",test_data_reg); //将数据按指定格式读入test_data_reg寄存器 cnt <= cnt + ; //移动文件指针到下一个数据
$fclose(fp_r); //关闭文件
$display("h_cnt = %d,v_cnt = %d, pixdata = %d",h_cnt,v_cnt,test_data_reg); //for debug use
end
end
end //水平计数器,每来一个时钟就+1,加到h_total置零重新计数
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
h_cnt <= # {{'b0}};
else begin
if(h_cnt == ((h_total -)))
h_cnt <= # {{'b0}};
else
h_cnt <= # h_cnt + 'b00000000001;
end
end //垂直计数器:水平计数器计满后+1,计满后清零
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
v_cnt <= # {{'b0}};
else begin
if(h_cnt == ((h_total - )))begin
if(v_cnt == ((v_total - )))
v_cnt <= # {{'b0}};
else
v_cnt <= # v_cnt + 'b00000000001;
end
end
end //场同步信号生成
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_vsync_temp <= # 'b1;
else begin
if(v_cnt >= sync_b & v_cnt <= sync_e)
test_vsync_temp <= # 'b1;
else
test_vsync_temp <= # 'b0;
end
end assign test_vsync = (~test_vsync_temp); //水平同步信号生成
always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_dvalid_tmp <= # 'b0;
else begin
if(v_cnt >= vld_b & v_cnt < ((vld_b + ih)))begin
if(h_cnt == 'b0000000000)
test_dvalid_tmp <= # 'b1;
else if(h_cnt == iw)
test_dvalid_tmp <= # 'b0;
end
else
test_dvalid_tmp <= # 'b0;
end
end //水平同步信号输出
assign test_dvalid = test_dvalid_tmp; always@(posedge clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
test_dvalid_r <= # 'b00;
else
test_dvalid_r <= # ({test_dvalid_r[],test_dvalid_tmp});
end endmodule
(2)video_cap.v代码如下,对书中的例程代码进行了补充。
//2020-02-17
//Huang.Wei
`timescale 1ns/1ns module video_cap(
reset_l, //异步复位信号
DVD, //输入视频流
DVSYN, //输入场同步信号
DHSYN, //输入行同步
DVCLK, //输入DV时钟
cap_dat, //输出RGB通道像素流,24位
cap_dvalid, //输出数据有效
cap_vsync, //输出场同步
cap_clk, //本地逻辑时钟
img_en,
cmd_rdy, //命令行准备好,代表可以读取
cmd_rdat, //命令行数据输出
cmd_rdreq //命令行读取请求
); parameter TRIG_VALUE = ; //读触发值,也即行消隐时间
parameter IW = ; //图像宽度
parameter IH = ; //图像高度 parameter DW_DVD = ; //输入像素宽度
parameter DVD_CHN = ; //输入像素通道: RGB 3通道
parameter DW_LOCAL = ; //本地捕获的数据宽度24位
parameter DW_CMD = ; //命令行数据宽度
parameter VSYNC_WIDTH = ; //9 //场同步宽度,9个时钟 parameter CMD_FIFO_DEPTH = ; //行缓存位宽
parameter CMD_FIFO_DW_DEPTH = ;
parameter IMG_FIFO_DEPTH = ; //异步fifo深度,选512
parameter IMG_FIFO_DW_DEPTH = ; //Port Declared
input reset_l;
input [DW_DVD-:] DVD;
input DVSYN;
input DHSYN;
input DVCLK; output reg [DW_LOCAL-:] cap_dat;
output reg cap_dvalid;
output cap_vsync;
input cap_clk;
output img_en; output reg cmd_rdy;
output [DW_CMD-:] cmd_rdat;
input cmd_rdreq; //首先完成数据位宽转换
wire pixel_clk;
reg [:] vc_reset;
reg dv_enable;
reg [:] count_lines;
reg cmd_en;
reg cmd_wrreq;
reg cmd_wrreq_r;
reg rst_cmd_fifo;
wire [DW_CMD-:] cmd_din;
reg [DW_CMD-:] cmd_dat; assign pixel_clk = DVCLK; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
vc_reset <= 'b00;
dv_enable <= 'b0;
end
else
begin
dv_enable <= # 'b1;
if((~(DVSYN)) == 'b1 & dv_enable == 1'b1)
vc_reset <= # ({vc_reset[],'b1});
end
end reg [DW_DVD-:] vd_r[:DVD_CHN-];
reg [DVD_CHN*DW_DVD-:] data_merge; reg vsync;
reg [DVD_CHN:] hsync_r;
reg mux;
reg mux_r; //缓存场同步和行同步信号
always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
vsync <= 'b0;
hsync_r <= {DVD_CHN+{'b0}};
end
else
begin
vsync <= # DVSYN;
hsync_r <= # {hsync_r[DVD_CHN-:],DHSYN};
end
end //像素通道计算,指示当前像素属于RGB那个通道
reg [DVD_CHN:] pixel_cnt; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
begin
pixel_cnt <= {DVD_CHN+{'b1}};
end
else
begin
if(hsync_r[] == 'b0)
pixel_cnt <= # {DVD_CHN+{'b1}};
else
if(pixel_cnt == DVD_CHN -)
pixel_cnt <= # {DVD_CHN+{'b0}};
else
pixel_cnt <= # pixel_cnt + 'b1;
end
end integer i;
integer j; //缓存输入DV,获得3个RGB通道值 always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l)))=='b1)
for(i=;i<DVD_CHN;i=i+)
vd_r[i] <= {DW_DVD{'b0}};
else
begin
vd_r[] <= # DVD;
for(j=;j<DVD_CHN;j=j+)
vd_r[j] <= vd_r[j-];
end
end //RGB 合并有效信号
wire mux_valid; always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
mux <= 'b0;
else begin
if(hsync_r[DVD_CHN-] == 'b0)
mux <= # 'b1;
else
if(mux_valid == 'b1)
mux <= # 'b1;
else
mux <= # 'b0;
end
end always@(posedge pixel_clk)
mux_r <= mux; wire [DVD_CHN*DW_DVD-:] dvd_temp;
wire mux_1st; assign mux_1st = (~hsync_r[DVD_CHN]) & (hsync_r[DVD_CHN-]); //一个颜色通道
generate
if(DVD_CHN == )
begin: xhdl1
assign mux_valid = hsync_r[];
assign dvd_temp = vd_r[];
end
endgenerate //两个颜色通道
generate
if(DVD_CHN == )
begin: xhdl2
assign mux_valid = mux_1st | (pixel_cnt == DVD_CHN - );
assign dvd_temp = {vd_r[],vd_r[]};
end
endgenerate //三个颜色通道,将三路RBG数据合并到dvd_temp信号中
generate
if(DVD_CHN == )
begin: xhdl3
assign mux_valid = mux_1st | (pixel_cnt == );
assign dvd_temp = {vd_r[],vd_r[],vd_r[]};
end
endgenerate //四个颜色通道
generate
if(DVD_CHN == )
begin: xhdl4
assign mux_valid = mux_1st | (pixel_cnt == );
assign dvd_temp = {vd_r[],vd_r[],vd_r[],vd_r[]};
end
endgenerate //将合并后的数据存入寄存器
always@(posedge pixel_clk or negedge reset_l)begin
if(((~(reset_l))) == 'b1)
data_merge <= {DVD_CHN*DW_DVD{'b0}};
else
begin
if(hsync_r[DVD_CHN] == 'b1 & mux == 1'b1)
data_merge <= # dvd_temp;
end
end //将合并后的数据打入异步fifo
wire [DW_DVD*DVD_CHN-:] fifo_din;
wire [DW_DVD*DVD_CHN-:] fifo_dout; wire [IMG_FIFO_DW_DEPTH-:] rdusedw;
reg [:] trig_cnt;
wire fifo_empty;
reg fifo_wrreq;
reg fifo_wrreq_r;
//wire fifo_wrreq; //assign fifo_wrreq = mux & hsync_r[DVD_CHN]; reg fifo_rdreq;
reg fifo_rdreq_r1;
reg rst_fifo; //实例化异步fifo
cross_clock_fifo img_fifo(
.data(fifo_din),
.rdclk(cap_clk),
.rdreq(fifo_rdreq),
.wrclk(pixel_clk),
.wrreq(fifo_wrreq),
.q(fifo_dout),
.rdempty(fifo_empty),
.rdusedw(rdusedw),
.aclr(rst_fifo)
); /*
defparam img_fifo.DW = DW_DVD*DVD_CHN;
defparam img_fifo.DEPTH = IMG_FIFO_DEPTH;
defparam img_fifo.DW_DEPTH = IMG_FIFO_DW_DEPTH;
*/ assign fifo_din = data_merge; //RGB合并时写入fifo
always@(posedge pixel_clk or negedge reset_l)begin
if(reset_l == 'b0)begin
fifo_wrreq <= # 'b0;
fifo_wrreq_r <= # 'b0;
end
else begin
fifo_wrreq <= hsync_r[DVD_CHN] & mux_r;
fifo_wrreq_r <= fifo_wrreq;
end
end //fifo中数据大于触发值时开始读,读完一行停止
always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
fifo_rdreq <= # 'b0;
else
begin
if((rdusedw >= TRIG_VALUE) & (fifo_empty == 'b0))
fifo_rdreq <= # 'b1;
else if(trig_cnt == (IW - ))
fifo_rdreq <= # 'b0;
end
end //读计数
always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
trig_cnt <= # {{'b0}};
else
begin
if(fifo_rdreq == 'b0)
trig_cnt <= # {{'b0}};
else
if(trig_cnt == (IW - ))
trig_cnt <= # {{'b0}};
else
trig_cnt <= # trig_cnt + 'b0000000001;
end
end wire [DW_LOCAL-:] img_din; assign img_din = ((cmd_en == 'b0)) ? fifo_dout[DW_LOCAL-1:0] : {DW_LOCAL{1'b0}}; assign cmd_din = ((cmd_en == 'b1)) ? fifo_dout[DW_CMD-1:0] : {DW_CMD{1'b0}}; //生成场同步信号、数据有效信号及像素数据输出
reg vsync_async;
reg vsync_async_r1;
reg [VSYNC_WIDTH:] vsync_async_r;
reg cap_vsync_tmp; always@(posedge cap_clk or negedge reset_l)begin
if(reset_l == 'b0)
begin
vsync_async <= # 'b0;
vsync_async_r1 <= # 'b0;
vsync_async_r <= {VSYNC_WIDTH+{'b0}};
cap_vsync_tmp <= # 'b0;
end
else
begin
vsync_async <= # (~vsync);
vsync_async_r1 <= # vsync_async;
vsync_async_r <= {vsync_async_r[VSYNC_WIDTH-:], vsync_async_r1};
if(vsync_async_r[] == 'b1 & vsync_async_r[0] == 1'b0)
cap_vsync_tmp <= # 'b1;
else if(vsync_async_r[VSYNC_WIDTH] == 'b0 & vsync_async_r[0] == 1'b0)
cap_vsync_tmp <= # 'b0;
end
end assign cap_vsync = cap_vsync_tmp; always@(posedge cap_clk or negedge reset_l)begin
if(reset_l=='b0)
begin
cap_dat <= # {DW_LOCAL{'b0}};
fifo_rdreq_r1 <= # 'b0;
cap_dvalid <= # 'b0;
cmd_dat <= # {DW_CMD{'b0}};
cmd_wrreq <= # 'b0;
cmd_wrreq_r <= # 'b0;
end
else
begin
cap_dat <= # img_din;
fifo_rdreq_r1 <= # fifo_rdreq;
cap_dvalid <= # fifo_rdreq_r1 & (~(cmd_en));
cmd_dat <= # cmd_din;
cmd_wrreq <= # fifo_rdreq_r1 & cmd_en;
cmd_wrreq_r <= cmd_wrreq;
end
end //frame count and img_en signal
reg [:] fr_cnt;
reg img_out_en; always@(posedge cap_clk)begin
if(vc_reset[] == 'b0)
begin
img_out_en <= 'b0;
fr_cnt <= {{'b0}};
end
else
begin
if(vsync_async_r1 == 'b0 & vsync_async == 1'b1)
begin
fr_cnt <= fr_cnt + 'b01;
if(fr_cnt == 'b11)
img_out_en <= 'b1;
end
end
end assign img_en = img_out_en; //行计数,确定cmd数据到来时刻
always@(posedge cap_clk)begin
if(cap_vsync_tmp == 'b1)
begin
count_lines <= {{'b0}};
cmd_en <= 'b0;
cmd_rdy <= 'b0;
end
begin
if(fifo_rdreq_r1 == 'b1 & fifo_rdreq == 1'b0)
count_lines <= # count_lines + 'h1;
if(count_lines == (IH - ))
rst_cmd_fifo <= 'b1;
else
rst_cmd_fifo <= 'b0;
if(count_lines >= IH)
cmd_en <= # 'b1;
if(cmd_wrreq_r == 'b1 & cmd_wrreq == 1'b0)
cmd_rdy <= 'b1;
if(cmd_wrreq_r == 'b1 & cmd_wrreq == 1'b0)
rst_fifo <= 'b1;
else
rst_fifo <= 'b0;
end
end //Instance a line buffer to store the cmd line
line_buffer_new
cmd_buf(
.aclr(rst_cmd_fifo),
.clock(cap_clk),
.data(cmd_dat),
.rdreq(cmd_rdreq),
.wrreq(cmd_wrreq),
.empty(),
.full(),
.q(cmd_rdat),
.usedw()
); /*
defparam cmd_buf.DW = DW_CMD;
defparam cmd_buf.DEPTH = CMD_FIFO_DEPTH;
defparam cmd_buf.DW_DEPTH = CMD_FIFO_DW_DEPTH;
defparam cmd_buf.IW = IW;
*/
endmodule
(3)仿真测试代码video_cap_tb.v如下,值得注意的是$fdisplay是自带换行的,因此代码中不需要添加换行符,加入后仿真结果不对。
`timescale 1ns/1ns module video_cap_tb; /*image para*/
parameter iw = ; //image width
parameter ih = ; //image height
parameter trig_value = ; // /*video parameter*/
parameter h_total = ;
parameter v_total = ;
parameter sync_b = ;
parameter sync_e = ;
parameter vld_b = ; parameter clk_freq = ; /*data width*/
parameter dvd_dw = ; //image source data width
parameter dvd_chn = ; //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
parameter local_dw = dvd_dw * dvd_chn; //local algorithem process data width
parameter cmd_dw = dvd_dw * dvd_chn; //local algorithem process data width /*test module enable*/
parameter cap_en = ; /*signal group*/
reg clk = 'b0;
reg reset_l;
reg [:] src_sel; /*input dv group*/
wire dv_clk;
wire dvsyn;
wire dhsyn;
wire [dvd_dw-:] dvd; /*dvd source data generated for simulation*/
image_src //#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
img_src_ins(
.clk(clk),
.reset_l(reset_l),
.src_sel(src_sel),
.test_data(dvd),
.test_dvalid(dhsyn),
.test_vsync(dvsyn),
.clk_out(dv_clk)
); defparam img_src_ins.iw = iw*dvd_chn;
defparam img_src_ins.ih = ih + ;
defparam img_src_ins.dw = dvd_dw;
defparam img_src_ins.h_total = h_total;
defparam img_src_ins.v_total = v_total;
defparam img_src_ins.sync_b = sync_b;
defparam img_src_ins.sync_e = sync_e;
defparam img_src_ins.vld_b = vld_b; /*data captured*/
wire cap_dvalid;
wire [local_dw-:] cap_data;
wire cap_vsync; /*command line*/
wire cmd_rdy;
wire [cmd_dw-:] cmd_rdat;
reg cmd_rdreq; /*local clk: also clk of all local modules*/
reg cap_clk = 'b0; /*img enable*/
wire img_en; /*video capture: capture image src and transfer it into local timing*/
video_cap //#(trig_value,iw,ih) /*default trig value 250*/
video_new(
.reset_l(reset_l),
.DVD(dvd),
.DVSYN(dvsyn),
.DHSYN(dhsyn),
.DVCLK(dv_clk),
.cap_dat(cap_data),
.cap_dvalid(cap_dvalid),
.cap_vsync(cap_vsync),
.cap_clk(cap_clk),
.img_en(img_en),
.cmd_rdy(cmd_rdy),
.cmd_rdat(cmd_rdat),
.cmd_rdreq(cmd_rdreq)
); defparam video_new.DW_DVD = dvd_dw;
defparam video_new.DW_LOCAL = local_dw;
defparam video_new.DW_CMD = cmd_dw;
defparam video_new.DVD_CHN = dvd_chn;
defparam video_new.TRIG_VALUE = trig_value;
defparam video_new.IW = iw;
defparam video_new.IH = ih; initial
begin: init
reset_l <= 'b1;
src_sel <= 'b0000;
#(); //reset the system
reset_l <= 'b0;
#();
reset_l <= 'b1;
end //dv_clk generate
always@(reset_l or clk)begin
if((~(reset_l)) == 'b1)
clk <= 'b0;
else
begin
if(clk_freq == ) //48MHz
clk <= # (~(clk)); else if(clk_freq == 51.84) //51.84MHz
clk <= # (~(clk)); else if(clk_freq == ) //72MHz
clk <= # (~(clk));
end
end //cap_clk generate: 25MHz
always@(reset_l or cap_clk)begin
if((~(reset_l)) == 'b1)
cap_clk <= 'b0;
else
cap_clk <= # (~(cap_clk));
end generate
if(cap_en != ) begin :capture_operation
integer fp_cap, cnt_cap=; always@(posedge cap_clk or posedge cap_vsync)begin
if(((~(cap_vsync))) == 'b0)
cnt_cap = ;
else
begin
if(cap_dvalid == 'b1)
begin
fp_cap = $fopen("E:/Modelsim/video_cap/sim/lena_rgb_4.txt","r+");
$fseek(fp_cap,cnt_cap,);
if(local_dw==)
begin
$fdisplay(fp_cap,"%06X",cap_data);
$fclose(fp_cap);
cnt_cap<=cnt_cap+;
end
else
begin
$fdisplay(fp_cap,"%02x\n",cap_data);
$fclose(fp_cap);
cnt_cap<=cnt_cap+;
end
end
end
end
end
endgenerate endmodule
(4)rgb2txt.m用于生成测试文件的matlab程序,生成640*512*24Bits的RGB图像数据,多加一行命令行数据;
%将256位的BMP灰度图像128*128大小生成TXT文档;
clc
close all I_rgb = imread('lena_512x512.jpg');
subplot(,,),imshow(I_rgb),title('lena-rgb') I_gray = rgb2gray(I_rgb);
subplot(,,),imshow(I_gray),title('lena-gray') % 改变图像尺寸
I_resize = imresize(I_gray,[ ],'nearest');%nearest(默认值) 最近邻插值 ‘bilinear’双线性插值 ‘bicubic’ 双三次插值
% I = imresize(I_gray,0.25);
% subplot(,,),imshow(I),title('lena-qtr') fid = fopen('./lena_640x512_hex.txt','wt');
for i = :size(I_resize,)
for j = :size(I_resize,)
fprintf(fid,'%2x\n',I_resize(i,j));%每个数据之间用空格分开
end
%fprintf(fid,'\n');
end %% 保存三通道RGB数据
I_resize_2 = imresize(I_rgb,[ ],'nearest');% [rows cols]
[m,n,c] = size(I_resize_2);
fid2 = fopen('lena_rgb_3.txt','wt');
for i=:m
for j = :n
for k = :c
fprintf(fid2,'%02X\n',I_resize_2(i,j,k));
end
end
%fprintf(fid2,'\n');
end
%
for a = :n
for b = :
fprintf(fid2,'%02X\n',rem(a,));
end
end
fclose(fid2); fid = fclose(fid);
I_data = load('./lena_640x512.txt');
(5)用于Modelsim仿真的.do文件的编写,video_cap.do代码如下:
#切换至工程目录
cd E:/Modelsim/video_cap/sim #打开工程
project open E:/Modelsim/video_cap/sim/video_cap #添加指定设计文件
project addfile E:/Modelsim/video_cap/sim/video_cap_tb.v
project addfile E:/Modelsim/video_cap/src/cross_clock_fifo.v
#project addfile E:/Modelsim/video_cap/src/cross_clock_fifo.qip
project addfile E:/Modelsim/video_cap/src/image_src.v
project addfile E:/Modelsim/video_cap/src/line_buffer_new.v
#project addfile E:/Modelsim/video_cap/src/line_buffer_new.qip
project addfile E:/Modelsim/video_cap/src/video_cap.v #编译工程内所有文件
project compileall #仿真Work库下面的video_cap_tb实例,同时调用altera_lib库,不进行任何优化
vsim -t 1ps -novopt -L altera_lib work.video_cap_tb #添加输入信号InputData
add wave -divider InputData add wave -position insertpoint \
sim:/video_cap_tb/video_new/DVCLK add wave -position insertpoint \
sim:/video_cap_tb/video_new/DVSYN add wave -position insertpoint \
sim:/video_cap_tb/video_new/DHSYN add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/DVD #添加RGB合并信号,RGB_Merge
add wave -divider RGB_Merge
add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/vd_r add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/data_merge add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/vsync add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/hsync_r add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/mux add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/mux_r add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/pixel_cnt add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/i add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/j add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/mux_valid add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/dvd_temp add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/mux_1st #添加Image_FIFO信号
add wave -divider Image_FIFO add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/fifo_din add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/fifo_dout add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/rdusedw add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/trig_cnt add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/fifo_empty add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/fifo_wrreq add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/fifo_wrreq_r add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/fifo_rdreq add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/fifo_rdreq_r1 add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/rst_fifo #添加CMD_BUF信号
add wave -divider CMD_BUF add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/count_lines add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cmd_en add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cmd_wrreq add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cmd_wrreq_r add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/rst_cmd_fifo add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/cmd_din add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/cmd_dat add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cmd_rdy add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cmd_rdreq add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/cmd_rdat #添加输出信号OutputData
add wave -divider OutputData add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/img_din add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/vsync_async add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/vsync_async_r1 add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/vsync_async_r add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cap_vsync_tmp add wave -radix unsigned -position insertpoint \
sim:/video_cap_tb/video_new/fr_cnt add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/img_out_en add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cap_clk add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cap_vsync add wave -radix binary -position insertpoint \
sim:/video_cap_tb/video_new/cap_dvalid add wave -radix hex -position insertpoint \
sim:/video_cap_tb/video_new/cap_dat #复位
restart #取消警告
set StdArithNoWarnings #开始
run ms
四、仿真结果
(1)整体输入/输出仿真结果,输入/输出的频率都为60Hz。
(2)输出cap_dat延时输入一段时间,由于本地带宽略大,行消隐时间也长一些。
(3)输出成功地将输入连续3个像素合并为24位位宽数据输出。
(4)cmd_rdreq置1后,cmd数据输出。
基于Modelsim的视频捕获模拟仿真的更多相关文章
- 基于Modelsim的直方图统计算法仿真
一.前言 本篇主要针对牟新刚编著<基于FPGA的数字图像处理及应用>第六章第五节中直方图统计相关类容进行总结,包括代码实现及 基于Modelsim的仿真.书读百遍,其意自现. 2020-0 ...
- 基于Modelsim的视频流仿真
一.前言 最近在看牟新刚写的<基于FPGA的数字图像处理原理及应用>,书中关于FPGA数字图像处理的原理的原理写的非常透彻,在网上寻找了很久都没有找到完整的源代码工程,因此尝试自己做了补充 ...
- 基于HTML5的燃气3D培训仿真系统
最近上线了的基于HTML5的燃气3D培训仿真系统,以前的老系统是采用基于C++和OpenGL的OpenSceneGraph引擎设计的,OSG引擎性能和渲染效果各方面还是不错的,但因为这次新产品需求要求 ...
- 【转】基于DM8168的视频智能分析系统的设计方案
[导读] 为了实现高清视频的智能分析功能,本文介绍了一种以TI公司的DM8168为核心的高清视频智能分析系统的设计方案,该方案从硬件设计和软件设计两个方面介绍了硬件组成.工作流程.软件架构,并 ...
- 【转】基于V4L2的视频驱动开发
编写基于V4L2视频驱动主要涉及到以下几个知识点:1> 摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等.2> Camera解码器.控制器 ...
- 基于V4L2的视频驱动开发【转】
转自:http://blog.chinaunix.net/uid-10747583-id-298573.html Tags:V4L2驱动框架.API.操作流程…… 原文地址:http://www.ee ...
- 基于Linux的视频传输系统(上大学时參加的一个大赛的论文)
文件夹 1原创性声明----------------------------------------------------3 2 摘要-------------------------------- ...
- VS2010 C++学习(5):基于DirectShow的视频预览录像程序
VS2010 C++学习(5):基于DirectShow的视频 预览录像程序 学习VC++编制的基于DirectShow视频捕获程序,主要练习基于DirectShow程序的应用. 一. ...
- OpenCV + python 实现人脸检测(基于照片和视频进行检测)
OpenCV + python 实现人脸检测(基于照片和视频进行检测) Haar-like 通俗的来讲,就是作为人脸特征即可. Haar特征值反映了图像的灰度变化情况.例如:脸部的一些特征能由矩形特征 ...
随机推荐
- Customized Mini LED Keychain For Better Brand Identity
Looking for products that tell people the brand name? Then you'll find an affordable product that wi ...
- Javascript标准参考教程学习记录
教程:http://javascript.ruanyifeng.com/ 基本语法 - 函数 1.函数名的提升 JavaScript引擎将函数名视同变量名,采用function命令声明函数时,整个函数 ...
- IDEA 运行项目、模块的多个实例
IDEA默认只能运行同一项目|模块的一个实例. 运行多个实例: 比如springcloud的端口设置: --server.port=9001 . 当然,也可以在项目的配置文件中修改参数. 命令行.ID ...
- import matplotlib.pyplot as plt出错
>>>import matplotlib.pyplot as plt /usr/lib/python2.7/dist-packages/matplotlib/font_manager ...
- kill pkill
首先说一下kill命令,它是通过pid(进程ID)来杀死进程,要得到某个进程的pid,我们可以使用ps(process status)命令,默认情况下,kill命令发送给进程的终止信号是15,但是有些 ...
- linux使用tree将目录结构写进txt
比如把caffe的二级目录结构写进txt: tree -L > /home/wmz/treecaffe.txt 则会在/home/wmz/目录下生成一个名为treecaffe.txt的文件,文件 ...
- Python 多任务(进程) day1(3)
进程间的通信 可以用socket进行进程间的通信 可以用同意文件来进行通信(但是在硬盘上读取和写入比较慢,内存运行太快了) Queue队列(记得是队列) 在同一内存中通信 因为进程之间不能共享全局变 ...
- Linux环境下mysql报错:bash: mysql: command not found 的解决方法
# mysql -u root-bash: mysql: command not found 原因:这是由于系统默认会查找/usr/bin下的命令. 如果这个命令不在这个目录下,当然会找不到命令. 我 ...
- 【设计模式】UML类图及Java的类之间的关系
UML类图展示 设计模式中的对象关系 关联和依赖的对比 依赖关系 虚线箭头 依赖是a类成员方法中有b类的属性,动物新陈代谢方法中有水和空气的属性,只有调这个方法的时候,才可能临时用一下 关联关系 实线 ...
- 什么是buffer?
Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的.且在 V8 堆外分配物理内存. Buffer 的大小在被创建时确定,且无法调整. Buffer 类在 Node.js 中是一个全 ...