VHDL設計入門 テキスト(パワーポイント文書)

Download Report

Transcript VHDL設計入門 テキスト(パワーポイント文書)

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
+5
37
100k
STOP
1
30
LED3 24
STOP
MACH210
0.1 μ
11 10 9
100Hz
2 10
8 13
LED2
b
f
VCC
20
14
WATCH
LED1 9
3
CLK
GND
1k
1k
TLR306
(アノードコモン)
g
1k
1k
7 セグメント LED
e
7
c
d
h
7
7
7
e
g
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