VHDLのTEXTIOライブラリでテキスト・ファイルからFPGAブロックRAMを初期化する
ブロックRAMを事前定義値で埋める最も効率的な方法は、ASCIIファイルからバイナリまたは16進リテラルを読み取ることです。この技術はROMの作成にも役立ちます。FPGAのROMは単に読み取り専用のRAMだからです。
以下の例では、次の定数とRAM型がVHDLファイルの宣言領域で宣言されていると仮定します:
constant ram_depth : natural := 256; constant ram_width : natural := 32; type ram_type is array (0 to ram_depth - 1) of std_logic_vector(ram_width - 1 downto 0);
この記事は、VHDLのTEXTIOライブラリに関するシリーズの一部です。 テストベンチでの刺激ファイル読み取り | BMPファイル・ビットマップ画像読み取り
TEXTIOによる行とデータの読み取り
標準stdライブラリにあるTEXTIOパッケージは、ファイルI/Oに必要なサブプログラムを提供します。自動的に利用可能なので、インポートするだけです:
use std.textio.all;
RAMデータは、各行が1メモリ・ワードを表すASCIIファイルに格納します。READLINE手順は1行をLINEオブジェクトに読み取ります。そのプロトタイプは:
procedure READLINE (file F: TEXT; L: inout LINE); type LINE is access STRING; -- 動的に割り当てられた文字列へのポインタ
VHDL-2008では、LINEオブジェクトから8進、16進、2進値を抽出するためのOREAD、HREAD、BREADが導入されました。簡潔さのため、16進と2進読み取りに焦点を当てます。関連宣言は:
procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR); alias BREAD is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN]; alias BREAD is READ [LINE, STD_ULOGIC_VECTOR];
レガシーVHDLバージョン(2008以前)では、標準READ手順を使用する必要がありますが、これは16進を直接サポートしていません。この場合、カスタム変換ルーチンが必要です。
16進データの読み込み
16進表記は便利です。16進数2桁が1バイトに直接マッピングされるためです。サンプルram_content_hex.txtファイルには256行含まれ、各行は00000001から00000100までの8文字16進文字列を保持します:
| 1 2 … 255 256 | 0000000100000002…000000FF00000100 |
以下の不純関数はファイルを読み取り、完全に初期化されたram_type配列を返します。VHDL-2008以降で動作します:
impure function init_ram_hex return ram_type is file text_file : text open read_mode is "ram_content_hex.txt"; variable text_line : line; variable ram_content : ram_type; begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); hread(text_line, ram_content(i)); end loop; return ram_content; end function;
関数定義後、RAM信号を初期内容でインスタンス化します:
signal ram_hex : ram_type := init_ram_hex;
レガシーVHDL: HREADの手動実装
HREADが利用できない場合(例: VHDL-93やVHDL-2002)、初期化関数に手動変換ルーチンを埋め込めます。以下の例はこのアプローチを示します:
impure function init_ram_hex return ram_type is file text_file : text open read_mode is "ram_content_hex.txt"; variable text_line : line; variable ram_content : ram_type; variable c : character; variable offset : integer; variable hex_val : std_logic_vector(3 downto 0); begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); offset := 0; while offset < ram_content(i)'high loop read(text_line, c); case c is when '0' => hex_val := "0000"; when '1' => hex_val := "0001"; when '2' => hex_val := "0010"; when '3' => hex_val := "0011"; when '4' => hex_val := "0100"; when '5' => hex_val := "0101"; when '6' => hex_val := "0110"; when '7' => hex_val := "0111"; when '8' => hex_val := "1000"; when '9' => hex_val := "1001"; when 'A' | 'a' => hex_val := "1010"; when 'B' | 'b' => hex_val := "1011"; when 'C' | 'c' => hex_val := "1100"; when 'D' | 'd' => hex_val := "1101"; when 'E' | 'e' => hex_val := "1110"; when 'F' | 'f' => hex_val := "1111"; when others => hex_val := "XXXX"; assert false report "Found non-hex character '" & c & "'"; end case; ram_content(i)(ram_content(i)'high - offset downto ram_content(i)'high - offset - 3) := hex_val; offset := offset + 4; end loop; end loop; return ram_content; end function;
専用のHREAD手順を定義することもできますが、ほとんどの合成ツール(例: Xilinx Vivado)はLINE引数を受け取る手順をサポートしていません。以下のエラーがこの制限を示しています:
Vivadoの場合:
[Synth 8-27] Procedure argument of type 'line' is not supported [init_ram_tb.vhd:15]
2進データの読み取り
RAM幅が8の倍数でない場合、2進リテラルの方が好ましい場合があります。サンプルram_content_bin.txtファイルには256行含まれ、各行に32ビット2進文字列があります:
| 1 2 … 255 256 | 0000000000000000000000000000000100000000000000000000000000000010…0000000000000000000000001111111100000000000000000000000100000000 |
VHDL-2008では、BREAD手順を使用して2進値を直接読み取れます:
impure function init_ram_bin return ram_type is file text_file : text open read_mode is "ram_content_bin.txt"; variable text_line : line; variable ram_content : ram_type; begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); bread(text_line, ram_content(i)); end loop; return ram_content; end function;
RAMを以下でインスタンス化:
signal ram_bin : ram_type := init_ram_bin;
レガシーVHDL: READによる2進読み取り
VHDL-2002やVHDL-93では、READ手順を使用する必要があります。これはbitベクトルを返すため、結果をTo_StdLogicVectorでstd_logic_vectorに変換します:
impure function init_ram_bin return ram_type is file text_file : text open read_mode is "ram_content_bin.txt"; variable text_line : line; variable ram_content : ram_type; variable bv : bit_vector(ram_content(0)'range); begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); read(text_line, bv); ram_content(i) := To_StdLogicVector(bv); end loop; return ram_content; end function;
モダンTEXTIO機能のバックポート
レガシー・コードを変更したくない場合、サードパーティ製のstd_logic_1164_additionsパッケージをインポートできます。このライブラリはHREADとBREAD手順を古いVHDLバージョンにバックポートしますが、追加依存関係のコストがかかります。
より詳細な例とさらなる読み物については、このシリーズの付随記事を参照してください。
VHDL
- マイクロメーター完全ガイド:読み方・使い方・種類を徹底解説
- OPC UAクライアント経由でPLCファンクションブロックを呼び出し:実践ガイド
- アルミニウムの腐食対策:長寿命化のための専門的戦略
- 金属元素と非金属元素:主要な違いを解説
- CNC図面読み取りマスター:現代機械工に必須のスキル
- 初めてのAWS CloudFormationテンプレート構築:実践ガイド
- クラウドコンピューティング vs 従来型コンピューティング:実践的比較
- C 言語のコメント完全ガイド: 明確で効果的な注釈の書き方
- FPGAベースのRCサーボ・コントローラ設計: 精密PWM実装
- Pythonで平均を計算する:実践ガイド
- Java BufferedReader でファイル読み込み:実践ガイドと例