IP の世界からこんにちは (3)

IP インテグレータを用いた回路やシステムの設計について学ぶコースの第3回です。前回は、IP パッケージャを使って、シリアル通信による文字送信回路の IP コアを作成しました。

今回は、これらと既存の IP コアとを組み合わせて、ブロック図を使った設計で「Hello, FPGA」を PC に送信する回路を作成して、その動作を確認していきます。

ブロック図作成の下準備

制御部の回路の再考

今回作成する回路は、「シリアル通信で Hello, FPGA (5)」 (以下、「以前の記事」と呼びます) で作成した拡張トップモジュールと同等の回路となります。念のため、以前の記事で紹介したブロック図を再掲します。

※ 以前の記事では記載を省いてしまいましたが、カウンタ値が一定値であるかどうかの信号もステートマシンへと入力されます。下図では点線で示します。

「Hello, FPGA」を送信する回路のブロック図。

「Hello, FPGA」を送信する回路は、文字送信回路のほか、全体の制御部 (ステートマシン)、カウンタ、デコーダから構成されます。制御部のステートマシンを HDL で記載するのもよいですが、ここではもう少し簡単な方法を探ってみます。

以前の記事では制御部について、送信開始 (SEND)、送信終了待ち (WAIT)、送信終了済み (FIN) の3種類の状態を考え、ステートマシンを設計しました。これとは別に、送信開始と送信終了待ちを送信中 (RUN) という1つの状態にまとめた設計を考えてみます。

簡略化された制御部の状態遷移図。

具体的な状態遷移図は上図に示す通りとなります。文字の送信を開始するかどうかの信号 WE は、状態を表す囲みではなく、状態遷移を表す矢印へと対応づけます (これは、Moore マシンではなく Mealy マシンを用いることを意味します)。そして、文字送信回路が文字を受け付けられる状態になった、つまり BUSY が ‘0’ になったならば、直ちに WE を ‘1’ にします。回路の動作タイミングは若干変化することになりますが、この設計でも回路は問題なく動作します。

この設計において、回路の状態は結局のところ「送信を開始したバイト数が12か否か」という信号と等価です。したがって、送信バイト数のカウンタの値を状態として用いれば、制御部自身にはもはや状態が必要なくなります。つまり、制御部は組合せ回路で構成できます。

具体的にどのような回路になるかも考えてみましょう。ここでは、送信を開始したバイト数が12であるときに ‘1’ となる信号を THRESH と定義します。このとき、WE は BUSY が ‘0’ で、THRESH が ‘0’ (送信バイト数が12でない) ときに ‘1’ となりますから、

WE = (~ BUSY) & (~ THRESH);

すなわち、

WE = ~ (BUSY | THRESH);

となります (ド・モルガンの定理)。したがって、簡略化された制御部の回路は、下図に示す通り、NOR ゲート1個 (あるいは OR ゲートと NOT ゲート1個ずつ) で表せることになります。

簡略化された制御部の回路。

COE ファイルの作成

以前の記事でも説明した通り、デコーダ部分は送信したい文字列を格納した ROM と捉えることができます。以前の記事ではこれを case 文を使った HDL で記述しましたが、今回はメモリの IP コアを使って作成します。

「FPGAをもっと活用するために IP コアを使ってみよう (4)」ではブロックメモリ (BRAM) を使っていましたが、今回の ROM は容量が小さい (4ビットアドレス・8ビットデータなので、24 x 8 = 128ビット) ため、分散 RAM とよばれる、FPGA の基本的な論理素子である LUT で作られたメモリを使用します。BRAM は最低単位が 18 k ビットですので、数十~数百ビットのメモリに BRAM を使うと、容量の効率が悪いです。こうした場合には分散 RAM が向いています。

メモリの IP コアで初期値を設定するには、拡張子 .coe のメモリ係数ファイルを用意する必要があります。詳細な記法は Xilinx 社の公式ドキュメントの文書番号 PG063 に記載されていますが、一言で言えば、データの基数とデータ列を記載したテキストファイルです。

デコーダの ROM に対するメモリ係数ファイルを以下に示します。適当なテキストエディタに以下の3行を記述し、拡張子を .coe として保存してください。

memory_initialization_radix = 16;
memory_initialization_vector =
48 65 6c 6c 6f 2c 20 46 50 47 41 0a 00 00 00 00;

1行目がデータの基数 (2進数、10進数、16進数のいずれか)、3行目がデータ列 (カンマ、スペース、改行のいずれかで区切り、最後にセミコロンを置く) です。

