VivadoHLSを使ったCベース設計に挑戦

C/C++ベースのFPGA開発をはじめるにあたって,まずはVivado HLSの使い方をFPGAで動作するビットストリームの作り方まで一通り学んでしまいましょう.この章では,とりあえずVivado HLSを使った開発ができるようになるために,ツールを一通り使ってC/C++で書いたコードからFPGAで動作するビットストリームを生成する作業までを一通り体験してみることにします.習うより慣れろ,ですね.

例題

VivaoHLSでは,基本的な制御構文のC/C++コードをHDLモジュールにすることができます.ここでは,簡単なプログラムをハードウェア化してみましょう.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int bitcount(int a)
{
  int i;
  int s = 0;
  int tmp = a;
  for(i = 0; i < 32; i++){
    if((tmp & 0x01) == 0x01){
      s++;
    }
    tmp = tmp >> 1;
  }
  return s;
}

Vivado HLSプロジェクトの作成

Vivado HLSを起動したところ.Vivado HLSはデスクトップのショートカットアイコンやスタートメニューから起動する.

Vivado HLSを起動したところ.Vivado HLSはデスクトップのショートカットアイコンやスタートメニューから起動する.

プロジェクト名と格納フォルダを指定.ここでは.ホームの下のVivadoの下に格納することとし,名前をhls_test_1とした

プロジェクト名と格納フォルダを指定.ここでは.ホームの下のVivadoの下に格納することとし,名前をhls_test_1とした

既存の設計ソースコードがあれば,ここで追加.ないのでNextで次へ.

既存の設計ソースコードがあれば,ここで追加.ないのでNextで次へ.

既存のテストベンチ用のソースコードがあれば,ここで追加.ないのでNextで次へ.

既存のテストベンチ用のソースコードがあれば,ここで追加.ないのでNextで次へ.

ターゲットFPGAを選択する.Part Selectionの中の&hellip;ボタンをクリック

ターゲットFPGAを選択する.Part Selectionの中の…ボタンをクリック

FPGAの型番を選択.xc7z020clg400-1を選択する.検索フィールドにxc7z020と入力していくと楽に探せる.

FPGAの型番を選択.xc7z020clg400-1を選択する.検索フィールドにxc7z020と入力していくと楽に探せる.

FPGAを選択し終えたらFinish.

FPGAを選択し終えたらFinish.

プロジェクトの作成が完了した

プロジェクトの作成が完了した

Vivado HLS上での設計

左ペインのSourcesアイコンの上で右クリック,New File&hellip;をクリック

左ペインのSourcesアイコンの上で右クリック,New File…をクリック

開いたファイル選択ダイアログに,作成するファイル test.c と入力して保存をクリック

開いたファイル選択ダイアログに,作成するファイル test.c と入力して保存をクリック

作成したtest.cの中身として,先のソースコードを入力する.

作成したtest.cの中身として,先のソースコードを入力する.

動作確認のために,テストベンチ用のソースコードを追加する.今度はTest Benchアイコンの上で右クリック,New File&hellip;をクリック

動作確認のために,テストベンチ用のソースコードを追加する.今度はTest Benchアイコンの上で右クリック,New File…をクリック

今度はtest_tb.cというファイルを作成することにする.ファイル名を入力して保存をクリック

今度はtest_tb.cというファイルを作成することにする.ファイル名を入力して保存をクリック

テストベンチの中身を記述する.テストベンチは単なるCプログラムなのでstdio.hやprintf関数が使える

テストベンチの中身を記述する.テストベンチは単なるCプログラムなのでstdio.hやprintf関数が使える

メニューのProjectからProject Settings&hellip;をクリックして,プロジェクトの設定をする

メニューのProjectからProject Settings…をクリックして,プロジェクトの設定をする

Synthesisをみると,Top Functionが指定されていないことが確認できる

Synthesisをみると,Top Functionが指定されていないことが確認できる

Browse&hellip;ボタンをクリックすると候補がでる.今回は一つだけ.候補に表示されたtest関数を選択してOKをクリック

