田中太郎
移動平均フィルタを実装しました
仕様
3×1の水平移動平均フィルタです(1/4-1/2-1/4)
画像の水平両端では注目画素を出力します
top.sv
main.svとctrl.svをインスタンスしている回路
module top #(
parameter IMG_W = 512,
parameter IMG_H = 512
)(
input clk, // クロック
input rst_n, // リセット
input enable, // Highになると、512x512ぶんのdinが有効データになる
input [7:0] din, // 画像が1画素ずつ入力される
output logic valid, // Highのときdoutが有効データとなりファイルに保存される
output logic [7:0] dout, // 出力画像データ
output logic interrupt // 画像をすべて出力し終わったらHighになる
);
localparam LATENCY = 1;
logic edge_en; // 画像の水平両端を知らせる
ctrl #(
.IMG_W(IMG_W),
.IMG_H(IMG_H),
.LATENCY(LATENCY)
) i_ctrl(
.clk(clk),
.rst_n(rst_n),
.enable(enable),
.edge_en(edge_en),
.valid(valid),
.interrupt(interrupt)
);
main #(
) i_main(
.clk(clk),
.rst_n(rst_n),
.edge_en(edge_en),
.din(din),
.dout(dout)
);
endmodule
main.sv
画像処理回路
移動平均フィルタ回路が実装されている
module main #(
)(
input clk,
input rst_n,
input [7:0] din,
input edge_en,
output logic [7:0] dout
);
logic [7:0] data_d1;
logic [7:0] data_d2;
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
data_d1 <= '0;
data_d2 <= '0;
end
else begin
data_d1 <= din;
data_d2 <= data_d1;
end
end
wire [7:0] buf1 = din >> 2; // 左画素は1/4
wire [7:0] buf2 = data_d1 >> 1; // 注目画素は1/2
wire [7:0] buf3 = data_d2 >> 2; // 右画素は1/4
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
dout <= '0;
end
else begin
if (edge_en) begin
dout <= data_d1; // 両端は注目画素をそのまま出力
end
else begin
dout <= buf1 + buf2 + buf3;
end
end
end
endmodule
ctrl.sv
制御回路
画像処理回路と全体の制御を行う
画像の水平両端を画像処理回路に伝えるedge_enを発行する
module ctrl #(
parameter IMG_W = 512,
parameter IMG_H = 512,
parameter LATENCY = 1
)(
input clk,
input rst_n,
input enable,
output logic edge_en,
output logic valid,
output logic interrupt
);
logic [18:0] cnt;
logic enable_d;
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
enable_d <= '0;
end
else begin
enable_d <= enable;
end
end
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
cnt <= '1;
end
else begin
if (enable & ~enable_d) begin
cnt <= '0;
end
else if (cnt < IMG_W*IMG_H + LATENCY) begin
cnt <= cnt + 1'b1;
end
end
end
always_comb begin
if (cnt == 0 || cnt%IMG_W == 0) begin
edge_en = 1'b1;
end
else begin
edge_en = 1'b0;
end
end
always_comb begin
if (cnt == IMG_W*IMG_H + LATENCY) begin
interrupt = '1;
end
else begin
interrupt = '0;
end
end
always_comb begin
if (LATENCY <= cnt && cnt < IMG_W*IMG_H + LATENCY) begin
valid = '1;
end
else begin
valid = '0;
end
end
endmodule
tb.sv
テストベンチ
画像データを読み込み、1クロック毎に1画素ずつ回路に送信する
回路から出力されるvalidがHighのときdoutを有効画素としてファイルに保存する
module tb;
initial begin
$dumpfile("wave.vcd");
$dumpvars(0, tb);
end
localparam IMG_W = 128; // 画像水平サイズ
localparam IMG_H = 128; // 画像垂直サイズ
int f;
bit [7:0] bmp[54+1024];
bit [7:0] data[IMG_H*IMG_W];
logic [7:0] din;
logic [7:0] dout;
bit enable;
bit valid;
bit interrupt;
bit clk = 0;
bit rst_n = 0;
always #5 clk = ~clk;
initial begin
f = $fopen("black_noise.bmp", "rb"); // 入力画像
$fread(bmp, f, 0, 54+1024); // BMPのヘッダ
$fread(data, f, 0, IMG_H*IMG_W);
$fclose(f);
f = $fopen("result.bmp", "wb"); // 出力画像
foreach (bmp[i])
$fwrite(f, "%c", bmp[i]); // ヘッダを書き込む
#50 rst_n = 1;
enable = 1;
fork
foreach (data[i]) begin
din = data[i]; // 1画素ずつ回路に転送する
@(posedge clk);
end
join_none
@(posedge clk);
enable = 0;
forever begin
if (valid) begin
$fwrite(f, "%c", dout); // validがHighのときのdoutが有効データ
end
@(posedge clk);
if (interrupt) begin // interrupがHighになるとテストは終了する
break;
end
end
$fclose(f);
$finish;
end
top #(
.IMG_W(IMG_W),
.IMG_H(IMG_H)
) i_top(
.clk(clk),
.rst_n(rst_n),
.enable(enable),
.din(din),
.valid(valid),
.dout(dout),
.interrupt(interrupt)
);
endmodule
入力画像
出力画像
まとめ
移動平均フィルタ回路を実装しました
コメント