ブロック図の作成

IP リポジトリの追加

それでは Vivado 上での作業に入ります。いつものようにプロジェクトを作成したら、まずは前回作成した IP コアを含むユーザリポジトリをプロジェクトに追加し、文字送信回路の IP コアを追加できるようにします。

「Project Manager → Settings」からプロジェクト設定を開き、設定ダイアログで「Project Settings → IP → Repository」を選択します。リスト上の「+」ボタンをクリックし、前回作成したユーザリポジトリ (ip_repo) を選択します。ここまでの操作が完了すると、下図に示す通りユーザリポジトリの場所が IP Repositories のリストに追加されます。

ユーザリポジトリが追加された様子。

「Project Manager → IP Catalog」で IP カタログを開くと、User Repositories という項目が追加されており、その中に前回作成した文字送信回路の IP コア (名前を変更していなければ serial_wrap_v1_0) の存在を確認できます。

ユーザリポジトリ追加後の IP カタログ。

ブロック図への IP コアの配置

それでは、IP インテグレータを使ってブロック図を作成していきます。ブロック図の作成は「IP Integrator → Create Block Design」から行えます。ブロック図の名前を適当に設定し (今回はデフォルトの design_1 のまま進めてみます)、OK をクリックすると、ブロック図の編集画面が表示されます。

ブロック図の新規作成画面。

なお、途中から作業を継続する場合には、既存のブロック図を開くことになるので、「IP Integrator → Open Block Design」をクリックしてください。

ブロック図の上にあるツールバーで「+」ボタンをクリックするか、ブロック図上で右クリックして「Add IP…」を選択すると、IP のリストが表示されます。今回は、以下の4種類5個の IP コアを使用します。検索窓に IP コアの名前の一部を入力して検索し、これらを順次追加します。

  • 前回作成した文字送信回路の IP コア (名前を変更していなければ serial_wrap_v1_0)
  • カウンタ (Binary Counter)
  • デコーダ (Distributed Memory Generator)
  • 論理ゲート (Utility Vector Logic) 2個
ブロック図上に IP コアを追加したときの様子。

レイアウトはブロック図の上にあるツールバーの「↻」ボタンをクリックするか、ブロック図上で右クリックして「Regenerate Layout」を選択すると、自動的に調整されます。もちろん、自分でブロック図上の IP コアをドラッグして動かしても構いません。適当に配置を調整すると、例えば上図に示すブロック図が得られます。

IP コアのパラメータの変更

次に、これらの IP コアのパラメータを作成する回路に合わせて適切に設定します。 IP コアのパラメータを修正するには、ブロック図上で IP コアをダブルクリックするか、右クリックして「Customize Block…」を選択します。

文字作成回路では、コア自体のパラメータ変更は不要ですが、よく見るとリセット入力の極性が誤って負論理に設定 (極性の反転を表すバブルが付加) されています。ここでは、ブロック図上でリセット入力の極性を正論理に変更しておきましょう。

※ 根本的には IP コアを作成する段階でリセット入力の極性を明示することで解決できますが、詳細は割愛します。

文字作成回路のリセット入力端子 (RST) をダブルクリックすると設定画面が表示されますので、下図に示す通り、Polarity を ACTIVE HIGH へと変更します。

文字送信回路のリセット入力を ACTIVE HIGH (正論理) に変更。

カウンタは、出力のビット幅 (Output Width) を4ビットに変更します。また、先述の THRESH、つまりカウンタの値が12であるかどうか (正確には12以上かどうか) を出力させるため、しきい値出力 (Sync Threshold Output) を有効にし、しきい値 (Threshold Value) を16進数で c に設定します。

また、カウンタをインクリメントするかどうかを定める有効入力と、初めにカウンタの値を0にリセットするためのリセット入力も必要です。これらは Control タブの Clock Enable (CE)、Synchronous Clear (SCLR) とそれぞれ対応しますので、これらの両方にチェックします。以上の設定項目を下図に示します。

カウンタを有効・リセット入力、しきい値出力つきの4ビットに変更。

デコーダは、アドレス入力が4ビット、データ出力が8ビットとなるので、メモリの深さ (Depth) と幅 (Data Width) はそれぞれ16ワード、8ビットとなります。また、Memory Type を ROM に設定した上で、RST & Initialization タブで先ほど作成したメモリ係数ファイル (拡張子 .coe) を参照するように設定します。設定例を下図に示します (ファイル名は環境により異なります)。