Browse…ボタンをクリックすると候補がでる.今回は一つだけ.候補に表示されたtest関数を選択してOKをクリック

合成するトップの関数がtestであることを指定できた

合成するトップの関数がtestであることを指定できた

Vivado HLSでの動作確認

まずはCレベルでの動作を確認するため,メニューのProjectからRun C Simulationをクリックする

まずはCレベルでの動作を確認するため,メニューのProjectからRun C Simulationをクリックする

特にオプションは指定せずにOKをクリック

特にオプションは指定せずにOKをクリック

しばらくすると結果が表示される.たとえば0xaの立っているビットは2個で答えが正しいことが確認できた

しばらくすると結果が表示される.たとえば0xaの立っているビットは2個で答えが正しいことが確認できた

ツールバーの再生ボタンアイコンをクリックしてCからHDLの合成を開始する

ツールバーの再生ボタンアイコンをクリックしてCからHDLの合成を開始する

しばらく待つと合成が完了する.ここでは生成された回路のクリティカルパス遅延が3.92nsであると表示されている.また,ひとつの答えを得るために33サイクル必要であることも確認できる.

しばらく待つと合成が完了する.ここでは生成された回路のクリティカルパス遅延が3.92nsであると表示されている.また,ひとつの答えを得るために33サイクル必要であることも確認できる.

合成したHDLの動作検証を行う.テストベンチはCシミュレーションのときと同じ

合成したHDLの動作検証を行う.テストベンチはCシミュレーションのときと同じ

特にオプションは指定せずにOKをクリック

特にオプションは指定せずにOKをクリック

シミュレーションにXSIM(RTLシミュレータ)が使用されていることがわかる

シミュレーションにXSIM(RTLシミュレータ)が使用されていることがわかる

RTLシミュレーションでも望み通りの答えがえられることが確認できた

RTLシミュレーションでも望み通りの答えがえられることが確認できた

IPコアの生成

ツールバーの荷物のようなアイコンをクリックしてIPコアを生成する

ツールバーの荷物のようなアイコンをクリックしてIPコアを生成する

IPコア生成に関する設定ダイアログが開いたところ.特に変更の必要はないのでOKをクリック

IPコア生成に関する設定ダイアログが開いたところ.特に変更の必要はないのでOKをクリック

しばらく待つと,Vivadoで利用可能なIPコアが生成される.

しばらく待つと,Vivadoで利用可能なIPコアが生成される.

生成されたリソースやファイルはsolution1の下のimplの下に格納されていて自由に確認することができる

生成されたリソースやファイルはsolution1の下のimplの下に格納されていて自由に確認することができる

生成したモジュールをFPGAプロジェクトで利用する

まずはVivadoプロジェクトを作成する

まずはVivadoプロジェクトを作成する

プロジェクト作成ダイアログ.Nextで次へ

プロジェクト作成ダイアログ.Nextで次へ

プロジェクト名をproject_3として,ホーム下のVivadoフォルダの下に保存することにする

プロジェクト名をproject_3として,ホーム下のVivadoフォルダの下に保存することにする

作成するプロジェクトはRTLプロジェクトを選択.

作成するプロジェクトはRTLプロジェクトを選択.

特にここで追加するファイルはないのでNextで次へ

特にここで追加するファイルはないのでNextで次へ

特にここで追加するファイルはないのでNextで次へ

特にここで追加するファイルはないのでNextで次へ

プロジェクトの開発ターゲットはボードリストからZybo Z7-20を選択

プロジェクトの開発ターゲットはボードリストからZybo Z7-20を選択

設定内容を確認してFinishをクリック.ウィザードを終了する.

設定内容を確認してFinishをクリック.ウィザードを終了する.

PROJECT NAVIGATORのSettingsをクリックして設定画面を呼び出す

PROJECT NAVIGATORのSettingsをクリックして設定画面を呼び出す

リストのIPを展開し,Repositriesを選択する

リストのIPを展開し,Repositriesを選択する

