Verilog 3x3图像模糊模块可综合性、正确性及赋值规则咨询
Verilog图像模糊模块的综合性、测试用例与赋值疑问解答
我是软件开发出身,编写了一个通过3x3像素滑动窗口计算平均值实现光栅图像模糊的Verilog模块。输入图像为按行从下到上(类似位图)存储的RGB像素字节流,模块在时钟上升沿选择并输出3x3窗口的平均值,时钟下降沿移动窗口,处理完成后拉高DONE信号。
目前仅使用Icarus Verilog测试代码,不确定测试用例是否正确,也不清楚代码是否具备可综合性,希望得到相关反馈。同时对何时/为何使用阻塞赋值(=)与非阻塞赋值(<=)存在疑问,虽了解=用于组合逻辑、<=用于时序逻辑,但不确定何时该使用组合逻辑或时序逻辑。
附上我的Verilog代码:
module blur #(parameter WIDTH = 640, // Image width HEIGHT = 426, // Image height INPUTBYTES = WIDTH*HEIGHT*3 ) ( input CLK, // clock input RST, // Reset (active low) input wire [7 : 0] input_memory [0 : INPUTBYTES-1],// memory to store 8-bit data image output reg [7 : 0] output_memory [0 : INPUTBYTES-1],// memory to store 8-bit data image output wire DONE // Done flag ); integer row = 0; // row index of the image integer col = 0; // col index of the image integer counter = 0; reg [7:0] DATA_R = 0; // 8 bit Red data reg [7:0] DATA_G = 0; // 8 bit Green data reg [7:0] DATA_B = 0; // 8 bit Blue data assign DONE = (counter > INPUTBYTES-1); always@(negedge CLK) begin if (col == WIDTH-3) begin col <= 0; row <= row+1; end else begin col <= col+1; end counter <= counter+3; end always@(posedge CLK) begin integer i; integer j; // Sum up all the R, G, B values of the 3x3 square for(i=0; i<3; i=i+1) begin for(j=0; j<3; j=j+1) begin DATA_R = DATA_R + input_memory[3*(WIDTH*(row+i)+(col+j))+0]; DATA_G = DATA_G + input_memory[3*(WIDTH*(row+i)+(col+j))+1]; DATA_B = DATA_B + input_memory[3*(WIDTH*(row+i)+(col+j))+2]; end end // Output is the averaged RGB values output_memory[counter+0] = DATA_R/9; output_memory[counter+1] = DATA_G/9; output_memory[counter+2] = DATA_B/9; end endmodule
一、代码可综合性问题
- 数组端口兼容性:多数FPGA综合工具不支持将多维数组(
input_memory、output_memory)直接作为模块端口,这类结构通常需要替换为单端口RAM接口(地址线+数据线),或者用扁平总线配合地址控制实现,否则会触发综合报错。 - 缺失复位逻辑:
row、col、counter这些integer变量没有复位分支,复位时无法回到初始值,会导致综合后的电路出现未知状态,必须在时钟触发的always块中添加RST分支来初始化这些变量。 - 双时钟沿触发风险:同时使用上升沿和下降沿触发always块,虽然语法合法,但会大幅增加布线难度,且多数FPGA厂商不推荐这种设计,容易引发时序违规。建议统一使用上升沿触发,将窗口移动逻辑整合到同一个always块中。
- 累加变量未清零:上升沿块中
DATA_R/G/B的累加操作未在每次计算前清零,会导致每次时钟沿都累积之前的计算值,最终输出完全错误。必须在循环累加前将DATA_R/G/B重置为0。 - 循环变量兼容性:always块内的
integer i,j属于自动变量,部分老版本综合工具可能不兼容,建议声明为reg类型,或者直接展开3x3的加法(因为循环次数固定,硬编码更稳定)。 - 除法资源优化:
/9整数除法会让综合工具生成除法器,占用大量逻辑资源。可以用近似计算替代,比如乘以118后右移10位(1/9≈0.1111,118/1024≈0.115,误差极小),或者用查找表实现除法,减少资源消耗。
二、测试用例建议
- 小尺寸图像验证:用3x3的极小图像测试,比如输入所有像素RGB均为255,预期输出也为255;或者输入梯度图像,手动计算模糊结果后对比模块输出,快速验证计算逻辑正确性。
- 边界场景测试:重点测试图像边缘(
row=0、row=HEIGHT-3、col=0、col=WIDTH-3),确保窗口不会越界访问input_memory(当前代码未做边界判断,边缘像素需直接输出原像素或做边缘扩展处理)。 - 复位功能验证:在测试中触发RST信号,确认
row、col、counter回到初始值,DONE信号同步拉低。 - DONE信号验证:当
counter达到INPUTBYTES时,确认DONE信号拉高,且模块不再输出新数据。
三、阻塞/非阻塞赋值使用场景
- 非阻塞赋值(<=):专门用于时序逻辑,也就是需要将值存储到触发器(寄存器)的场景。比如时钟沿触发的always块中,更新
row、col、counter这类状态寄存器,或者向output_memory写入数据。非阻塞赋值会在时钟沿完成后更新值,避免竞争条件,保证时序逻辑的正确性。 - 阻塞赋值(=):用于组合逻辑,也就是不需要存储,仅根据输入实时计算输出的场景。比如组合逻辑always块(
@*触发)中的中间值计算,或者时序逻辑块内的临时变量(如循环累加的中间值、临时地址计算)。注意:同一时序always块内不要混合使用阻塞和非阻塞赋值,否则极易引发时序错误。 - 你的代码中的问题:
- 上升沿块中
DATA_R = DATA_R + ...的阻塞赋值本身没问题,但必须在累加前清零DATA_R/G/B,否则会累积历史值; - 下降沿块中使用非阻塞赋值是正确的,但建议将窗口移动逻辑整合到上升沿块,避免双沿触发的风险。
- 上升沿块中
内容的提问来源于stack exchange,提问作者chris
相关产品推荐
相关产品推荐

