Verilog設計演習 Ⅰ 入 門 編 広島県立西部工業技術センター Verilog設計演習 Verilog設計演習 Ⅰ 入 門 編 広島県立西部工業技術センター 広島県立西部工業技術センター
1.作業ディレクトリの作成 まず、エクスプローラを起動し、 ルートディレクトリの下にseminarというディレクトリを、 その下にVerilogというディレクトリを作成して下さい。 (DOS/Vの場合) C:¥seminar¥Verilog (98の場合) A:¥seminar¥Verilog 以下、Verilog演習で作成するファイルはVerilogの下に作ります。 それ以外の、ディレクトリには何も作らないで下さい。
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
(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)わかりやすい様に( )を使ってよい。
クイックロジック社QuickWorks 配置配線ツールSpDE 兼 統合化環境 回路図入力 SYNARIO 論理合成 Synplify 兼 統合化環境 回路図入力 SYNARIO 論理合成 Synplify Verilogシミュレータ SILOSⅢ Verilog学習 VHDL学習 HDLエディタ ターボライタ
3.SpDEの起動 スタート - プログラム - QuickLogic ー SpDEで SpDEを起動します。
4.HDLエディタの起動 SpDEのツールバーから、HDLエディタのアイコン を 押して、HDLエディタを起動し、クリエイトアイコン を押す。
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を指定し、論理合成ツールを 起動します。
7.ターゲットデバイスの指定と論理合成 RUNボタンのすぐ上のChangeボタンを押して、 Partをp8x12b、Packageをpl44に変更し、OKを押します。 次に、RUNボタンを押して、論理合成をかけます。
8.エラーの修正 ソースファイルにエラーがあると、下の画面で停止します。 ViewLogボタンでエラー内容を確認した後、Editボタンを 押して、HDLエディタを再度起動し、エラー箇所を修正します。
9.論理合成プロジェクトの保存 エラーがなければDoneが表示されて、下の画面で停止します。 「はい」を選んで論理合成プロジェクトを保存し、SpDEに戻ります。
10.配置配線の実行 SpDEのツールバーからRunToolsアイコン を押して、 配置配線を実行します。途中、RUNと「いいえ」を選択します。
11.配置配線結果の確認 SpDEツールバーからFullFitアイコン を選択し、 チップ全体を表示させます。
次に、View-NormalFitメニューを選択し、Zカーソルを 回路のある部分でクリックして、回路部分を拡大表示します。 さらに拡大したければZoomInアイコン を使います。
回路部分のみを拡大すると、下図のようになります。 台形印のセレクタはr=s・p+s・qを表しますので、全体として a・b=1またはc・d=1の時 f=0 それ以外の時 f=1 となり、最初のVerilogコードを満す回路が生成されていること が分かります。 s f p b r a q 入力s 出力r 0 p 1 q ※論理合成結果を確認したら、 File-Saveメニューで結果を 保存します。 d c
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; endmodule A B C D F AB CD O
(1)やや複雑な機能を記述する時は,wireで宣言した ローカル信号を使うこともできる。 ab,cd,o (2)1ビット幅のwireは宣言なしでも使える。暗黙宣言。 後出の複数ビット幅のwireは宣言が必要。 (3)//は1行のみのコメント行。 /*・・・・・*/は複数行にわたるコメント行である。 修正が終わったらソースコードを保存し、論理合成をかけて 配置配線を実行して下さい。結果は同じになります。
問題1 インバータLS04.vを設計し、結果を確認しなさい。 問題2 NANDゲートLS00.vを設計し、結果を確認しなさい。 問題3 NORゲートLS02.vを設計し、結果を確認しなさい。
13.階層設計 例題2 マルチプレクサ mux2.v `include "aoi.v" module inv(a,f); input a; output f; assign f=~a; endmodule module mux2(sel,a,b,f); input sel,a,b; inv g1 (sel,selb); aoi g2 (sel,a,selb,b,fb); inv g3 (.a(fb),.f(f)); g1 g2 g3 SELB FB SEL A B AOI F
(1)もっと複雑な回路記述には階層設計を使う。 (2) 階層設計は,複数のモジュール宣言と モジュールインスタンスで行う。 (3) 複数モジュールの宣言は, invの様に,同一ファイル内に書いても良いし, aoiの様に,別ファイルに書いて,`includeしても良い。 (4) モジュールインスタンスは,サブルーチンコールの様なもので, モジュール名 インスタンス名(ポートリスト); の形式で行う。上の例では,g1,g2,g3がインスタンス名。
(5) モジュールインスタンスのポートリストは上位モジュールと 下位モジュールを接続するもので, g1,g2の様に,並びによる接続が一般的。 g3の様に,名前による接続も使える。 (6) 出力信号を接続しない時はカンマを余分に書く。 (7) 上の例ではselbとfbは,暗黙宣言されたwireである。 ※ `はバック・シングル・クォートで DOSVでは shift + @ 98 では shift + ^ shift + 7 ではないことに注意。
例題2を入力し、論理合成、配置配線を実行します。 結果は、論理圧縮の効果で例題1より簡単になり、 下図のようになります。 sel=1なら f=a sel=0なら f=b となっています。 b a sel f
14.条件付きassign文 例題3 セレクタ mux21.v module mux21(sel,a,b,f); input sel,a,b; output f; assign f=sel ? a:b; endmodule mux21 sel a f b (1) 例題2(mux2.v)では階層設計を説明するため,複雑な書き方を したが,セレクタ自体はもっと簡単に記述できる。 (2)assign文にはC言語の3項演算子(条件演算子ともいう)に似た 条件付きassign文があり, assign 左辺=(条件式1) ? (右辺1) : (右辺2); が使える。 (3)上の例では,sel=1ならf=a,sel=0ならf=bになる。
15.always文とif文 例題3 セレクタ mux21.v module mux21(sel,a,b,f); input sel,a,b; output f; reg f; always@(sel or a or b) begin if(sel==1'b1) f=a; else f=b; end endmodule mux21 sel a f b (1)3項演算子よりも,わかりやすいif文もあるが, module-endmodule間に,ダイレクトには記述できない。 上の例のように,alwaysブロックの中で記述する。
(2)always文は always@(信号名) begin : end ←セミコロンがないのに注意 の形で記述し,( )内の信号名の値が変化したときのみ評価される。 つまり,( )内にはalwaysブロックとしての入力信号を記述する。 正しくはセンシティビティ・リストという。 複数の入力信号が有る時は,カンマではなく orで区切って記述する。 (3)alwaysブロックで組合せ回路を生成する場合,入力信号を 全てセンシティビティ・リストに記述しなければならない。 (順序回路の場合はそうとは限らない)
(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です。
(7)最も重要なのは,alwaysブロック内での ・信号代入には,assign文は使わないこと。 ・代入文の左辺にくる信号は,レジスタ宣言 しなければならないこと。 である。上の例では f がこれにあたる。 (8)上の例からわかる様に,生成される回路が組合せ回路で あってもレジスタ宣言が必要である。逆に言うと,レジスタ宣言 してもフリップフロップが必ず生成されるわけではない。 (9)if文で全ての条件が列挙されていれば,つまり, ifの数だけelseがあれば,組合せ回路が, そうでなければ,順序回路が生成される。 例題3を入力し、論理合成、配置配線を実行します。 結果は例題2と同じになります。
Verilog設計演習 Ⅱ シミュレーション編 広島県立西部工業技術センター
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 (7) endmodule // t
(1)timescale文はシミュレーションの時間単位を定めるもので、 /の前がこのモジュール内での時間記述#の時間単位、 /の後ろがシミュレーション時に使用される時間精度 になります。この例ではいずれも1nsになります。 (2)テストフィクスチャのモジュールには、ポートリストが有りません。 モジュール名は何でもかまいませんが、ここでは t です。 (3)元の設計データの入力信号は、このモジュール内では値を代入 するので、レジスタ宣言します。 (4)出力信号は観測するだけなので、ワイヤ宣言します。 (5)元の設計データをモジュールインスタンスとして、呼び出します。 信号の接続には、名前による接続が行われていますが、 並びによる接続でも構いません。 (6)この位置に、実際のテストパターン用コードを追加します。
`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; #100 sel=0; a=1; b=0; #100 sel=0; a=1; b=1; #100 sel=1; a=0; b=0; #100 sel=1; a=0; b=1; #100 sel=1; a=1; b=0; #100 sel=1; a=1; b=1; end endmodule // t それでは、6行目の位置に 右の四角の部分を追加して テスト・フィクスチャを完成さ せてから、mux21.tfとして 保存して下さい。 この例で分かるように、テスト フィクスチャでの信号値の代入 には、initial文を使用します。 意味としては、 sel,a,bの初期値として 全て0を代入した後、100ns 毎に異なる値を代入しています。
2.機能シミュレーション(Pre-Layout) (1)SpDEのツールバーから、シミュレータのアイコン を 押して、SILOS3を起動します。 シミュレーションタイプでPre-Layoutを選択します。 テストフィクスチャがmux21.tf、 トップレベルモジュールがmux21.vになっていることを 確認後、OKを押して下さい。
(2)mux21.vは論理合成のチェックを通っているので、 エラーがあるとすればmux21.tfの方です。 エラーがあるとoutputウインドウにエラーメッセージが表示され、 下の画面で停止するので、キャンセルを押します。 outputウインドウにエラーがなければ、(4)に進みます。
(3)エラーメッセージを確認後、SILOS3のFileーOpenメニュー からmux21.tfを選択し、エラー箇所を修正します。 修正が終わったら、SILOS3のFile-Saveで保存し、 mux21.tfウインドウのアイコン化ボタン でアイコン化します。 Load/Reloadアイコン を押して、修正結果をSILOS3に 反映させます。 outputウインドウにエラーが無ければ、GOアイコン を 押して(4)に進みます。 outputウインドウにエラーが有れば、エラーメッセージを確認後 アイコン化していたmux21.tfを通常の大きさに戻し、 修正を繰り返します。
(4)テストフィクスチャにエラーが無ければ、下の画面で停止します ので、シミュレーション時間1000nsを入力し、OKを押します。
(5)エラーが無ければ、outputウインドウに 32 State changes on observable nets. Simulation stopped at the end of time 1.000us.が表示されるので データアナライザのアイコン を押して、波形表示ウインドウ を開きます。
次に、モジュールエクスプローラのアイコン を押して、 t:tのa,b,f,selを選択後、マウス右クリックから AddSignaltoAnalyzerを選ぶと、波形が表示されます。
データアナライザ・ウインドウをアクティブにしてから、 View-ZoomAllを選ぶと、波形全体が表示されます。 sel=0の時f=b、 sel=1の時f=aを確認します。
(1)データアナライザ・ウインドウの時間軸上で、マウス右クリックし、 timescaleを選んで、切りの良い値を入れれば、時間軸を見やすい 値に変更できます。 (2)波形上でマウスを左クリックすると、青色の第1カーソル、 右クリックすると、赤色の第2カーソルを置くことができます。 各カーソルの時間および時間差がT1,T2,Tdeltaで表示されます。 (3)信号名を選択後、カーソルスキャン・アイコン を 押すと、カーソルが選択信号の変化点に移動します。 この機能を使って、入力信号a,b,selと出力信号fの時間差を 測定してみると、Tdelta=0になります。 これは、現在表示している結果が、遅延時間情報の入っていない 機能シミュレーション(Pre Layout)であるためです。 結果を確認したら、SILO3のFile-Exitを選び、途中、「はい」を 選んで終了します。
3.遅延シミュレーション(Post-Layout) 再びSpDEのツールバーから、シミュレータのアイコン を 押して、SILOS3を起動します。 今度はシミュレーションタイプでPost-Layoutを選択します。 テストフィクスチャがmux21.tf、 SDFがmux21.sdf、 トップレベルモジュールがmux21.vq になっていることを 確認後、OKを押して下さい。
1000nsを確認後、OKを押します。
outputウインドウに92 State changes on observable nets. Simulation stopped at the end of time 1000.000ns.が表示され、 モジュールエクスプローラ、データアナライザウインドウも表示 されるので、ズームオール で波形全体を表示させます。
(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を選び、終了します。 興味があれば、*.sdf、*.vqをエディタで開いて見て下さい。
遅延シミュレーションの結果
問題4 インバータLS04.vのテストパターンを設計し、 機能シミュレーションと遅延シミュレーションを 実行しなさい。 問題5 NANDゲートLS00.vのテストパターンを設計し、 問題6 NORゲートLS02.vのテストパターンを設計し、
4.ピン配置の指定 ピン配置は配置配線ツールが遅延時間や配線効率を考慮して 最適に近いものに自動配置するので、これを使うのが無難です。 しかし、設計の最終段階に近く、プリント基板の変更ができない 場合などは、次の方法でピン配置を直接指定することも可能です。 SpDEのTools-Optionsメニューから、BackAnnotationタブ を選択し、FixPlacementのIOcellsをチェックonにします。
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 ql_placement="IO2"; portprop a ql_placement="IO3"; portprop sel ql_placement="IO4"; portprop b ql_placement="IO5";
論理合成Synplifyを立ち上げて、Addボタンを押します。 ファイルの種類をPropertyFiles(*.sc*)にして、 mux21.scpを選択し、「開く」を押します。
SourceFilesにmux21.scpが追加されたことを確認後、 RUNを押して論理合成をかけます。 途中「はい」とOKを選択すると、mux21.scpで指定した ピン配置での配置配線が実行されます。
Verilog設計演習 Ⅲ 基 礎 編 広島県立西部工業技術センター
1.ビット幅のある信号の表現 例題1 コンパレータ comp.v module comp(a,b,eq,ge,le); parameter n=4; input [n-1:0] a,b; output eq,ge,le; 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 4 comp ge a eq 4 le b
(1)今までは1ビット幅の信号だけ扱ってきたが, ビット幅のある信号も扱える。input,output宣言時に [MSB:LSB] 信号名,信号名; とすれば良い。 同じビット幅,ビットオーダーの信号は1行で宣言できる。 [15:0] [8:1] [1:16] [0:7] のいずれも使える。MSB,LSBの値の大小にかかわらず, 左端がMSB,右端がLSBである。 (2)ビット幅を変更しやすくするためにparameterを使うことができる。
例題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); endmodule 4 comp ge a eq 4 le b (3)上の例の様に,コンパレータ等はif文よりもassign文の方が コンパクトに記述できる。
(1)initial文の中では integer宣言した i 、jを用いた forループが使えます。 i++ではないことに注意。 例題1のテストフィクスチャ comp.tf `timescale 1ns/1ns module t; parameter n=4; reg [n-1:0] a,b; wire eq,ge,le; integer i,j; comp m (.a(a),.b(b),.eq(eq),.ge(ge),.le(le)); // Enter fixture code here 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 (1)initial文の中では integer宣言した i 、jを用いた forループが使えます。 i++ではないことに注意。
2.case文とビット幅のある定数 例題2 デコーダ decoder.v decode enb 8 y 3 adr 例題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 adr enb y 3 8
(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)も許される。 (4)else y=8'b11111111; が無いとラッチが生成されるので注意。
例題2のテストフィクスチャ decoder.tf initial begin enb=0; for(i=0;i<8;i=i+1) begin adr=i; #100; end enb=1; endmodule // t 例題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
3.算術演算子と連接演算子 例題3 アダー adder.v 4 adder 4 a s 4 b cin cout 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 4 adder 4 a s 4 b cin cout (1)加算器,減算器,乗算器を生成するのに算術演算子+、-、*が使える。 (2)加算結果には,キャリ出力がつきものであるが,連接演算子{ , }を使って {cout,s}=a+b+cin; とわかりやすく記述できる。
例題3のテストフィクスチャ adder.tf 例題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; endmodule // t このテストフィクスチャでは連接演算子を使って、二重ループを一重ループで済ませています。
4.フリップフロップの記述 例題4 フリップフロップ dff.v d clk q d clk qsc sclr d clk qac aclr 例題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 // 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 // if(enb) qen <= d; endmodule d clk q 通常 FF d clk qsc sclr 同期 クリア FF d clk qac aclr 非同期 クリア FF d clk qen enb イネーブル 機能付 FF
(1)フリップフロップの生成には always@(posedge clk) を使う。clk信号の立ち上がりエッジでalways文が実行される。 立ち下がり動作のFFにはnegedgeを使います。 (2)同期クリア信号は,always@(信号名)の センシティビティ・リストに記述しない。 (3)非同期クリア信号は,センシティビティ・リストに記述する。 aclrが正論理なら、posedgeを使います。 (4)イネーブル信号は同期クリア信号と同じ。 (5)この例で用いている<=はノンブロッキング代入文と呼ばれ, 順序回路では,これを使った方がよい。 =はブロッキング代入文と呼ばれ組合せ回路用。 但し,上の例では全て=にしても同じ回路が生成される。
例題4のテストフィクスチャ dff.tf ※クロック生成の無限ループにはforever を使うことができる。 module t; 例題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 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; end endmodule // t ※クロック生成の無限ループにはforever を使うことができる。
5.ブロッキング代入文とノンブロッキング代入文 例題5 エッジ検出 edg.v 例題5 エッジ検出 edg.v module edg(clk,d,reset,rise,fall); input clk,d,reset; output rise,fall; reg q1,q2; always@(posedge clk) begin if(!reset) begin q1=0; end else begin q1=d; end end if(!reset) begin q2=0; end else begin q2=q1; end assign rise= q1 & !q2; assign fall=!q1 & q2; endmodule clk d q1 q2 fall rise
(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)ブロッキング=は、一つの代入処理が終了するまで次の処理が 行われない。記述の順番に動作が影響される。組合せ回路向き。
例題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 initial begin reset=0; d=0; #75 reset=1; #200 d=1; #300 d=0; #400 d=1; #400 d=0; end endmodule // t
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 4 count clk clr (1)同期クリア付カウンタは,同期クリアFFのクリア信号の書き方と, 算術演算子+の応用であり,上の例の様にかける。 順序回路なので代入には、ノンブロッキングを使った方が良い。 (小テスト)これを10進カウンタにするには、どうすればよいか。
例題6のテストフィクスチャ countsc.tf 例題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 initial begin clr=1; #125; clr=0; #200; clr=1; end endmodule // t
countac 7.非同期クリア付きカウンタ 例題7 countac.v module countac(clk,clr,count); input clk,clr; output [3:0]count; reg [3:0]count; always@(posedge clk or negedge clr) begin if(!clr) count <= 0; else count <=count+1; end endmodule 4 count clk clr (1)非同期クリア付カウンタは,非同期クリアFFのクリア信号の 書き方と,算術演算子+の応用であり,上の例の様にかける。 順序回路なので代入には、ノンブロッキングを使った方が良い。 (小テスト)これにキャリー入出力を付けるには、どうすればよいか。
例題7のテストフィクスチャ countac.tf 例題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 initial begin clr=1; #125; clr=0; #200; clr=1; end endmodule // t
8.ステートマシンの記述 例題8 state.v 1 S11 a a a a S01 S10 a a S00 例題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; 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 1 S11 a a a a S01 S10 a a S00
(1)verilogのステートマシン記述では,上の例の様に ステートの値は,parameterを使って,物理的な値を割り当てます。 (2)このステートマシンは,s00が初期状態で, 入力 a=1なら s00 → s01 → s11 (但し,s01でa=0なら,s01のまま) ↑ │ └─────┘ 入力 a=0なら s00 → s10 → s11 (但し ,s10でa=1なら,s10のまま) と回るものです。case文を使えば,上述のようにすっきり書けます。 (3)ステートマシンには、出力値が ステート値のみに依存するムーア型と 入力値とステート値に依存するミーリー型 がありますが、ここでは深くは述べません。
例題8のテストフィクスチャ state.tf 例題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 initial begin res=0;a=0; #100; res=1; #700; res=0;a=1; end endmodule // t
9.トライステート出力 例題9 triout.v module triout(a,oe,y); input a,oe; output y; assign y=oe? a: 1'bZ; endmodule oe a y (1)トライステート出力、双方向バスの記述は実用的なLSIを 設計する上でさけて通れない。 (2)1ビットのハイインピーダンスは1'bZと書く。 Zだけではローカル信号になるので注意。 8ビットは 8'bZZZZ_ZZZZ または 8'hZZ (3)トライステート出力は,上の例の様に条件付assign文で記述する。 トライステート出力はoutput宣言する。 (小問題)if文を使って、トライステート出力を記述しなさい。
例題9のテストフィクスチャ triout.tf 例題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
(1)双方向バスも,上の例の様に条件付assign文で記述する。 双方向バスはinout宣言する。 10.双方向バスの記述 例題10 bidir.v module bidir(rd,wr,db); input rd,wr; inout db; wire idb; reg odb; assign idb=db; always@(posedge wr) odb<=idb; assign db=rd? 1'bZ : odb; endmodule rd wr db idb odb D Q (1)双方向バスも,上の例の様に条件付assign文で記述する。 双方向バスはinout宣言する。 (2)if文を使って書こうとしても、双方向バスはうまくいかない。
例題10のテストフィクスチャ bidir.tf 0 50 100 150 200 rd rd_dt 前の値 rd↑時のdbの値 例題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 endtask 0 50 100 150 200 rd rd_dt 前の値 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); #100; wr_task(0); rd_task(rd_data); end endmodule // t 0 50 100 150 200 db wr_dt Z wr
(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の書込み、読出しの確認をしている。
(6)双方向バスdbはテストフィクスチャ内では、ワイヤ宣言する。 (7)双方向バスdbの初期値=Zとする。(Zは代入可能) (8)双方向バスdbにZ以外の値を代入するときは、force文を使う。 (9)強制代入を解除するときは、release文を使う。 (10)$displayはoutputウインドウに値を表示するための システムタスクであり、書式はC言語のprintf文と同じ。 $timeは現在時刻を表すシステム関数。
Verilog設計演習 Ⅳ 応 用 編 広島県立西部工業技術センター
次の回路図のハードウエアを用意しています。 1.ストップウオッチの設計 次の回路図のハードウエアを用意しています。 (問題1) 100Hzの方形波をカウントして、下記の仕様の4桁ストップ ウオッチをVerilog-HDLで記述しなさい。 startスイッチを押すと、カウント開始 stopスイッチを押すと、カウント停止 両方同時に押すと、リセット (問題2) startスイッチだけで、スタート、ストップ、リセットができる 仕様に変更しなさい。
①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; 1: led=~7'h06; 2: led= ; 3: led= ; 4: led= ; 5: led= ; 6: led= ; 7: led= ; 8: led= ; 9: led= ; default: led=~7’h00; endcase end endmodule ①モジュールdigitはLED一桁に相当す るサブモジュールで、カウンタ部と7セグ メントデコーダ部で構成されています。 ②カウンタ部はclkをクロック、resを正論 理の同期クリア信号とする4ビットカウン タです。10進カウンタなので、9の次は 0に戻らなければなりません。 ③キャリー入力が有り、かつカウント値が9の時、キャリー出力が出なければなりません。 ④7セグメントデコーダ部は、カウント値を数字表示用データに変換する組合せ回路です。
⑤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( & ) 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 ⑤モジュールwatch1がメインモジュールで reset,count,displayの3つの状態を遷移する ステートマシンです。条件は下記の通り。 startのみでcountへ 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
2.並列IOの設計 下記仕様の8ビット×3ポート入出力LSI (インテル8255の簡易版)を設計しなさい。 cs rd wr adr 動 作 下記仕様の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 ADR PA CS PB RD WR PC DB CR RES 8 2 8 8 8 RESはPA,PB,PCを全て 入力モードにする負論理の 非同期リセット信号。
① 並列IO pio.v module pio(cs,rd,wr,adr,res,db,pa,pb,pc); parameter n=8; 並列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@( or ) begin if( )begin qa=0; qb=0; qc=0; cr=8'hFF; end else if( ) case( ) 0:qa=db; 1:qb=db; 2:qc=db; 3:cr=db; endcase ① ①qa,qb,qc,crはwrをクロック、 ~resを非同期クリア信号とするFFで csがアクティブの時、dbの値がadrで 選択されたqa,qb,qc,crのいずれか に書き込まれる。
② ③ ④ /***** port tri-state assign *****/ assign pa=( )?qa:8'hZZ; assign pb=( )?qb:8'hZZ; assign pc=( )?qc:8'hZZ; /***** data selecter *****/ always@( or or or or ) 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=( & )? odb:8'hZZ; endmodule ② ②paは~cr[0]を制御信号とする双方向バッファ pbは~cr[1]を制御信号とする双方向バッファ pcは~cr[2]を制御信号とする双方向バッファ ③ ③はpa,pb,pc,crをデータ入力 adrをセレクト信号 odbをデータ出力とする データセレクタ ④ ④dbは~csと~rdの論理積を 制御信号とする双方向バッファ
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
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); #1000; $finish; endmodule // t
解答例 watch1.v ①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; 1: led=~7'h06; 2: led= ~7’h5b; 3: led=~7’h4f; 4: led= ~7’h66; 5: led=~7’h6d; 6: led=~7’h7d; 7: led=~7’h27; 8: led=~7’h7f; 9: led=~7’h6f; default: led=~7’h00; endcase end endmodule 解答例 watch1.v
⑤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
解答例 pio.v module pio(cs,rd,wr,adr,res,db,pa,pb,pc); parameter n=8; 解答例 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
/***** 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