ここにVivado HLSで作成したIPコアのフォルダを指定すれば利用できるようになる.+アイコンをクリック.

ここにVivado HLSで作成したIPコアのフォルダを指定すれば利用できるようになる.+アイコンをクリック.

IPコア検索用のフォルダとしてVivado HLSで作成したプロジェクトフォルダ(hls_test_1)の下の,solusion1\yen ipを選択して,Selectをクリック

IPコア検索用のフォルダとしてVivado HLSで作成したプロジェクトフォルダ(hls_test_1)の下の,solusion1\yen ipを選択して,Selectをクリック

追加したリポジトリ情報が表示される.TestというIPコアが含まれていれば設定は正しい.OKでダイアログを閉じる

追加したリポジトリ情報が表示される.TestというIPコアが含まれていれば設定は正しい.OKでダイアログを閉じる

リポジトリに追加できたので,OKで設定ダイアログを閉じる

リポジトリに追加できたので,OKで設定ダイアログを閉じる

IP Catalogを選択してIPを呼び出す

IP Catalogを選択してIPを呼び出す

IP Catalogの中にVivado HLSで作成したTestがあるので,ダブルクリックする

IP Catalogの中にVivado HLSで作成したTestがあるので,ダブルクリックする

IPコアの設定画面が開くが,することもない.OKを押してダイアログを閉じる

IPコアの設定画面が開くが,することもない.OKを押してダイアログを閉じる

Vivadoプロジェクト内にIPコア保存用のフォルダを作る確認を求められる.OKをクリック

Vivadoプロジェクト内にIPコア保存用のフォルダを作る確認を求められる.OKをクリック

IPコア関連のファイルをVivadoに生成させるためGenerateをクリック

IPコア関連のファイルをVivadoに生成させるためGenerateをクリック

IPコア関連のファイルをVivadoに生成させるためGenerateをクリック

IPコア関連のファイルをVivadoに生成させるためGenerateをクリック

合成がかかる旨のメッセージが表示されたらOKで閉じる

合成がかかる旨のメッセージが表示されたらOKで閉じる

呼びだしたIPコアのインスタンスを持つためのトップモジュールを作成する.モジュールの作成は,Design Sourcesの上で右クリックしてAdd Sources&hellip;選択すればよい

呼びだしたIPコアのインスタンスを持つためのトップモジュールを作成する.モジュールの作成は,Design Sourcesの上で右クリックしてAdd Sources…選択すればよい

今回はデザインファイルを作るので,Add or create design sourcesを選択

今回はデザインファイルを作るので,Add or create design sourcesを選択

Create Fileをクリック.

Create Fileをクリック.

VHDLで,topという名前のモジュールを作ることにしてOKをクリック.

VHDLで,topという名前のモジュールを作ることにしてOKをクリック.

作成したモジュールがリストに追加されたのでFinishで終了.

作成したモジュールがリストに追加されたのでFinishで終了.

topモジュールの入出力ポートの生成ウィザードが開くので,clkと入出力用のポートを定義してOKをクリックする(あとでテキストで記述してもよいので無視してOKをクリックしても構わない).

topモジュールの入出力ポートの生成ウィザードが開くので,clkと入出力用のポートを定義してOKをクリックする(あとでテキストで記述してもよいので無視してOKをクリックしても構わない).

生成したトップモジュールをVivadoのエディタで開いたところ

生成したトップモジュールをVivadoのエディタで開いたところ

top.vhdの内容は次の内容で書きかえる.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity top is
    Port ( clk : in STD_LOGIC;
           a : in STD_LOGIC_VECTOR (3 downto 0);
           q : out STD_LOGIC_VECTOR (3 downto 0));
end top;

architecture Behavioral of top is

 component test_0
  port (
        ap_clk : in std_logic;
        ap_rst : in std_logic;
        ap_start : in std_logic;
        ap_done : out std_logic;
        ap_idle : out std_logic;
        ap_ready : out std_logic;
        a : in std_logic_vector(31 downto 0);
        ap_return : out std_logic_vector(31 downto 0)
  );
  end component;
  
  attribute mark_debug : string; -- 動作確認のためにmark_debugアトリビュートを使う
  
  signal q_i : std_logic_vector(31 downto 0);
  
