SystemVerilog|移動平均フィルタの実装

Systemverilog
田中太郎
田中太郎

移動平均フィルタを実装しました

仕様

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
入力画像
出力画像

まとめ

移動平均フィルタ回路を実装しました

コメント

タイトルとURLをコピーしました