Verilog实现图像处理的行缓存Line Buffer

在图像处理中,难免会遇到对图像进行卷积或者模板的局部处理,例如ISP中的一些算法,很大部分都需要一个窗口,在实时视频处理中,可以利用行缓存Line buffer可以暂存几行数据,然后同时输出每行中的对应列的像素。

下面以一个三行缓存进行解释

DVP像素流从Shif_in输入,像素不流不断在行缓存中移动,行缓存的最后一个像素使用line_out来保存,通过这种方式,lin_out输出部分就是每行对应列中的像素,在算法模块中,对line_out寄存三拍,就可以得到3x3的窗口大小。

暂存区的设计可以使用一个双口同步RAM来实现:

cpp 复制代码
module simple_dp_ram
#(
	parameter DW = 8,
	parameter AW = 4,
	parameter SZ = 2**AW
)
(
	input           clk,
	input           wen,
	input  [AW-1:0] waddr,
	input  [DW-1:0] wdata,
	input           ren,
	input  [AW-1:0] raddr,
	output [DW-1:0] rdata
);
	reg [DW-1:0] mem [SZ-1:0];
	always @ (posedge clk) begin
		if (wen) begin
			mem[waddr] <= wdata;
		end
	end
	reg [DW-1:0] q;
	always @ (posedge clk) begin
		if (ren) begin
			q <= mem[raddr];
		end
	end
    
	// always @(posedge clk) begin
	// 	// 先执行读操作
	// 	if (ren) begin
	// 		q <= mem[raddr];
	// 	end

	// 	// 后执行写操作
	// 	if (wen) begin
	// 		mem[waddr] <= wdata;
	// 	end
	// end

	assign rdata = q;
endmodule

这里涉及到一个问题,当读地址和写地址相同时,会导致冲突吗?

我这里找到的解释是,具体行为会依赖于底层的实现,在同一个时钟周期同时读写同一个地址在FPGA中通常是读优先的,即会先读取该地址的旧值,然后向该地址写如新值。(有大佬了解的可以帮忙解释以下:))。

行缓存LineBuffer源码如下所示:

cpp 复制代码
///基于卷积或者模板的算法需要使用该模块
module shift_register
#(
	parameter BITS = 8,         
	parameter WIDTH = 480,    //行缓存宽度
	parameter LINES = 3       //行缓存行数
)
(
	input                clock,    
	input                clken, 
	input  [BITS-1:0]    shiftin,        //输入数据流
	output [BITS-1:0]    shiftout,       //当前行最后一个像素的数据输出。
	output [BITS*LINES-1:0] tapsx        //行缓存的输出:包含每行最后一个像素的输出,拼接成一个长向量
);

	localparam RAM_SZ = WIDTH - 1;   //每行的实际大小
	localparam RAM_AW = clogb2(RAM_SZ);  //计算地址宽度


    ///地址指针逻辑
	reg [RAM_AW-1:0] pos_r;   //pos_r:记录当前写入的位置。
    ///(RAM_SZ[RAM_AW-1:0] - 1'b1)实际上就是WIDTH - 2
    //行缓存的最后一个像素使用line_out来保存,RAM实际上只保存了WDITH-1个像素(pos在0~WIDTH-2之间)
	wire [RAM_AW-1:0] pos = pos_r < RAM_SZ ? pos_r : (RAM_SZ[RAM_AW-1:0] - 1'b1);  ///当 pos_r 超过范围时限制其值,用于确保地址有效
	always @ (posedge clock) begin
		if (clken) begin     
			if (pos_r < RAM_SZ - 1)  
				pos_r <= pos_r + 1'b1;   //地址指针 pos_r 循环递增,直到到达最大地址后重置为 0
			else
				pos_r <= 0;
		end
	end

	reg [BITS-1:0] in_r;  
	always @ (posedge clock) begin
		if (clken) begin
			in_r <= shiftin;      //寄存输入数据 shiftin,用于提供给第 0 行的 RAM 写入。
		end
	end

	wire [BITS-1:0] line_out[LINES-1:0];    //记录每行的最后一个像素

    ///生成菊花链行缓存结构,在第二行开始才将上一行末尾连接到下一行的输入中,第一行输入in_r,每一行的最后一个像素寄存在line_out中
	generate          
		genvar i;
		for (i = 0; i < LINES; i = i + 1) begin : gen_ram_inst
            //在当前周期,line_out[i] 先从 pos 地址中读取之前存储的数据。随后将 (i > 0 ? line_out[i-1] : in_r) 写入 pos 地址。
			simple_dp_ram #(BITS, RAM_AW, RAM_SZ) u_ram(clock, clken, pos, (i > 0 ? line_out[i-1] : in_r), clken, pos, line_out[i]);
		end
	endgenerate


	assign shiftout = line_out[LINES-1];     //移位输出
	generate
		genvar j;
		for (j = 0; j < LINES; j = j + 1) begin : gen_taps_assign
			assign tapsx[(BITS*j)+:BITS] = line_out[j];
		end
	endgenerate


    //计算需要位宽
	function integer clogb2;    
	input integer depth;
	begin
		for (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1)
			depth = depth >> 1;
	end
	endfunction
endmodule

这里使用了一个clogb2函数来获取配置的WIDTH需要的位宽。在代码中涉及了同一时钟周期对双口RAM中同一地址的读写问题,既然项目最后能跑起来,应该就是读优先的。

相关推荐
XINVRY-FPGA21 小时前
XC3S1000-4FGG320I Xilinx AMD Spartan-3 SRAM-based FPGA
嵌入式硬件·机器学习·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
Coding茶水间1 天前
基于深度学习的35种鸟类检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Pyqt5界面+训练代码+数据集)
图像处理·人工智能·深度学习·yolo·目标检测·计算机视觉
ReinaXue1 天前
跨模态预训练大模型【CLIP】:Contrastive Language–Image Pre-training
图像处理·人工智能·深度学习·计算机视觉·语言模型
北京青翼科技1 天前
【TES818 】基于 VU13P FPGA+ZYNQ SOC 的 8 路 100G 光纤通道处理平台
图像处理·人工智能·fpga开发·信号处理·智能硬件
c#上位机1 天前
halcon图像分割之动态阈值分割
图像处理·人工智能·计算机视觉·c#·halcon
9527华安1 天前
FPGA纯verilog实现JESD204B协议,基于ADRV9009数据环回收发,提供2套工程源码和技术支持
fpga开发·verilog·jesd204b·adrv9009
明洞日记1 天前
【VTK手册017】 深入详解 vtkImageMathematics:医学图像的基本算术运算
c++·图像处理·算法·vtk·图形渲染
AndrewHZ1 天前
【复杂网络分析】复杂网络分析技术在图像处理中的经典算法与应用实践
图像处理·人工智能·算法·计算机视觉·图像分割·复杂网络·图论算法
棒棒的皮皮1 天前
【OpenCV】Python图像处理之图像加密解密
图像处理·python·opencv·计算机视觉
Mr_Oak1 天前
【multi-model】DINOv2(包含iBOT)& 问答
图像处理·人工智能·深度学习·算法·多模态·对比学习·视觉大模型