一、前言

  针对牟新刚编著的《基于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的视频捕获模拟仿真的更多相关文章

  1. 基于Modelsim的直方图统计算法仿真

    一.前言 本篇主要针对牟新刚编著<基于FPGA的数字图像处理及应用>第六章第五节中直方图统计相关类容进行总结,包括代码实现及 基于Modelsim的仿真.书读百遍,其意自现. 2020-0 ...

  2. 基于Modelsim的视频流仿真

    一.前言 最近在看牟新刚写的<基于FPGA的数字图像处理原理及应用>,书中关于FPGA数字图像处理的原理的原理写的非常透彻,在网上寻找了很久都没有找到完整的源代码工程,因此尝试自己做了补充 ...

  3. 基于HTML5的燃气3D培训仿真系统

    最近上线了的基于HTML5的燃气3D培训仿真系统,以前的老系统是采用基于C++和OpenGL的OpenSceneGraph引擎设计的,OSG引擎性能和渲染效果各方面还是不错的,但因为这次新产品需求要求 ...

  4. 【转】基于DM8168的视频智能分析系统的设计方案

        [导读] 为了实现高清视频的智能分析功能,本文介绍了一种以TI公司的DM8168为核心的高清视频智能分析系统的设计方案,该方案从硬件设计和软件设计两个方面介绍了硬件组成.工作流程.软件架构,并 ...

  5. 【转】基于V4L2的视频驱动开发

    编写基于V4L2视频驱动主要涉及到以下几个知识点:1> 摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等.2> Camera解码器.控制器 ...

  6. 基于V4L2的视频驱动开发【转】

    转自:http://blog.chinaunix.net/uid-10747583-id-298573.html Tags:V4L2驱动框架.API.操作流程…… 原文地址:http://www.ee ...

  7. 基于Linux的视频传输系统(上大学时參加的一个大赛的论文)

    文件夹 1原创性声明----------------------------------------------------3 2 摘要-------------------------------- ...

  8. VS2010 C++学习(5):基于DirectShow的视频预览录像程序

    VS2010 C++学习(5):基于DirectShow的视频 预览录像程序 学习VC++编制的基于DirectShow视频捕获程序,主要练习基于DirectShow程序的应用. 一.         ...

  9. OpenCV + python 实现人脸检测(基于照片和视频进行检测)

    OpenCV + python 实现人脸检测(基于照片和视频进行检测) Haar-like 通俗的来讲,就是作为人脸特征即可. Haar特征值反映了图像的灰度变化情况.例如:脸部的一些特征能由矩形特征 ...

随机推荐

  1. 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 ...

  2. Javascript标准参考教程学习记录

    教程:http://javascript.ruanyifeng.com/ 基本语法 - 函数 1.函数名的提升 JavaScript引擎将函数名视同变量名,采用function命令声明函数时,整个函数 ...

  3. IDEA 运行项目、模块的多个实例

    IDEA默认只能运行同一项目|模块的一个实例. 运行多个实例: 比如springcloud的端口设置: --server.port=9001 . 当然,也可以在项目的配置文件中修改参数. 命令行.ID ...

  4. import matplotlib.pyplot as plt出错

    >>>import matplotlib.pyplot as plt /usr/lib/python2.7/dist-packages/matplotlib/font_manager ...

  5. kill pkill

    首先说一下kill命令,它是通过pid(进程ID)来杀死进程,要得到某个进程的pid,我们可以使用ps(process status)命令,默认情况下,kill命令发送给进程的终止信号是15,但是有些 ...

  6. linux使用tree将目录结构写进txt

    比如把caffe的二级目录结构写进txt: tree -L > /home/wmz/treecaffe.txt 则会在/home/wmz/目录下生成一个名为treecaffe.txt的文件,文件 ...

  7. Python 多任务(进程) day1(3)

    进程间的通信 可以用socket进行进程间的通信 可以用同意文件来进行通信(但是在硬盘上读取和写入比较慢,内存运行太快了) Queue队列(记得是队列)  在同一内存中通信 因为进程之间不能共享全局变 ...

  8. Linux环境下mysql报错:bash: mysql: command not found 的解决方法

    # mysql -u root-bash: mysql: command not found 原因:这是由于系统默认会查找/usr/bin下的命令. 如果这个命令不在这个目录下,当然会找不到命令. 我 ...

  9. 【设计模式】UML类图及Java的类之间的关系

    UML类图展示 设计模式中的对象关系 关联和依赖的对比 依赖关系 虚线箭头 依赖是a类成员方法中有b类的属性,动物新陈代谢方法中有水和空气的属性,只有调这个方法的时候,才可能临时用一下 关联关系 实线 ...

  10. 什么是buffer?

    Buffer 类的实例类似于整数数组,但 Buffer 的大小是固定的.且在 V8 堆外分配物理内存. Buffer 的大小在被创建时确定,且无法调整. Buffer 类在 Node.js 中是一个全 ...