2.一维形态学腐蚀/膨胀子模块(Morph_1D) 就代码设计而言,一维单元的设计作为二维计算的基础,需要考 虑的问题主要是代码的可重用性和可移植性。主要包括三个参数的设 计:数据位宽、处理窗口宽度和最值方向(即是选择服饰操作还是膨 胀操作)。同时,考虑到尽量节省资源,将上述参数作为编译时参数 进行设计。 模块定义如下:
module Morph_1D(
rst_n, //异步复位信号clk, //同步时钟信号
din, //输入数据流
din_valid, //输入数据有效信号
dout_valid, //输出数据有效
dout //输出数据流
);
parameter DW = 14; //数据位宽
parameter KSZ = 3;
parameter ERO_DIL = 1; //选择腐蚀或者膨胀,0膨胀操作,1腐
蚀操作
input rst_n;
input clk;
input [DW-1:0]din;
input din_valid;
output dout_valid;
output [DW-1:0]dout;
代码设计完全按照上述方法实现即可,需要完成的工作是根据窗 口尺寸实例化比较单元并完成时序对齐。 关键代码如下:
localparam med_idx = ((KSZ >> 1)); //处理尺寸半径
reg [KSZ-1:0] din_valid_r; //输入有效缓存,用于时序对齐
wire [DW-1:0] reg_din; //寄存数据
//中间比较结果寄存器min max,对于窗口尺寸为5,则有4个中间
寄存器
wire [DW-1:0] min[0:KSZ-2]; //中间结果
wire [DW-1:0] max[0:KSZ-2]; //中间结果
//缓存输入有效信号用于时序对齐always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1)
din_valid_r <= #1 {KSZ{1'b0}};
else
din_valid_r <= #1 ({din_valid_r[KSZ -
2:0],din_valid});
//首先将输入数据缓存一拍
register_data #(DW,1)
din_r(
.rst_n(rst_n),
.clk(clk),
.din_valid(din_valid),
.din(din),
.reg_din(reg_din) //缓存1拍后的输入数据流
);
//调用第一个比较模块,比较前两拍数据
MinMax #(DW,1)
cmp_min_max(
.clk(clk),
.valid(din_valid_r[0]),
.din_a(reg_din),
.din_b(din),
.dout_min(min[0]), //前两拍的较小值
.dout_max(max[0]) //前两拍的较大值
);
// 如果是腐蚀操作
generateif (ERO_DIL == 1)
begin : MAP2
begin : xhdl0
genvar i;
//例化剩余的比较器 ,共KSZ-2个
for (i = 3; i <= KSZ; i = i + 1)
begin : gen_cmp_min
MinMax #(DW,1)
cmp_min_inst(
.clk(clk),
.valid(din_valid_r[i - 2]),
.din_a(min[i - 3]),
.din_b(din),
.dout_min(min[i - 2]),
.dout_max(max[i - 2])
);
end
end
assign dout = min[KSZ - 2]; //输出与最后一个比较器对齐
end
endgenerate
// 如果是膨胀操作
generate
if ((~(ERO_DIL == 1)))
begin : MAP3
begin : xhdl1
genvar i;//例化剩余的比较器 ,共KSZ-2个
for (i = 3; i <= KSZ; i = i + 1)
begin : gen_cmp_max
MinMax #(DW,1)
cmp_max_inst(
.clk(clk),
.valid(din_valid_r[i - 2]),
.din_a(max[i - 3]),
.din_b(din),
.dout_min(min[i - 2]),
.dout_max(max[i - 2])
);
end
end
assign dout = max[KSZ - 2];
end
endgenerate
//输出有效时序对齐
assign dout_valid = din_valid_r[KSZ - 1 - med_idx];
3.二维形态学腐蚀/膨胀子模块(Morph_2D) 二维形态学腐蚀/膨胀模块需要调用一维运算模块和行缓存,同时 生产行列同步电路。需要例化的参数有数据宽度、处理窗口尺寸、图 像宽度、图像高度、腐蚀/膨胀操作选择。 模块定义如下:
module Morph_2D(
rst_n, //异步复位信号
clk, //同步时钟信号din_valid, //输入数据有效信号
din, //输入数据流
dout, //输出数据流
vsync, //输入场同步信号
vsync_out, //输出场同步信号
dout_valid //输出数据有效信号
);
parameter DW = 14; //数据位宽
parameter KSZ = 7; //处理尺寸
parameter IH = 512; //图像高度
parameter IW = 640; //图像宽度
parameter ERO_DIA = 1;//腐蚀或膨胀选择
input rst_n;
input clk;
input din_valid;
input [DW-1:0] din;
output [DW-1:0]dout;
input vsync;
output vsync_out;
output dout_valid;
关键代码如下所示:
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;reg [KSZ-2:0] line_wren;
wire [9:0] line_count[0:KSZ-2];
wire [DW-1:0] min_max_value;
wire [DW-1:0] min_max_value_r;
wire din_valid_r;
reg [DW-1:0] min[0:KSZ-1];
reg [DW-1:0] max[0:KSZ-1];
reg [KSZ-2:0] buf_pop_en;
reg valid_r;
reg [10:0] in_line_cnt;
reg [15:0] flush_cnt;
reg flush_line;
reg [15:0] out_pixel_cnt;
reg [10:0] out_line_cnt;
reg [DW-1:0] dout_temp_r;
reg dout_valid_temp_r;
wire [DW-1:0] dout_temp;
wire dout_valid_temp;
wire is_boarder;
wire valid;
reg din_valid_r2;
wire [31:0] j;
wire [31:0] k;
wire rst_all_low;
wire data_tmp1[0:KSZ-2];
wire [DW-1:0] data_tmp2;
wire [DW-1:0] data_tmp3;//帧同步复位信号
always @(posedge clk or posedge rst_n)
if (((~(rst_n))) == 1'b1)
rst_all <= #1 1'b1;
else
begin
if (vsync == 1'b1)
rst_all <= #1 1'b1;
else
rst_all <= #1 1'b0;
end
//低电平帧同步复位信号
assign rst_all_low = (~(rst_all));
//全局有效信号
assign valid = din_valid | flush_line;
//一维方向上的Morph操作
Morph_1D #(DW,KSZ,ERO_DIA)
row_1st(
.rst_n(rst_all_low),
.clk(clk),
.din(din),
.din_valid(valid),
.dout_valid(din_valid_r), //一维输出有效信号
.dout(min_max_value) //一维输出min_max_value
);
//缓存一维输出有效信号用于时序对齐
always @(posedge clk)din_valid_r2 <= #1 din_valid_r;
always @(*) min[0]<= min_max_value;
always @(*) max[0]<= min_max_value;
generate
begin : xhdl0
genvar i;
for (i = 0; i <= KSZ - 2; i = i + 1)
begin : buf_cmp_inst
assign data_tmp1[i]= din_valid_r &
line_rden[i];
//输入有效信号
//例化列方向上的比较电路
MinMax #(DW,1)
cmp_inst(
clk,
data_tmp1[i],
line_dout[i],
min_max_value,
min[i + 1],
max[i + 1]
);
if (ERO_DIA == 1)
begin : MAP10
always @(*) line_din[i]<= min[i]; //腐蚀取
最小值
end
if ((~(ERO_DIA == 1)))begin : MAP11
always @(*) line_din[i]<= max[i]; //膨胀取
最大值
end
if (i == 0)
begin : MAP12
always @(din_valid_r)
line_wren[i]<= din_valid_r;
//第一个行缓存的输入为一维输出数据
end
if ((~(i == 0)))
begin : MAP13
always @(posedge clk)
begin
if (rst_all == 1'b1)
line_wren[i]<= 1'b0;
else
line_wren[i]<= #1 line_rden[i - 1];
//其他行缓存接成菊花链结构
end
end
assign line_rden[i]= buf_pop_en[i]& din_valid_r;
//行缓存装满后允许流水线开始运行
always @(posedge clk)
begin
if (rst_all == 1'b1)
buf_pop_en[i]<= #1 1'b0;else if (line_count[i]== IW)
buf_pop_en[i]<= #1 1'b1;
end
//例化行缓存
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 (ERO_DIA == 1)
begin : MAP14
assign dout_temp = ((line_rden[KSZ - 2] == 1'b0))
?min[radius]:
min[KSZ - 1];
end
endgenerategenerate
if ((~(ERO_DIA == 1)))
begin : MAP15
assign dout_temp = ((line_rden[KSZ - 2] == 1'b0))
?max[radius]:
max[KSZ - 1];
end
endgenerate
//输出有效信号
assign dout_valid_temp = ((line_rden[KSZ - 2]== 1'b0)) ?
line_wren[radius]:
(din_valid_r2 & buf_pop_en[KSZ - 2]);
//输出场同步
generate
if (KSZ == 3)
begin : MAP16
assign vsync_out=
((din_valid_r2==1'b1&line_wren[1]==1'b0))?1'b1 :
1'b0;
end
endgenerate
generate
if ((~(KSZ == 3)))
begin : MAP17
assign vsync_out = ((line_wren[radius - 1]== 1'b1
&
line_wren[radius]== 1'b0)) ? 1'b1 :1'b0;
end
endgenerate
//边界清零
assign data_tmp2 = ((dout_valid_temp == 1'b1)) ?
dout_temp :
{DW{1'b0}};
assign data_tmp3 = ((is_boarder == 1'b1)) ? {DW{1'b0}}
:
data_tmp2;
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
dout_temp_r <= #1 {DW{1'b0}};
dout_valid_temp_r <= #1 1'b0;
valid_r <= #1 1'b0;
end
else
begin
dout_temp_r <= #1 data_tmp3;
dout_valid_temp_r <= #1 dout_valid_temp;
valid_r <= #1 valid;
end
end
//输出数据流与输出数据有效
assign dout = dout_temp_r;assign dout_valid = dout_valid_temp_r;
//输入行计数
always @(posedge clk)
begin
if (rst_all == 1'b1)
in_line_cnt <= #1 {11{1'b0}};
else if (((~(valid))) == 1'b1 & valid_r == 1'b1)
in_line_cnt <= #1 in_line_cnt +
11'b00000000001;
end
//溢出行计数及溢出行内计数
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
flush_line <= #1 1'b0;
flush_cnt <= #1 {16{1'b0}};
end
else
begin
if (flush_cnt >= ((IW - 1)))
flush_cnt <= #1 {16{1'b0}};
else if (flush_line == 1'b1)
flush_cnt <= #1 flush_cnt +
16'b0000000000000001;
if (flush_cnt >= ((IW - 1)))
flush_line <= #1 1'b0;else if (in_line_cnt >= IH & out_line_cnt <
((IH - 1)))
flush_line <= #1 1'b1;
end
end
//输出行计数和输出行内计数
always @(posedge clk)
begin
if (rst_all == 1'b1)
begin
out_pixel_cnt <= #1 {16{1'b0}};
out_line_cnt <= #1 {11{1'b0}};
end
else
begin
if (dout_valid_temp_r == 1'b1 & (( ~
(dout_valid_temp))) == 1'b1)
out_line_cnt <= #1 out_line_cnt +
11'b00000000001;
else
out_line_cnt <= #1 out_line_cnt;
if (dout_valid_temp_r ==1'b1 & (( ~
(dout_valid_temp))) == 1'b1)
out_pixel_cnt <= #1 {16{1'b0}};
else if (dout_valid_temp == 1'b1)
out_pixel_cnt <= #1 out_pixel_cnt +
16'b0000000000000001;end
end
//边界判决
assign is_boarder = (((dout_valid_temp == 1'b1) &
(out_pixel_cnt <=((radius - 1)) | out_pixel_cnt >= ((IW -
radius)) | out_line_cnt <= ((radius- 1)) | out_line_cnt >=
((IH - radius))))) ? 1'b1 :
1'b0;
4.二维形态学开运算子模块(Morph_Open_2D) 开运算只需例化1个二维腐蚀操作和1个二维膨胀操作,同时将腐 蚀操作的输出接入膨胀操作的输入即可。
模块定义如下:
module Morph_Open_2D(
din, //输入数据流
dout_valid, //输出数据有效
dout, //输出数据流
rst_n, //异步复位信号
din_valid, //输入有效信号
vsync, //输入场同步信号
vsync_out, //输出场同步信号
clk //同步时钟信号
);
parameter DW = 14; //数据位宽
parameter KSZ = 7; //处理尺寸
parameter IW = 640; //图像宽度
parameter IH = 512; //图像高度
input [DW-1:0] din;output dout_valid;
output [DW-1:0]dout;
input rst_n;
input din_valid;
input vsync;
output vsync_out;
input clk;
关键代码如下:
wire erode_valid;
wire erode_vsync;
wire [DW-1:0] dout_erode;
wire dout_valid_tmp1;
wire vsync_out_tmp1;
wire [DW-1:0] out_data_tmp1;
//首先进行腐蚀操作
Morph_2D #(DW,KSZ,IH,IW,1)
morph_erode(
.rst_n(rst_n),
.clk(clk),
.din_valid(din_valid),
.din(din),
.dout(dout_erode),
.vsync(vsync),
.vsync_out(erode_vsync),
.dout_valid(erode_valid)
);
//接着进行膨胀操作Morph_2D #(DW,KSZ,IH,IW,0)
morph_dilate(
.rst_n(rst_n),
.clk(clk),
.din_valid(erode_valid),
.din(dout_erode),
.dout(dout_data_tmp1),
.vsync(erode_vsync),
.vsync_out(vsync_out_tmp1),
.dout_valid(dout_valid_tmp1)
);
//输出信号赋值
assign dout_valid = dout_valid_tmp1;
assign dout = dout_data_tmp1;
assign vsync_out = vsync_out_tmp1;