# ソフトウェアの人々のためのHDLRubyチュートリアル このチュートリアルでは、ソフトウェアの観点からHDLRubyを使用したデジタル回路の記述の基本を学びます。具体的には、以下のことを学びます: 1. [HDLRubyとそのフレームワークの使い方について。](#hdlruby) 2. [回路の表現方法について。](#circuit) 3. [回路で実装されるアルゴリズムの記述方法について。](#algorithm) 4. [アルゴリズムに並列処理を追加する方法について。](#parallelism) 5. [低レベルハードウェア設計に向けて:プロセスについて。](#process) 6. [これだけでは足りない?汎用性、オブジェクト指向、メタプログラミングについては?](#advance) これらのトピックの中で、以下の高レベルな概念がHDLRubyでどのように使用できるかについても説明されます: * オブジェクト指向プログラミング * リフレクション * 汎用性 * メタプログラミング しかし、さらに進む前に、いくつかの... ## 前提条件 このチュートリアルは、ソフトウェアの人々を対象としているため、プログラミングや関連するツール(エディタ、コンパイラなど)について十分に理解していると仮定されます。ただし、デジタルハードウェア設計に関する知識は必要ありません。また、Rubyプログラミング言語の知識は推奨されますが、必須ではありません。 HDLRubyを使用するには、以下のソフトウェアが必要です。 以下のソフトウェアも推奨されます。 * Ruby言語の配布版。 * テキストエディタ。シンタックスハイライトやその他の高度な機能を好む場合は、Rubyに対応したエディタを選択してください。 * コマンドラインインターフェース(コマンドプロンプト、ターミナルエミュレータなど)。 以下のソフトウェアも推奨されます。 * vcdファイルをサポートするウェーブビューア(例:GTKWave) ## 1. HDLRubyとそのフレームワークの使い方について。 HDLRubyは、Rubyプログラミング言語に基づくハードウェア記述言語(HDL)です。Rubyライブラリとして実装されているため、HDLRubyの記述の中でRubyコードを使用して実行できます。 さらに進む前に、[HDL](#hdl)とは何かを簡単に説明します。その後、[HDLRubyのインストール方法](#install-hdlruby)と、[HDLRubyの使用方法](#use-hdlruby)について詳しく説明します。 ### 1.1 ハードウェア記述言語(HDL)とは? ハードウェア記述言語(HDL)は、電子回路を記述するために使用されるプログラミング言語に似た形式的な言語です。このような回路は、アナログ回路とデジタル回路の2つのカテゴリに分けることができます。アナログ回路を記述するためのHDLも存在しますが、その大部分はデジタル回路のみをサポートしているため、実際にはHDLとはデジタル回路の記述のための言語を指します。複数のHDLがありますが、2つのHDLが事実上の標準となっています。それらはVerilog HDLとVHDLです。 HDLは回路の製造を支援するためにあります。現代においては、HDLの記述から回路を自動的に生成する強力なソフトウェアツールが存在します。ただし、ソフトウェアと同様に、記述に誤りがあったり、最適ではなかったりすると、最終的な回路が機能しないか、いくつかの制約を満たさなくなる可能性があります。残念ながら、ハードウェアでは、回路の製造には非常に時間と費用がかかるため、ソフトウェアとは異なり、エラーまたは低性能の結果を可能な限り早期に回避する必要があります。このため、ハードウェアでは、回路の記述を製造する前に、回路を**シミュレーション**することが一般的です。言い換えると、ソフトウェアでは以下のループを実行することが一般的です。 ``` ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ │ │ │ │ │ write program ├─────►│ compile program ├─────►│ test program │ │ │ │ │ │ │ └──────────────────┘ └──────────────────┘ └────────┬─────────┘ ▲ │ │ │ │ ▼ │ ┌──────────────────┐ │ │ │ └───────────────┤ fix program │ │ │ └──────────────────┘ ``` ハードウェアの場合、デザインループは以下のようになります: ``` ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ │ │ │ │ │ │ write description ├─────►│ simulate description ├─────►│ produce circuit │ │ │ │ │ │ │ └──────────────────────┘ └─────┬────────────────┘ └───────────┬──────────┘ │ ▲ │ │ │ │ ▼ │ ▼ ┌────────────────┴─────┐ ┌──────────────────────┐ │ │ │ │ │ fix description │◄─────┤ test circuit │ │ │ │ │ └──────────────────────┘ └──────────────────────┘ ``` 最初に、HDLはCやJavaのような古典的なプログラミング言語と同じような外観と感覚を持っています。式、制御文、および種類の変数を含みます。ただし、計算モデルは根本的に異なり、回路は本質的に並列デバイスであるため、これについてはこのチュートリアルの進行に合わせて徐々に説明されます。今のところ、次のことを覚えておいてください: * HDLはデジタル回路を記述するために使用され、最も一般的なものはVerilog HDLとVHDLです。 * 合成ツールと呼ばれるソフトウェアツールを使用すると、HDLの記述から実際のデジタル回路を生成できます。ただし、その前にシミュレーションを実行する必要があります。 * HDLは一般的なプログラミング言語のように見えますが、同じように機能しません。 #### それで、HDLRubyについてはどうでしょうか。 HDLRubyは、Verilog HDLやVHDLのようなデジタル回路の記述のためのHDLであり、Rubyプログラミング言語から多くのコンセプトを引き継いでいるため、それらよりも柔軟性と生産性を高めることを目的としています。そのため、HDLについて先に述べたすべてがHDLRubyにも当てはまりますが、設計者がより簡単になるように努めています。 ### 1.2. HDLRubyのインストール HDLRubyはRubyGemsパッケージとして配布されています。したがって、以下のコマンドを使用してインストールできます: ```bash gem install HDLRuby ``` 問題なければ、以下のように表示されます: ``` Fetching HDLRuby-.gem Building native extensions. This could take a while... Successfully installed HDLRuby- Parsing documentation for HDLRuby- Done installing documentation for HDLRuby after seconds. ``` `version`の数値はHDLRubyの最新バージョンである必要があります。 そして、HDLRubyが正しくインストールされているかどうかは、以下のコマンドで確認することができます: ```bash hdrcc --version ``` そして、その結果は、こうなるはずです: ``` ``` もし結果の version 番号がインストール番号と一致しない場合、どこかで問題が発生した可能性があります。ターミナルまたはコマンドプロンプトを閉じて、新しいものでコマンドを再度実行することをお勧めします。 ### 1.3. HDLRubyの使用方法 今まで、HDLRubyは言語であると言いましたが、実際にはデジタル回路の設計とシミュレーションのための完全なフレームワークです。複数のコンパイラ、シミュレータ、ライブラリが含まれており、すべて1つのコマンド hdrcc でアクセスできます。 基本的に、`hdrcc`は以下のように使用する: ```bash hdrcc ``` `options`は実行するアクションを指定し、`input file`は入力する HDLRuby ファイルを指定し、`output directory`はコマンドの結果を保存するディレクトリを指定します。一般的なルールとして、入力ファイルが指定された場合は、出力ディレクトリも指定する必要があります。 `hdrcc`を使用すると、いくつかのアクションが可能である。主なものは以下の通りである: * 回路の記述をシミュレートする: ```bash hdrcc --sim ``` * 同等のVerilog HDLコードを生成する: ```bash hdrcc --verilog ``` HDLRubyはまだ合成ツールでサポートされていないため、実際の回路を作成する場合は、この2番目のアクションが必要です。 __注__: また、以下のコマンドでVHDL生成も可能です: ```bash hdrcc --vhdl ``` しかしながら、実用的な理由から、Verilog HDL の出力は VHDL の出力よりも頻繁にテストされるため、より信頼性が高いとされています。 以下のチュートリアルの残りを参照して、実行可能なアクションの詳細、入力ファイルの書き方、および出力の種類について詳細を確認してください。 ## 2. HDLRubyで回路を表現する方法 このセクションでは、以下を見ていきます: * [回路を宣言する方法](#circuit-declare) * [既に宣言された回路を再利用する方法](#circuit-use) ### 2.1. 回路を宣言する方法 HDLRuby においては他の HDL と同様に、回路はポートを介して環境と通信する箱として扱われます。以下の図は、6つのポートを持つ回路を含む回路のこのような視点を示しています: ``` A port │ │ │ ┌────────────────────────┐ └───►┌┼┐ ┌┼┐ └┼┘ └┼┘ ┌┼┐ A circuit ┌┼┐ └┼┘ └┼┘ ┌┼┐ ┌┼┐ └┼┘ └┼┘ └────────────────────────┘ ``` ただし、ポートは単純なエントリーポイントではありません。なぜなら、データ型と次のいずれかの方向を持つからです: * `input`: このようなポートは、回路の外部から内部にデータを伝送するためにのみ使用できます。 * `output`: このようなポートは、回路の内部から外部にデータを伝送するためにのみ使用できます。 * `inout`: このようなポートは、`input`ポートと`output`ポートの両方として使用できます。しかし、身体的な制限のため、これらのポートを使用するのは困難です。そのため、このチュートリアルの[進んだ部分](#tri-state)まで無視されます。 #### それはそれでいいのですが、HDLRubyのコードはいつ書けばいいのでしょうか? それを念頭に置いて、回路を宣言することは、その名前とポートを指定することです。HDLRubyでは、次のように行われます: ```ruby system :my_circuit do input :clk, :rst [16].input :addr input :ce [8].input :data_in [8].output :data_out end ``` そこで、上記のコードを(例えば)`my_circuit.rb`というファイルに書いて、その意味を説明させてください。 * 最初の行で、キーワード `system` は新しい回路が記述されることを示しています。その後ろにあるコロンの後には、この場合は my_circuit という名前が指定されています。 * `do` → `end` までのブロックには、回路の記述が含まれます。ここでは、ポートのみが次のように指定されています: - 2行目では、`input` が、それぞれ `clk` および `rst` という名前の1ビットの入力ポート2つを指定しています。 - 3行目では、16-ビットの入力ポート `addr` を指定しています。 - 4行目では、1-ビットの入力ポート `ce` を指定しています。 - 5行目では、8ビットの入力ポート `data_in` を指定しています。 - 6行目では、8ビットの出力ポート `data_out` を指定しています。 ``` ┌───────────────────────────┐ clk ┌┴┐ ┌┴┐ ce ────►│1│ │1│◄──── └┬┘ └┬┘ │ │ rst ┌┴┐ ┌┴┐ data_in ────►│1│ my_circuit │8│◄──── └┬┘ └┬┘ │ │ addr ┌┴┐ ┌┴┐ data_out ────►│1│ │8├────► │6│ └┬┘ └┬┘ │ └───────────────────────────┘ ``` まとめると: * `system` は新しい回路の記述を宣言します。 * `input` は1つまたは複数の入力ポートを、`output` は1つまたは複数の出力ポートを、`inout` は1つまたは複数の入出力ポートを指定します。 * ポートのデータ型は、方向の前に次のように指定されます: ```ruby .input ``` HDLRubyのデータ型については、後で詳しく説明します。 では、次のコマンドで、回路の記述が問題ないかどうか確認してみましょう: ```bash hdrcc my_circuit.rb work ``` ...何も起きなかったのですか?素晴らしい、それはあなたの記述に構文エラーがなかったことを意味します。では、別のことを試してみましょう: ```bash hdrcc --hdr my_circuit.rb work ``` 問題がなければ、`work`ディレクトリに `my_circuit.rb` というファイルができているはずです。テキストエディタで開くと、以下のような内容になっているはずである: ```ruby system :"__:T:0:1" do bit. input :clk bit.input :rst bit[15..0].input :addr bit.input :ce bit[7..0].input :data_in bit[7..0].output :data_out end ``` これは、あなたが書いたコードにかなり似ています。それは、HDLRubyでの回路の内部表現(IR)です。回路の名前が奇妙な文字列に変わり、データ型も変わったことがわかります。名前は名前の衝突を避けるためであり、気にする必要はありません。データ型は、最初のファイルで使用されたデータ型と同じデータ型の低レベル表現です。それでも、この低レベル表現は元の表現に非常に近く、回路に機能が追加されるにつれて、それがますますそうでなくなることになります。 では、Verilog HDLと同等のコードはどのように見えるのでしょうか。それを確認するために、次のコマンドを入力してください: ```bash hdrcc --verilog my_circuit.rb work ``` 問題がなければ、`work`ディレクトリに`my_circuit.v`という名前のファイルができているはずです。テキストエディタで開くと、以下のような内容になっているはずです: ```verilog `timescale 1ps/1ps module _v0_1( _v1_clk, _v2_rst, _v3_addr, _v4_data_in, _v5_data_out ); input _v1_clk; input _v2_rst; input [15:0] _v3_addr; input _v4_ce; input [7:0] _v5_data_in; output [7:0] _v6_data_out; endmodule ``` 構文は確かにHDLRubyとは少し異なっていますが、回路の説明を認識できるはずです。ただし、ポートの名前は異なります。これは、HDLRubyが名前に任意のUNICODE文字をサポートしているため、Verilogを生成するときに名前を再作成して、互換性の問題を回避するためです。それでも、元の名前を維持するよう努力しています。例えば、 clk は `_v1_clk` になりました。しかし、楽しみのために、HDLRubyファイルの `:addr` を `:☺` に置き換えて、Verilog HDLを再生成してください...それでも動作します!結果は以下の通りです: ```verilog `timescale 1ps/1ps module _v0_1( _v1_clk, _v2_rst, _v3_, _v4_data_in, _v5_data_out ); input _v1_clk; input _v2_rst; input [15:0] _v3_; input _v4_ce; input [7:0] _v5_data_in; output [7:0] _v6_data_out; endmodule ``` 残念ながら、これ以上笑顔の顔文字は使えません。これは、Verilog HDLが名前にASCIIのサブセットしかサポートしていないためです。しかし、笑顔のないコードでも、HDLRubyフレームワークがVerilog HDLに適した名前を再作成したため、有効なコードとなっています。 ### 2.2. 既に宣言された回路を再利用する方法 ソフトウェアの関数と同様に、回路はしばしば1つまたは複数の大きな回路の一部として使用されます。ただし、回路は物理的にコピーされて再利用する必要があります。このコピーは*インスタンス*と呼ばれ、*インスタンス化*と呼ばれます。HDLRubyでは、インスタンス化は以下のように行われます: ```ruby (:) ``` 例えば、先に定義した回路 `my_circuit` のコピーを `another_circuit` という新しい回路で使いたい場合、以下のようにします: ```ruby system :another_circuit do input :clk, :rst [16].input :addr input :ce0, :ce1 [8].input :data_in [8].output :data_out my_circuit(:my_circuit0) my_circuit(:my_circuit1) end ``` テスト用に、上記のコードを `another_circuit.rb` という別のファイルに書いて、そこからVerilog HDLを生成してみる: ```bash hdrcc --verilog another_circuit.rb work ``` あ、以下が表示されるはずなので、何かが間違っていたようです: ``` another_circuit.rb:8:in `block in
': undefined HDLRuby construct, local variable or method `my_circuit'. ``` このエラーメッセージは、`my_circuit`が不明であることを示しています。これは、Ruby言語と同様に、HDLRubyでは使用するファイルを指定する必要があるためです。以下のコードを `another_circuit.rb` ファイルの1行目として追加してください: ```ruby require_relative "my_circuit.rb" ``` その後、Verilog HDL生成コマンドを再試行します: ```bash hdrcc --verilog another_circuit.rb work ``` work`ディレクトリに3つの新しいファイル、`_v10_5.v`, `_v8_4.v` と `another_circuit.v` が出現しているはずです。3つ目のファイルを開くと、次のように表示されるはずです: ```verilog `timescale 1ps/1ps module _v0_3( _v1_clk, _v2_rst, _v3_addr, _v4_ce0, _v5_ce1, _v6_data_in, _v7_data_out ); input _v1_clk; input _v2_rst; input [15:0] _v3_addr; input _v4_ce0; input _v5_ce1; input [7:0] _v6_data_in; output [7:0] _v7_data_out; _v8_4 _v9_my_circuit0(); _v10_5 _v11_my_circuit1(); endmodule ``` 再び、生成されたVerilog HDLコードと元のHDLRubyコードの類似点が見られます。しかし、 `_v8_4` と `_v10_5` は何ですか?対応するファイル `_v8_4.rb` および `_v10_5.rb` を開くことで、これらはVerilog HDLで my_circuit の説明です。