基于Modelsim的视频流仿真

一、前言

  最近在看牟新刚写的《基于FPGA的数字图像处理原理及应用》,书中关于FPGA数字图像处理的原理的原理写的非常透彻,在网上寻找了很久都没有找到完整的源代码工程,因此尝试自己做了补充/仿真,并与书中结果进行了比对。2020-02-16 15:40:21

二、视频时序模拟

  如图一所示,为参考书中的代码及说明整理出来的时序图,其中仿真视频图像宽度(IW=640),高度(IH=512),sync_b为场前肩参数,sync_e为场同步脉冲参数,vld为场后肩参数。从时序图中可以看出dvsync标记了一副新图像的起始点,书中并没有标记图像传输结束到下一帧图像开始(图像的场前肩)需要多少时间,通过v_total可以计算出有23行图像的时间(不知道实际场景下这个参数是否也是按照v_total来换算)。同理书中也没有标注sync_h(行消隐脉冲的时间),通过计算可知为(h_total-640)=800个像素时钟。

三、代码注解与分析

  (1)image_src.v文件分析,该部分代码主要用于实现图一中视频流的模拟时序包括行/场信号的生成及像素数据的生成。

  1 /*
  2 ***********************************************************************************************************
  3 **    Input file: None
  4 **    Component name: image_src.v
  5 **    Author:    zhengXiaoliang
  6 **  Company: WHUT
  7 **    Description: to simulate dvd stream
  8 ***********************************************************************************************************
  9 */
 10 
 11 `timescale 1ns/1ns
 12 
 13 `define SEEK_SET 0 
 14 `define SEEK_CUR 1
 15 `define SEEK_END 2
 16 
 17 module image_src(
 18     reset_l,        //全局复位
 19     clk,            //同步时钟
 20     src_sel,        //数据源通道选择
 21     test_vsync,        //场同步输出
 22     test_dvalid,     //像素有效输出
 23     test_data,        //像素数据输出
 24     clk_out            //像素时钟输出
 25 );
 26 
 27 parameter iw = 640;        //默认视频宽度
 28 parameter ih = 512;        //默认视频高度
 29 parameter dw = 8;        //默认像素数据位宽
 30 
 31 parameter h_total = 1440;    //行总数
 32 parameter v_total = 600;    //垂直总数
 33 
 34 parameter sync_b = 5;        //场前肩
 35 parameter sync_e = 55;        //场同步脉冲
 36 parameter vld_b = 65;        //场后肩
 37 
 38 //port decleared
 39 input reset_l,clk;
 40 input [3:0] src_sel;    //to select the input file
 41 output test_vsync, test_dvalid,clk_out;
 42 output [dw-1:0] test_data;
 43 
 44 
 45 //variable decleared
 46 reg [dw-1:0] test_data_reg;
 47 reg test_vsync_temp;
 48 reg test_dvalid_tmp;
 49 reg [1:0] test_dvalid_r;
 50 
 51 reg [10:0] h_cnt;
 52 reg [10:0] v_cnt;
 53 
 54 integer fp_r;
 55 integer cnt = 0;
 56 
 57 //输出像素时钟
 58 assign clk_out = clk;    //output the dv clk
 59 
 60 //输出像素数据
 61 assign test_data = test_data_reg; //test data output 
 62 
 63 //当行同步有效时,从文件读取像素数据输出到数据线上
 64 always@(posedge clk or posedge test_vsync_temp)begin
 65     if(((~(test_vsync_temp))) == 1'b0) //场同步清零文件指针
 66         cnt <= 0; //clear file pointer when a new frame comes
 67     else begin
 68         if(test_dvalid_tmp == 1'b1)begin //行同步有效,说明当前时钟数据有效
 69             case(src_sel) //选择不同的数据源
 70                 4'b0000: fp_r = $fopen("E:/Modelsim/image_src/txt_source/test_src3.txt","r");
 71                 4'b0001: fp_r = $fopen("txt_source/test_scr1.txt","r");
 72                 4'b0010: fp_r = $fopen("txt_source/test_scr2.txt","r");
 73                 4'b0011: fp_r = $fopen("txt_source/test_scr3.txt","r");
 74                 4'b0100: fp_r = $fopen("txt_source/test_scr4.txt","r");
 75                 4'b0101: fp_r = $fopen("txt_source/test_scr5.txt","r");
 76                 4'b0110: fp_r = $fopen("txt_source/test_scr6.txt","r");
 77                 4'b0111: fp_r = $fopen("txt_source/test_scr7.txt","r");
 78                 4'b1000: fp_r = $fopen("txt_source/test_scr8.txt","r");
 79                 4'b1001: fp_r = $fopen("txt_source/test_scr9.txt","r");
 80                 4'b1010: fp_r = $fopen("txt_source/test_scr10.txt","r");
 81                 4'b1011: fp_r = $fopen("txt_source/test_scr11.txt","r");
 82                 4'b1100: fp_r = $fopen("txt_source/test_scr12.txt","r");
 83                 4'b1101: fp_r = $fopen("txt_source/test_scr13.txt","r");
 84                 4'b1110: fp_r = $fopen("txt_source/test_scr14.txt","r");
 85                 4'b1111: fp_r = $fopen("txt_source/test_scr15.txt","r");
 86                 default: fp_r = $fopen("txt_source/test_src3.txt","r");
 87             endcase
 88             
 89             $fseek(fp_r,cnt,`SEEK_SET); //查找当前需要读取的文件位置
 90             $fscanf(fp_r,"%02x\n",test_data_reg); //将数据按指定格式读入test_data_reg寄存器
 91         
 92             cnt <= cnt + 4; //移动文件指针到下一个数据
 93             $fclose(fp_r); //关闭文件
 94             $display("h_cnt = %d,v_cnt = %d, pixdata = %d",h_cnt,v_cnt,test_data_reg); //for debug use    
 95         end    
 96     end
 97 end
 98 
 99 //水平计数器,每来一个时钟就+1,加到h_total置零重新计数
100 always@(posedge clk or negedge reset_l)begin
101     if(((~(reset_l))) == 1'b1)
102         h_cnt <= #1 {11{1'b0}};
103     else begin
104         if(h_cnt == ((h_total -1)))
105             h_cnt <= #1 {11{1'b0}};
106         else
107             h_cnt <= #1 h_cnt + 11'b00000000001;
108     end
109 end
110 
111 //垂直计数器:水平计数器计满后+1,计满后清零
112 always@(posedge clk or negedge reset_l)begin
113     if(((~(reset_l))) == 1'b1)
114         v_cnt <= #1 {11{1'b0}};
115     else begin
116         if(h_cnt == ((h_total - 1)))begin
117             if(v_cnt == ((v_total - 1)))
118                 v_cnt <= #1 {11{1'b0}};
119             else
120                 v_cnt <= #1 v_cnt + 11'b00000000001;
121         end
122     end
123 end
124 
125 //场同步信号生成
126 always@(posedge clk or negedge reset_l)begin
127     if(((~(reset_l))) == 1'b1)
128         test_vsync_temp <= #1 1'b1;
129     else begin
130         if(v_cnt >= sync_b & v_cnt <= sync_e)
131             test_vsync_temp <= #1 1'b1;
132         else
133             test_vsync_temp <= #1 1'b0;
134     end
135 end
136 
137 assign test_vsync = (~test_vsync_temp);
138 
139 //水平同步信号生成
140 always@(posedge clk or negedge reset_l)begin
141     if(((~(reset_l))) == 1'b1)
142         test_dvalid_tmp <= #1 1'b0;
143     else begin
144         if(v_cnt >= vld_b & v_cnt < ((vld_b + ih)))begin
145             if(h_cnt == 10'b0000000000)
146                 test_dvalid_tmp <= #1 1'b1;
147             else if(h_cnt == iw)
148                 test_dvalid_tmp <= #1 1'b0;    
149         end
150         else 
151             test_dvalid_tmp <= #1 1'b0;
152     end
153 end
154 
155 //水平同步信号输出
156 assign test_dvalid = test_dvalid_tmp;
157 
158 always@(posedge clk or negedge reset_l)begin
159     if(((~(reset_l))) == 1'b1)
160         test_dvalid_r <= #1 2'b00;
161     else
162         test_dvalid_r <= #1 ({test_dvalid_r[0],test_dvalid_tmp});
163 end
164 
165 endmodule

  (2)视频流仿真代码image_src_tb.v的补充编写。

 1 `timescale 1ns/1ns
 2 
 3 module image_src_tb;
 4 
 5 parameter iw = 640;        //图像宽度
 6 parameter ih = 512;     //图像高度
 7 parameter dvd_dw = 8;    //数据位宽
 8 
 9 //视频时序参数
10 parameter h_total = 1440;
11 parameter v_total = 600;
12 parameter sync_b = 5;
13 parameter sync_e = 55;
14 parameter vld_b = 65;
15 
16 reg clk;
17 reg reset_l;
18 
19 wire dv_clk;
20 wire dvsyn;
21 wire dhsyn;
22 wire [dvd_dw-1:0] dvd;
23 
24 /*根据时序参数例化一个视频源*/
25 image_src #(iw,ih,dvd_dw,h_total,v_total,sync_b,sync_e,vld_b)
26 image_src_ins(
27     .clk(clk),
28     .reset_l(reset_l),
29     .src_sel(4'b0000),
30     .test_data(dvd),
31     .test_dvalid(dhsyn),
32     .test_vsync(dvsyn),
33     .clk_out(dv_clk)
34 );
35 
36 initial begin
37     clk = 0;
38     reset_l = 1;
39     #100;
40     reset_l = 0;
41     #100;
42     reset_l = 1;
43 end
44 
45 //时钟产生:≈51.84MHz
46 always@(reset_l or clk)begin
47     if((~(reset_l)) == 1'b1)
48         clk <= 1'b0;
49     else
50         clk <= #9645 (~(clk));
51 end
52 
53 endmodule

  (3)视频码流文件的生成,由于书中并没有描述如何生成视频码流的文件,因此用Maltab编写了一个rgb2txt文件,生成了一份码流文件经过测试可用。

 1 %将256位的BMP灰度图像128*128大小生成TXT文档;
 2 clc
 3 close all
 4 
 5 I_rgb = imread('lena_512x512.jpg');
 6 subplot(1,3,1),imshow(I_rgb),title('lena-rgb')
 7 
 8 I_gray = rgb2gray(I_rgb);
 9 subplot(1,3,2),imshow(I_gray),title('lena-gray')
10 
11 %% 改变图像尺寸
12 I_resize = imresize(I_gray,[640 512],'nearest');%nearest(默认值) 最近邻插值 ‘bilinear’双线性插值 ‘bicubic’ 双三次插值
13 % I = imresize(I_gray,0.25);
14 % subplot(1,3,3),imshow(I),title('lena-qtr')
15 
16 fid = fopen('./lena_640x512_hex.txt','wt');
17 for i = 1:size(I_resize,1)
18     for j = 1:size(I_resize,2)
19         fprintf(fid,'%2x\n',I_resize(i,j));%每个数据之间用空格分开
20     end
21     %fprintf(fid,'\n');
22 end
23 
24 fid = fclose(fid);
25 I_data = load('./lena_640x512.txt');

  (4)用于Modelsim仿真的.do文件的编写,image_src.do代码如下:

 1 #切换至工程目录
 2 cd E:/Modelsim/image_src
 3 
 4 #打开工程
 5 project open E:/Modelsim/image_src
 6 
 7 #编译相关文件(一般情况下为频繁改动的文件)
 8 vlog E:/Modelsim/image_src/image_src.v
 9 vlog E:/Modelsim/image_src/image_src_tb.v
10 
11 #开始仿真
12 vsim -novopt -L altera_lib work.image_src_tb
13 
14 #添加顶层所有的信号
15 add wave *
16 
17 #取消警告
18 set StdArithNoWarnings 1
19 
20 #开始运行
21 run -all

 

四、仿真结果

  如图二所示,为测试仿真结果与书中仿真结果图像一致。

 

 

 

 

 

 

posted @ 2020-02-16 16:36  TheSkyIsMine  阅读(700)  评论(0编辑  收藏  举报