FPGA图像处理------常用语法(参数化、条件编译、位宽匹配以及二维数组)

1、参数化

        在图像处理的过程中,图像宽度、高度、位宽和处理尺寸等最好可以作为可配置的参数,方便代码的维护和移植。参数化主要有两种方式:define关键字和parameter关键字。

        parameter关键字类似与C与语言中的形参,可以在模块调用时实例化参数,但是在运行中不可更改,编译好就确定了。

示例:

//canny算子模块
module canny (
    rst_n,
    clk,
    din_valid,
    din,
    dout,
    dout_valid,
    vsync,
    vsync_out    
);

parameter  DW       = 14  ;    // 图像数据位宽
parameter  KSZ      = 3   ;    //Sobel算子尺寸
parameter  IH       = 512 ;    //图像高度
parameter  IW       = 640 ;    //图像宽度
parameter  ThrHight = 20  ;    //阈值化高度
parameter  ThrLow   = 10 ;    //阈值话宽度

//cordic module data with 

parameter  DW_OUT = 20 ;
parameter  DW_IN  = 16 ;

//本地参数
localparameter   NMS_LATENCY  = 5 ;
localparameter   HT_LATENCY   = 4 ;

input    rst_n           ;
input    clk             ;
input    [DW - 1:0] din  ;
input    vsync           ;
output   [DW - 1:0] dout ;
output   vsync_out       ;
output   dout_valid      ;

        在进行模块调用时有两种方法,在这里只介绍一种比较直观地方法。

canny u_canny(
    .rst_n     (rst_n ); 
    .clk       (clk); 
    .din_valid (din_valid); 
    .din       (din); 
    .dout      (dout); 
    .dout_valid(dout_valid); 
    .vsync     (vsync); 
    .vsync_out (vsync_out);   
);

defparam   u_canny.DW       = 8 ;
defparam   u_canny.KSZ      = 3 ;
defparam   u_canny.IH       = 512 ;
defparam   u_canny.IW       = 640 ;
defparam   u_canny.ThrHight = 20 ;
defparam   u_canny.ThrLow   = 10 ;
defparam   u_canny.DW_out   = 640 ;
defparam   u_canny.DW_IN    = 640 ;

        模块定义中的参数为默认参数,在调用过程中没有实例化的时候就默认为这些参数。

         define关键字,类似于C语言中的define,主要用于本地模块的一些参数定义。例如状态机及其常量定义。例如:SDRAM的初始化操作状态机

        

`define                 I_NOP                 5'd0      //等待上电200us稳定器结束
`define                 I_PRE                 5'd1      //预充电状态
`define                 I_TRP                 5'd2      //等待预充电完成trp
`define                 I_AR1                 5'd3      //第一次刷新
`define                 I_TRF1                5'd4      //等待第一次自刷新结束tRFC 
`define                 I_AR2                 5'd5      //第二次刷新
`define                 I_TRF2                5'd6      //等待第二次自刷新结束tRFC 
`define                 I_AR3                 5'd7      //第三次刷新
`define                 I_TRF3                5'd8      //等待第三次自刷新结束tRFC 
`define                 I_AR4                 5'd9      //第四次刷新
`define                 I_TRF4                5'd10      //等待第四次自刷新结束tRFC 
`define                 I_MRS                 5'd11      //模式寄存器设置
`define                 I_TMRD                5'd12     //等待寄存器设置
`define                 I_DONE                5'd21     //初始化完成


