アウトプットブログ

勉強したことをまとめていきます。

DPI-CでBMPファイルリード

ひとまずの目標であるDPI-Cでの画像読み込みです。前回作成したC言語でのBMPファイル読込モジュールを使用して、画像データをVHDLモジュールへ入力することが出来ました。ヘッダファイルを自作したためコンパイル方法が変わるかも、と思いましたが、ModelSimプロジェクト(mpfファイル)があるフォルダにヘッダファイルを格納するだけで認識しました。

テストベンチの流れは以下の通りです。
1. シミュレーション開始直後にBMPファイル読込。
2. 毎クロック画素データをリード。C関数側で画素インデックスを自動で進めるため、テストベンチ側では毎クロック関数を呼ぶだけ。
3. 入力値を2倍して出力するVHDLモジュールに画素データを入力。

以下ソースです。
hoge_sv.sv

timeunit 1ns;
timeprecision 10ps;
module hoge_sv();
    // C関数をインポート
    import "DPI-C" function void callReadBMP();
    import "DPI-C" function byte getBMPData();
    import "DPI-C" function void freeImage();
    // 信号宣言
    bit clk_i = 1'b0;   // クロック
    bit srst_i = 1'b1;  // リセット
    byte d_i;           // 画像データ
    byte d_o;           // 画素値2倍データ
    // クロック
    always begin
        #5ns clk_i = ~clk_i;
    end
    // リセット
    initial begin
        #100ns;
        @(posedge clk_i) #1ps srst_i = 1'b0;
    end
    // シミュレーション開始直後にBMPファイル読み込み
    initial begin
        callReadBMP();
    end
    // C関数を使用して毎クロック画像データを読み込み
    always @(posedge clk_i) begin
        if (srst_i) begin
            d_i = 8'h00;
        end else begin
            d_i = getBMPData();
        end
    end
    // 入力データを2倍して出力(high clip)
    hoge_vhd u_vhd (
        .srst_i(srst_i),    //: in  std_logic;
        .clk_i(clk_i),      //: in  std_logic;
        .d_i(d_i),          //: in  std_logic_vector (7 downto 0);
        .d_o(d_o)           //: out std_logic_vector (7 downto 0)
    );
endmodule

hoge_c.c
※前回作成したBMPファイル読込ではファイル読込に"fopen_s","fread_s"を使用しました。これらはマイクロソフト独自関数らしく、gccではコンパイル不可。使用可能な"fopen","fread"に置き換えたため再掲します。またBMP.hの内容は変更ありません。前回記事を参考ください。

#include <stdio.h>
#include <stdlib.h>
#include "BMP.h"

BMPINFO bmpInfo;    // BMP構造体
int x, y, stride;   // 画像インデックス
int width, height;  // 画像情報

// BMPファイル読込関数呼び出し
void callReadBMP()
{
    char path[64] = "LENNA.bmp";                // 読み込み画像
    readBMP(path, &bmpInfo);                    // BMPファイル読み込み
    x = 0;                                      // x座標インデックス初期化
    y = 0;                                      // y座標インデックス初期化
    stride = bmpInfo.bitmapInfoHeader.biWidth;  // ストライドは8bitなので横幅
    width = bmpInfo.bitmapInfoHeader.biWidth;   // 画像幅
    height = bmpInfo.bitmapInfoHeader.biHeight; // 画像高さ
}

// 画素値取得
char getBMPData()
{
    char data = bmpInfo.imageData[x + y * stride];  // 画素値取得
    // インデックスを進める
    // 画像左上から右へ進み、一番右まで進んだら次行の左端からといったように進み
    // 画像右下まで進んだらまた画像左上から開始して、以下ループ
    if (x == (width - 1)) {
        x = 0;
        if (y == (height - 1))  y = 0;
        else                    y += 1;
    } else {
        x += 1;
    }

    return data;
}

// 画像メモリ領域解放
void freeImage()
{
    free(bmpInfo.imageData);
}

// BMPファイル読み込み
int readBMP(char* path, BMPINFO* bmpInfo)
{
    FILE *fp;

    // BMPファイルオープン&存在確認
    if ((fp = fopen(path, "rb")) == NULL) {
        printf("error!!\n");
        return -1;
    }
    // BITMAPFILEHEADER/bfType読み込み
    fread(&bmpInfo->bitmapFileHeader.bfType, sizeof(unsigned short), 1, fp);
    // BMPであることを確認
    if (bmpInfo->bitmapFileHeader.bfType != 0x4d42) return -2;
    // その他情報を読み込む
    fread(&bmpInfo->bitmapFileHeader.bfSize, sizeof(unsigned long), 1, fp);       // bfSize
    fread(&bmpInfo->bitmapFileHeader.bfReserved1, sizeof(unsigned short), 1, fp); // bfReserved1
    fread(&bmpInfo->bitmapFileHeader.bfReserved2, sizeof(unsigned short), 1, fp); // bfReserved2
    fread(&bmpInfo->bitmapFileHeader.bfOffBits, sizeof(unsigned long), 1, fp);    // bfOffBits
    // BITMAPINFOHEADER読み込み
    fread(&bmpInfo->bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
    // BMPINFOHEADERの種類を確認
    if (bmpInfo->bitmapInfoHeader.biSize != 40) return -3;
    // カラーテーブル取得
    fread(&bmpInfo->rgbQuad, sizeof(RGBQUAD), 256, fp);
    // 画像データ格納用領域確保
    bmpInfo->imageData = (unsigned char*)malloc(sizeof(unsigned char) * bmpInfo->bitmapInfoHeader.biWidth * bmpInfo->bitmapInfoHeader.biHeight);
    // 画像データ取得
    fread(bmpInfo->imageData,
        sizeof(char),
        bmpInfo->bitmapInfoHeader.biWidth * bmpInfo->bitmapInfoHeader.biHeight,
        fp);

    fclose(fp);

    return 0;
}

hoge_vhd.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity hoge_vhd is
    port (
        srst_i  :   in  std_logic;
        clk_i   :   in  std_logic;
        
        d_i     :   in  std_logic_vector (7 downto 0);
        d_o     :   out std_logic_vector (7 downto 0)
    );
end hoge_vhd;

architecture rtl of hoge_vhd is

signal add  :   std_logic_vector (8 downto 0);

begin
-- 加算
add <= ('0' & d_i) + ('0' & d_i);
-- high clipして出力
process (clk_i) begin
    if (rising_edge(clk_i)) then
        if (srst_i = '1') then
            d_o <= (others => '0');
        else
            if (add(8) = '1') then
                d_o <= x"ff";
            else
                d_o <= add(7 downto 0);
            end if;
        end if;
    end if;
end process;

end rtl;

以下シミュレーション波形です。VHDLモジュールの入力データとして画像データっぽいものが入力され、2倍して出力しています。
f:id:HappyField:20151202020746p:plain

恐らく出来ているとは思うのですが、これを検証に使用するにはC関数側でコンペアを行いたいところです。次回のネタにしようと思います。