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

Download Report

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

Verilog設計演習
Ⅰ
入 門 編
広島県立西部工業技術センター
1
1.作業ディレクトリの作成
まず、エクスプローラを起動し、
ルートディレクトリの下にseminarというディレクトリを、
その下にVerilogというディレクトリを作成して下さい。
(DOS/Vの場合)
C:¥seminar¥Verilog
(98の場合)
A:¥seminar¥Verilog
以下、Verilog演習で作成するファイルはVerilogの下に作ります。
それ以外の、ディレクトリには何も作らないで下さい。
2
2.Verilogの基本ブロック
例題1 aoiゲート aoi.v
module aoi(a,b,c,d,f);
input a,b,c,d;
output f;
assign f=~((a & b)|(c & d));
endmodule
A
B
C
D
F
3
(1)verilogの基本ブロックはモジュール。
(2)モジュールはキーワードmoduleではじまり,endmoduleで終わる。
(3)キーワードmoduleの次にモジュール名と(ポートリスト);がつづく。
(4)ポートリストに書いた信号名の入出力宣言を次に行う。
入 力 = input
出 力 = output
双方向 = inout
(5)入出力宣言とendmoduleの間にモジュールの本体機能を書く。
endmoduleにはセミコロン;はいらない。
(6)簡単な本体機能は,信号代入文assignを使って記述する。
assign 左辺 = 右辺
(7)Verilogのビット演算子は,C言語と同じ。
&
AND 小林テキストP64 表3.2
|
OR
~
NOT
^
EX-OR
(8)わかりやすい様に( )を使ってよい。
4
クイックロジック社QuickWorks
配置配線ツールSpDE
兼 統合化環境
論理合成
Synplify
回路図入力
SYNARIO
Verilogシミュレータ
SILOSⅢ
Verilog学習
HDLエディタ
ターボライタ
VHDL学習
5
3.SpDEの起動
スタート - プログラム - QuickLogic ー SpDEで
SpDEを起動します。
6
4.HDLエディタの起動
SpDEのツールバーから、HDLエディタのアイコン
押して、HDLエディタを起動し、クリエイトアイコン
を
を押す。
7
5.Verilogコードの入力、保存
例題1のVerilogコードを入力し、FileーSaveAsメニューから
¥seminar¥verilogの下に、ファイル名aoi.vで保存する。
拡張子が定まると、module、inputなどのVerilogキーワード
が紺色でハイライトされます。紺色は見にくいのでHDLエディタの
WindowーColorsーKeyword
で、青色に変更して下さい。
File-ExitでHDLエディタを終了し、SpDEに戻ります。
6.論理合成Synplifyの起動
SpdeのFile-Import-Verilogメニューから
¥seminar¥verilog¥aoi.vを指定し、論理合成ツールを
起動します。
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
となり、最初のVerilogコードを満す回路が生成されていること
が分かります。
s
f
p
b
r
a
q
入力s
0
1
d
c
出力r
p
q
※論理合成結果を確認したら、
File-Saveメニューで結果を
15
保存します。
12.基本ブロック(続き)
HDLエディタを起動し、aoi.vを次のように修正します。
module aoi(a,b,c,d,f);
input a,b,c,d;
output f;
//
assign f=~((a & b)|(c & d));
wire ab,cd,o;
assign ab=a & b;
assign cd=c & d;
assign o =ab | cd;
assign f = ~o;
A
B
C
D
AB
O
F
CD
endmodule
16
(1)やや複雑な機能を記述する時は,wireで宣言した
ローカル信号を使うこともできる。
ab,cd,o
(2)1ビット幅のwireは宣言なしでも使える。暗黙宣言。
後出の複数ビット幅のwireは宣言が必要。
(3)//は1行のみのコメント行。
/*・・・・・*/は複数行にわたるコメント行である。
修正が終わったらソースコードを保存し、論理合成をかけて
配置配線を実行して下さい。結果は同じになります。
17
問題1
インバータLS04.vを設計し、結果を確認しなさい。
問題2
NANDゲートLS00.vを設計し、結果を確認しなさい。
問題3
NORゲートLS02.vを設計し、結果を確認しなさい。
18
13.階層設計
例題2 マルチプレクサ mux2.v
`include "aoi.v"
module inv(a,f);
SEL
input a;
A
output f;
g1
assign f=~a;
SELB
endmodule
B
g2
AOI
FB
g3
F
module mux2(sel,a,b,f);
input sel,a,b;
output f;
inv g1 (sel,selb);
aoi g2 (sel,a,selb,b,fb);
inv g3 (.a(fb),.f(f));
endmodule
19
(1)もっと複雑な回路記述には階層設計を使う。
(2) 階層設計は,複数のモジュール宣言と
モジュールインスタンスで行う。
(3) 複数モジュールの宣言は,
invの様に,同一ファイル内に書いても良いし,
aoiの様に,別ファイルに書いて,`includeしても良い。
(4) モジュールインスタンスは,サブルーチンコールの様なもので,
モジュール名
インスタンス名(ポートリスト);
の形式で行う。上の例では,g1,g2,g3がインスタンス名。
20
(5) モジュールインスタンスのポートリストは上位モジュールと
下位モジュールを接続するもので,
g1,g2の様に,並びによる接続が一般的。
g3の様に,名前による接続も使える。
(6) 出力信号を接続しない時はカンマを余分に書く。
(7) 上の例ではselbとfbは,暗黙宣言されたwireである。
※ `はバック・シングル・クォートで
DOSVでは shift + @
98 では
shift + ^
shift + 7 ではないことに注意。
21
例題2を入力し、論理合成、配置配線を実行します。
結果は、論理圧縮の効果で例題1より簡単になり、
下図のようになります。
sel=1なら f=a
sel=0なら f=b
となっています。
b
a
sel
f
22
14.条件付きassign文
例題3 セレクタ mux21.v
module mux21(sel,a,b,f);
input sel,a,b;
output f;
assign f=sel ? a:b;
endmodule
sel
a
b
mux21
f
(1) 例題2(mux2.v)では階層設計を説明するため,複雑な書き方を
したが,セレクタ自体はもっと簡単に記述できる。
(2)assign文にはC言語の3項演算子(条件演算子ともいう)に似た
条件付きassign文があり,
assign 左辺=(条件式1) ? (右辺1) : (右辺2);
が使える。
(3)上の例では,sel=1ならf=a,sel=0ならf=bになる。
23
15.always文とif文
例題3 セレクタ mux21.v
module mux21(sel,a,b,f);
input sel,a,b;
output f;
reg f;
sel
a
b
mux21
f
always@(sel or a or b) begin
if(sel==1'b1)
f=a;
else
f=b;
end
endmodule
(1)3項演算子よりも,わかりやすいif文もあるが,
module-endmodule間に,ダイレクトには記述できない。
上の例のように,alwaysブロックの中で記述する。
24
(2)always文は
always@(信号名) begin
:
:
end
←セミコロンがないのに注意
の形で記述し,( )内の信号名の値が変化したときのみ評価される。
つまり,( )内にはalwaysブロックとしての入力信号を記述する。
正しくはセンシティビティ・リストという。
複数の入力信号が有る時は,カンマではなく
orで区切って記述する。
(3)alwaysブロックで組合せ回路を生成する場合,入力信号を
全てセンシティビティ・リストに記述しなければならない。
(順序回路の場合はそうとは限らない)
25
(4)alwaysブロック内では,if文,case文が使え,
if(条件式) 式1; else 式2;
と書く。多重if文も使える。
if(条件式1) 式1;
else if(条件式2) 式2;
else 式3;
(5)条件式に使う関係演算子はC言語と同じで
== 等しい
> 大
>= 以上
!= 等しくない
< 小
<= 以下
が使え,かつ,それらの論理演算
&& 論理積
|| 論理和
! 論理否定
も使える。
(6)1ビット幅の定数は
1,0,1'b1,1'b0
と書く。 ' はSHIFT+7です。
26
(7)最も重要なのは,alwaysブロック内での
・信号代入には,assign文は使わないこと。
・代入文の左辺にくる信号は,レジスタ宣言
しなければならないこと。
である。上の例では f がこれにあたる。
(8)上の例からわかる様に,生成される回路が組合せ回路で
あってもレジスタ宣言が必要である。逆に言うと,レジスタ宣言
してもフリップフロップが必ず生成されるわけではない。
(9)if文で全ての条件が列挙されていれば,つまり,
ifの数だけelseがあれば,組合せ回路が,
そうでなければ,順序回路が生成される。
例題3を入力し、論理合成、配置配線を実行します。
結果は例題2と同じになります。
27
Verilog設計演習
Ⅱ シミュレーション編
広島県立西部工業技術センター
28
1.テスト・フィクスチャの準備
例題3の設計mux21.vを、Verilogシミュレータを用いて
検証します。
設計検証用テストパターンを発生させるVerilogコードのことを
テスト・フィクスチャと呼びます。拡張子は通常 .tf を使います。
HDLエディタでmux21.vを開いた状態で、
HDLー GenerateTestBench
を実行すると、テストフィクスチャの雛形mux21.tfが生成されます。
(遅いマシンでは数分かかることも有ります。)
(1) `timescale 1ns/1ns
(2) module t;
(3) reg sel,a,b;
(4) wire f;
(5)
mux21 m (.sel(sel),.a(a),.b(b),.f(f));
(6)
// Enter fixture code here
29
(7) endmodule // t
(1)timescale文はシミュレーションの時間単位を定めるもので、
/の前がこのモジュール内での時間記述#の時間単位、
/の後ろがシミュレーション時に使用される時間精度
になります。この例ではいずれも1nsになります。
(2)テストフィクスチャのモジュールには、ポートリストが有りません。
モジュール名は何でもかまいませんが、ここでは t です。
(3)元の設計データの入力信号は、このモジュール内では値を代入
するので、レジスタ宣言します。
(4)出力信号は観測するだけなので、ワイヤ宣言します。
(5)元の設計データをモジュールインスタンスとして、呼び出します。
信号の接続には、名前による接続が行われていますが、
並びによる接続でも構いません。
(6)この位置に、実際のテストパターン用コードを追加します。
30
`timescale 1ns/1ns
module t;
reg sel,a,b;
wire f;
mux21 m (.sel(sel),.a(a),.b(b),.f(f));
// Enter fixture code here
initial begin
この例で分かるように、テスト
sel=0; a=0; b=0;
フィクスチャでの信号値の代入
#100 sel=0; a=0; b=1;
には、initial文を使用します。
#100 sel=0; a=1; b=0;
意味としては、
#100 sel=0; a=1; b=1;
sel,a,bの初期値として
#100 sel=1; a=0; b=0;
全て0を代入した後、100ns
#100 sel=1; a=0; b=1;
毎に異なる値を代入しています。
#100 sel=1; a=1; b=0;
#100 sel=1; a=1; b=1;
end
endmodule // t
31
それでは、6行目の位置に
右の四角の部分を追加して
テスト・フィクスチャを完成さ
せてから、mux21.tfとして
保存して下さい。
2.機能シミュレーション(Pre-Layout)
(1)SpDEのツールバーから、シミュレータのアイコン
押して、SILOS3を起動します。
シミュレーションタイプでPre-Layoutを選択します。
テストフィクスチャがmux21.tf、
トップレベルモジュールがmux21.vになっていることを
確認後、OKを押して下さい。
を
32
(2)mux21.vは論理合成のチェックを通っているので、
エラーがあるとすればmux21.tfの方です。
エラーがあるとoutputウインドウにエラーメッセージが表示され、
下の画面で停止するので、キャンセルを押します。
outputウインドウにエラーがなければ、(4)に進みます。
33
(3)エラーメッセージを確認後、SILOS3のFileーOpenメニュー
からmux21.tfを選択し、エラー箇所を修正します。
修正が終わったら、SILOS3のFile-Saveで保存し、
mux21.tfウインドウのアイコン化ボタン
Load/Reloadアイコン
反映させます。
でアイコン化します。
を押して、修正結果をSILOS3に
outputウインドウにエラーが無ければ、GOアイコン
押して(4)に進みます。
を
outputウインドウにエラーが有れば、エラーメッセージを確認後
アイコン化していたmux21.tfを通常の大きさに戻し、
修正を繰り返します。
34
(4)テストフィクスチャにエラーが無ければ、下の画面で停止します
ので、シミュレーション時間1000nsを入力し、OKを押します。
35
(5)エラーが無ければ、outputウインドウに
32 State changes on observable nets.
Simulation stopped at the end of time 1.000us.が表示されるので
データアナライザのアイコン
を押して、波形表示ウインドウ
を開きます。
36
次に、モジュールエクスプローラのアイコン
を押して、
t:tのa,b,f,selを選択後、マウス右クリックから
AddSignaltoAnalyzerを選ぶと、波形が表示されます。
37
データアナライザ・ウインドウをアクティブにしてから、
View-ZoomAllを選ぶと、波形全体が表示されます。
sel=0の時f=b、
sel=1の時f=aを確認します。
38
(1)データアナライザ・ウインドウの時間軸上で、マウス右クリックし、
timescaleを選んで、切りの良い値を入れれば、時間軸を見やすい
値に変更できます。
(2)波形上でマウスを左クリックすると、青色の第1カーソル、
右クリックすると、赤色の第2カーソルを置くことができます。
各カーソルの時間および時間差がT1,T2,Tdeltaで表示されます。
(3)信号名を選択後、カーソルスキャン・アイコン
押すと、カーソルが選択信号の変化点に移動します。
この機能を使って、入力信号a,b,selと出力信号fの時間差を
測定してみると、Tdelta=0になります。
を
これは、現在表示している結果が、遅延時間情報の入っていない
機能シミュレーション(Pre Layout)であるためです。
結果を確認したら、SILO3のFile-Exitを選び、途中、「はい」を
39
選んで終了します。
3.遅延シミュレーション(Post-Layout)
再びSpDEのツールバーから、シミュレータのアイコン
を
押して、SILOS3を起動します。
今度はシミュレーションタイプでPost-Layoutを選択します。
テストフィクスチャがmux21.tf、 SDFがmux21.sdf、
トップレベルモジュールがmux21.vq になっていることを
確認後、OKを押して下さい。
40
1000nsを確認後、OKを押します。
41
outputウインドウに92 State changes on observable nets.
Simulation stopped at the end of time 1000.000ns.が表示され、
モジュールエクスプローラ、データアナライザウインドウも表示
されるので、ズームオール
で波形全体を表示させます。
42
(1)波形上でマウスを左クリックすると、青色の第1カーソル、
右クリックすると、赤色の第2カーソルを置くことができます。
各カーソルの時間および時間差がT1,T2,Tdeltaで表示されます。
(2)信号名を選択後、カーソルスキャン・アイコン
押すと、カーソルが選択信号の変化点に移動します。
この機能を使って、入力信号a,b,selと出力信号fの時間差を
測定してみると、今度はTdelta=9ns程度が表示されます。
を
これは、現在表示している結果が、遅延時間情報(mux21.sdf)
を考慮した遅延シミュレーション(Post Layout)であるためです。
また、Verilogコードも元のmux21.vではなく、配置配線ツール
の出力したmux21.vqが使用されています。
SDFはスタンダード・ディレイ・フォーマットといい、遅延時間情報の
標準フォーマットです。
結果を確認したら、SILO3のFile-Exitを選び、終了します。
43
興味があれば、*.sdf、*.vqをエディタで開いて見て下さい。
遅延シミュレーションの結果
44
問題4 インバータLS04.vのテストパターンを設計し、
機能シミュレーションと遅延シミュレーションを
実行しなさい。
問題5 NANDゲートLS00.vのテストパターンを設計し、
機能シミュレーションと遅延シミュレーションを
実行しなさい。
問題6 NORゲートLS02.vのテストパターンを設計し、
機能シミュレーションと遅延シミュレーションを
実行しなさい。
45
4.ピン配置の指定
ピン配置は配置配線ツールが遅延時間や配線効率を考慮して
最適に近いものに自動配置するので、これを使うのが無難です。
しかし、設計の最終段階に近く、プリント基板の変更ができない
場合などは、次の方法でピン配置を直接指定することも可能です。
SpDEのTools-Optionsメニューから、BackAnnotationタブ
を選択し、FixPlacementのIOcellsをチェックonにします。
46
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";
47
論理合成Synplifyを立ち上げて、Addボタンを押します。
ファイルの種類をPropertyFiles(*.sc*)にして、
mux21.scpを選択し、「開く」を押します。
48
SourceFilesにmux21.scpが追加されたことを確認後、
RUNを押して論理合成をかけます。
途中「はい」とOKを選択すると、mux21.scpで指定した
ピン配置での配置配線が実行されます。
49
Verilog設計演習
Ⅲ
基 礎 編
広島県立西部工業技術センター
50
1.ビット幅のある信号の表現
例題1 コンパレータ comp.v
module comp(a,b,eq,ge,le);
4
parameter n=4;
a
input [n-1:0] a,b;
4
output eq,ge,le;
b
reg eq,ge,le;
always@(a or b) begin
if(a==b) eq=1; else eq=0;
if(a>=b) ge=1; else ge=0;
if(a<=b) le=1; else le=0;
end
endmodule
comp
ge
eq
le
51
(1)今までは1ビット幅の信号だけ扱ってきたが,
ビット幅のある信号も扱える。input,output宣言時に
[MSB:LSB] 信号名,信号名;
とすれば良い。
同じビット幅,ビットオーダーの信号は1行で宣言できる。
[15:0]
[1:16]
[8:1]
[0:7]
のいずれも使える。MSB,LSBの値の大小にかかわらず,
左端がMSB,右端がLSBである。
(2)ビット幅を変更しやすくするためにparameterを使うことができる。
52
例題1の続き
module comp(a,b,eq,ge,le);
parameter n=4;
input [n-1:0] a,b;
output eq,ge,le;
assign eq=(a==b);
assign ge=(a>=b);
assign le=(a<=b);
a
b
4
comp
ge
eq
le
4
endmodule
(3)上の例の様に,コンパレータ等はif文よりもassign文の方が
コンパクトに記述できる。
53
例題1のテストフィクスチャ comp.tf
`timescale 1ns/1ns
module t;
parameter n=4;
(1)initial文の中では
reg [n-1:0] a,b;
wire eq,ge,le;
integer宣言した i 、jを用いた
integer i,j;
forループが使えます。
comp m (.a(a),.b(b),.eq(eq),.ge(ge),.le(le));
// Enter fixture code here
i++ではないことに注意。
initial begin
for(i=0;i<16;i=i+1) begin
a=i;
for(j=0;j<16;j=j+1) begin b=j; #100; end
end
end
endmodule // t
54
2.case文とビット幅のある定数
例題2 デコーダ decoder.v
module decoder(enb,adr,y);
input enb;
input [2:0] adr;
output [7:0] y;
reg [7:0] y;
always @(enb or adr) begin
if(!enb)
case(adr)
3'b000: y=8'b11111110;
3'b001: y=8'b11111101;
3'b010: y=8'b11111011;
3'b011: y=8'b11110111;
3'b100: y=8'b11101111;
3'b101: y=8'b11011111;
3'b110: y=8'b10111111;
3'b111: y=8'b01111111;
endcase
else y=8'b11111111;
end
endmodule
decode
enb
adr
8
3
y
55
(1)デコーダは,if文を並べて書くこともできるが,
case文を使った方がわかりやすい。
case(信号名)
ケース1:式1;
ケース2:式2;
:
:
default:式n;
endcase
と記述する。endcaseにはセミコロンはつけない。
(2)ビット幅のある定数は,
( 2進数)3'b001,8'b1111_1101,8'bZZZZ_ZZZZ
(16進数)3'h1,8'hfc,8'hZZと書く。
'の前はビット幅である。
(3)if文の条件(!enb)は,本来(~enb)と書くべきであるが,
(!enb)も許される。
56
(4)else y=8'b11111111; が無いとラッチが生成されるので注意。
例題2のテストフィクスチャ decoder.tf
`timescale 1ns/1ns
module t;
reg enb;
reg [2:0] adr;
wire [7:0] y;
integer i;
decoder m (.enb(enb),.adr(adr),.y(y));
// Enter fixture code here
initial begin
enb=0;
for(i=0;i<8;i=i+1) begin
adr=i; #100;
end
enb=1;
for(i=0;i<8;i=i+1) begin
adr=i; #100;
end
end
endmodule // t
57
3.算術演算子と連接演算子
例題3 アダー adder.v
module adder(cin,a,b,cout,s);
parameter n=4;
input cin;
input [n-1:0]a,b;
output [n-1:0]s;
output cout;
//
assign {cout,s}=a+b+cin;
reg
[n-1:0]s;
reg
cout;
always@(a or b or cin) begin
{cout,s}=a+b+cin;
end
endmodule
a
b
cin
4 adder
4
4
s
cout
(1)加算器,減算器,乗算器を生成するのに算術演算子+、-、*が使える。
(2)加算結果には,キャリ出力がつきものであるが,連接演算子{ , }を使って
{cout,s}=a+b+cin;
58
とわかりやすく記述できる。
例題3のテストフィクスチャ adder.tf
`timescale 1ns/1ns
module t;
parameter n=4;
reg cin;
reg [n-1:0]a,b;
wire [n-1:0]s;
wire cout;
integer i;
adder m (.cin(cin),.a(a),.b(b),.cout(cout),.s(s));
// Enter fixture code here
initial begin
cin=0;
for(i=0;i<256;i=i+1) begin
{a,b}=i; #100;
end
cin=1;
for(i=0;i<256;i=i+1) begin
{a,b}=i; #100;
end
end
endmodule // t
このテストフィクスチャでは連接演算子を使って、二重ループを一重ループで済ませています。
59
4.フリップフロップの記述
例題4 フリップフロップ dff.v
module dff(d,clk,sclr,aclr,enb,q,qsc,qac,qen);
input d,clk,sclr,aclr,enb;
output q,qsc,qac,qen;
reg q,qsc,qac,qen;
// simple ff //
always@(posedge clk)
q <= d;
// sync clear ff //
always@(posedge clk)
if(~sclr) qsc <= 0;
else qsc <= d;
// async clear ff //
always@(posedge clk or negedge aclr)
if(~aclr) qac <= 0;
else qac <= d;
// enb ff //
always@(posedge clk)
if(enb) qen <= d;
endmodule
d
clk
通常
FF
q
d
clk
sclr
同期
クリア
FF
qsc
d
clk
非同期
クリア
FF
qac
aclr
d イネーブル
clk 機能付
FF
enb
qen
60
(1)フリップフロップの生成には
always@(posedge clk)
を使う。clk信号の立ち上がりエッジでalways文が実行される。
立ち下がり動作のFFにはnegedgeを使います。
(2)同期クリア信号は,always@(信号名)の
センシティビティ・リストに記述しない。
(3)非同期クリア信号は,センシティビティ・リストに記述する。
aclrが正論理なら、posedgeを使います。
(4)イネーブル信号は同期クリア信号と同じ。
(5)この例で用いている<=はノンブロッキング代入文と呼ばれ,
順序回路では,これを使った方がよい。
=はブロッキング代入文と呼ばれ組合せ回路用。
但し,上の例では全て=にしても同じ回路が生成される。 61
例題4のテストフィクスチャ dff.tf
module t;
reg d,clk,sclr,aclr,enb;
wire q,qsc,qac,qen;
dff m (.d(d),.clk(clk),
.sclr(sclr),.aclr(aclr),.enb(enb),
.q(q),.qsc(qsc),.qac(qac),.qen(qen));
// Enter fixture code here
initial begin
clk=0;
forever begin
#50; clk=~clk;
end
end
initial begin
aclr=1; sclr=1; enb=1; d=1;
#125 d=0;
#200 d=1;
#300 aclr=0;sclr=0;enb=0;
#200 d=0;
#300 aclr=1;sclr=1;enb=1;
#200 d=1;
end
endmodule // t
※クロック生成の無限ループにはforever
を使うことができる。
62
5.ブロッキング代入文とノンブロッキング代入文
例題5 エッジ検出 edg.v
module edg(clk,d,reset,rise,fall);
q1
d
fall
q2
input clk,d,reset;
output rise,fall;
clk
reg q1,q2;
always@(posedge clk) begin
if(!reset) begin q1=0; end
else begin q1=d; end
end
rise
always@(posedge clk) begin
if(!reset) begin q2=0; end
else begin q2=q1; end
end
assign rise= q1 & !q2;
assign fall=!q1 & q2;
endmodule
63
(1)スイッチが押された時,1回だけある動作をさせたい時など,
入力信号の立ち上がり,立ち下がりを検出する手段として,
図のようなエッジ検出回路が使用される。
(2)このrise,fall検出回路を1つのalways文で実現しようとすれば,
<= ノンブロッキング
= ブロッキング
の違いがわかる。上記の四角の部分は下記に置き換えられるが、
<=の代わりに、=を使うと,q1とq2が同じになり,rise,fallも消える。
always@(posedge clk) begin
if(!reset) begin
q1<=0; q2<=0; end
else begin q1<=d; q2<=q1; end
end
(3)ノンブロッキング<=は、各右辺の処理が終了してから代入処理
が行われる。記述の順番に動作が影響されないので順序回路向き。
(4)ブロッキング=は、一つの代入処理が終了するまで次の処理が
行われない。記述の順番に動作が影響される。組合せ回路向き。
64
例題5のテストフィクスチャ edg.tf
`timescale 1ns/1ns
module t;
reg clk,d,reset;
wire rise,fall;
edg m (.clk(clk),.d(d),.reset(reset),
.rise(rise),.fall(fall));
// Enter fixture code here
initial begin clk=0;
forever begin
#50 clk=~clk;
end
end
initial begin
reset=0; d=0;
#75 reset=1;
#200 d=1;
#300 d=0;
#400 d=1;
#400 d=0;
end
endmodule // t
65
6.同期クリア付きカウンタ
例題6 countsc.v
module countsc(clk,clr,count);
input clk,clr;
output [3:0]count; reg [3:0]count;
always@(posedge clk) begin
if(!clr) count <= 0;
else count <=count+1;
end
endmodule
countsc
clk
clr
4
count
(1)同期クリア付カウンタは,同期クリアFFのクリア信号の書き方と,
算術演算子+の応用であり,上の例の様にかける。
順序回路なので代入には、ノンブロッキングを使った方が良い。
(小テスト)これを10進カウンタにするには、どうすればよいか。
66
例題6のテストフィクスチャ countsc.tf
`timescale 1ns/1ns
module t;
reg clk,clr;
wire [3:0]count;
countsc m (.clk(clk),.clr(clr),.count(count));
// Enter fixture code here
initial begin
clk=0;
forever begin
#50; clk=~clk;
end
end
initial begin
clr=1;
#125; clr=0;
#200; clr=1;
end
endmodule // t
67
7.非同期クリア付きカウンタ
例題7 countac.v
module countac(clk,clr,count);
input clk,clr;
output [3:0]count; reg [3:0]count;
countac
clk
clr
4
count
always@(posedge clk or negedge clr) begin
if(!clr) count <= 0;
else count <=count+1;
end
endmodule
(1)非同期クリア付カウンタは,非同期クリアFFのクリア信号の
書き方と,算術演算子+の応用であり,上の例の様にかける。
順序回路なので代入には、ノンブロッキングを使った方が良い。
(小テスト)これにキャリー入出力を付けるには、どうすればよいか。
68
例題7のテストフィクスチャ countac.tf
`timescale 1ns/1ns
module t;
reg clk,clr;
wire [3:0]count;
countac m (.clk(clk),.clr(clr),.count(count));
// Enter fixture code here
initial begin
clk=0;
forever begin
#50; clk=~clk;
end
end
initial begin
clr=1;
#125; clr=0;
#200; clr=1;
end
endmodule // t
69
8.ステートマシンの記述
例題8 state.v
module state(clk,a,res,ss);
input clk,a,res;
output [1:0]ss;
reg [1:0]ss;
parameter s00=2'b00;
parameter s01=2'b01;
parameter s10=2'b10;
parameter s11=2'b11;
1
S11
a
a
a
a
S01
always@(posedge clk) begin
if(~res) ss=s00;
else case(ss)
s00: if(a) ss=s01; else ss=s10;
s01: if(a) ss=s11;
s10: if(~a) ss=s11;
s11: ss=s00;
endcase
end
endmodule
S10
a
a
S00
70
(1)verilogのステートマシン記述では,上の例の様に
ステートの値は,parameterを使って,物理的な値を割り当てます。
(2)このステートマシンは,s00が初期状態で,
s00 → s01 → s11 (但し,s01でa=0なら,s01のまま)
↑
│
└─────┘
入力 a=0なら s00 → s10 → s11 (但し ,s10でa=1なら,s10のまま)
↑
│
└─────┘
と回るものです。case文を使えば,上述のようにすっきり書けます。
入力 a=1なら
(3)ステートマシンには、出力値が
ステート値のみに依存するムーア型と
入力値とステート値に依存するミーリー型
がありますが、ここでは深くは述べません。
71
例題8のテストフィクスチャ state.tf
`timescale 1ns/1ns
module t;
reg clk,a,res;
wire [1:0]ss;
state m (.clk(clk),.a(a),.res(res),.ss(ss));
// Enter fixture code here
initial begin
clk=0;
forever begin
#50; clk=~clk;
end
end
initial begin
res=0;a=0;
#100; res=1;
#700; res=0;a=1;
#100; res=1;
end
endmodule // t
72
9.トライステート出力
例題9 triout.v
oe
module triout(a,oe,y);
input a,oe;
a
y
output y;
assign y=oe? a: 1'bZ;
endmodule
(1)トライステート出力、双方向バスの記述は実用的なLSIを
設計する上でさけて通れない。
(2)1ビットのハイインピーダンスは1'bZと書く。
Zだけではローカル信号になるので注意。
8ビットは 8'bZZZZ_ZZZZ または 8'hZZ
(3)トライステート出力は,上の例の様に条件付assign文で記述する。
トライステート出力はoutput宣言する。
(小問題)if文を使って、トライステート出力を記述しなさい。
73
例題9のテストフィクスチャ triout.tf
`timescale 1ns/1ns
module t;
reg a,oe;
wire y;
integer i;
triout m (.a(a),.oe(oe),.y(y));
// Enter fixture code here
initial begin
oe=0; #450;
oe=1; #500;
end
initial begin
a=0;
for(i=0;i<10;i=i+1)
#100 a=~a;
end
endmodule // t
74
10.双方向バスの記述
例題10 bidir.v
module bidir(rd,wr,db);
input rd,wr;
inout db;
wire idb;
reg odb;
assign idb=db;
rd
odb
db
idb
D
Q
wr
always@(posedge wr)
odb<=idb;
assign db=rd? 1'bZ : odb;
endmodule
(1)双方向バスも,上の例の様に条件付assign文で記述する。
双方向バスはinout宣言する。
(2)if文を使って書こうとしても、双方向バスはうまくいかない。
75
例題10のテストフィクスチャ
bidir.tf
`timescale 1ns/1ns
module t;
reg rd,wr;
wire db;
reg rd_data;
bidir m (.rd(rd),.wr(wr),.db(db));
// Enter fixture code here
task wr_task;
input wr_dt;
begin
force db=wr_dt; #50;
wr=0; #50;
wr=1; #50;
release db;
#50;
end
db
wr
50 100
wr_dt
150
50
rd
rd_dt 前の値
100
150
200
rd↑時のdbの値
task rd_task;
output rd_dt;
begin
rd=0; #100;
rd_dt=db;
rd=1; #100;
end
endtask
initial begin
rd=1; wr=1; db=1'bZ;
#100; wr_task(1); rd_task(rd_data);
$display("time=%4d rd_data=%b",
$time,rd_data);
endtask
0
0
200
Z
#100; wr_task(0); rd_task(rd_data);
$display("time=%4d rd_data=%b",
$time,rd_data);
end
76
endmodule // t
(1)タスクはテストフィクスチャで使用されるサブルーチン。
(2)タスクはキーワードtaskで始まり、endtaskで終わる。
(3)キーワードtaskの次に、タスク名を書く。
上の例ではでは wr_task と rd_task がタスク名。
(4)タスク名の次に引数宣言を行う。
wr_taskでは、入力引数wr_dt
rd_taskでは、出力引数rd_dt
が宣言されている。
(5)上の例ではinitial文のメインルーチンから、
wr_taskとrd_taskを2回づつコールして、
データバスから1と0の書込み、読出しの確認をしている。
77
(6)双方向バスdbはテストフィクスチャ内では、ワイヤ宣言する。
(7)双方向バスdbの初期値=Zとする。(Zは代入可能)
(8)双方向バスdbにZ以外の値を代入するときは、force文を使う。
(9)強制代入を解除するときは、release文を使う。
(10)$displayはoutputウインドウに値を表示するための
システムタスクであり、書式はC言語のprintf文と同じ。
$timeは現在時刻を表すシステム関数。
78
Verilog設計演習
Ⅳ
応 用 編
広島県立西部工業技術センター
79
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桁ストップ
ウオッチをVerilog-HDLで記述しなさい。
startスイッチを押すと、カウント開始
stopスイッチを押すと、カウント停止
両方同時に押すと、リセット
(問題2)
startスイッチだけで、スタート、ストップ、リセットができる
仕様に変更しなさい。
LEDi[6]
LEDi[5]
LEDi[4]
LEDi[3]
LEDi[2]
LEDi[1]
LEDi[0]
(i=1~4)
80
①module digit(clk,res,cin,cout,led);
input clk,res,cin;
output cout;
output [6:0]led;
reg [3:0]dgt;
reg [6:0]led;
②always@(
clk) begin
if(
) dgt<=0;
else if(cin && (
)) dgt<=dgt+1;
else if(cin && (dgt==9)) dgt<=0;
end
③assign cout=cin & (
);
④always@(dgt) begin
case(dgt)
0:
led=~7'h3f;
2:
led=
;
4:
led=
;
6:
led=
;
8:
led=
;
default: led=~7’h00;
endcase
end
endmodule
①モジュールdigitはLED一桁に相当す
るサブモジュールで、カウンタ部と7セグ
メントデコーダ部で構成されています。
②カウンタ部はclkをクロック、resを正論
理の同期クリア信号とする4ビットカウン
タです。10進カウンタなので、9の次は
0に戻らなければなりません。
③キャリー入力が有り、かつカウント値
が9の時、キャリー出力が出なければな
りません。
④7セグメントデコーダ部は、カウント値
を数字表示用データに変換する組合せ
回路です。
1:
3:
5:
7:
9:
led=~7'h06;
led=
;
led=
;
led=
;
led=
;
81
⑤module watch1(start,stop,clk,led1,led2,led3,led4);
input start,stop,clk;
⑤モジュールwatch1がメインモジュールで
output [6:0]led1,led2,led3,led4;
reset,count,displayの3つの状態を遷移する
reg [1:0]state;
ステートマシンです。条件は下記の通り。
wire dres,enb1,enb2,enb3,enb4;
startのみでcountへ
⑤parameter reset=2'b00;
parameter count=2'b01;
parameter display=2'b10;
⑤always@(posedge clk) begin
if(start & stop)
state=reset;
else if(
&
) state=count;
else if(
&
) state=display;
end
assign enb1=(
);
assign dres=(state==reset);
⑥digit digit1(clk,dres,enb1,enb2,led1);
⑦digit digit2(clk,dres,
,
,led2);
digit digit3(clk,dres,
,
,led3);
digit digit4(clk,dres,
,
,led4);
endmodule
stopのみでdisplayへ
両方でresetへ
⑥digit1~digit4はそれぞれ
digit1 1/100秒の桁
digit2 1/10秒の桁
digit3
1秒の桁
digit4 10秒の桁
に相当するモジュールインスタンスです。
⑦enb1~enb4はdigit1~digit4のキャリ入力
とキャリ出力を接続するローカル信号です。
digit4 enb4 digit3 enb3 digit2 enb2 digit1 enb1
82
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を全て
入力モードにする負論理の
非同期リセット信号。
83
並列IO pio.v
module pio(cs,rd,wr,adr,res,db,pa,pb,pc);
parameter n=8;
input cs,rd,wr,res;
input [1:0]adr;
inout [n-1:0]db,pa,pb,pc;
reg [n-1:0]qa,qb,qc,cr,odb;
① /***** internal register (reset , cs & wr) *****/
always@(
if(
end
else if(
or
)begin
qa=0; qb=0;
qc=0; cr=8'hFF;
) begin
①qa,qb,qc,crはwrをクロック、
~resを非同期クリア信号とするFFで
csがアクティブの時、dbの値がadrで
選択されたqa,qb,qc,crのいずれか
に書き込まれる。
) case(
)
0:qa=db; 1:qb=db;
2:qc=db; 3:cr=db;
endcase
end
84
/***** port tri-state assign *****/
)?qa:8'hZZ;
② assign pa=(
assign pb=(
)?qb:8'hZZ;
assign pc=(
)?qc:8'hZZ;
②paは~cr[0]を制御信号とする双方向バッファ
pbは~cr[1]を制御信号とする双方向バッファ
pcは~cr[2]を制御信号とする双方向バッファ
/***** data selecter *****/
or
or
or
or
) begin
③ always@(
case(adr)
0:odb=pa; 1:odb=pb; 2:odb=pc; 3:odb=cr;
endcase
end
/***** databus tri-state assign (cs & rd) *****/
&
)? odb:8'hZZ;
④ assign db=(
endmodule
③はpa,pb,pc,crをデータ入力
adrをセレクト信号
odbをデータ出力とする
データセレクタ
④dbは~csと~rdの論理積を
制御信号とする双方向バッ
ファ
85
PIOのテストフィクスチャ pio.tf
`timescale 1ns/1ns
module t;
reg cs,rd,wr,res;
reg [1:0]adr;
wire [7:0]db,pa,pb,pc;
integer i;
reg [7:0]rd_data;
pio m (.cs(cs),.rd(rd),.wr(wr),.adr(adr),.res(res),.db(db),.pa(pa),.pb(pb),.pc(pc));
assign pa=pb; // connect PB to PA
// Enter fixture code here
task wr_task;
input [1:0]adr_dt; input [7:0]wr_dt;
begin
cs=0; adr=adr_dt; force db=wr_dt; #50;
wr=0; #100;
wr=1; #50;
cs=1; adr=2'b11; release db;
#50;
end
endtask
86
task rd_task;
input [1:0]adr_dt; output [7:0]rd_dt;
begin
cs=0; adr=adr_dt; #50;
rd=0; #100;
rd_dt=db;
rd=1; #50;
cs=1; adr=2'b11; #50;
end
endtask
initial begin
cs=1; rd=1; wr=1; adr=2'b11; db=8'bZZ; res=0; #50;
res=1; #50;
wr_task(3,1);
//write CW to CR (PA=in PB,PC=out)
for(i=0;i<=255;i=i+1) begin
wr_task(1,i);
// write to PB
rd_task(0,rd_data); // read from PA
$display("i=%x rd_data=%x",i,rd_data);
end
#1000; $finish;
end
endmodule // t
87
88
①module digit(clk,res,cin,cout,led);
input clk,res,cin;
output cout;
output [6:0]led;
reg [3:0]dgt;
reg [6:0]led;
②always@(posedge clk) begin
if(res) dgt<=0;
else if(cin && (dgt!=9)) dgt<=dgt+1;
else if(cin && (dgt==9)) dgt<=0;
end
③assign cout=cin & (dgt==9);
④always@(dgt) begin
case(dgt)
0:
led=~7'h3f;
2:
led= ~7’h5b;
4:
led= ~7’h66;
6:
led=~7’h7d;
8:
led=~7’h7f;
default: led=~7’h00;
endcase
end
endmodule
解答例
watch1.v
1:
3:
5:
7:
9:
led=~7'h06;
led=~7’h4f;
led=~7’h6d;
led=~7’h27;
led=~7’h6f;
89
⑤module watch1(start,stop,clk,led1,led2,led3,led4);
input start,stop,clk;
output [6:0]led1,led2,led3,led4;
reg [1:0]state;
wire dres,enb1,enb2,enb3,enb4;
⑤parameter reset=2'b00;
parameter count=2'b01;
parameter display=2'b10;
⑤always@(posedge clk) begin
if(start & stop)
state=reset;
else if(start & ~stop) state=count;
else if(~start & stop) state=display;
end
assign enb1=(state==count);
assign dres=(state==reset);
⑥digit digit1(clk,dres,enb1,enb2,led1);
⑦digit digit2(clk,dres,enb2,enb3,led2);
digit digit3(clk,dres,enb3,enb4,led3);
digit digit4(clk,dres,enb4,
,led4);
endmodule
90
解答例
pio.v
module pio(cs,rd,wr,adr,res,db,pa,pb,pc);
parameter n=8;
input cs,rd,wr,res;
input [1:0]adr;
inout [n-1:0]db,pa,pb,pc;
reg [n-1:0]qa,qb,qc,cr,odb;
/***** internal register (reset , cs & wr) *****/
always@(posedge wr or negedge res) begin
if(~res)begin
qa=0; qb=0;
qc=0; cr=8'hFF;
end
else if(~cs) case(adr)
0:qa=db; 1:qb=db;
2:qc=db; 3:cr=db;
endcase
end
91
/***** port tri-state assign *****/
assign pa=(~cr[0])?qa:8'hZZ;
assign pb=(~cr[1])?qb:8'hZZ;
assign pc=(~cr[2])?qc:8'hZZ;
/***** data selecter *****/
always@(adr or pa or pb or pc or cr) begin
case(adr)
0:odb=pa; 1:odb=pb; 2:odb=pc; 3:odb=cr;
endcase
end
/***** databus tri-state assign (cs & rd) *****/
assign db=(~cs & ~rd)? odb:8'hZZ;
endmodule
92