reg [4:0]        init_state   //SDRAM 初始化设置
always@(posedge clk or negedge rst_n)   begin 
    if(!rst_n) 
        init_state <= 'I_NOP ;
    else begin 
        case(init_state):
            `I_NOP = init_state ? `I_PRE : `I_NOP ;   //上电复位200us
            `I_PRE = init_state <= (TRP_CLK == 0) ? `I_AR1 : `I_TRP ;   //预充电
            `I_TRP = init_state <= ('end_trp) ? `I_AR1 : `I_TRP ;       //预充电等待两个TRP时钟周期        
            `I_AR1  = init_state   <= (TRFC_CLK == 0) ? 'I_AR2 :'I_TF1;
            `I_TRF1 = init_state   <= (`end_trfc )    ? 'I_AR2 :'I_TF1;
            `I_AR2  = init_state   <= (TRFC_CLK == 0) ? 'I_AR3 :'I_TF2;
            `I_TRF2 = init_state   <= (`end_trfc)     ? 'I_AR3 :'I_TF2;
            `I_AR3  = init_state   <= (TRFC_CLK == 0) ? 'I_AR4 :'I_TF3;
            `I_TRF3 = init_state   <= (`end_trfc)     ? 'I_AR4 :'I_TF3;
            `I_AR4  = init_state   <= (TRFC_CLK == 0) ? 'I_MSR :'I_TF4;
            `I_TRF4 = init_state   <= (`end_trfc)     ? 'I_MSR :'I_TF4;
            `I_MRS  = init_state   <= (TMRD_CLK == 0) ? 'I_DONE :'I_TMRD;  //寄存器模式设置
            `I_DONE:init_state     <= (`end_tmrd) ? `I_DONE : `I_TMRD ;
            //等待模式寄存器设置完成
            `I_DONE:init_state     <= `I_DONE  ;    //SDRAM的初始化配置设置完成标志
            default :init_state <= `I_NOP ;
            endcase


     end
end

2、条件编译

        条件编译在图像处理算法方面特别有用,由于FPGA本身资源限制,在处理不同尺寸的图像时或者是算法使用两个不同尺寸的算子进行配合时,设置独立的电路非常麻烦。在这时可以使用Verilog语言中的Generate语句,实现条件编译功能,此语句类似于C语言中的#ifdef语句。

        如在二维卷积过程中的应用,根据不同的处理尺寸例化不同数目的行缓存。

parameter    KSZ = 3 ; //处理尺寸
reg          rst_all ;
reg          [DW-1:0]   line_din   [0:KSZ-2] ;
wire         [DW-1:0]   line_dout  [0:KSZ-2] ;
wire         [KSZ-2:0]  line_empty;
wire         [KSZ-2:0]  line_full ;
wire         [KSZ-2:0]  line_rden ;
wire         [9:0]      line_count[0:KSZ-2];
reg          [KSZ-2:0]  line_wren ;

generate 
    begin : line_buffer
        genvar   i ;
        for(i = 0 ;i<=KSZ-2 ; i= i+1)
            begin
                line_buffer #(DW,IW)
                line_buf_inst(
                    .rst(rst_all),
                    .clk(clk),
                    .din(line_din[i]),
                    .dout(line_dout[i]),
                    .wr_en(line_wren[i]),
                    .rd_en(line_rden[i]),
                    .empty(line_empty[i]),
                    .full(line_full[i]),
                    .count(line_count[i])
                );
            end
    end
endgenerate

        对于不同参数电路的细节不同,如下

generate 
    if(KSZ == 3)
    begin : MAP16
        //针对窗口为三算法进行处理
    end
endgenerate

generate
    if(KSZ == 5 )
    begin : MAP17
        //针对尺寸为5的算法进行处理
    end
endgenerate 

3、位宽匹配 

         Verilog和HDL相比,位宽匹配不够严格,也就是表达式两边的信号位宽可能不一致,为了保证后续计算的准确性,要进行位宽匹配:

        例子如下:

parameter DW = 8 ; //位宽数据
reg [DW-1:0] cnt ;
always@(posedge clk or negedge rst_n) begin

    if(reset_1 == 1'b0)
        cnt <= #1   {DW{1'b0}};
    else
    
        cnt <= #1 cnt + {{DW-1{1'b0}},1'b1};
end 

4、二维数组 

        图像处理为一个二维计算领域,二维数组和for循环在Verilog电路设计中十分有用,一幅图像可以存储在一个二维数组中,这个数组的尺寸分别为宽度和高度。虽然这样存储比较方便,但是所占用的存储空间很大,实际的应用中,将二维数组作为处理算法中的串口,例如二维卷积窗口。 

parameter DW i ;
parameter KSZ = 3;
reg [DW-1;0] window_buf[0:KSZ*KSZ - 1];

        window_buf为定义的二维数组,数组的个数为矩形窗口内的像素总数,如上代码,窗口尺寸为3,数据个数为9.

 

 

 

 参考资料:基于FPGA的数字图像处理原理以及意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值