VHDL設計演習 Ⅰ 入 門 編 広島県立西部工業技術センター 1 1.作業ディレクトリの作成 まず、エクスプローラを起動し、 ルートディレクトリの下にseminarというディレクトリを、 その下にVHDLというディレクトリを作成して下さい。 (DOS/Vの場合) C:¥seminar¥VHDL (98の場合) A:¥seminar¥VHDL 以下、VHDL演習で作成するファイルはVHDLの下に作ります。 それ以外の、ディレクトリには何も作らないで下さい。 2 2.VHDLの基本ブロック 例題1 aoiゲート aoi.vhd library ieee; use ieee.std_logic_1164.all; entity aoi is port(a,b,c,d:in std_logic; f :out std_logic); end aoi; A B C D F architecture aoi_body of aoi is begin f<=not((a and b)or(c and d)); end aoi_body; 3 (1)VHDLの基本ブロックはエンティティとアーキテクチャ。 (2)エンティティは ブロックの名前(これをエンティティ名と呼ぶ。) 入出力ピン名とその方向,ビット幅など ブロックを外部から見た場合の仕様を記述する部分。 (3)アーキテクチャはブロックの内部表現, 論理回路としての構造を記述する部分。 (4)上の例ではaoiがエンティティ名、aoi_bodyがアーキテクチャ名。 (5)libraryは使用するライブラリ名を, useはライブラリ中の使用するパッケージ名を宣言する。 両方とも決まり文句と思ってよい。 (6)入出力ピン宣言はエンティティのport部分で行い, 入 力 = in std_logic 出 力 = out std_logic 双方向 = inout std_logic (7)簡単な機能は,信号代入文<=で記述する。 (8)VHDLのビット演算子は論理演算子と共用で and or not xor が使える。 (9)わかりやすい様に( )を使ってよい。 参照:長谷川テキストp17~23 4 クイックロジック社QuickWorks 配置配線ツールSpDE 兼 統合化環境 論理合成 Synplify 回路図入力 SYNARIO Verilogシミュレータ SILOSⅢ Verilog学習 HDLエディタ ターボライタ VHDL学習 5 3.SpDEの起動 スタート - プログラム - QuickLogic ー SpDEで SpDEを起動します。 6 4.HDLエディタの起動 SpDEのツールバーから、HDLエディタのアイコン 押して、HDLエディタを起動し、クリエイトアイコン を を押す。 7 5.VHDLコードの入力、保存 例題1のVHDLコードを入力し、FileーSaveAsメニューから ¥seminar¥VHDLの下に、ファイル名aoi.vhdで保存する。 拡張子が定まると、entity、architectureなどのVHDLキーワード が紺色でハイライトされます。紺色は見にくいのでHDLエディタの Window-ColorsーKeyword で、青色に変更して下さい。 File-ExitでHDLエディタを終了し、SpDEに戻ります。 6.論理合成Synplifyの起動 SpdeのFile-Import-VHDLメニューから ¥seminar¥VHDL¥aoi.vhdを指定し、論理合成ツールを 起動します。 8 7.ターゲットデバイスの指定と論理合成 RUNボタンのすぐ上のChangeボタンを押して、 Partをp8x12b、Packageをpl44に変更し、OKを押します。 次に、RUNボタンを押して、論理合成をかけます。 9 8.エラーの修正 ソースファイルにエラーがあると、下の画面で停止します。 ViewLogボタンでエラー内容を確認した後、Editボタンを 押して、HDLエディタを再度起動し、エラー箇所を修正します。 10 9.論理合成プロジェクトの保存 エラーがなければDoneが表示されて、下の画面で停止します。 「はい」を選んで論理合成プロジェクトを保存し、SpDEに戻ります。 11 10.配置配線の実行 SpDEのツールバーからRunToolsアイコン を押して、 配置配線を実行します。途中、RUNと「いいえ」を選択します。 12 11.配置配線結果の確認 SpDEツールバーからFullFitアイコン チップ全体を表示させます。 を選択し、 13 次に、View-NormalFitメニューを選択し、Zカーソルを 回路のある部分でクリックして、回路部分を拡大表示します。 さらに拡大したければZoomInアイコン を使います。 14 回路部分のみを拡大すると、下図のようになります。 台形印のセレクタはr=s・p+s・qを表しますので、全体として a・b=1またはc・d=1の時 f=0 それ以外の時 f=1 となり、最初のVHDLコードを満す回路が生成されていること が分かります。 s f p b r a q 入力s 0 1 d c 出力r p q ※論理合成結果を確認したら、 File-Saveメニューで結果を 15 保存します。 12.ここで、この後のシミュレーションのために、シミュレータ・ タイプを変更しておきます。 SpDEツールバーからToolOptionsボタン を押して、 BackAnnotationタブのSimulatorを Vital3.0 Compliant に変更してから、SaveSettingしてから、OKを押します、 16 13.基本ブロック(続き) HDLエディタを起動し、aoi.vhdを次のように修正します。 library ieee; use ieee.std_logic_1164.all; entity aoi is port(a,b,c,d:in std_logic; f :out std_logic); end aoi; A B C D AB O F CD architecture aoi_body of aoi is signal ab,cd,o:std_logic; begin -- f<=not((a and b)or(c and d)); ab<=a and b; cd<=c and d; o <=ab or cd; f <= not o; end aoi_body; 17 (1)やや複雑な機能は、signal宣言したローカル信号を使う ことができる。上の例ではab,cd,oがローカル信号。 ローカル信号はアーキテクチャ内部でのみ有効。 (2)VHDLには暗黙宣言はない。 (3)--は1行のみのコメント行。 VHDLには、Verilogの/* */の様に 複数行を一度にコメントアウトする方法はない。 修正が終わったらソースコードを保存し、論理合成をかけて 配置配線を実行して下さい。結果は同じになります。 18 問題1 インバータLS04.vhdを設計し、結果を確認しなさい。 問題2 NANDゲートLS00.vhdを設計し、結果を確認しなさい。 問題3 NORゲートLS02.vhdを設計し、結果を確認しなさい。 19 14.階層設計 例題2 マルチプレクサ mux2.vhd library ieee; use ieee.std_logic_1164.all; -- inverter --entity inv is port(a:in std_logic; f:out std_logic); end inv; architecture inv_body of inv is begin f<=not a; end inv_body; --------------------------------------library ieee; use ieee.std_logic_1164.all; -- multiplexer -entity mux2 is port(sel,a,b:in std_logic; f :out std_logic); end mux2; SEL A g2 g1 SELB AOI O g3 B architecture mux2_body of mux2 is component inv port(a:in std_logic; f:out std_logic); end component; component aoi port(a,b,c,d:in std_logic; f :out std_logic); end component; signal selb,o:std_logic; begin g1:inv port map(sel,selb); g2:aoi port map(sel,a,selb,b,o); g3:inv port map(o,f); 20 end mux2_body; F (1)もっと複雑な回路記述には、階層設計を使う。 (2)階層設計は複数のエンティティ,アーキテクチャ宣言と コンポーネント宣言およびコンポーネントインスタンスを使う。 (3)複数のエンティティ,アーキテクチャ宣言は invの様に同一ファイル内に書いても良いし, aoiの様に別ファイルに書いても良い。 ただし、libraryとuseはエンティティ毎に必要。 (4)コンポーネントインスタンスは,サブルーチンコールの様なもので, g1,g2,g3の様に インスタンス名:コンポーネント名 port map(ポートリスト); の形式で行う。verilogとは順序が逆なのに注意。 参照:長谷川テキストp24~26 21 (5)コンポーネントインスタンスのポートリストは、 呼び出す側と呼び出される側のポートを接続するもので, 上の例の様に並びによる接続が一般的。 次の例の様に名前による接続も使える。 g3:inv port map(a=>o,f=>f); 出力信号を接続しない場合は、予約語openを使う。 (6)コンポーネント・インスタンスをするためには, アーキテクチャ内で予めコンポーネント宣言して おかなければならない。 component コンポーネント名 port(ポートリスト); end component; の形式で行う。エンティティ宣言からisを取ったのと同じ形式。 C言語のプロトタイプ宣言に似た概念。 コンポーネント宣言時の信号名は、エンティティ宣言時と 同じであること。 22 (7)メインブロックmux2のサブブロックaoiは、別ファイルに入って いるので、下図のようにSynplifyで2つのファイルを指定して 論理合成します。ファイルを追加するにはAddボタンを使います。 この時、メインのmux2が一番下に来るように指定します。 23 例題2を入力し、論理合成、配置配線を実行します。 結果は、論理圧縮の効果で例題1より簡単になり、 下図のようになります。 sel=1なら f=a sel=0なら f=b となっています。 b a sel f 24 15.条件付き信号代入文 例題3 セレクタ mux21.vhd library ieee; use ieee.std_logic_1164.all; -- multiplexer -entity mux21 is port(sel,a,b:in std_logic; f :out std_logic); end mux21; architecture v1 of mux21 is begin f <= a when (sel='1') else b; end v1; sel a b mux21 f ※後のシミュレーションの為に ファイル名、エンティティ名とも 必ず、mux21にして下さい。 ファイル名とエンティティ名が 一致しないと、遅延シミュレー ション時にエラーが出ます。 25 (1)前の例(mux2)では,階層設計を説明するため,複雑な 書き方をしたが,セレクタ自体はもっと簡単に記述できる。 (2)信号代入文<=には,C言語の条件演算子に似た 条件付信号代入文があり, 左辺<=(右辺1) when (条件式) else (右辺2); が使える。 (3)上の例では,sel=1ならf=a,sel=0ならf=bになる。 (4)条件式に使う関係演算子は = 等しい > 大 >= 以上 /= 等しくない < 小 <= 以下 が使え,それらの論理演算 AND 論理積 OR 論理和 NOT 論理否定 も使える。 (5)1ビット幅の定数は '1' '0' と書く。 26 16.process文とif文 例題3 セレクタ mux21.vhd library ieee; use ieee.std_logic_1164.all; -- multiplexer -entity mux21 is port(sel,a,b:in std_logic; f :out std_logic); end mux21; sel a b mux21 f architecture v2 of mux21 is begin process(sel,a,b) begin if(sel='1') then f<=a; else f<=b; end if; end process; end v2; 27 (1)条件演算子よりもわかりやすいif文もあるが, アーキテクチャ内にダイレクトに書くことはできない。 上の例の様にprocessブロックの中で記述する。 (verilogのalwaysブロックに相当) (2)process文は process(信号名) begin : end process; の形で記述し,( )内の信号名が変化したときのみ評価される。 つまり( )内にはprocessブロックとしての入力信号を記述する。 これをセンシティビティ・リストという。 複数の入力信号が有る場合は,カンマで区切って記述する。 (3)processブロックで組合せ回路を生成する場合,入力信号を 全てセンシティビティ・リストに記述しなければならない。 (順序回路の場合はそうとは限らない。) 28 (4)processブロック内では,if文,case文が使え if(条件式) then 式1; else 式2; end if; と書く。(then と end if がある点が,verilogと違う)。 多重ifは, if(条件式1) then 式1; elsif(条件式2) then 式2; else 式3; end if; と書く。elseif でない点に、特に注意。 (5)if文で全ての条件が列挙されていれば組合せ回路, そうでなければ順序回路が生成されるのはverilogと同じ。 例題3を入力し、論理合成、配置配線を実行します。 結果は例題2と同じになります。 29 VHDL設計演習 Ⅱ シミュレーション編 広島県立西部工業技術センター 30 1.テスト・ベンチの準備 例題3の設計mux21.vhdを、VHDLシミュレータを用いて検証します。 設計検証用テストパターンを発生させるVHDLコードのことを テスト・ベンチと呼びます。拡張子は通常 .tb を使います。 HDLエディタでmux21.vhdを開いた状態で、 HDLー GenerateTestBench を実行すると、テストベンチの雛形mux21.tbが生成されます。 ※遅いマシンでは数分かかることもあります。 library ieee; use ieee.std_logic_1164.all; ①ENTITY TestBench IS END TestBench; ②ARCHITECTURE HTWTestBench OF TestBench IS ③COMPONENT mux21 PORT (sel,a,b:in std_logic; f :out std_logic); END COMPONENT; ④SIGNAL selSignal,aSignal,bSignal: std_logic; ④SIGNAL fSignal : std_logic; BEGIN ⑤U1 : mux21 PORT MAP (sel => selSignal, a => aSignal, b => bSignal, f => fSignal); ⑥ 31 ②END HTWTestBench; ①テストベンチのエンティティには、ポートリストが有りません。 エンティティ名は何でもかまいませんが、ここではTestBenchです。 ②アーキテクチャ名は何でもかまいませんが、 ここではHTWTestBenchです。 ③シミュレーション対象であるmux21をコンポーネント宣言します。 ④入力信号、出力信号ともに、シグナル宣言します。信号名は 元の信号名にSignalをつけたものが自動生成されています。 ⑤シミュレーション対象mux21をコンポーネント・インスタンスとして、 呼び出します。信号の接続には、名前による接続が行われていま すが、並びによる接続でも構いません。 ⑥この位置に、実際のテストパターン用コードを追加します。 32 それでは、⑥の位置に下記を追加して、 テストベンチを完成させてから、mux21.tbとして保存して下さい。 process begin selSignal <='0'; aSignal <='0'; bSignal <='0'; wait for 100ns; selSignal <='0'; aSignal <='0'; bSignal <='1'; wait for 100ns; selSignal <='0'; aSignal <='1'; bSignal <='0'; wait for 100ns; selSignal <='0'; aSignal <='1'; bSignal <='1'; wait for 100ns; selSignal <='1'; aSignal <='0'; bSignal <='0'; wait for 100ns; selSignal <='1'; aSignal <='0'; bSignal <='1'; wait for 100ns; selSignal <='1'; aSignal <='1'; bSignal <='0'; wait for 100ns; selSignal <='1'; aSignal <='1'; bSignal <='1'; wait for 100ns; wait; end process; この例で分かるように、テストベンチでの信号値の代入には process文とwait文を使用します。 意味としては、sel,a,bの初期値として全て0を代入した後、 100ns毎に異なる値を代入しています。最後のwaitが無いと、 800ns毎に同じ信号入力が繰り返されてしまいます。 33 2.機能シミュレーション(Pre-Layout) (1)QuickWorksにはVHDLシミュレータは付属していないので、 Accolade社のPeakVHDLを使います。 スタート ー プログラム ー Accolade PeakVHDL ー PeakVHDL Version4 でPeakVHDLを立ち上げます。 34 (2)PeakVHDLは拡張子が*.vh*のファイルしか入力できない ので、エクスプローラでテストベンチmux21.tbのファイル名を mux21.tb.vhdに変更して下さい。 (重要!!) (3)PeakVHDLのNewProjectアイコン を押して、新しいプロ ジェクトをオープンし、File-SaveProjectAsでこのプロジェクト に名前を付けます。ここでは¥seminar¥VHDL¥mux21.acc として保存します。 35 (4)AddModuleアイコン を押して、mux21.vhdと mux21.tb.vhdを選択します。 36 (5)Rebuildアイコン を押して、階層構造の再構築をし、 モジュールの前に+マークが付くことを確認します。 37 (6)オプションボタン を押して、Optionsウインドウを 表示させます。 ①Compileタブの compile only if out of date をチェック無 ②Linkタブ の link only if out of date をチェック無 ③Simulateタブの Run to time を1000 Time unit を ns ④Systemタブの Save options as default をチェック有 に設定してから、OKを押して下さい。 ③以外は1回だけ行えば、結構です。 ③はこれから行うシミュレーション時間を1000nsに設定する ものですので、別の設計をシミュレーションするなど、シミュレ ーションすべき時間が変わった場合は、適宜、変更します。 ※シミュレーション時間は波形表示ウインドウ(10)で マウス右ボタンのoptionsからも変更できます。 38 ① ② ③ ④ 39 (7)mux21.tb.vhdをクリックして、ハイライトした状態で シミュレーションボタン を押します。エラーがあると トランスクリプト・ウインドウにエラーメッセージが表示されます。 エラーがなければ、(9)に進みます。 40 (8)mux21.vhdは論理合成のチェックを通っているので、 エラーがあるとすれば、mux21.tb.vhdの方です。 エラーメッセージを確認後、mux21.tb.vhdをダブルクリック すると、エディタが立ち上がるので、エラー箇所を修正します。 修正が終わったら、File-SaveModuleで保存し、 mux21.tb.vhdウインドウのアイコン化ボタン でアイコン化 します。 シミュレーションボタン (9)に進みます。 を押して、エラーがなければ、 エラーが有れば、エラーメッセージ確認後、アイコン化していた mux21.tb.vhdを通常の大きさに戻し、修正を繰り返します。 41 (9)エラーがなければ、下の画面で停止するので、 AddPrimariesボタンとCloseボタンを、この順番に押します。 42 (10)下の画面で停止するので、GOボタン を押します。 波形の一部が表示されるので、PeakSimウインドウの ツールバーからView-ZoomAllを選び、波形全体を 表示させます。 43 (11)sel=0の時 f=b、 sel=1の時 f=a を確認します。 ZoomINボタン を押して拡大しても、入力信号 a,b、selと 出力信号fには時間差がありません。 これは、現在表示している結果が遅延時間情報の入っていない 機能シミュレーション(Pre Layout)である為です。 結果を確認したら、PeakSimウインドウを閉じた後、 SaveProjectアイコン を押して、プロジェクトを保存します。 44 3.遅延シミュレーション(Post-Layout) PeakVHDLで遅延シミュレーションを行うには、デバイスメーカ 各社が供給するVITALライブラリを、以下の(1)~(4)の手順で PeakVHDL用にコンパイルする必要があります。 VITALはVhdl Initiative Toward Asic Libarayの頭文字で、VHDL で遅延シミュレーションをするための手法の総称です。 (1)~(4)は1度だけ実行すれば、以後は必要ありません。 (1)まず、エクスプローラを起動して、 ¥ACC-EDA¥LIB4 の下に QLOGIC というディレクトリを作成します。 次に、クイックロジック社の供給するVITALライブラリ ¥PASIC¥SPDE¥DATA¥QLVTL95.VHD を、今作成したディレクトリ ¥ACC-EDA¥LIB4¥QLOGIC にコピーします。 45 (2)PeakVHDLのツールバーから、NewProject を押して、 新しいプロジェクトをオープンし、File-SaveAsで ¥ACC-EDA¥LIB4¥QLOGIC¥QLOGIC.acc として名前を付けて、保存します。 46 (3)AddModuleアイコン Rebuildアイコン 確認します。 でQLVTL95.VHDを追加し、 でモジュールの前に+マークが付くことを、 47 (3)QLVTL95.VHDをクリックしてハイライトした状態で、 Optionアイコン を押して、Compileタブの Compile into Libraryに QLPRIMS を設定します。 48 (4)QLVTL95.VHDをクリックしてハイライトした状態で、 コンパイル を押します。 エラーが無いことを確認後、 SaveProjectアイコン を押して、このプロジェクトを 保存します。これでライブラリQLPRIMSができました。 49 (5)ここからmux21.vhdの遅延シミュレーションを始めます。 NewProjectアイコン を押して、新しいプロジェクトを オープンし、File-SaveAsで ¥seminar¥vhdl¥mux21vtl.acc と名前を付けて、保存します。 50 (6)AddModuleアイコン を押して、mux21.tb.vhd と mux21.vhq (vhdでないことに注意)を追加し、Rebuildアイ コン でモジュールの前に+マークが付くことを確認します。 mux21.vhqが見つからない場合は、入門編の12を確認します。 51 (7)Optionsボタン を押して、 SystemタブのSystem Library Pathを c:\acc-eda\lib4;c:\acc-eda\lib4\qlogic; に設定します。(2番目のc:の前にスペースを入れないこと。) 52 (8)Linkタブの SDF File Name を mux21.sdf SDF Instance Path を U1 SDF Timing を Max に設定します。 53 (9)あとは機能シミュレーションの時と同様に、 mux21.tb.vhdをクリックしてハイライトした状態で、 シミュレーションボタン を押して、シミュレーションを 開始します。 結果は下の様に、入力信号a,bと出力信号fとの間に 時間差9ns程度が観測されます。 これは現在表示している結果が、遅延時間情報(mux21.sdf)を 考慮した遅延シミュレーション(Post Layout)であるためです。 また、VHDLコードも配置配線ツールの出力したmux21.vhqを 使用しています。SDFはスタンダード・ディレイ・フォーマットと呼び、 遅延時間情報の標準フォーマットです。 興味があれば*.sdf、*.vhqをエディタで開いて見て下さい。 54 (10)結果を確認したら、PeakSimウインドウを閉じた後、 SaveProjectアイコン を押して、プロジェクトを保存します。 機能シミュレーションのプロジェクトはmux21.acc 遅延シミュレーションのプロジェクトはmux21vtl.acc として保存されています。 遅延シミュレーションを実行した後、機能シミュレーションを実行 しようとすると、リンクエラーが出ることがありますが、この時は CleanProjectアイコン を押せば、リンクエラーを回避できます。 これ以外にも、意味不明のエラーが出る場合はCleanProject アイコン を試して見て下さい。 ※*.vhq内のエンティティ名には、自動的に主ファイル名が採用 されるので、*.vでエンティティ名と主ファイル名が一致していな いと、コンポーネントが見つからない旨のエラーが出る。 ※シミュレータタイプがVITAL3.0ではなく、VITALになっていると VITALライブラリが無い旨のエラーが出る。 ※System Library Pathの途中に、スペースが入っていると、 55 QLPRIMSライブラリが無い旨のエラーが出る。 問題4 インバータLS04.vhdのテストパターンを設計し、 機能シミュレーションと遅延シミュレーションを 実行しなさい。 問題5 NANDゲートLS00.vhdのテストパターンを設計し、 機能シミュレーションと遅延シミュレーションを 実行しなさい。 問題6 NORゲートLS02.vhdのテストパターンを設計し、 機能シミュレーションと遅延シミュレーションを 実行しなさい。 56 4.ピン配置の指定 ピン配置は配置配線ツールが遅延時間や配線効率を考慮して 最適に近いものに自動配置するので、これを使うのが無難です。 しかし、設計の最終段階に近く、プリント基板の変更ができない 場合などは、次の方法でピン配置を直接指定することも可能です。 SpDEのTools-Optionsメニューから、BackAnnotationタブ を選択し、FixPlacementのIOcellsをチェックonにします。 57 OKを選択後、RunToolsアイコン を押して、配置配線を 実行すると、ピン配置情報mux21.scpが生成されます。 現在、ピン16,17,18,19が割り当てられていることを確認 します。(マシンによっては異なることも有ります。) エディタでql_placement以下のピン番号を変更し、保存します。 #mux21.scp #Synplicity Synthesis pin location command file #Automatically generated by SpDE version SpDE 7.0 #Date: 8/10/98 at 10:02 # #---Fixed I/O cells--portprop f portprop a portprop sel portprop b ql_placement="IO2"; ql_placement="IO3"; ql_placement="IO4"; ql_placement="IO5"; 58 論理合成Synplifyを立ち上げて、Addボタンを押します。 ファイルの種類をPropertyFiles(*.sc*)にして、 mux21.scpを選択し、「開く」を押します。 59 SourceFilesにmux21.scpが追加されたことを確認後、 RUNを押して論理合成をかけます。 途中「はい」とOKを選択し、RunToolアイコン を押すと、 mux21.scpで指定したピン配置での配置配線が実行されます。 60 VHDL設計演習 Ⅲ 基 礎 編 広島県立西部工業技術センター 61 1.ビット幅のある信号の表現 例題1 コンパレータ comp.vhd library ieee; use ieee.std_logic_1164.all; a b 4 comp ge eq 4 le entity comp is generic(n:integer :=4); port(a,b:in std_logic_vector(n-1 downto 0); eq,ge,le:out std_logic); end comp; architecture v1 of comp is begin eq <= '1' when (a= b) else '0'; ge <= '1' when (a>=b) else '0'; le <= '1' when (a<=b) else '0'; end v1; 62 -----------------------------------architecture v2 of comp is begin process(a,b) begin if(a= b) then eq<='1'; else eq<='0'; end if; if(a>=b) then ge<='1'; else ge<='0'; end if; if(a<=b) then le<='1'; else le<='0'; end if; end process; end v2; (1)1つのエンティティは複数のアーキテクチャを持つことができる。 上の例ではcompがエンティティで、v1とv2がアーキテクチャ。 明示的にアーキテクチャを指定する場合はコンフィグレーション 文を使うが,シンプリファイライトはコンフィグレーションをサポ ートしていない。 上の例ではv2の方がコンパイルされる。 63 (2)今までは1ビット幅の信号だけ扱ってきたが,ビット幅の ある信号も扱える。in,out宣言で std_logic_vector(15 downto 0) あるいは, std_logic_vector(0 to 15) を使う。MSBは左,LSBは右。 MSB>LSBならdowntoを,MSB<LSBならtoを使う。 (3)ビット幅を変更しやすくするためには generic(n:integer :=4); を使う。 (4)上の例の様にコンパレータ等は、if文よりも条件付信号 代入文の方がコンパクトに記述できる。 64 例題1のテストベンチ comp.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; …① ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT comp GENERIC (n:integer :=4); PORT (a,b:in std_logic_vector(n-1 downto 0); eq,ge,le:out std_logic); END COMPONENT; constant n:integer:=4; …② SIGNAL aSignal,bSignal: std_logic_vector(n-1 downto 0); SIGNAL eqSignal,geSignal,leSignal: std_logic; 65 BEGIN U1 : comp GENERIC MAP (n => 4 ) PORT MAP (a => aSignal, b => bSignal, eq => eqSignal, ge => geSignal, le => leSignal); process begin …③ L1:for i in 0 to 15 loop …④ aSignal <=conv_std_logic_vector(i,4); …⑤ L2:for j in 0 to 15 loop …⑥ bSignal <=conv_std_logic_vector(j,4); …⑦ wait for 100ns; end loop L2; end loop L1; wait; end process; END HTWTestBench; 66 ①あとで、関数conv_std_logic_vector()を使うために必要です。 ②次の行のSignal宣言に出てくる n のために必要です。 ③このprocess文が、実際のテストパターンを発生させます。 ④回数指定の繰り返しは for…loop で記述します。 ラベル名:for ループ変数 in 初期値 to 終了値 loop : end loop ラベル名; ※ループ変数 i は宣言が不要です。ラベル名は省略可能。 ⑤関数conv_std_logic_vector()を使って、整数 i を4ビット幅の std_logic_vector に変換してから、aSignalに代入します。 VHDLは型チェックが厳しいので、直接代入はできません。 67 ⑥整数 j のループです。 ⑦整数 j を型変換したあと、bSignalに代入します。 シミュレーション時間は、100ns×16×16=25600ns以上 に設定します。結果は下図のようになります。 68 2.case文とビット幅のある定数 例題2 デコーダ decoder.vhd library ieee; use ieee.std_logic_1164.all; architecture v1 of decoder is begin process(enb,adr) begin entity decoder is if(enb='0') then case(adr) is port(enb:in std_logic; adr:in std_logic_vector(2 downto 0); when "000" => y<="11111110"; y :out std_logic_vector(7 downto 0)); when "001" => y<="11111101"; end decoder; when "010" => y<="11111011"; when "011" => y<="11110111"; when "100" => y<="11101111"; decode when "101" => y<="11011111"; enb 8 when "110" => y<="10111111"; y when others => y<="01111111"; 3 end case; adr else y<="11111111"; end if; end process; end v1; 69 (1)デコーダは,if文を並べて書くこともできるが, case文を使った方がわかりやすい。 case(信号名) is when ケース1 => 式1; when ケース2 => 式2; : : when others => 式n; end case; と記述する。othersは列記したケース以外、全ての場合とい う意味です。これが無いとラッチを生成する処理系もある。 (2)ビット幅のある定数は, ( 2進数) B"001" B"1111_1110" (16進数) X"FC" と書く。2進数を表すBは省略できる。 (3)else y<= "11111111"がないと,ラッチが生成される。 70 例題2のテストベンチ decoder.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT decoder PORT (enb:in std_logic; adr:in std_logic_vector(2 downto 0); y :out std_logic_vector(7 downto 0)); END COMPONENT; SIGNAL enbSignal: std_logic; SIGNAL adrSignal: std_logic_vector(2 downto 0); SIGNAL ySignal : std_logic_vector(7 downto 0); BEGIN U1 : decoder PORT MAP (enb => enbSignal, adr => adrSignal, y => ySignal); 71 process begin enbSignal <='0'; for i in 0 to 7 loop adrSignal <=conv_std_logic_vector(i,3); wait for 100ns; end loop; enbSignal <='1'; for i in 0 to 7 loop adrSignal <=conv_std_logic_vector(i,3); wait for 100ns; end loop; wait; end process; END HTWTestBench; シミュレーション時間=2000ns 72 3.算術演算子と連接子 例題3 アダー adder.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity adder is generic (n :integer :=4); port(cin :in std_logic; a,b :in std_logic_vector(n-1 downto 0); cout:out std_logic; s :out std_logic_vector(n-1 downto 0)); end adder; a b cin 4 adder 4 4 s cout 73 architecture v1 of adder is signal ax,bx,cx,sx:std_logic_vector(n downto 0); begin ax(n downto 0)<= '0' & a(n-1 downto 0); bx(n downto 0)<= '0' & b(n-1 downto 0); cx <= (0=>cin, others=>'0'); sx <= ax+bx+cx; s(n-1 downto 0)<= sx(n-1 downto 0); cout <= sx(n); end v1; -------------------------------------------------------------architecture v2 of adder is signal sx:std_logic_vector(n downto 0); begin sx <= ('0' & a)+('0' & b)+cin; s <= sx(n-1 downto 0); cout <= sx(n); end v2; 74 (1)加算器,減算器,乗算器を生成するのに算術演算子 +、ー、*が使える。 (2)算術演算子を使うためには, use ieee.std_logic_unsigned.all; 符号なし算術演算パッケージ または, use ieee.std_logic_signed.all; 符号有り算術演算パッケージ が必要。 (3)キャリ入出力cin,coutがなければ s <= a + b; とコンパクトに記述できる。 (4)verilogの連接演算子{ , }に相当するものはVHDLでは 連接子( & )であるが,verilogの様に信号代入文の左辺 には使えないのでアーキテクチャv2の様に, a,bより1ビット多いローカル信号sxを使って記述するしかない。 参照:長谷川テキストp28~30 75 また,VHDLは型チェックが厳しく,左辺と右辺のビット幅が 異なると,代入できないので, sx <= a+b+cin; はエラーになってしまう。 (5)アーキテクチャv2も,コンパイラによっては通らないかもしれない。 なぜなら,sxとcinのビット幅が違うためである。そのときは(6)の ように書く。 (6)0ビット目がcinで,他のビットが全て0の幅nビットの信号は, アーキテクチャv1の様に (0 => cin , others => '0') と書く。これは,集合体と呼ばれ,0ビット目に限らず,ビット位置を 指定してビット幅のある信号を記述するのに使われる。 ただし,集合体の要素としては,ビット幅1のものしか書けない。 つまり,('0',a)はエラーになる。 76 参照:長谷川テキストp28~30 例題3のテストベンチ adder.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT adder GENERIC (n :integer :=4); PORT (cin :in std_logic; a,b :in std_logic_vector(n-1 downto 0); cout:out std_logic; s :out std_logic_vector(n-1 downto 0)); END COMPONENT; constant n:integer:=4; SIGNAL cinSignal : std_logic; SIGNAL aSignal,bSignal : std_logic_vector(n-1 downto 0); SIGNAL coutSignal: std_logic; SIGNAL sSignal : std_logic_vector(n-1 downto 0); BEGIN U1 : adder GENERIC MAP (n => 4) PORT MAP (cin => cinSignal, a => aSignal, b => bSignal, cout => coutSignal, s => sSignal); 77 process begin cinSignal <='0'; L1:for i in 0 to 15 loop aSignal <=conv_std_logic_vector(i,4); L2:for j in 0 to 15 loop bSignal <=conv_std_logic_vector(j,4); wait for 100ns; end loop L2; end loop L1; cinSignal <='1'; L3:for i in 0 to 15 loop aSignal <=conv_std_logic_vector(i,4); L4:for j in 0 to 15 loop bSignal <=conv_std_logic_vector(j,4); wait for 100ns; end loop L4; end loop L3; wait; end process; END HTWTestBench; シミュレーション時間=16x16x2x100ns=51200ns 78 4.フリップフロップの記述 例題4 フリップフロップ dff.vhd library ieee; use ieee.std_logic_1164.all; entity dff is port(d,clk,sclr,aclr,enb:in std_logic; q,qsc,qac,qen:out std_logic); end dff; d clk 通常 FF q d clk sclr 同期 クリア FF qsc architecture RTL of dff is begin -- simple ff --process(clk) begin if(clk'event and clk='1') then q <= d; end if; end process; -- sync clear ff --process(clk) begin if(clk'event and clk='1') then if(sclr='0') then qsc <= '0'; else qsc <= d; end if; end if; end process; 79 -- async clear ff -process(clk,aclr) begin if (aclr='0') then qac <= '0'; elsif(clk'event and clk='1') then qac <= d; end if; end process; -- enb ff -process(clk) begin if(clk'event and clk='1') then if(enb='1') then qen <= d; end if; end if; end process; end RTL; d clk 非同期 クリア FF qac aclr d イネーブル clk 機能付 FF enb qen 80 (1)フリップフロップの生成には,上の例の様に if(clk'event and clk='1') then を使う。if文なのでprocessブロックの中で使う。 (2)同期クリア信号は,process(信号名)のセンシティビティ・リスト には記述しないで,はじめのif文の条件にクロックイベントを 次のif文の条件にクリア信号を記述する。 (3)非同期クリア信号は,センシティビティ・リストに記述するとともに はじめのif文の条件にクリア信号を, 次のelsif文の条件に,クロックイベントを記述する。 (4)イネーブル信号は,同期クリア信号同じ考え方。 つまり、センシティビティ・リストには記述しないで, はじめのif文の条件にクロックイベントを 次のif文の条件にイネーブル信号を記述する。 81 例題4のテストベンチ dff.tb.vhd library ieee; use ieee.std_logic_1164.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT dff PORT (d,clk,sclr,aclr,enb:in std_logic; q,qsc,qac,qen:out std_logic); END COMPONENT; SIGNAL dSignal,clkSignal,sclrSignal,aclrSignal,enbSignal: std_logic; SIGNAL qSignal,qscSignal,qacSignal,qenSignal: std_logic; BEGIN U1 : dff PORT MAP (d => dSignal, clk => clkSignal, sclr => sclrSignal, aclr => aclrSignal, enb => enbSignal, q => qSignal, qsc => qscSignal, qac => qacSignal, qen => qenSignal); process begin clkSignal <='0'; loop …①クロック生成には for無しloop が使える。 wait for 50ns; clkSignal <=not clkSignal; end loop; end process; 82 process begin aclrSignal <='1'; sclrSignal <='1'; enbSignal <='1'; dSignal <='1'; wait for 125ns; dSignal <='0'; wait for 200ns; dSignal <='1'; wait for 300ns; aclrSignal <='0'; sclrSignal <='0'; enbSignal <='0'; wait for 200ns; dSignal <='0'; wait for 300ns; aclrSignal <='1'; sclrSignal <='1'; enbSignal <='1'; wait for 200ns; dSignal <='1'; wait; end process; END HTWTestBench; シミュレーション時間=2000ns 83 5.エッジ検出回路の記述 例題5 エッジ検出 edg.vhd library ieee; use ieee.std_logic_1164.all; entity edg is port(clk,d,reset:in std_logic; rise,fall:out std_logic); end edg; fall d clk q1 architecture v1 of edg is signal q1,q2:std_logic; begin process(clk) begin if(clk'event and clk='1') then if(reset='0') then q1<='0'; q2<='0'; else q1<=d; q2<=q1; end if; end if; end process; rise<= q1 and (not q2); fall <=(not q1) and q2 ; end v1; q2 rise reset 84 (1)スイッチが押された時,1回だけある動作をさせたい時など, 入力信号の立ち上がり,立ち下がりを検出する手段として, 図のようなエッジ検出回路が使用される。 (2)VHDLでは立ち下がり,立ち上がりエッジの検出は 上の例の様に書ける。 (3)VHDLの場合、q1用とq2用のprocessブロックを分けても, 分けなくても同じ論理合成結果が得られる。 Verilogの場合は、ブロックを分けるか分けないか、また =を使うか、<=を使うかで論理合成結果が異なる。 85 例題5のテストベンチ edg.tb.vhd library ieee; use ieee.std_logic_1164.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT edg PORT (clk,d,reset:in std_logic; rise,fall:out std_logic); END COMPONENT; SIGNAL clkSignal,dSignal,resetSignal: std_logic; SIGNAL riseSignal,fallSignal: std_logic; BEGIN U1 : edg PORT MAP (clk => clkSignal, d => dSignal, reset => resetSignal, rise => riseSignal, fall => fallSignal); clock:process begin …①processにも名前が付けられる。 clkSignal <='0'; loop wait for 50ns; clkSignal <= not clkSignal; end loop; end process; 86 d_reset:process begin resetSignal <='0'; dSignal <='0'; wait for 75ns; resetSignal <='1'; wait for 200ns; dSignal <='1'; wait for 300ns; dSignal <='0'; wait for 400ns; dSignal <='1'; wait for 400ns; dSignal <='0'; wait; end process; END HTWTestBench; シミュレーション時間=2000ns 87 6.同期クリア付きカウンタ 例題6 countsc.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; countsc clk clr 4 count entity countsc is port(clk,clr:in std_logic; count :out std_logic_vector(3 downto 0)); …② end countsc; architecture countsc_body of countsc is signal count_tmp:std_logic_vector(3 downto 0); begin count <= count_tmp; …③ process(clk) begin if(clk'event and clk='1') then if(clr='0') then count_tmp <= "0000"; else count_tmp <= count_tmp+1; end if; end if; end process; end countsc_body; 88 (1)同期クリア付カウンタは同期クリアFFのクリア信号の書き方と 算術演算子+の応用である。 (2)同期クリア信号はイベントリストに記述しないで、 最初のif文の条件にクロックイベントを、 次のif文の条件に同期クリア信号を記述する。 (4)重要なのはVHDLのout宣言された信号(この例ではcount)は, アーキテクチャ内部で,読み出し参照できない点である。 (5)信号代入文の左辺には,もちろん書けるのでcount_tmpの様に, ローカル信号を宣言し,再代入してやればよい。 (小テスト)これを10進カウンタにするには、どうすればよいか。 89 例題6のテストベンチ countsc.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT countsc PORT (clk,clr:in std_logic; count :out std_logic_vector(3 downto 0)); END COMPONENT; SIGNAL clkSignal:std_logic:='0'; …① SIGNAL clrSignal:std_logic:='1'; …① SIGNAL countSignal : std_logic_vector(3 downto 0); BEGIN U1 : countsc PORT MAP (clk => clkSignal, clr => clrSignal, count => countSignal); clrSignal<='0' after 125ns, '1' after 325ns; …② clkSignal<=(not clkSignal) after 50ns; …③ 90 END HTWTestBench; ①シグナルの初期値は、シグナル宣言時に与えることも可能。 =や<=ではなく、:=であることに注意。 ②簡単なシグナル値は、カンマで区切って与えることができる。 wait forの場合は相対時間であったが、afterの場合は 絶対時間であることに注意。この例では、clrの初期値は1で、 125nsで0,325nsで1になる。 ③初期値を与えたクロック信号はprocess文で囲まなくても、 この書き方で50ns毎に反転する信号になる。 ④同期クリアなので、clr=0の次のclk立ち上がりでcount=0と なることを確認する。 91 7.非同期クリア付きカウンタ 例題7 countac.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; countac clk clr 4 count entity countac is port(clk,clr:in std_logic; count :out std_logic_vector(3 downto 0)); end countac; architecture countac_body of countac is signal count_tmp:std_logic_vector(3 downto 0); begin count <= count_tmp; process(clk,clr) begin if(clr='0') then count_tmp <= "0000"; elsif(clk'event and clk='1') then count_tmp <= count_tmp+1; end if; end process; end countac_body; 92 例題7のテストベンチ countac.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT countac PORT (clk,clr:in std_logic; count :out std_logic_vector(3 downto 0)); END COMPONENT; SIGNAL clkSignal:std_logic:='0'; SIGNAL clrSignal:std_logic:='1'; SIGNAL countSignal : std_logic_vector(3 downto 0); BEGIN U1 : countac PORT MAP (clk => clkSignal, clr => clrSignal, count => countSignal); clrSignal<='0' after 125ns, '1' after 325ns; clkSignal<=(not clkSignal) after 50ns; 93 END HTWTestBench; (1)非同期クリア付カウンタは非同期クリアFFのクリア信号の 書き方と算術演算子+の応用である。 (2)非同期クリア信号は、イベントリストに記述すると共に 、 はじめのif文の条件にクリア信号を、 次のelsif文の条件にクロックイベントを記述する。 (2)テストベンチは同期クリア付きカウンタと同じもの。 非同期クリアなので、clr=0と同時に、count=0となっている ことを確認する。 (小テスト)これにキャリー入出力をつけるにはどうすればよいか。 94 1 8.ステートマシンの記述 例題8 state.vhd library ieee; use ieee.std_logic_1164.all; S11 a a a a S01 entity state is a port(clk,a,res:in std_logic; ss:out std_logic_vector(1 downto 0)); S00 end state; architecture v1 of state is signal ss_tmp:std_logic_vector(1 downto 0); constant s00:std_logic_vector(1 downto 0):=B"00"; constant s01:std_logic_vector(1 downto 0):=B"01"; constant s10:std_logic_vector(1 downto 0):=B"10"; constant s11:std_logic_vector(1 downto 0):=B"11"; S10 a 95 begin ss<=ss_tmp; process(clk) begin if(clk'event and clk='1') then if(res='0') then ss_tmp<=s00; else case(ss_tmp) is when s00=> if(a='1') then ss_tmp<=s01; else ss_tmp<=s10; end if; when s01=> if(a='1') then ss_tmp<=s11; end if; when s10=> if(a='0') then ss_tmp<=s11; end if; when s11=> ss_tmp<=s00; end case; end if; end if; end process; end v1; (1)VHDLのステートマシン記述では,ステートの値は ① type宣言した論理的な値 ② constant宣言した物理的な値 のどちらかを用いる。 (2)上の例では,ステートの値を出力として観測したいので, 96 constant宣言した物理的な値を用いている。 (2)このステートマシンは,Verilogの時と同様にs00が初期状態で, s00 → s01 → s11 (但し,s01でa=0なら,s01のまま) ↑ │ └─────┘ 入力 a=0なら s00 → s10 → s11 (但し ,s10でa=1なら,s10のまま) ↑ │ └─────┘ と回るものです。case文を使えば,上述のようにすっきり書けます。 入力 a=1なら (3)case文のwhenに続くケース値にステートマシンのステート値を 記述することになるが,これはprocessブロック内の読み出し参 照にあたるので,カウンタの時と同様に,ローカル信号ss_tmpを 宣言し使っている。 97 (4)ステートの値を直接,出力として観測する必要がなければ, type宣言した論理的な値を用いても良い。 この場合,アーキテクチャは次のように記述する。 architecture v2 of state is type st_val is (s00,s01,s10,s11); signal st:st_val; begin process(clk) begin : ss_tmpを : stに書き換えたもの end process; process(st) begin : stと出力の : 関係の記述 end process; end v2; 98 例題8のテストベンチ state.tb.vhd library ieee; use ieee.std_logic_1164.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT state PORT (clk,a,res:in std_logic; ss:out std_logic_vector(1 downto 0)); END COMPONENT; SIGNAL clkSignal,aSignal,resSignal: std_logic; SIGNAL ssSignal: std_logic_vector(1 downto 0); BEGIN U1 : state PORT MAP (clk => clkSignal,a => aSignal, res => resSignal,ss => ssSignal); clock:process begin clkSignal<='0'; loop wait for 50ns; clkSignal<=not clkSignal; end loop; end process; 99 other:process begin resSignal<='0'; aSignal<='0'; wait for 100ns; resSignal<='1'; wait for 700ns; resSignal<='0'; aSignal<='1'; wait for 100ns; resSignal<='1'; wait; end process; END HTWTestBench; 100 9.トライステート出力 例題9 triout.vhd library ieee; use ieee.std_logic_1164.all; entity triout is port(a,oe:in std_logic; y :out std_logic); end triout; architecture v1 of triout is begin y<=a when oe='1' else 'Z'; end v1; oe a y architecture v2 of triout is begin process(a, oe) begin if(oe='1') then y<=a; else y<='Z'; end if; end process; end v2; 101 (1)トライステート出力、双方向バスの記述は実用的なLSIを 設計する上でさけて通れない。 (2)1ビットのハイインピーダンスは'Z'と書く。 8ビットは X"ZZ" または B"ZZZZ_ZZZZ" と書く。 ただし、PeakVHDLではX"ZZ"は使えない。 (others => 'Z')と書けば、ビット幅に無関係に使える。 (3)トライステート出力は, V1のように、条件付信号代入文で記述しても良いし、 V2のように、process文とif文で記述しても良い。 (4)トライステート出力はout宣言する。 102 例題9のテストベンチ triout.tb.vhd library ieee; use ieee.std_logic_1164.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT triout PORT (a,oe:in std_logic; y :out std_logic); END COMPONENT; SIGNAL aSignal:std_logic:='0'; SIGNAL oeSignal:std_logic:='0'; SIGNAL ySignal : std_logic; 103 BEGIN U1 : triout PORT MAP (a => aSignal, oe => oeSignal, y => ySignal); aSignal <=(not aSignal) after 100ns; oeSignal <= '1' after 450ns; END HTWTestBench; 104 10.双方向バスの記述 例題10 bidir.vhd rd library ieee; odb use ieee.std_logic_1164.all; entity bidir is idb port(rd,wr:in std_logic; db D Q db :inout std_logic); wr end bidir; architecture v1 of bidir is signal idb,odb:std_logic; begin idb<=db; db <= odb when(rd='0') else 'Z'; process(wr) begin if(wr'event and wr='1') then odb<=idb; end if; end process; 105 end v1; architecture v2 of bidir is signal idb,odb:std_logic; begin idb<=db; process(odb , rd) begin if(rd=‘0’) then db<=odb; else db<=‘Z’; end if; end process; process(wr) begin if(wr'event and wr='1') then odb<=idb; end if; end process; end v2; (1)双方向バスも, V1のように条件付信号代入文で記述しても良いし、 V2のようにprocess文とif文で記述しても良い。 ※Verilogのalways文では,トライステート出力は書けたが, 双方向バスは書けなかった。 (2)双方向バスはinout宣言する。 106 例題10のテストベンチ bidir.tb.vhd library ieee; use ieee.std_logic_1164.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT bidir PORT (rd,wr:in std_logic; db :inout std_logic); END COMPONENT; SIGNAL rdSignal,wrSignal: std_logic; SIGNAL dbSignal : std_logic; signal rd_data:std_logic; BEGIN U1 : bidir PORT MAP (rd => rdSignal, wr => wrSignal, db => dbSignal); 107 process procedure wr_proc(wr_dt:in std_logic) is begin 0 50 dbSignal <= wr_dt; wait for 50ns; wrSignal <='0'; wait for 50ns; db wr_dt wrSignal <='1'; wait for 50ns; wr dbSignal <='Z'; wait for 50ns; end procedure; procedure rd_proc(signal rd_dt:out std_logic) is begin 0 50 rdSignal <='0'; wait for 100ns; rd_dt <= dbSignal; rd rdSignal <='1'; wait for 100ns; rd_dt 前の値 end procedure; begin rdSignal <='1'; wrSignal <='1'; dbSignal <='Z'; wait for 100ns; wr_proc('1'); rd_proc(rd_data); report"time="&time'image(NOW); report"rd_data="&std_logic'image(rd_data); wait for 100ns; wr_proc('0'); rd_proc(rd_data); report"time="&time'image(NOW); report"rd_data="&std_logic'image(rd_data); wait; end process; END HTWTestBench; 100 100 150 200 Z 150 200 rd↑時のdbの値 108 (1)プロシージャはテストベンチで使用されるサブルーチン。 (2)プロシージャはキーワードprocedureで始まり、 end procedure;で終わる。 (3)キーワードprocedureの次に、プロシージャ名を書く。 上の例ではでは wr_proc と rd_proc がプロシージャ名。 (4)プロシージャ名の続きの( )内で引数宣言を行う。 wr_procでは、std_logic型の入力引数wr_dt rd_procでは、std_logic型の出力引数rd_dt が宣言されている。値をメインルーチンに戻すときはrd_dtの ようにsignal宣言する。 (5)上の例ではprocess文のメインルーチンから、 wr_procとrd_procを2回づつコールして、 データバスから1と0の書込み、読出しの確認をしている。109 (6)report文はメッセージを出力する命令であり、 NOWは現在時刻を返す関数です。 信号値などをreport文で表示するときは、型’image(変数)を 使って、型タイプの変数を文字列に変換してから表示します。 110 VHDL設計演習 Ⅳ 応 用 編 広島県立西部工業技術センター 111 1.ストップウオッチの設計 次の回路図のハードウエアを用意しています。 COM1 f 10秒 +5 1秒 +5 g a b 1/10秒 1/100秒 +5 +5 +5 a +5 44,22 100k 3 START 0.1 μ 4 11 START LED4 43 2 10 11 10 9 STOP MACH210 0.1 μ STOP 100Hz 1 30 LED3 24 8 13 LED2 20 14 WATCH LED1 9 3 CLK GND 1k 1k TLR306 (アノードコモン) g 1k 37 100k b f VCC +5 7 セグメント LED e 7 c d h 7 7 1k e g 7 a 1,12,23,34 d c h COM2 COM1 COM2 h g f e d c b a h (問題1) 100Hzの方形波をカウントして、下記の仕様の4桁ストップ ウオッチをVHDLで記述しなさい。 startスイッチを押すと、カウント開始 stopスイッチを押すと、カウント停止 両方同時に押すと、リセット (問題2) startスイッチだけで、スタート、ストップ、リセットができる 仕様に変更しなさい。 LEDi[6] LEDi[5] LEDi[4] LEDi[3] LEDi[2] LEDi[1] LEDi[0] (i=1~4) 112 ①エンティティdigitはLED一桁に相当す library ieee; るサブモジュールで、カウンタ部と7セグ use ieee.std_logic_1164.all; メントデコーダ部で構成されています。 use ieee.std_logic_unsigned.all; ②カウンタ部はclkをクロック、resを正論 ① entity digit is 理の同期クリア信号とする4ビットカウン port(clk,res,cin:in std_logic; タです。10進カウンタなので、9の次は cout:out std_logic; 0に戻らなければなりません。 led :out std_logic_vector(6 downto 0)); ③キャリー入力が有り、かつカウント値 end digit; が9の時、キャリー出力が出なければな りません。 architecture v1 of digit is signal dgt:std_logic_vector(3 downto 0); begin ② process(clk) begin if( and ) then if( ) then dgt<=X"0"; elsif(cin='1' and ) then dgt<=dgt+1; elsif(cin='1' and dgt =X"9") then dgt<=X"0"; end if; end if; end process; ) else '0'; ③ cout<=cin when( 113 ④7セグメントデコーダ部は、カウント値を 数字表示用データに変換する組合せ回路です。 ④ process(dgt) begin case(dgt) is when X"0"=> led<= not B"011_1111"; --3f; when X"1"=> led<= not B"000_0110"; --06; when X"2"=> led<= ; when X"3"=> led<= ; when X"4"=> led<= ; when X"5"=> led<= ; when X"6"=> led<= ; when X"7"=> led<= ; when X"8"=> led<= ; when X"9"=> led<= ; when others=>led<= ; end case; end process; end v1; 114 library ieee; use ieee.std_logic_1164.all; ⑤エンティティwatch1がメインモジュールで reset,count,displayの3つの状態を遷移する ⑤ entity watch is ステートマシンです。条件は下記の通り。 startのみでcountへ port(start,stop,clk:in std_logic; stopのみでdisplayへ led1,led2,led3,led4 両方でresetへ :out std_logic_vector(6 downto 0)); end watch; architecture v1 of watch is component digit port(clk,res,cin:in std_logic; cout:out std_logic; led :out std_logic_vector(6 downto 0)); end component; signal dres,enb1,enb2,enb3,enb4:std_logic; signal state:std_logic_vector(1 downto 0); ⑤ constant reset:std_logic_vector(1 downto 0):="00"; constant count:std_logic_vector(1 downto 0):="01"; constant display:std_logic_vector(1 downto 0):="10"; 115 begin ⑤ process(clk) begin if(clk'event and clk='1') then if(start='1' and stop='1') then state<=reset; elsif( and ) then state<=count; elsif( and ) then state<=display; end if; end if; end process; enb1<= ; dres<='1' when (state=reset) else '0'; ⑥ digit1: digit port map(clk,dres,enb1,enb2,led1); , ,led2); ⑦ digit2: digit port map(clk,dres, digit3: digit port map(clk,dres, , ,led3); digit4: digit port map(clk,dres, , ,led4); end v1; ⑥digit1~digit4はそれぞれ digit1 1/100秒の桁 digit2 1/10秒の桁 digit3 1秒の桁 digit4 10秒の桁 に相当するコンポーネント インスタンスです。 ⑦enb1~enb4はdigit1~digit4 のキャリ入力とキャリ出力を 接続するローカル信号です。 digit4 enb4 digit3 enb3 digit2 enb2 digit1 enb1 116 2.並列IOの設計 下記仕様の8ビット×3ポート入出力LSI (インテル8255の簡易版)を設計しなさい。 cs rd wr adr 動 作 H × × ×× 非 動 作 00 DB←PAピン L L H 01 DB←PBピン 10 DB←PCピン 11 DB←CRレジスタ 00 DB→PAレジスタ L H ↑ 01 DB→PBレジスタ 10 DB→PCレジスタ 11 DB→CRレジスタ CRレジスタはPA、PB、PCの入出力を 決める内部レジスタ。 CR(0) 0 PA=出力モード 1 PA=入力モード CR(1) 0 PB=出力モード 1 PB=入力モード CR(2) 0 PC=出力モード 1 PC=入力モード PIO 2 ADR CS RD WR 8 DB RES PA PB PC 8 8 8 CR RESはPA,PB,PCを全て 入力モードにする負論理の 非同期リセット信号。 117 並列IO pio.vhd ①qa,qb,qc,crはwrをクロック、 library ieee; ~resを非同期クリア信号とするFFで use ieee.std_logic_1164.all; csがアクティブの時、dbの値がadrで entity pio is 選択されたqa,qb,qc,crのいずれか generic(n:integer:=8); に書き込まれる。 port(cs,rd,wr,res:in std_logic; adr :in std_logic_vector(1 downto 0); db,pa,pb,pc :inout std_logic_vector(n-1 downto 0)); end pio; architecture pio_body of pio is signal qa,qb,qc,cr,odb:std_logic_vector(n-1 downto 0); begin ----- internal register (reset , cs & wr) ----① process(wr,res) begin if( ) then qa<=(others=>'0'); qb<=(others=>'0'); qc<=(others=>'0'); cr<=(others=>'1'); elsif( and ) then if( ) then case( ) is when "00"=> qa<=db; when "01"=> qb<=db; when "10"=> qc<=db; when others=> cr<=db; end case; end if; end if; end process; 118 ----- port tri-state assign ----②paは~cr[0]を制御信号とする ) else (others => 'Z'); ② pa<=qa when ( 双方向バッファ pb<=qb when ( ) else (others => 'Z'); pbは~cr[1]を制御信号とする pc<=qc when ( ) else (others => 'Z'); 双方向バッファ ----- data selecter ----, , , , ) begin ③ process( case( ) is when "00"=> odb<=pa; when "01"=> odb<=pb; when "10"=> odb<=pc; when others=> odb<=cr; end case; end process; pcは~cr[2]を制御信号とする 双方向バッファ ③はpa,pb,pc,crをデータ入力 adrをセレクト信号 odbをデータ出力とする データセレクタ ----- databus tri-state assign (cs & rd) ----and ) else (others => 'Z'); ④ db<=odb when( end pio_body; ④dbは~csと~rdの論理積を 制御信号とする双方向バッ ファ 119 PIOのテストベンチ pio.tb.vhd library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; ENTITY TestBench IS END TestBench; ARCHITECTURE HTWTestBench OF TestBench IS COMPONENT pio GENERIC (n:integer:=8); PORT (cs,rd,wr,res:in std_logic; adr:in std_logic_vector(1 downto 0); db,pa,pb,pc :inout std_logic_vector(n-1 downto 0)); END COMPONENT; constant n:integer:=8; SIGNAL csSignal,rdSignal,wrSignal,resSignal: std_logic; SIGNAL adrSignal : std_logic_vector(1 downto 0); SIGNAL dbSignal,paSignal,pbSignal,pcSignal : std_logic_vector(n-1 downto 0); signal rd_data:std_logic_vector(n-1 downto 0); BEGIN U1 : pio GENERIC MAP (n => 8) PORT MAP (cs => csSignal, rd => rdSignal, wr => wrSignal, res => resSignal, adr => adrSignal, db => dbSignal, pa => paSignal, pb => pbSignal, pc => pcSignal); paSignal <= pbSignal after 1ns; -- connect from PB to PA 120 process procedure wr_proc(adr_dt:in std_logic_vector(1 downto 0); wr_dt:in std_logic_vector(7 downto 0)) is begin csSignal <='0'; adrSignal <= adr_dt; dbSignal <= wr_dt; wait for 50ns; wrSignal <='0'; wait for 100ns; wrSignal <='1'; wait for 50ns; csSignal <='1'; adrSignal <= B"11"; dbSignal <= B"ZZZZ_ZZZZ"; wait for 50ns; end procedure; procedure rd_proc(adr_dt:in std_logic_vector(1 downto 0); signal rd_dt:out std_logic_vector(7 downto 0)) is begin csSignal <='0'; adrSignal <= adr_dt; wait for 50ns; rdSignal <='0'; wait for 100ns; rd_dt <= dbSignal; rdSignal <='1'; wait for 50ns; csSignal <='1'; adrSignal <= B"11"; wait for 50ns; end procedure; begin csSignal <='1'; rdSignal <='1'; wrSignal <='1'; dbSignal <=B"ZZZZ_ZZZZ"; resSignal <='0'; wait for 50ns; resSignal <='1'; wait for 50ns; wr_proc(B"11",X"01"); -- write CW to CR for i in 0 to 255 loop wr_proc(B"01",conv_std_logic_vector(i,8)); -- write to PB rd_proc(B"00",rd_data); -- read from PA report "i="&integer'image(i)&" rd_data="&integer'image(conv_integer(rd_data)); end loop; wait; 121 end process; END HTWTestBench; 122 library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; entity digit is port(clk,res,cin:in std_logic; cout:out std_logic; led :out std_logic_vector(6 downto 0)); end digit; architecture v1 of digit is signal dgt:std_logic_vector(3 downto 0); begin process(clk) begin if(clk'event and clk='1') then if(res='1') then dgt<=X"0"; elsif(cin='1' and dgt/=X"9") then dgt<=dgt+1; elsif(cin='1' and dgt =x"9") then dgt<=X"0"; end if; end if; end process; cout<=cin when(dgt=X"9") else '0'; 解答例 watch1.vhd 1/4 123 解答例 watch1.vhd 2/4 process(dgt) begin case(dgt) is when X"0"=> led<= not B"011_1111"; when X"1"=> led<= not B"000_0110"; when X"2"=> led<= not B"101_1011"; when X"3"=> led<= not B"100_1111"; when X"4"=> led<= not B"110_0110"; when X"5"=> led<= not B"110_1101"; when X"6"=> led<= not B"111_1101"; when X"7"=> led<= not B"010_0111"; when X"8"=> led<= not B"111_1111"; when X"9"=> led<= not B"110_1111"; when others=>led<= not B"000_0000"; end case; end process; end v1; --X"3f"; --X"06"; --X"5b"; --X"4f"; --X"66"; --X"6d"; --X"7d"; --X"27"; --X"7f"; --X"6f"; --X"00"; 124 library ieee; use ieee.std_logic_1164.all; entity watch is port(start,stop,clk:in std_logic; led1,led2,led3,led4:out std_logic_vector(6 downto 0)); end watch; architecture v1 of watch is component digit port(clk,res,cin:in std_logic; cout:out std_logic; led :out std_logic_vector(6 downto 0)); end component; signal dres,enb1,enb2,enb3,enb4:std_logic; signal state:std_logic_vector(1 downto 0); constant reset:std_logic_vector(1 downto 0):="00"; constant count:std_logic_vector(1 downto 0):="01"; constant display:std_logic_vector(1 downto 0):="10"; 解答例 watch1.vhd 3/4 125 begin process(clk) begin if(clk'event and clk='1') then if(start='1' and stop='1') then state<=reset; elsif(start='1' and stop='0') then state<=count; elsif(start='0' and stop='1') then state<=display; end if; end if; end process; 解答例 watch1.vhd 4/4 enb1<=state(0); dres<='1' when (state=reset) else '0'; digit1: digit port map(clk,dres,enb1,enb2,led1); digit2: digit port map(clk,dres,enb2,enb3,led2); digit3: digit port map(clk,dres,enb3,enb4,led3); digit4: digit port map(clk,dres,enb4,open,led4); end v1; 126 library ieee; use ieee.std_logic_1164.all; entity pio is generic(n:integer:=8); port(cs,rd,wr,res:in std_logic; adr :in std_logic_vector(1 downto 0); db,pa,pb,pc :inout std_logic_vector(n-1 downto 0)); end pio; 解答例 pio.vhd 1/2 architecture pio_body of pio is signal qa,qb,qc,cr,odb:std_logic_vector(n-1 downto 0); begin ----- internal register (reset , cs & wr) ----process(wr,res) begin if(res='0') then qa<=(others=>'0'); qb<=(others=>'0'); qc<=(others=>'0'); cr<=(others=>'1'); elsif(wr'event and wr='1') then if(cs='0') then case(adr) is when "00"=> qa<=db; when "01"=> qb<=db; when "10"=> qc<=db; when others=> cr<=db; end case; end if; end if; 127 end process; ----- port tri-state assign ----pa<=qa when (cr(0)='0') else (others => 'Z'); pb<=qb when (cr(1)='0') else (others => 'Z'); pc<=qc when (cr(2)='0') else (others => 'Z'); 解答例 pio.vhd 2/2 ----- data selecter ----process(adr,pa,pb,pc,cr) begin case(adr) is when "00"=> odb<=pa; when "01"=> odb<=pb; when "10"=> odb<=pc; when others=> odb<=cr; end case; end process; ----- databus tri-state assign (cs & rd) ----db<=odb when(cs='0' and rd='0') else (others => 'Z'); end pio_body; 128
© Copyright 2025 ExpyDoc