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的数字图像处理原理以及意义