按照二维扩展的思路,将每一行的一维算子的计算结果对齐在列方向上再进行一维运算,得到的结果即是二维运算结果。
上面的结构图中涉及到:
(1)minmax(0)为当前行的一维运算数据流。
(2)Line_buffer(0)中数据为第(i-1)行的一维运算数据流。
(3)Line_buffer(i)中数据为第(i-1)行的一维运算数据流与第i行的一维运算数据流的比较结果。
`timescale 1ns / 1ps
module morph_2D(
rst_n,
clk,
din,
din_valid,
dout_valid,
dout,
vsync, //输入场同步信号
vsync_out //输出场同步信号
);
parameter IH = 512;
parameter IW = 640;
parameter DW = 14;
parameter KSZ = 3;
parameter ERO_DIL = 1; //0 膨胀操作
//1 腐蚀操作
localparam med_idx = (KSZ>>1);
input rst_n;
input clk;
input [DW-1:0] din;
input din_valid;
input vsync;
output [DW-1:0] dout;
output dout_valid;
output vsync_out;
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 [15: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 negedge rst_n)
if(~rst_n)
rst_all <= 1'b1;
else
begin
if(vsync)
rst_all <= 1'b1;
else
rst_all <= 1'b0;
end
//低电平帧同步复位信号
assign rst_all_low = ~rst_all;
//全局有效信号
assign valid = din_valid | flush_line;
//一维方向上的Morph操作
morph_1D 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 <= din_valid_r;
always @(*) min[0] <= min_max_value;
always @(*) max[0] <= min_max_value;
generate
begin : xdhl0
genvar i;
for(i=0;i<=KSZ-2;i=i+1)
begin : buf_cmp_inst
assign data_tmp[i] = din_valid_r & line_rden[i];
//输入有效信号
//例化列方向上的比较电路
minmax cmp_inst(
.clk(clk),
.valid(data_tmp1[i]),
.din_a(line_dout[i]),
.din_b(min_max_value),
.dout_min(min[i+1]),
.dout_max(max[i+1])
);
if(ERO_DIL)
begin : map10
always @(*) line_din[i] <= min[i];//腐蚀取最小值
end
if(~ERO_DIL)
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)
line_wren[i] <= 1'b0;
else
line_wren[i] <= line_rden[i-1]; //其他行缓存接成菊花链结构
end
end
assign line_rden[i] = buf_pop_en[i] & din_valid_r;
//行缓存装满后允许流水线开始运行
always @(posedge clk)
begin
if(rst_all)
buf_pop_en[i] <= 1'b0;
else if(line_count[i] == IW)
buf_pop_en[i] <= 1'b1;
end
//例化行缓存
fifo_generator_0 line_buffer_inst (
.clk(clk), // input wire clk
.rst(rst_all), // input wire rst
.din(line_din[i]), // input wire [13 : 0] din
.wr_en(line_wren[i]), // input wire wr_en
.rd_en(line_rden[i]), // input wire rd_en
.dout(line_dout[i]), // output wire [13 : 0] dout
.full(line_full[i]), // output wire full
.empty(line_empty[i]) // output wire empty
);
end
end
endgenerate
generate
if(ERO_DIL)
begin : map14
assign dout_temp = (line_rden[KSZ-2]==1'b0) ? min[med_idx] : min[KSZ-1];
end
endgenerate
generate
if(~ERO_DIL)
begin : map15
assign dout_temp = (line_rden[KSZ-2]==1'b0) ? max[med_idx] : max[KSZ-1];
end
endgenerate
//输出有效信号
assign dout_valid_temp = (line_rden[KSZ-2]==1'b0) ? line_wren[med_idx] : (din_valid_r2 & buf_pop_en[KSZ-2]);
//输出场同步信号
generate
if(KSZ == 5)
begin : map16
assign vsync_out = ((din_valid_r2==1'b1)&(line_wren[1]==1'b0)) ? 1'b1 : 1'b0;
end
endgenerate
//边界清零
assign data_tmp2 = (dout_valid_temp) ? dout_temp : {DW{1'b0}};
assign data_tmp3 = (is_boarder) ? {DW{1'b0}} :data_tmp2;
always @(posedge clk)
begin
if(rst_all)
begin
dout_temp_r <= {DW{1'b0}};
dout_valid_temp <= 1'b0;
valid_r <= 1'b0;
end
else
begin
dout_temp_r <= data_tmp3;
dout_valid_temp_r <= dout_valid_temp;
valid_r <= valid;
end
end
//输出数据流与输出数据有效
assign dout = dout_temp_r;
assign dout_valid = dout_valid_temp_r;
//输入行计数
always @(posedge clk)
begin
if(rst_all)
in_line_cnt <= {11{1'b0}};
else
in_line_cnt in_line_cnt +1'b1;
end
//溢出行计数及溢出行内计数
always @(posedge clk)
begin
if(rst_all)
begin
flush_line <= 1'b0;
flush_cnt <= {16{1'b0}};
end
else
begin
if(flush_cnt >= (IW-1))
flush_cnt <= {16{1'b0}};
else if(flush_line)
flush_cnt <= flush_cnt +1'b1;
if(flush_cnt >= (IW-1))
flush_line <= 1'b0;
else if(in_line_cnt>=IH&out_line_cnt<(IH-1))
flush_line <= 1'b1;
end
end
//输出行计数和输出行内计数
always @(posedge clk)
begin
if(rst_all)
begin
out_pixel_cnt <= {16{1'b0}};
out_line_cnt <= {11{1'b0}};
end
else
begin
if(dout_valid_temp_r==1'b1 & dout_valid_temp==1'b0)
out_line_cnt <= out_line_cnt + 1'b1;
else
out_line_cnt <= out_line_cnt;
if(dout_valid_temp_r==1'b1 & dout_valid_temp==1'b0)
out_pixel_cnt <= {16{1'b0}};
else
out_pixel_cnt <= out_pixel_cnt + 1'b1;
end
end
//边界判决
assign is_boarder = (((dout_valid_temp==1'b1)
&(out_pixel_cnt<=(med_idx-1)
|out_pixel_cnt >= (IW-med_idx)
|out_line_cnt <= (med_idx-1)
|out_line_cnt >= (IH-med_idx)) ? 1'b1 : 1'b0;
endmodule