begin

 q <= q_i(3 downto 0);

 U : test_0
  port map(
        ap_clk => clk,
        ap_rst => '0',
        ap_start => '1',
        ap_done => ap_done,
        ap_idle => ap_idle,
        ap_ready => ap_ready,
        a(31 downto 4) => (others => '0'),
        a(3 downto 0) => a,
        ap_return => q_i
  );

end Behavioral;
コードを正しく記述し終えると,top→test_0というデザインツリーが作られる

コードを正しく記述し終えると,top→test_0というデザインツリーが作られる

PROJECT NAVIGATORのRun Synthesisをクリックして合成を開始.ダイアログはOKで閉じてステップを進める.

PROJECT NAVIGATORのRun Synthesisをクリックして合成を開始.ダイアログはOKで閉じてステップを進める.

合成を終えたら,一度Open Synthesized Designで合成結果を開く

合成を終えたら,一度Open Synthesized Designで合成結果を開く

メニューのLayoutからI/O Planningをクリックしてピン配置設定モードを呼び出す

メニューのLayoutからI/O Planningをクリックしてピン配置設定モードを呼び出す

クロック(K17),入力4bit(T16,W13,P15,G15),出力4bit(D18,G14,M15,M14)を設定.電圧は,すべてLVCMOS33に設定.

クロック(K17),入力4bit(T16,W13,P15,G15),出力4bit(D18,G14,M15,M14)を設定.電圧は,すべてLVCMOS33に設定.

設定を終えたらGenerate Bitstreamをクリック

設定を終えたらGenerate Bitstreamをクリック

設定したピン配置を保存していいか聞いてくるので,Saveで保存

設定したピン配置を保存していいか聞いてくるので,Saveで保存

設定の保存でSynthesisが無効になる可能性があるという案内がでたら.OKでステップを進める

設定の保存でSynthesisが無効になる可能性があるという案内がでたら.OKでステップを進める

制約保存用のファイルがないので作成ダイアログに従って作成.名前をtopとしてOKをクリックして完了

制約保存用のファイルがないので作成ダイアログに従って作成.名前をtopとしてOKをクリックして完了

Synthesisからやりなおすことの許可を求められるのでYesでステップをすすめる

Synthesisからやりなおすことの許可を求められるのでYesでステップをすすめる

合成の開始.パラメタはそのままでOKで次へ.

合成の開始.パラメタはそのままでOKで次へ.

しばらく待つと合成,配置配線が完了してFPGA用のビットストリームが生成される.Open Hardware Managerを選択してOKをクリック.

しばらく待つと合成,配置配線が完了してFPGA用のビットストリームが生成される.Open Hardware Managerを選択してOKをクリック.

Hardware Managerが開いたところ.Open targetをクリック.

Hardware Managerが開いたところ.Open targetをクリック.

Auto ConnectでFPGAボードと接続する

Auto ConnectでFPGAボードと接続する

Program deviceで生成したbitファイルをFPGAに書き込む

Program deviceで生成したbitファイルをFPGAに書き込む

実機で動作を確認

実機での動作を確認できます.DIPスイッチをON/OFFしたときにONの個数がカウントできていることがわかります.が,残念ながらよくみてみると,DIPスイッチを2つONにした状態,つまり1bit目のLEDのみが点灯すべきケースで,0bit目のLEDが若干点灯していることがわかります.これはバグなので原因を探してみましょう.

LEDが一つだけ点灯するはずが0bit目のLEDが若干明るい

LEDが一つだけ点灯するはずが0bit目のLEDが若干明るい

ここでは,ILAを使って,実機で動作を確認してみます.まず,topモジュールを次のように書き変えて,いくつかの信号をILAで観測できるようにしましょう.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity top is
    Port ( clk : in STD_LOGIC;
           a : in STD_LOGIC_VECTOR (3 downto 0);
           q : out STD_LOGIC_VECTOR (3 downto 0));
