FPGA视频仿真

时间:2021-08-05
本文章向大家介绍FPGA视频仿真,主要包括FPGA视频仿真使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

视频图像处理仿真测试系统

最近看《基于FPGA的数字图像处理原理及应用》看到了第五章,本章内容主要讲如何搭建一个视频图像处理仿真测试系统,我参考了书上的内容,自己设计了一个基于Qt creator的仿真测试系统。

1.仿真测试系统框架

仿真测试系统所包含的功能:
(1)模拟可配置的视频流(单帧的视频即为一副图像)
(2)模拟视频捕获,生成视频数据
(3)测试系统与testbench及视频流的数据共享
(4)可视化的图像及视频操作
(5)对FPGA的处理结果进行验证
仿真测试系统结构如下图所示:

上面是从书上的截图,这里我将Qt测试程序代替MFC测试程序。

仿真测试系统由Qt平台和Modelsim共同搭建完成。模拟测试的完整过程如下:
1.Qt将一张图片所有的RGB颜色信息存储在文本中。
2.modelsim模拟视频流传输,读取(1)中图像信息生成视频流,并模拟捕获视频流,并将这些图像信息写入另一个文本中。
3.Qt将模拟捕获的图像文本信息读取并生成图像。

通过以上可知,Qt平台与FPGA模拟视频流及处理结果通过文本实现数据共享。

2 视频时序模拟

2.1 基本视频时序

图2-1是一个典型的CMOS视频输出时序图:

图2-1
下面是上面时序各个信号的说明:
(1)HSYNC:行同步信号
(2)FILED:场信号,表示当前的场是奇数场还是偶数场
(3)VSYNC:帧同步信号
(4)CLK:像素时钟即PCLK
(5)DATA:有效像素数据,仅在行同步有效时才有效
(6)h_totol:一行的总像素个数
(7)v_totol:一副图像总的行数
(8)sync_h:行消隐
(9)sync_v:场同步脉冲
(10)torch_f:场前肩
(11)torch_b:场后肩

2.2 Verilog示例代码

参考书上的代码,我自己实现了上述视频流的模拟时序(像素数据从文本中得到,一个像素RGB各占一行)
这里自定义模拟视频流的参数信息如下:
图像有效宽度iw = 640;
图像有效高度ih = 480;
一行总像素个数h_total = 2000;
总行数v_total = 600 ;
场前肩torch_f = 5;
场同步脉冲sync_v = 20;
场后肩torch_b = 20;

/************************************************************************
 * Author        : Ye Qiang
 * Email         : 1247836708@qq.com
 * Create time   : 2019-03-22 11:12
 * Last modified : 2019-03-25 15:51
 * Filename      : image_src.v
 * Description   : 
 * *********************************************************************/
module  image_src(
        input                   clk                             ,//系统时钟
        input                   rst_n                           ,//系统复位
        input         [ 3: 0]   src_sel                         ,//图像选择  
        output  wire            pclk                            ,//像素时钟
        output  wire            Vsync                           ,//帧同步
        output  reg             Hsync                           ,//行同步
        output  wire  [ 7: 0]   img_data                         //8位图像数据
);
//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
//图像大小为640x480,RGB格式
parameter       iw              =       640*3                   ;//1920
parameter       ih              =       480                     ;

parameter       h_total         =       2000                    ;
parameter       v_total         =       600                     ;

parameter       torch_f         =       5                       ;//场前肩
parameter       sync_v          =       20                      ;//场同步脉冲
parameter       torch_b         =       20                      ;//场后肩

parameter       sync_b          =       torch_f                 ;
parameter       sync_e          =       sync_b+sync_v           ;
parameter       vld_b           =       sync_e+torch_b          ;
parameter       sync_h          =       h_total-iw              ;


reg     [10: 0]                 h_cnt                           ;
wire                            add_h_cnt                       ;
wire                            end_h_cnt                       ;

reg     [ 9: 0]                 v_cnt                           ;
wire                            add_v_cnt                       ;
wire                            end_v_cnt                       ;