メモリを16ワード8ビットの初期値つき ROM に変更。

先述した通り、制御部分の組合せ回路は OR ゲートと NOT ゲートを使って実現します。そのため、1つ目の論理ゲートは、ビット幅 (C_SIZE) を1に、演算の種類 (C_OPERATION) を or に設定します。2つ目の論理ゲートは、ビット幅を1、演算の種類は not に設定します。各論理ゲートの設定を下図の左右にそれぞれ示します。

論理ゲートを1ビット幅の OR、NOT ゲートに変更。

これら全ての設定が完了すると、各 IP コアの入力・出力が適切に変更されています。このときのブロック図の例を下図に示します。

IP コアのパラメータ変更が完了したときの様子。

入出力の接続

次のステップは、入出力ポート (クロック入力 CLK、リセット入力 RST、シリアル出力 TXD) への信号の接続です。今回のブロック図で該当するのは、

  • クロック入力: 文字送信回路とカウンタの CLK
  • リセット入力: 文字送信回路の RST とカウンタの SCLR
  • シリアル出力: 文字送信回路の DATA_OUT

の各信号となります。まずはこれらの接続の方法を説明します。

ある信号を新たな入出力ポートに接続するには、端子を右クリックし、「Make External」を選択します。ここでは文字送信回路の CLK を選択したものとします。そうすると、自動的に入出力ポートが作成され、IP コアの端子と接続されます。作成された入出力ポートをクリックし、ブロック図の左下にあるプロパティ画面で Name の欄を修正すると、ポートの名前を変更できます。一例として、クロック入力のポートの名前を CLK に変更している様子を下図に示します。

入出力ポートの名前を変更している様子。

こうして作成された入出力ポートと別の IP コアの端子とを接続するには、ブロック図上で端子をクリックし、接続したい入出力ポートへとドラッグします。すると、下図に例示する通り、接続が可能な場合には端子同士が太線で結ばれます。このままマウスをドロップすると、新たな接続が形成されます。

IP コアの端子を入出力ポートに接続している様子。

なお、ブロック図上で接続するかわりに、リストから接続先を選んで接続することもできます。この方法を用いる場合には、端子を右クリックして、「Make Connection」を選択します。

リセット入力・シリアル出力についても、同様に入出力ポートを作成して名前を変更し、接続をしていきます。

IP コア間の接続

IP コア間の接続についても同様です。ブロック図上でドラッグ&ドロップを使うか、端子を右クリックしてから「Make Connection」を選択し、接続を進めてください。

必要な IP コア間の接続は以下のとおりです。

  • カウンタの Q 出力を、デコーダの a 入力へ
  • デコーダの spo 出力を、文字送信回路の DATA_IN 入力へ
  • 文字送信回路の BUSY 出力とカウンタの THRESH0 出力を、OR ゲートの Op1, Op2 入力へ (順不同)
  • OR ゲートの Res 出力を、NOT ゲートの Op1 入力へ
  • NOT ゲートの Res 出力を、カウンタの CE 入力と文字送信回路の WE 入力へ

前述の通り、レイアウトは適宜「↻」ボタンをクリック、または「Regenerate Layout」を選択することで、自動的に調整されます。また、IP コアの配置はそのままに配線だけを調整したい場合には、「↻」ボタンの右隣にある、配線の左上に「↻」が描かれたアイコン (Optimize Routing) をクリックしてください。

なお、ブロック図上でのレイアウトや配線はあくまで見た目上の問題で、実際に合成される回路の配置や配線とは関係ありません。操作がしづらくなければ、レイアウトや配線の調整はしなくても構いません。

全ての接続を行い、レイアウトを適当に調整したブロック図の例を、下図に示します。

完成したブロック図の例。

回路の合成と動作確認

論理合成の準備

ブロック図が完成したら、まずは作成したブロック図に問題がないか Vivado にチェックさせます。ブロック図の上にあるツールバーのチェックボックスのアイコンをクリックするか、ブロック図を右クリックして「Validate Design」を選択します。少し待つと検証結果が表示されるので、もし問題があれば適切に修正します。

次に、ブロック図から各種のファイルの生成を行います。「IP Integrator → Generate Block Design」を選択し、表示されたダイアログで Generate ボタンを押します。バックグラウンドで IP コアの合成が始まります (これには ACRi 利用環境、ACRi ルームで数分かかります)。