end top;

architecture Behavioral of top is

 component test_0
  port (
        ap_clk : in std_logic;
        ap_rst : in std_logic;
        ap_start : in std_logic;
        ap_done : out std_logic;
        ap_idle : out std_logic;
        ap_ready : out std_logic;
        a : in std_logic_vector(31 downto 0);
        ap_return : out std_logic_vector(31 downto 0)
  );
  end component;
  
  attribute mark_debug : string; -- 動作確認のためにmark_debugアトリビュートを使う
  
  signal q_i : std_logic_vector(31 downto 0);
  attribute mark_debug of q_i : signal is "true"; -- mark_debugに指定
  
  -- ステータス信号を引出しILAで観測するために追加
  signal ap_done : std_logic;  
  signal ap_idle : std_logic;
  signal ap_ready : std_logic;
  attribute mark_debug of ap_done : signal is "true";
  attribute mark_debug of ap_idle : signal is "true";
  attribute mark_debug of ap_ready : signal is "true";
  -- ステータス信号を引出しILAで観測するために追加,ここまで
  
begin

 q <= q_i(3 downto 0);

 U : test_0
  port map(
        ap_clk => clk,
        ap_rst => '0',
        ap_start => '1',
        ap_done => ap_done,
        ap_idle => ap_idle,
        ap_ready => ap_ready,
        a(31 downto 4) => (others => '0'),
        a(3 downto 0) => a,
        ap_return => q_i
  );

end Behavioral;
コードを書き変えたらRun Synthesisをクリックして合成を行なう

コードを書き変えたらRun Synthesisをクリックして合成を行なう

合成がおわったらOpen Synthesized Designで合成結果を開く

合成がおわったらOpen Synthesized Designで合成結果を開く

ILAの設定をしたいので,メニューのLayoutからDebugを選択

ILAの設定をしたいので,メニューのLayoutからDebugを選択

虫のアイコンをクリックしてILA設定ウィザードを開く

虫のアイコンをクリックしてILA設定ウィザードを開く

ILA設定ウィザードの開始

ILA設定ウィザードの開始

観測したい信号のクロックをみつけることができずundefinedになっている

観測したい信号のクロックをみつけることができずundefinedになっている

クロックを設定したいアイテムの上で右クリックしてSelect Clock Domeinを選ぶ

クロックを設定したいアイテムの上で右クリックしてSelect Clock Domeinを選ぶ

Global_CLOCKがないという案内が表示されるがOKで閉じる

Global_CLOCKがないという案内が表示されるがOKで閉じる

Global_CLOCKのかわりにALL_CLOCKを選択するとclk_IBUFが見つかるので選択,OKをクリックする

Global_CLOCKのかわりにALL_CLOCKを選択するとclk_IBUFが見つかるので選択,OKをクリックする

同様の手順で全ての信号のClock Domainをclk_IBUFに設定したらNextで次へ

同様の手順で全ての信号のClock Domainをclk_IBUFに設定したらNextで次へ

サンプル数や利用機能はデフォルトのままでよいのでNextで次へ

サンプル数や利用機能はデフォルトのままでよいのでNextで次へ

サマリを確認したらFinishでウィザードを閉じる

サマリを確認したらFinishでウィザードを閉じる

ILAの挿入ができたので,あらためてGenerate Bitstreamで合成と配置配線を行なう

ILAの挿入ができたので,あらためてGenerate Bitstreamで合成と配置配線を行なう

ILAに関する設定を保存するか確認を求められたらSaveをクリック

ILAに関する設定を保存するか確認を求められたらSaveをクリック

しばらく待つとILAを仕込んだビットストリームが生成される.Open Hardware Managerを選択してOKをクリック

しばらく待つとILAを仕込んだビットストリームが生成される.Open Hardware Managerを選択してOKをクリック