reg                             Hsync_temp                      ;
reg                             Vsync_temp                      ;

wire                            h_flag                          ;
integer                         p_cnt = 0                       ;
integer                         fp_r                            ;
reg     [ 7: 0]                 test_data_reg                   ;

//======================================================================\
//**************************** Main Code *******************************
//======================================================================/

assign  pclk        =           clk                             ;

//h_cnt
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        h_cnt <= 0;
    end
    else if(add_h_cnt)begin
        if(end_h_cnt)
            h_cnt <= 0;
        else
            h_cnt <= h_cnt + 1;
    end
end

assign  add_h_cnt        =       1'b1;
assign  end_h_cnt        =       add_h_cnt && h_cnt == h_total-1;

//v_cnt  行计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        v_cnt <= 0;
    end
    else if(add_v_cnt)begin
        if(end_v_cnt)
            v_cnt <= 0;
        else
            v_cnt <= v_cnt + 1;
    end
end

assign  add_v_cnt        =       end_h_cnt;
assign  end_v_cnt        =       add_v_cnt && v_cnt ==  v_total;

//Vsync_temp
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        Vsync_temp <=  1'b1;
    end
    else if(v_cnt >= sync_b && v_cnt <= sync_e )begin
        Vsync_temp <=  1'b0;
    end
    else begin
        Vsync_temp <=  1'b1;
    end
end

assign Vsync = Vsync_temp;

//h_flag
assign h_flag = (v_cnt >= vld_b) && (v_cnt < (vld_b+ih))        ;

//Hsync_temp
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        Hsync_temp <=  1'b0;
    end
    else if(h_flag)begin
        if(h_cnt == 0)begin
            Hsync_temp <=  1'b1;  
        end
        else if(h_cnt == iw)
            Hsync_temp <=  1'b0;
    end
    else begin
        Hsync_temp <=  1'b0;
    end
end

//Hsync
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        Hsync  <=  1'b0;
    end
    else begin
        Hsync  <=  Hsync_temp;
    end
end