さらに、制約ファイル (XDC) をプロジェクトに追加します。使用する制約ファイルは、「シリアル通信で Hello, FPGA (4)」 のものと同一ですが、念のため以下に再掲します。

set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { CLK }];
create_clock -add -name sys_clk_pin -period 10.00 [get_ports { CLK }];
set_property -dict { PACKAGE_PIN D9    IOSTANDARD LVCMOS33 } [get_ports { RST }];
set_property -dict { PACKAGE_PIN D10    IOSTANDARD LVCMOS33 } [get_ports { TXD }];

最後に、ブロック図を論理合成できるようにするためのラッパ回路を作成します。Sources のリストの Design Sources からブロック図 (デフォルトでは design_1) を探して右クリックし、「Create HDL Wrapper」を選択します。下図に示すダイアログが表示されるので、「Let Vivado manage~」を選択して、OK ボタンを押します。

ラッパ回路の生成ダイアログ。

なお、ラッパ回路の作成は最初の1回だけ行います。この作業を行わずに論理合成を行おうとすると、ブロック図はそのまま論理合成できないという旨のエラーが表示されます。

論理合成から動作確認まで

以上ですべての準備が整いました。ここから先の作業はこれまでと同じです。論理合成 (Run Synthesis) からプログラミングファイルの作成 (Generate Bitstream) を行い、Hardware Manager で FPGA にプログラミングファイルを書き込みます。

「シリアル通信で Hello, FPGA (4)」 のときと同様、事前に Tera Term (Windows の場合)、ないし GTKTerm (Linux の場合) を起動して適切に設定していれば、出力として「Hello, FPGA」が表示されることが確認できるはずです。ACRi Room の検証環境で「Hello, FPGA」の出力を確認したときの様子を下図に示します。

「Hello, FPGA」の出力が確認できたときの様子。

文字送信回路のパラメータの変更

ここまででひと通りの動作検証は終わりですが、せっかく前回の最後で文字送信回路のパラメータを候補の中から選択できるようにしたので、その挙動も確認してみましょう。

改めてブロック図を開き、文字送信回路をダブルクリック (または右クリックして「Customize Block…」) して、設定画面を開くと、パラメータ WAIT_DIV についてドロップダウンリストで変更できることが確認できます。ここでは、下図に示す通り、1736 を選択してみます。

文字送信回路のパラメータを候補から選択する様子。

ブロック図を保存する (ctrl + s) と、変更が検知され、合成結果が最新でないことを示すメッセージが Vivado の画面右上に表示されます。合成をやり直すにあたっては、「Generate Block Design」から順番に行ってもよいですが、単に「Generate Bitstream」を実行しても構いません。この場合は、再生成からプログラミングファイルの作成までの一連の処理を続けて行ってくれます。

新しいプログラミングファイルが作成できたら、これを改めて FPGA に書き込んで、動作を確認してみます。動作例を下図に示します。

FPGA 側の通信速度を変更したときの様子。

……文字化けしましたね。シリアル通信では、送信側と受信側で通信速度などの取り決めが一致していないと、正しいタイミングでデータを取り込むことができなくなってしまい、その結果がこのような文字化けとなって現れます。

ここでは WAIT_DIV を2倍、つまり通信速度を半分の 57,600 bps にしているので、PC 側の端末の設定もそれに合わせて 57,600 bps へと変更する必要があります。設定を変更した上で、再度 FPGA に回路を書き込むことで、今度は正しく「Hello, FPGA」が表示されます。

まとめ

今回は、文字送信回路の IP コアと既存の IP コアとを組み合わせて、「Hello, FPGA」の回路を作成し、動作検証を行いました。今回のポイントは以下のとおりです。

  • ちょっとした回路であれば、Vivado に用意された基本・ユーティリティ IP コアを組み合わせて構成できる。
  • IP コアのパラメータは、ブロック図上の IP コアをダブルクリックするか、右クリックし「Customize Block…」を選択して設定する。
  • ブロック図が完成したら、ブロック図の検証 (Validate Design) とブロック図からのファイルの生成 (Generate Block Design) を行い、初回のみラッパ回路を作成 (Create HDL Wrapper) してから、論理合成を行う。

IP インテグレータによるブロック図を使った設計が真に効果を発揮するのは、プロセッサと組み合わせたときです。次回からは、MicroBlaze プロセッサと文字送信回路の IP コアとを組み合わせて使う方法の説明に進んでいきます。

愛知工業大学 藤枝直輝

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