新しく作ったビットストリームを書き込むためにProgram Deviceをクリック

新しく作ったビットストリームを書き込むためにProgram Deviceをクリック

ビットストリームとILA用の定義ファイルがセットされていることを確認してProgramをクリック

ビットストリームとILA用の定義ファイルがセットされていることを確認してProgramをクリック

二重三角マークをクリックすると内部信号が確認できる

二重三角マークをクリックすると内部信号が確認できる

ap_doneが'1&rsquo;でない間に値がふらふらしていることが確認できた.

ap_doneが'1’でない間に値がふらふらしていることが確認できた.

Vivado HLSのシミュレーション結果を詳細に確認する

実はILAを挿入するまでもなく,Vivado HLSのC/RTL協調シミュレーションの結果を注意深く確認することで,今回のバグは防ぐことができます.試してみましょう.

Vivado HLSでC/RTLシミュレーションを再度実行する

Vivado HLSでC/RTLシミュレーションを再度実行する

シミュレーション実行パラメタのDump Traceでallを選択してOKをクリック

シミュレーション実行パラメタのDump Traceでallを選択してOKをクリック

シミュレーションが完了すると,ツールバーのOpen Wave Viewerをクリックする

シミュレーションが完了すると,ツールバーのOpen Wave Viewerをクリックする

しばらく待つとVivadoシミュレータの波形ビューワが開く.ここでC/RTLシミュレータの結果を確認できる.

しばらく待つとVivadoシミュレータの波形ビューワが開く.ここでC/RTLシミュレータの結果を確認できる.

結果の信号やap_doneなどを波形ビューワに追加してみたところ.ap_doneが'1&rsquo;でない場合に値が確定していないため注意しなければならない,ということがみてとれる.

結果の信号やap_doneなどを波形ビューワに追加してみたところ.ap_doneが'1’でない場合に値が確定していないため注意しなければならない,ということがみてとれる.

再度実機での動作確認

バグ修正を反映して,もう一度,実機で動作を確認してみましょう.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity top is
    Port ( clk : in STD_LOGIC;
           a : in STD_LOGIC_VECTOR (3 downto 0);
           q : out STD_LOGIC_VECTOR (3 downto 0));
end top;

architecture Behavioral of top is

 component test_0
  port (
        ap_clk : in std_logic;
        ap_rst : in std_logic;
        ap_start : in std_logic;
        ap_done : out std_logic;
        ap_idle : out std_logic;
        ap_ready : out std_logic;
        a : in std_logic_vector(31 downto 0);
        ap_return : out std_logic_vector(31 downto 0)
  );
  end component;
  
  attribute mark_debug : string;
  
  signal q_i : std_logic_vector(31 downto 0);
  attribute mark_debug of q_i : signal is "true";
  
  signal ap_done : std_logic;
  signal ap_idle : std_logic;
  signal ap_ready : std_logic;
  attribute mark_debug of ap_done : signal is "true";
  attribute mark_debug of ap_idle : signal is "true";
  attribute mark_debug of ap_ready : signal is "true";
  
begin
 -- バグ修正のために追加
 process(clk)
 begin
   if rising_edge(clk) then
     if ap_done = '1' then
       q <= q_i(3 downto 0);
     end if;
   end if;
 end process;
 -- バグ修正のために追加,ここまで

 U : test_0
  port map(
        ap_clk => clk,
        ap_rst => '0',
        ap_start => '1',
        ap_done => ap_done,
        ap_idle => ap_idle,
        ap_ready => ap_ready,
        a(31 downto 4) => (others => '0'),
        a(3 downto 0) => a,
        ap_return => q_i
  );

end Behavioral;
コードを書き変えたらGenerate Bitstreamをクリックして,再度ビットストリームを生成する

コードを書き変えたらGenerate Bitstreamをクリックして,再度ビットストリームを生成する

再び実機で動作を確認したところ.LEDが正しく一つだけ点灯していることがわかる.

再び実機で動作を確認したところ.LEDが正しく一つだけ点灯していることがわかる.