//当行同步有效时,从文件读取像素数据输出到数据线上
always  @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        p_cnt   <=  0;
        test_data_reg   <=  {8{1'b0}};
    end
    else if(Vsync_temp == 1'b0)begin
        p_cnt   <=  0;
    end
    else begin
        if(Hsync_temp)begin
            case(src_sel)
                4'b0000 :fp_r   =   $fopen("txt_source/test_src0.txt", "r");
                4'b0001 :fp_r   =   $fopen("txt_source/test_src1.txt", "r");
                4'b0010 :fp_r   =   $fopen("txt_source/test_src2.txt", "r");
                4'b0011 :fp_r   =   $fopen("txt_source/test_src3.txt", "r");
                4'b0100 :fp_r   =   $fopen("txt_source/test_src4.txt", "r");
                4'b0101 :fp_r   =   $fopen("txt_source/test_src5.txt", "r");
                4'b0110 :fp_r   =   $fopen("txt_source/test_src6.txt", "r");
                4'b0111 :fp_r   =   $fopen("txt_source/test_src7.txt", "r");
                4'b1000 :fp_r   =   $fopen("txt_source/test_src8.txt", "r");
                4'b1001 :fp_r   =   $fopen("txt_source/test_src9.txt", "r");
                4'b1010 :fp_r   =   $fopen("txt_source/test_src10.txt", "r");
                4'b1011 :fp_r   =   $fopen("txt_source/test_src11.txt", "r");
                4'b1100 :fp_r   =   $fopen("txt_source/test_src12.txt", "r");
                4'b1101 :fp_r   =   $fopen("txt_source/test_src13.txt", "r");
                4'b1110 :fp_r   =   $fopen("txt_source/test_src14.txt", "r");
                4'b1111 :fp_r   =   $fopen("txt_source/test_src15.txt", "r");
                default :fp_r   =   $fopen("txt_source/test_src0.txt", "r" );
            endcase
        $fseek(fp_r, p_cnt, 0);//查找当前需要读取的文件位置
        $fscanf(fp_r, "%02x\n", test_data_reg);//将数据按指定格式读入test_data_reg寄存器
        p_cnt <=  p_cnt + 4;//移动文件指针到下一个数据
        $fclose(fp_r);//关闭文件
        //$display("%02x", test_data_reg);
        end
    end
end

assign  img_data   =   test_data_reg                            ;

endmodule

3 视频捕获模拟

本模块的目的是对上一节产生的视频信号进行捕获并解析

3.1 完成内容

(1) 位宽转换:将八位视频流数据转换成24位数据。这里通过一个深度为1024的fifo来辅助实现
(2) 生成本地帧同步和行同步信号
(3) 将图像数据写入文本中,以供Qt平台读取显示

3.2 Verilog代码设计

/************************************************************************
 * Author        : Ye Qiang
 * Email         : 1247836708@qq.com
 * Create time   : 2019-03-25 15:55
 * Last modified : 2019-03-27 16:58
 * Filename      : video_cap.v
 * Description   : video_cap
 * *********************************************************************/
module  video_cap(
        input                   pclk                            ,//像素时钟
        input                   rst_n                           ,//系统复位
        input                   Vsync                           ,//帧同步
        input                   Hsync                           ,//行同步
        input         [ 7: 0]   img_data                        ,//八位图像数据
        input                   cap_clk                         ,//本地时钟
        output  wire            cap_Vsync                       ,//本行帧同步
        output  reg             cap_Hsync                       ,//本地行同步
        output  wire  [23: 0]   cap_img_data                     //捕获的24位图像数据
);

//======================================================================\
//************** Define Parameter and Internal Signals *****************
//======================================================================/
parameter       iw      =       10'd640                         ;
parameter       ih      =       9'd480                          ;


reg     [ 2: 0]                 cap_Vsync_r                     ;
wire                            cap_Vsync_pos                   ;
wire                            cap_Vsync_neg                   ;

reg     [23: 0]                 fifo_in_data                    ;
reg                             rdreq                           ;
reg                             wrreq                           ;
wire    [23: 0]                 fifo_out_data                   ;
wire                            rdempty                         ;
wire    [ 9: 0]                 rdusedw                         ;
wire                            wrfull                          ;
wire    [ 9: 0]                 wrusedw                         ;

reg     [ 1: 0]                 pix_cnt                         ;
wire                            add_pix_cnt                     ;
wire                            end_pix_cnt                     ;

reg     [ 9: 0]                 rdata_cnt                       ;
wire                            add_rdata_cnt                   ;
wire                            end_rdata_cnt                   ;
//======================================================================\
//**************************** Main Code *******************************
//======================================================================/


//bit width conversion 8 to 24
//pix_cnt
always @(posedge pclk or negedge rst_n)begin
    if(!rst_n)begin
        pix_cnt <= 0;
    end
    else if(add_pix_cnt)begin
        if(end_pix_cnt)
            pix_cnt <= 0;
        else
            pix_cnt <= pix_cnt + 1;
    end
end

assign  add_pix_cnt     =       Vsync && Hsync;       
assign  end_pix_cnt     =       add_pix_cnt && pix_cnt == 3-1;   

//fifo_in_data
always  @(posedge pclk or negedge rst_n)begin
    if(!rst_n)begin
        fifo_in_data    <=  {24{1'b0}};
    end
    else if(add_pix_cnt)begin
        fifo_in_data    <=  {fifo_in_data[15:0], img_data};
    end
end

//wrreq
always  @(posedge pclk or negedge rst_n)begin
    if(!rst_n)begin
        wrreq   <=  1'b0;
    end
    else if(!wrfull && end_pix_cnt)begin
        wrreq   <=  1'b1;
    end
    else begin
        wrreq   <=  1'b0;
    end
end

//rdreq
always  @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        rdreq   <=  1'b0;
    end
    else if(wrusedw == iw)begin
        rdreq   <=  1'b1;
    end
    else if(end_rdata_cnt)begin
        rdreq   <=  1'b0;
    end
end

//cap_Vsync_r
always  @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        cap_Vsync_r <=  {3{1'b1}};
    end
    else begin
        cap_Vsync_r <=  {cap_Vsync_r[1:0], Vsync};
    end
end

assign  cap_Vsync   =   cap_Vsync_r[2]                          ;

assign  cap_Vsync_neg   =   cap_Vsync_r[2] && ~cap_Vsync_r[1]   ;
assign  cap_Vsync_pos   =   ~cap_Vsync_r[2] && cap_Vsync_r[1]   ;

//cap_Hsync
always  @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        cap_Hsync   <=  1'b0;
    end
    else begin
        cap_Hsync   <=  rdreq;
    end
end

//rdata_cnt
always @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        rdata_cnt <= 0;
    end
    else if(add_rdata_cnt)begin
        if(end_rdata_cnt)
            rdata_cnt <= 0;
        else
            rdata_cnt <= rdata_cnt + 1;
    end
end

assign  add_rdata_cnt       =       rdreq;       
assign  end_rdata_cnt       =       add_rdata_cnt && rdata_cnt == iw-1;   

//cap_img_data
assign  cap_img_data        =       fifo_out_data               ;

//将图像数据写入文本
integer             fp_w                                        ;
integer             p_cnt                                       ;

reg     [ 3: 0]                 v_pos_cnt                       ;
wire                            add_v_pos_cnt                   ;
wire                            end_v_pos_cnt                   ;

//v_pos_cnt
always @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        v_pos_cnt <= 0;
    end
    else if(add_v_pos_cnt)begin
        if(end_v_pos_cnt)
            v_pos_cnt <= 0;
        else
            v_pos_cnt <= v_pos_cnt + 1;
    end
end

assign  add_v_pos_cnt     =       cap_Vsync_pos;       
assign  end_v_pos_cnt     =       add_v_pos_cnt && v_pos_cnt == 4-1;   


//当行同步有效时,从文件读取像素数据输出到数据线上
always  @(posedge cap_clk or negedge rst_n)begin
    if(!rst_n)begin
        p_cnt   <=  0;
    end
    else if(cap_Vsync_pos)begin
        p_cnt   <=  0;
        case(v_pos_cnt)
            4'd0:   fp_w   =   $fopen("txt_result/imgDataOut0.txt", "w");
            4'd1:   fp_w   =   $fopen("txt_result/imgDataOut1.txt", "w");
            4'd2:   fp_w   =   $fopen("txt_result/imgDataOut2.txt", "w");
            4'd3:   fp_w   =   $fopen("txt_result/imgDataOut3.txt", "w");
            4'd4:   fp_w   =   $fopen("txt_result/imgDataOut4.txt", "w");
            4'd5:   fp_w   =   $fopen("txt_result/imgDataOut5.txt", "w");
            4'd6:   fp_w   =   $fopen("txt_result/imgDataOut6.txt", "w");
            4'd7:   fp_w   =   $fopen("txt_result/imgDataOut7.txt", "w");
            4'd8:   fp_w   =   $fopen("txt_result/imgDataOut8.txt", "w");
            4'd9:   fp_w   =   $fopen("txt_result/imgDataOut9.txt", "w");
            4'd10:  fp_w   =   $fopen("txt_result/imgDataOut10.txt", "w");
            4'd11:  fp_w   =   $fopen("txt_result/imgDataOut11.txt", "w");
            4'd12:  fp_w   =   $fopen("txt_result/imgDataOut12.txt", "w");
            4'd13:  fp_w   =   $fopen("txt_result/imgDataOut13.txt", "w");
            4'd14:  fp_w   =   $fopen("txt_result/imgDataOut14.txt", "w");
            4'd15:  fp_w   =   $fopen("txt_result/imgDataOut15.txt", "w");
            default:fp_w   =   $fopen("txt_result/imgDataOut0.txt", "w");
        endcase
    end
    else if(cap_Vsync_neg)begin
        $fclose(fp_w);//关闭文件,写完一帧再关闭,否则仿真速度会很慢
        p_cnt   <=  0;
    end
    else if(cap_Hsync)begin
        $fseek(fp_w, p_cnt, 0);//查找当前需要读取的文件位置
        $fwrite(fp_w, "%06x\n", fifo_out_data);//将图像数据按指定格式写入文本中
        p_cnt <=  p_cnt + 8;//移动文件指针到下一个数据   一个字符占一个字节,回车占两个字符6+2=8    
    end    
end

//例化
myFifo myFifo_inst(
	.data(fifo_in_data),
	.rdclk(cap_clk),
	.rdreq(rdreq),
	.wrclk(pclk),
	.wrreq(wrreq),
	.q(fifo_out_data),
	.rdempty(rdempty),
	.rdusedw(rdusedw),
	.wrfull(wrfull),
	.wrusedw(wrusedw)
);

endmodule

4 激励文件

4.1 捕获要求

假定视频分辨率为64048024Bit RGB数据,传输数据位宽为8位,扫描为60Hz,每一帧的有效像素数为:
pixel_total = 3640480*60
本地的图像数据位数为24位,如果我们要完全捕获视频流,我们要求
本地像素时钟频率>模拟像素输出时钟频率/3

这里,自定义模拟像素输出时钟周期为10ns,本地像素时钟周期为24ns

下面是Verilog代码

`timescale		1ns/1ns

module	tb_top;

//=====================================================================\
// ********** Define Parameter and Internal Signals *************
//=====================================================================/
reg                             clk                             ;       
reg                             rst_n                           ;       
reg     [ 3: 0]                 src_sel                         ;

wire                            Vsync                           ;
wire                            Hsync                           ;
wire    [ 7: 0]                 img_data                        ;

reg                             cap_clk                         ;

wire                            cap_Vsync                       ;
wire                            cap_Hsync                       ;
wire    [23: 0]                 cap_img_data                    ;

//======================================================================
// ***************      Main    Code    ****************
//======================================================================
always  #5      clk     =   ~clk                                ;
always  #12     cap_clk =   ~cap_clk                            ;

initial begin
	clk 		<=  1'b1;
    cap_clk     <=  1'b1;
	rst_n	    <=  1'b0;
    src_sel     <=  4'd0;
	#100
	rst_n	    <=  1'b1;
    #11000000
    src_sel     <=  4'd1;

end

//例化
image_src   image_src_inst(
        .clk                    (clk                            ),
        .rst_n                  (rst_n                          ),
        .src_sel                (src_sel                        ),
        .pclk                   (pclk                           ),
        .Vsync                  (Vsync                          ),
        .Hsync                  (Hsync                          ),
        .img_data               (img_data                       )
);

video_cap   video_cap_inst(
        .pclk                   (pclk                           ),
        .rst_n                  (rst_n                          ),
        .Vsync                  (Vsync                          ),
        .Hsync                  (Hsync                          ),
        .img_data               (img_data                       ),
        .cap_clk                (cap_clk                        ),
        .cap_Vsync              (cap_Vsync                      ),
        .cap_Hsync              (cap_Hsync                      ),
        .cap_img_data           (cap_img_data                   )
);

endmodule

5 仿真结果

图5-1是整个系统的仿真情况。图中两根光标所指示的位置是图像的帧同步信号。

                        图5-1
                        
图5-2是一副图像中的一行数据,两根光标所指示的就是行同步信号。

                       图5-2

图5-3是将原始图像和经过FPGA处理后的图像经过Qt测试平台显示后的结果

                        图5-3

下面是系统仿真所有程序源码下载链接,直接用Qt软件打开工程,读懂代码,就可以自己搭建仿真测试系统
源码下载链接:
https://download.csdn.net/download/qq_31348733/11065858

原文地址:https://www.cnblogs.com/amxiang/p/15102318.html