Verilog:FPGAおよびVerilog HDLを使用したデジタルエレクトロニクス設計に関する簡単なチュートリアルシリーズ:21ステップ

Verilog:FPGAおよびVerilog HDLを使用したデジタルエレクトロニクス設計に関する簡単なチュートリアルシリーズ:21ステップ

目次:

Anonim

Verilogでのこの短い一連のセミショートレッスンは、言語の紹介として、そして読者がFPGAデザインをさらに検討するよう奨励することを意図しています。

これらのチュートリアルでは、基本的な論理回路や論理式、さらに関連する動作に精通していることを前提としています。

F =(A•B)で、Aが論理ハイ、Bが論理ローの場合、Fは何ですか?あなたがこの質問に答えることができないならば、私はあなたがあなたの論理回路研究を再訪問することを勧めます。十分な要求がなされるならば、論理回路チュートリアルは可能性です。

始める前に知っておくべき基本事項のチェックリスト:

- 基本論理ゲートの機能(AND、OR、NAND、XOR、…)

- 「論理ハイ」と「論理ロー」の違い、およびどちらが「1」で表され、どちらが「0」で表されるか

C / C ++に精通していることは(時には)役に立ちますが、まったく必要ではありません。

このチュートリアルに従う準備ができているなら、2つのことが必要になります。

- Verilog用のIDE(ザイリンクスISEデザインスイートを強くお勧めします。無料で登録した後は、この非常に堅牢なIDEをダウンロードできます)

- FPGAデジタルデザインボード(使いやすさと「破綻しにくい」デザインのためにDigilent Inc.のボードを好む)

ISEに関する豊富なドキュメントが公開されているので、ソフトウェアの使用方法については説明しません。

この概要の図は、テキストエディタ、モジュールディレクトリ、およびプロセスペインが開いた状態の典型的なISEウィンドウです。

ああ、そしていくつかのスライドでVM x.xで始まるタイトルを見るでしょう。これはVerilogモジュールの略で、追跡するのが少し簡単になります。

Verilogへ!

用品:

ステップ1:VM 1.0:Verilogの背景

とにかく、Verilogとは何ですか? 簡単に言えば、ハードウェア記述言語です。

しかし、いかなる文脈もそれ以上の定義もなければ、それがすべてVerilogがあなたにとって意味のあることです。ハードウェア記述言語(電気工学の文脈で)は、回路の動作を正式に記述するために使用されます。それほど創造的ではない「ハードウェア記述言語」というタイトル(これは多くの場合HDLと呼ばれることが多い)を考えれば、これは驚くべきことではありません。いくつかのHDLが存在しますが、おそらく遭遇する2つの最も一般的な言語はVerilogとVHDLです(あなたはVHDLに "Verilog HDL"を短くすることに誘惑されるかもしれません、それで単にそれをVerilogと呼んでください!)。

私たちはVerilogのようなHDLを多くのことに使うことができます。しかし、最も重要な用途は、特定の回路を明確に記述することです。これには、その入力、出力、および動作の説明が含まれます。 FPGA(Field Programmable Gate Array)ボードと組み合わせて使用​​すると、Verilogの記述を変更するだけで無限の数の回路を作成できます。強力なプロトタイプ作成ツール。物理的なデバイスなしでVerilogを使用して回路をシミュレートし、HDLで「テストベンチ」を実行して実際に構築した場合の回路の動作を確認することもできます(つまり、回路は望みどおりに動作しましたか?)。

写真:ステートマシンを記述するVerilogの一部。この短い入門書は、その複雑なコードの近くのどこにも来ないでしょう!

ステップ2:FPGAの概要とUCFファイル

Digilent Basys 2などのFPGAボードが登場する前は、デジタル回路のプロトタイプを作成することは、回路の個々のコンポーネント(ゲート、マルチプレクサなどの構造ブロックなど)を購入し、すべての部品をブレッドボードで手で接続することでした。そしてジャンパー線。 FPGAチップであるVerilogを使用し、ボード上に組み込まれた刺激(例:LED、スイッチ、ボタン)を使用すると、簡単に回路を作成して結果を比較的短時間で確認できます。これはどのように作動しますか?

FPGAチップはあなたのデジタル回路のための空白の状態として機能します。ザイリンクスISE Design SuiteなどのIDE(統合開発環境)は、「ハイレベル」のVerilogをFPGAに実装するものを指示するビットストリームに変換することができます。したがって、マルチプレクサ(マルチプレクサとも呼ばれる)、スイッチ、およびLEDを購入する代わりに、入力と出力を持つマルチプレクサをVerilogで記述し、さらにVerilogでデザインボード上の入力がどこにあるかをFPGAに通知できます。どこから来たのか、どこへ出力したいのか。フル回路の作成は、Verilogモジュラーコードとユーザー制約ファイル(UCF)の2つの部分で行います。

ご想像のとおり、Verilogコード(「モジュール」と呼ばれるチャンクで作成します)は回路で何が起こるべきかを記述し、ユーザー制約ファイルはFPGAチップにその各ピンが回路で何をするのかを伝えます。 FPGAチップには、入力と出力として機能し、使用する回路を明確にするために必要な回路を明確に記述するという私たちの探求を完成させるための多数のピンがあります。たとえば、デザインボードのG12ピンが物理的にLEDに接続されており、Verilogコードでは最終的にそのLEDを駆動する回路があるとします。 VerilogでそのLEDを「LED」という非創造的な名前で参照している場合は、(ビットストリームが生成される前に)UCFファイルを使用してFPGAに指示する必要があります。ピンG12に。」これによりエンジニアは、低レベルのコードを生成することができながらも、回路内で見つかったコンポーネントに「わかりやすい」意味のある名前を付けることができます。もちろん、これはそのステートメントをUCFに入れる方法ではなく、これがUCFで指定できる唯一のものでもありませんが、別のモジュールのUCFに飛び込むことにします。

UCFに関する最後の注意事項:デジタルデザインボードの製造元によっては、コンポーネントごとに異なるピン位置が使用されます。各モデルはユニークです。通常、ボード製造元からすべての可能性のある刺激を含む「マスター」UCFファイルを入手できます。適用しない制約をコメントアウトし、必要に応じて名前を変更することで、このファイルをプロジェクトで再利用できます。初心者は、名前以外のUCFの他の属性を変更しないでください。高度な知識がなくてもこれを行うと、ボードの損傷やその他の「おもしろい」そして望ましくない結果を招く可能性があります。

ウィキペディアからここに描かれているのは、ザイリンクスによって製造されたFPGAチップです。このチップはあなたがVerilogを使って記述した回路を実装するでしょう。

ステップ3:VM 2.0:あなたの最初のVerilogプロジェクト

注:私は「コード」ではなく「プロジェクト」と言いました。プログラミング言語に似たものを使用してFPGAデザインを開発するという事実にもかかわらず、結果は ではない プログラム;あなたの努力の全てが実際の回路を生み出します。

あなたがVerilogに辛うじてさらされていなくても、私はあなたがHDLプールに飛び込むことによってあなたが最もよく学ぶのを感じると思います。しかし心配しないでください、私たちはすべてのコードを一緒に見て行きます、そして私はなぜそしてどのように物事が働くのかを説明することを確実にするでしょう。私はVerilogが非常に早くそれ自身の上に構築される難しい方法を学びました、それでそれはあなたが理解のしっかりした基礎を得ることを望みます。

注:コードをコピーして貼り付けると便利ですが、理解しやすいようにすべてを再入力することを強くお勧めします。それを試してみてください;できます。

始める前に、私のコードの次の特徴に特に注意を払ってください。

•大文字と小文字の区別:Verilogでは大文字と小文字が区別されます。キーワードは適切な大文字小文字(小文字)で入力しなければならず、input、output、または他のコードと名付けられたユーザは、最初に宣言されたときと同じように参照されなければなりません。

•インデント:この例ではそれほど明白ではありませんが、Verilogではインデントが重要です(他の多くのプログラミング言語と同様)。コンパイラとソフトウェアスイートはフォーマットが不適切なコードでも動作しますが、インデント規則を守らないのは適切ではありません(これについては後で説明します)。

それで、さらに面倒なことなしに、私の意見では、Verilogに相当する "Hello、World"は次のようになります。

----------------------------------------------------

`タイムスケール1ns / ps

//これはVerilogのコメントです

モジュールhello_world(

入力スイッチ0

入力スイッチ1

導かれた出力);

led = switch 0&〜switch 1を割り当てます。

エンドモジュール

------------------------------------------------------

今、これはモジュールの最も効率的な実装ではありません。コードを少しずつ見ていき、それを改善する方法を見つけられるかどうかを確認しましょう。

ステップ4:「タイムスケール」

`タイムスケール1ns / ps

このコードは、「参照時間」(この場合は1ns)と「精度時間」(この場合は1ps)を設定するコンパイラ指令です。デザインボードに実際に実装する回路では、FPGAは特定のゲート遅延やその他の時間操作を実装できないため、タイムスケールはそれほど重要ではありません。しかし、シミュレーションではこの行は非常に重要です。

後で、ゲート遅延をシミュレートしてグリッチを見つけたり、システムが条件を変更する前に一定時間「待機」させることができることに気付くでしょう。 「参照時間」は、デフォルトの単位として考えることができます。「#5」の時間遅延(これについては後のモジュールで説明します)をコーディングすると、コンパイラーはデフォルトの5ナノ秒になります。シミュレーション目的のために、あなたができる最も正確なものは「精密時間」、この場合はピコ秒の分解能によって決定されます。

ステップ5:Verilogでのコメント

//これはVerilogのコメントです

C ++と同じように(VerilogにはC / C ++と多少似た構文があることがわかります)、Verilogのコメントは「//」で示されています。その行の二重スラッシュの後にあるものはすべて、コンパイラによって無視されます。コメントはデバッグ中に驚くほど役に立ち、コードを他の人に理解しやすくします。コメントブロックを開くには "/ *"を、閉じるには "* /"を使用して複数行のコメントを作成できます。

コメントを気にしないでください、しかし過度にしないでください。あなたがコメントしたものは何でも、コメントを最新のものにしておくことを忘れないでください!あなたが古いコメントをすることを計画しているなら、あなたは全くコメントしないかもしれません!

ステップ6:モジュール宣言

モジュールhello_world(

入力スイッチ0

入力スイッチ1

導かれた出力);

モジュール はVerilogのキーワードです。型宣言のように考えてください。 C ++では、 "void hello_world()の関数宣言を持つことができます。類似点に注意してください。

Verilogは値を返さず( "void" C ++関数のように)、実際には関数でもありませんが、その構文は似ており、moduleを型宣言として、そしてhello_worldを名前として考えることができます( Verilogの名前にスペースを含めることはできません。優れたVerilogコードは「モジュラー」であり、それが何を意味するのかについては後で説明します(今後のチュートリアルは近いうちに私の個人用WebサイトおよびおそらくInstructablesで公開される予定です)。

宣言の括弧の中には、パラメータリストと同じものがあります。つまり、モジュールは 入力 switch 0と呼ばれる、もう1つ 入力 switch 1と呼ばれ、 出力 と呼ばれる。 入力 そして 出力 本質的に可変型です。次のモジュールで見ることになるモジュール宣言へのいくつかの癖があります。

入出力宣言を3つのステートメントから2つに減らすことができると思いますか。

ヒント:Verilogでは、(バスと呼ばれる)配列を作成できます。

すべての入力と出力が宣言された後、パラメータリストは対応する括弧とセミコロンで閉じられます。セミコロンは、C ++と同じように、ステートメントの「終わり」を表します。セミコロンを忘れない限り、Verilogの便利な機能の1つは、長いロジックステートメントなどの大きなステートメントを読みやすくするために複数行に分割できることです。

図は、モジュールの宣言の典型的な色分けを示しています。

ステップ7:Verilogのロジック

led = switch 0&〜switch 1を割り当てます。

この行は「魔法」が起こるところです。ここまでで、コードはすべての入力と出力を記述してきました。次に、それらの入力に基づいて出力を操作します。 assignは別のVerilogキーワードです。何度も何度も使います。割り当てステートメントは、1つ以上の入力に基づく出力の永続的な説明です。出力ごとに複数のassignステートメントを使用することはできませんが、割り当てごとに複数の単純な論理式を使用することができます。代入と同等の最も近いC ++は、値に等しい変数を設定することです。Verilogでは、設定後の値は入力の物理的状態に依存するため、設定後に「値」を変更することはできません。

覚えておいて、Verilogはあなたが回路がどのように振る舞うかを説明しているだけであることを意味するハードウェア記述言語です。スイッチ0とスイッチ1の状態は、回路が動作している間は変化する可能性があります(例:スイッチ0を論理Low電圧に切り替える)が、代入文は解決されません。 ledは "switch 0&〜switch 1"で論理ハイ電圧に駆動されるだけです。

それで、 "switch 0&〜switch 1"はどうでしょうか。両方とも入力であることはわかっていますが、「&」と「〜」はどういう意味ですか?

デジタル回路の論理式を手書きするとき、ANDには '•'、ORには '+'、XORには¹を使います。もし用語の逆、または「not」を示したい場合は、上にバーを引きます。それ。添付の表に示すように、Verilogにはこれらすべてに相当するものがあります。

括弧を使って文をまとめることができます。 NAND、NOR、およびXNORゲートを作成する場合に特に便利です。ゲートの非反転バージョンの場合と同様に式を実装し、式の周囲に括弧を追加し、その先頭に「〜」を追加するためです。たとえば、 "FはA xnor Bに等しい"というVerilogステートメントを作成するには、どこから始めますか?確かに我々はFに値を "割り当てる"必要があるので、 "assign F ="をコーディングするのは良いスタートでしょう…そう、A xnor B … xnorシンボルはありません!心配する必要はありません。AXOR Bにしてから、用語全体を反転させることができます。これにより、 "assign F =〜(A ^ B);"という最終的な解が導き出されます。

演算子の優先順位に注意してください。 "INV"演算子はその左側の項に関連付けられ、AND、OR、XORなどは標準の論理式の優先順位規則に従います。

代入ステートメントの後のセミコロンを忘れないでください。

それでは、例に戻り、led = switch 0&〜switch 1;を代入します。スイッチ0が論理 "1"で、スイッチ1が論理 "0"の場合に限り、ledは論理ハイ電圧に駆動されます。

ステップ8:Verilogモジュールを終了する

エンドモジュール

このコード行は、最初は場違いに見えるかもしれません。明示的に開始せずにどのようにして「モジュールを終了する」ことができますか? Verilogでは、ファイルごとに1つのモジュールしか定義していません。各モジュールは他のモジュールのインスタンスを含むかもしれませんが、それらはそれらを定義しないかもしれません。

このキーワードは(あなたが推測したかもしれないように)モジュールの終わりを示し、すべての「有用な」情報がその点より上にあることをコンパイラに知らせます。時々、IDEは特に気になることができます エンドモジュール ただし、ANDの前後に空白行があることを確認した場合 エンドモジュール コマンド エンドモジュール コードはまったくインデントされていません、あなたはほとんどの問題を避けるべきです。

ステップ9:FPGAボードをプログラムするための準備

これでこのモジュールはすべてなので、これをFPGAボードに配置する準備が整いました。ただの冗談、私たちは何を忘れていますか?

そう、UCFです。

前述のように、各デジタルデザインボードメーカーはボードごとに異なる制約を持っています。サンプル制約は次のとおりです。

NETは "主導" LOC = "M5";

NETはすべてのステートメントの前に、ネット名(引用符で囲まれている)、実際のLOCation制約(引用符で囲まれている)が続きます。名前はエンジニアが望むものに設定することができ(最良の名前は最小のスペースで最も意味を伝えるものですが)、ロケーション制約は「ネット」が物理的にFPGAに接続されている場所を示します。 UCF内のコメントはVerilogモジュール内とは異なる方法で示されます。 UCFでは、コメントを示すために '#'を使用します。一部のIDEには、目的のコードを強調表示してIDEのホットキーの組み合わせまたはボタンを押すことによって、コードをコメントアウトすることができる機能が組み込まれています。

そのため、ボードのUCFファイルを作成してそれぞれの制約を含むUCFファイルを作成するとします。 入力 そして 出力あなたのIDEは ビットストリーム 以前に作成したVerilogモジュールと組み合わせて、あなたのFPGAボードのために。

異なるFPGAボードをプログラムするプロセスはさまざまです。具体的な手順については、ボード製造元にお問い合わせください。 含まれている写真は私のFPGAボードと私の2セントのピンジャンパー交換品のものです(一時的で、私がしなければならなかったように絶対にデバッグを続けなければならない限りこれをすることはお勧めしません)。

補足説明:Verilogモジュールは.v拡張子の付いたファイルと.ucfフォーマットのUCFファイルに保存されています。どちらのファイルも、メモ帳またはその他のテキストエディタを使用して編集できます(ワードパッドはメモ帳よりもインデント規則を重視する傾向があります)。

ステップ10:反射

あなたの最初のプログラムはうまくいきましたか。 そうでない場合は、その理由を理解してみてください。もしそうであれば、assignステートメントで他の演算子をいじってみることを考えてみてください。switch 0が論理0なら、またはswitch 1が論理1なら、論理1を導き出すことができますか。

写真は、VerilogとUCFで有効なビットストリームが作成された場合にISEでProcessesパネルがどのように表示されるかを示しています。これは回路があなたが望むように振る舞うことを保証するものではありません。 ある 回路 FPGAボード上に作成されます。

ステップ11:VM 2.0:Verilogのポートとワイヤの種類

最後の例では、次のようなモジュール宣言があります。

モジュールhello_world(

入力スイッチ0

入力スイッチ1

導かれた出力);

この文脈で入出力とはどういう意味ですか?これらは、作成している「ポート」の種類をコンパイラに知らせるための識別子です。 Verilogでは、モジュール宣言で次の識別子を扱います。

入力 :入力ポート、タイプ "wire"が想定されています。入力ポートにはモジュール内で値を割り当てないでください。

出力 :出力ポート、タイプ "wire"が想定されています。出力ワイヤには、代入文を使用して値を代入することも、別のモジュールのインスタンスによって間接的にその値を決定させることもできます。出力ワイヤには、どのような条件付きロジックでも値を明示的に割り当てることはできません(例: "if"ステートメント)。

出力登録 :タイプ "reg"の出力ポート、 "register"の略です。このタイプのポートは、回路の他の部分から独立した値を与えられ(例えば、それに論理「1」を割り当てる)、変更されるまでその値を保つことができる。 "always"ブロック(パラメータを変更したときに実行されるコードの塊)内からポートの値を変更する場合は、regを使用する必要があります。

要するに、実際のワードソースからANDゲートまでのように、ワイヤは1つのポイントから別のポイントへのコネクタとして機能し、regは何らかの目的地に向かう独自のソースを作成することができます。

モジュールの内部で内部ワイヤを作成し、モジュールの内部での使用を登録することができます。これは、モジュールの1つのインスタンスの入力を他のインスタンス化されたモジュールの出力と等しくする必要がある場合に役立ちます。ワイヤはそれ自身のドライバを持っていないので、そのソースをあるモジュールの出力にし、その宛先を別のモジュールの入力にすることができます。これを念頭に置いて、モジュールの内部で使用するためのwireまたはregをどのように宣言すると思いますか。

ワイヤー my_wire;

reg my_reg;

wire / regが内部配線とregの入力か出力かを指定する必要がないことに注意してください。これをデザイン内で正しく配線するために、最終的にソースと宛先を指定するようにします。

Verilogでは、内部のregまたはwireに等しい出力ワイヤを割り当てることができます。 regを変更すると出力も変更されるので、これによって出力の値を条件付きで変更できます(出力の値を直接変更することは禁止されていることに注意してください)。

前述のすべてのポート、ワイヤ、およびレジスタに適用できるのは、バス、またはベクトル(配列)と呼ばれるものです。バスを使って同じような信号をまとめることができます。最後の例では、「合計3つの入力と出力に対して2つのポートしか定義できないのですか」という質問を投げかけました。私たちができるバスを使って。 "input switch0、input switch1"と宣言する代わりに、単に "input 1:0 switch"と宣言することができます。これは「スイッチ」と呼ばれる2ビットバスを作成します。 "switch 0"のように演算子を使うことで "switch"の特定の部分を参照することができます。 Verilogではバスを作成する必要があるコードの量が減るため、バスは非常に便利です。

次の構文を使用してバスを宣言します( "type"は入力、出力、ワイヤ、またはレジスタを表します)。

:

モジュール内でバスを宣言している場合は、最後にセミコロンを追加してください。モジュール宣言でバスを宣言する場合は、必ずすべての定義をコンマで区切ってください。

バスはUCFファイルにどのような影響を与えますか?バスのすべてのコンポーネントには、共通名で始まり、バス内のインデックスが続く名前が付けられます。そのため、 "1:0 switch"は "switch <0>"と "switch <1>"の2つのエントリとしてUCFに入ります。 UCFでは<>演算子を忘れずに使用してください。

写真はブレッドボードのジャンパー線です。スイッチなどの刺激が組み込まれているため、ほとんどのFPGAデザインボード上のものについて心配する必要はありません。

ステップ12:VM 3.0:モジュール設計

モジュール設計には細心の注意を払ってください。これはおそらくVerilogの中で最大の唯一のトピックであり、おそらくその最も強力で便利な機能でもあります。

ここまでで、基本的なVerilog演算子(|、&、…)を使用して単純な論理関数を実装し、それをFPGAボード上で正常に実行できるようになるはずです。まだその段階になっていない場合は、VM 2.0から始めてください。

「VM 2.0:私の最初のVerilogプロジェクト」では、「モジュール」と「モジュラー」コードの概念を紹介しました。熟練した「モジュラーデザイナー」になるためにあなたが知る必要があるモジュールのニュアンスがいくつかあります。しかし、最初に「モジュラーデザインとは何か」を議論する必要があります。

「hello_world」の演習を完了した場合は、Verilogのキーワード「module」が紹介されました。各Verilogソースファイルには、モジュール定義が1つだけ含まれています。それでは、どのようにして多くの小さなモジュールを含む複雑なプロジェクトを作成することができるでしょうか。タイマー制御のLED回路を作成する例を見てみましょう。あるスイッチが論理1で、50MHzのクロックしかない場合、LEDを1秒ごとに点滅させるには、回路に何が必要ですか。

必要なものを、入力、出力、および内部コンポーネントの3つのタイプに分類します。

入力:オシレータ(「クロック」)、スイッチ、ボタン、データポートなどの物理的な信号源

アウトプット:LED、データポート、ディスプレイなどのような回路によって駆動される物理的な出力

内部コンポーネント:所望の出力を生成するために入力の状態をとるコンポーネント。これらすべてを「最上位」ポートに接続する必要はありませんが、最終的にはすべて「ルーティング」する必要があります。

これらの分類をもう一度読んでください。違いを知っていることは極めて重要です。また、新しい語彙「トップレベル」と「ルート」にも注意してください。

これらの用語が何を意味するのか考えてみてください…私は正式にそれらをすぐに定義します。

これら3種類の成分を代数方程式にすると、入力は独立変数、出力は従属変数、そして内部成分は実際の方程式を表します。このアナロジーを覚えておくと、特定のコンポーネントが入力、出力、または内部コンポーネントのいずれであるかをプロジェクトで作業するときに決めるのに役立ちます。

LED点滅回路の場合、これは非常に高いレベルでのものです。

入力:50MHzクロック、スイッチ

アウトプット: LED

内部:50MHzから1Hzのクロック分周器、1つのANDゲート(分周されたクロックとスイッチの両方が論理1の場合にのみLEDが点灯するはずです)

非常に高いレベル(ゲートパターンではなくコンポーネントを表すための「ブラックボックス」を意味する)で、この回路をどのようにブロック図化しますか。あなたはいくつかのインバータと一緒にいくつかのDフリップフロップ(DFF)を接続することによってクロック分周器を作ることができることを思い出してください。

回答については添付の図を参照してください。

課題:実際には、クロック分周器のすべてのDFFをリセットする物理的なボタンがあります。このブロック図に "RST"というボタンの始点と終点を描けますか。

このようなブロックダイアグラムは、ダイアグラム内のすべてのボックスがコード内のモジュールのインスタンスになるため、Verilogプログラムの作成方法を理解するための最初のステップです。いくつかの小さなブロックを含む1つの大きなブロックがあることに注意してください。これを「トップ」モジュールと呼びます。 「トップ」モジュールは、回路のすべての内部コンポーネントを物理的な入力と出力に接続するためのものです。

このトップモジュールの中には2つのことがあります。クロック分周器とANDゲートです。しかし、クロック分周器ブロックにもいくつかの小さいブロック(DFF)が含まれています。トップモジュールでこれらのDFFをすべて作成する必要はありません。すべてのDFFを含む独立したクロック分周器モジュールを作成し、最上位モジュールにそのクロック分周器のインスタンスを作成できます。

ご想像のとおり、クロック分周器で何度もインスタンス化できるDFFモジュールも作成する必要があります。これにより、D-Flip Flop ONCEの動作を定義し、そのDFFの複数のインスタンスをインスタンス化することができます。私たちのVerilogコードでやらなければならないのは、DFFの各インスタンスを適切なポートに接続すること(つまり、その入力と出力を接続すること)ですが、後でモジュールのインスタンス化を行うことです。

注:この点滅しているLEDプロジェクトをまだコーディングしないでください。カバーする必要があるものがまだいくつかあります。

このモジュールのポイントはあなたをモジュラーデザインにさらすことです。なぜ私がこれらのチュートリアルを「モジュール」と呼ぶのかわかりますか。あなたがVerilogを学びそして使用し続けるので、この考えはあなたの心に根付くはずです。

ステップ13:VM 3.1:トップモジュール

Verilogプロジェクトの最上位モジュールは、プロジェクトの最高レベルを表します。複雑な回路、たとえば内部が複雑ではない複数のコンポーネントで構成されているストップウォッチを作成する場合、一番上のモジュールはユーザーが最後に表示するものを表します。ボタン、停止ボタン、リセットボタン。

しかし、その最上位モジュールには、ユーザには見えないが、回路によって使用されるが、必ずしも最上位ポートに接続されていない、カウンタ、比較器、フリップフロップなどの他の多数のモジュールがある。

最上位ポート:プロジェクトの絶対的な始まりと終わり。実世界の物理信号の発信元と宛先

最上位モジュールは、そのインスタンス化されたモジュールすべてを最上位レベル内にあるソースおよび宛先に正しく接続する責任がありますが、インスタンス化されたモジュールによって示される動作を定義するものではありません。ファイルごとに定義できるモジュールは1つだけです。

トップモジュールは、プロジェクト内の他のすべてのモジュールをまとめて「ラップ」するため、「ラッパー」と呼ばれることがあります。

次に、モジュールのインスタンス化がどのように機能するのかを調べます。

ステップ14:VM 3.2:インスタンス化されたモジュール

ここまででインスタンス化されたモジュールについてたくさん議論しましたので、基本的な概念を理解する時が来ました。

トップモジュールは、プロジェクトの要件を満たすために他の有用なモジュール式コンポーネントをインスタンス化することによって、プロジェクトをまとめます。しかし、これらのインスタンス化されたモジュールはそれぞれ他のモジュールをインスタンス化することもできます。

つまり、あるトップモジュールはクロック分周器のようなものをインスタンス化でき、そのクロック分周器は多数のDFFをインスタンス化できますが、DFFを互いに接続するタスクはクロック分周器モジュールに与えられます。

おなじみのコンセプトのように聞こえますか? C、C ++、Javaなどの言語でプログラミングに手を出したことがある人なら誰でも、 "scope"に精通しているはずです。インスタンス化されたモジュールの範囲は、それをインスタンス化したモジュールに限定されています。これは、たとえばC ++と非常によく似ています。 C ++プログラムを作成している場合、すべてをまとめたmain()関数があり、そのmain関数で他の関数を呼び出すことができますが、それらの関数の内部動作(およびこれらの関数もこれらの関数を呼び出すことができます)は異なります。 main関数を実行するにもかかわらず、mainでアクセス可能であるため、すべての補助関数も実行する必要があります。

つまり、クロック分周器の場合、一番上のモジュールにはクロック分周器が含まれているため、多数のDFFも含まれていますが、一番上のモジュールに関する限り、アクセス可能なDFFはありません。

ステップ15:VM 3.3:モジュールをインスタンス化する方法

前のモジュールで、Verilogでモジュールをインスタンス化することとC / C ++で関数を呼び出すことの類似点について述べました。

Verilogでモジュールをどのようにインスタンス化するかを見てみましょう。

これが私たちが取り組むモジュールです:

`タイムスケール1ns / 1ps

モジュールスクラッチ(

入力S、

入力R、

出力Q);

ワイヤQ_int、Qn_int。

Q_int =〜(S&Qn_int)を割り当てます。

Qn_int =〜(R&Q_int)を割り当てます。

Q = Q_intを割り当てます。

エンドモジュール

今のところ、このモジュールには説明できないVerilogはありません。

それでは、2つのSRラッチ(実際にはこの回路がどれほど無意味であるかに関係なく)を使用し、それぞれに "設定"操作用のスイッチと共有リセットボタンを使用した回路を作りたいとしましょう。各ラッチの出力はLEDになります。

これらの仕様を念頭に置いて、最上位モジュールの定義を作成しましょう。トップモジュールの宣言には、現実の世界との間でやり取りされる入力と出力のみが含まれます。

`タイムスケール1ns / 1ps

//課題:バスを使用してこのモジュール宣言を書き直す

モジュールトップ(

入力スイッチ0、

入力スイッチ1、

入力btn0、

出力led0、

出力led1);

エンドモジュール

すばらしいです!今私達は私達の回路に2つのSRラッチを追加する必要があります。これをどのようにしますか。

「2つのSRラッチの動作をハードコーディングして、それぞれ入力と出力を割り当てる」と言ってはいけません。 SR-Latchのインスタンスを2つインスタンス化します。これが私たちのトップモジュールがインスタンス化の世話をする方法です:

`タイムスケール1ns / 1ps

モジュールトップ(

入力スイッチ0、

入力スイッチ1、

入力btn0、

出力led0、

出力led1);

srlatch sr0(

.S(スイッチ0)

.R(btn0)、

.Q(led0));

srlatch sr1(

.S(スイッチ1)

.R(btn0)、

.Q(led1));

エンドモジュール

それでは、やるべきことは何ですか?何もない。絶対に何もありません(あなたが既にUCFファイルを用意していると仮定して)。 どうして?

その理由はモジュールのインスタンス化にあります。もう少し詳しく見てみましょう。

srlatch sr0(

.S(スイッチ0)

.R(btn0)、

.Q(led0));

最初の行 srlatch sr0(

これは非常に重要な行であり、その形式をメモリにコミットする必要があります。最初はスラッチです。これは、インスタンス化しているベースモジュールの名前です。 sr0はインスタンス名です。括弧はインスタンス化を開きます。すべてのインスタンス化はそのように始まります。 「 ('.

インスタンス化がわずかに異なるいくつかのあまり一般的でないケースがありますが、それでもこの形式は存在します。

次の3行

.S(スイッチ0)

.R(btn0)、

.Q(led0)

srlatchモジュールのポート名を見てから、これら3行を振り返ってください。これは、回り道では、私たちの コール C / C ++の関数。これがパラメータを渡す場所です。

違いは、Verilogでは両方の入力をモジュールに渡し、出力をモジュールに渡すことです。つまり、モジュールの特定のインスタンスが、その入力がどこから来ているのか、そしてどこに出力が行きたいのかを伝えています。

もう1つの見方は、モジュールに入力と出力のセットを渡すことです。それぞれの値は、モジュールの内部(出力)またはモジュールの外部(入力)から継続的に更新されます。

ステートメントを分解しましょう。 .S(スイッチ0)

すべてのモジュールのインスタンス化には、これに似た一連のステートメント、つまり入出力ごとに1つのステートメントがあります。 「.S」はsrlatchモジュール内の入力Sを参照し、(switch0)はを参照しています。 入力スイッチ0 トップモジュールにあります。パターンを見ますか?

. ()

この概念は、C / C ++のような他のプログラミング言語で関数を呼び出すのにも似ています。関数呼び出しでは、(変数名で知っている)値を関数に渡します。この値をmainなどで1つのものと呼びますが、関数内では別のものと呼ばれます。このVerilogの例では、トップモジュールに "という名前の入力があります。swtich0「そして、欲しい」S「の値」sr0「 "switch0"が何であれに等しい」

前述したように、インスタンス化しているモジュールではポートごとに1つのポート初期化ステートメントが必要ですが、たとえば7:0バス入力が "T"のモジュールをインスタンス化している場合は必要ありません。 ".T 0(Input 0)"のように8つのステートメントを作る。名前だけを参照することでバス全体を渡すことができるので、 "。T(Input)"と記述することで "Input"を "T"に渡すことができます。ただし、バスの寸法は一致しなければなりません(つまり、5ポートのバスを3ポートのバスを持つモジュールに渡すことはできません)。

さらに、ポートタイプは一致する必要があります。入力には入力を、出力には出力(またはワイヤ。詳細は後述)しか渡すことができません。

ポート初期設定ステートメントは単一のコンマで区切ります。適切なコーディングスタイルは、各行に1つのインスタンス化のみが含まれることを示しています。これから3行のコードを理解できるはずです。

一致するかっことセミコロンでインスタンス化を閉じます。

インスタンス化が成功した後、インスタンス化されたモジュールの出力は、本質的に、現在のモジュール内でアクセス可能になります。その後、これらの出力を最上位ポートまたは他のインスタンス化されたモジュールの入力にもルーティングできます。

この議論を終える前に、モジュールを開始する方法を要約しましょう(「現在のモジュール」はインスタンス化をしているモジュールを指すことを覚えておいてください):

(. (), …

. ());

ステップ16:VM 3.3:高度なインスタンス生成のトピック

(元々Digilent社によって作成された画像。このチュートリアルのために私が修正しました)

前のモジュールはVerilogでのインスタンス生成の概念を導入し、うまくいけばそれを導きました。それでは、モジュールのインスタンス化に関するいくつかの高度なトピック(もうすぐ追加されるトピック)を見ていきます。

カウンタ制御のインスタンス化

クロック分周回路ですべて一緒に接続されたDFFの束(例として50)を生成する必要がある場合はどうなりますか?一般的なDFFモジュールの作成方法は既にわかっているので、50回インスタンス化する必要があります。そして、それぞれのクロックピンをその前にあるDFFの出力に接続し、resetをグローバルリセットに接続し、D inピンをDFFの出力の反転(Q)に接続します。添付の図を参考にしてください。

これはあなたが手作業で書くにはたくさんのインスタンス化になるでしょう(50インスタンス化×インスタンス化あたり6行は300行です)。しかしVerilogでは、カウンタコントローラループ、具体的には "for"ループを使用して、ごくわずかなコード行でこれらのすべてのモジュール(最初のモジュールを除く)をインスタンス化(生成)できます。しかし、注意点が1つあります。 「生成ループ」を適切に使用できるように、設計を慎重に計画する必要があります。

各DFFを次のDFFに接続する方法は、一様で予測可能です。もちろん、最初のDFFのクロックはトップレベルモジュールからのクロック入力になり、最後のDFFの出力はトップレベルモジュールのどこかに行きますが、それらを個別に扱うことができます。生成中は、手動で指定する最初のクロックと最後の出力配線を除く、デザイン内の各内部配線に明示的にソースとドレインが割り当てられます。ソフトウェアは、残りの2本のワイヤにソース(最初のクロック)またはドレイン(最後のデータ)のいずれかが必要であることを認識しています。1本を提供するのはエンジニアの責任です。

前述のように、生成ループを使用するにはデザインを正しく設定する必要があります。これには何が関係しますか?各DFFが前のもの(最初のものは含まない)に依存してクロックを持つことになるので、バス内でこれらの類似した信号をグループ化できることは当然のことです。これにより、C / C ++の配列のようにシグナルグループを参照し、演算子を使用してインデックスで個々のワイヤを呼び出すことができます。

各DFFは同時にリセットできる必要があるため、各DFFのリセットピンは共通のリセットから発生します。

生成ループの詳細をまだ知らなくても、設計で使用するバスを作成することはできます。どのタイプのバスを作りますか?これらはソースからドレインに信号を伝えるだけなので、ワイヤーを作ります。

49:0 // DFF出力/入力用50ビット幅バス

最初のDFFをインスタンス化することもできます。チュートリアルモジュール3.2のDFFモジュールを使用して、私たちのクロックディバイダが入力clk(クロックピン)、入力rst(リセットピン)、および出力out(それがLEDを駆動して点滅効果を作り出すとしましょう)を持っていると仮定します。 )

dff dff0(

.clk(clk)、

.rst(rst)、

.D(〜out 0)、

.Q(out 0));

今度は生成ループです。これはC / C ++のループに似ていますが、独自の微妙な違いがあります。まずカウンター変数を作成する必要があります。 Verilogでは、変数の型は "genvar"と呼ばれ、変数を宣言するために使用されます。以下のように "y"と呼びます。

genvar y;

生成ループはキーワード "generate"で始まり、 "endgenerate"で終わります。

注:生成ループの範囲外のgenvar変数は宣言する必要があります。

我々は今持っています:

genvar y;

生成する

終末する

"generate"キーワードの直後に実際のループがあります。その宣言はC / C ++のループのようなものですが、Verilogには左右の中括弧({と})という豪華さはありませんが、同等のVerilogがあります。beginとendです。さらに、Verilogは後置操作をサポートしていませんので、 "y ++"と記述することはできず、代わりに "y = y + 1"と記述する必要があります。 forループを作成するときは、最初のDFF(DFF0)がすでに作成されているので、1から49まで(50より小さい)を生成します。

genvar y;

生成する

(y = 1; y <50; y = y + 1)//スペースは省略可能

ベギン

終わり

終末する

生成ループでは、インスタンス化ループを「開始」し、このプロセスに名前を付ける必要があります。この名前は他の目的には使用せず、後の設計では参照しません(シンセサイザーの内部で使用されます)。これを行うには、 ":"という形式の文を追加し、それを "begin"と同じ行に配置してスタイルを整えます。これを "dff_generation"と呼びましょう。

genvar y;

生成する

(y = 1; y <50; y = y + 1)//スペースは省略可能

はじめに:dff_generation

終わり

終末する

インスタンス化モデルを作成するということが、今や最も重要で最も難しい部分です。これは他のインスタンス化と同じように見えますが、モジュールを通過するワイヤはgenvarを値または値修飾子として使用することを許可されています。この場合、各インスタンス化の後にgenvarの値が1ずつ変わることを思い出してください。

インスタンス化の各行を辿るのではなく、コードブロック全体を説明し、認識できないものを指摘します。この回路がどのように実装されているかについては、必ず付属の写真を参照してください。

genvar y;

生成する

(y = 1; y <50; y = y + 1)//スペースは省略可能

はじめに:dff_generation

//下記のインスタンス名は関係ありません

dff dff_insts(

.clk(out y-1)、// DFF "y"のclk inが "y-1"から外れています

.rst(rst)、//各DFFは同じリセットを受けます

.D(〜out y)//入力は反転出力になる

.Q(out y)// output);

終わり

終末する

生成プロセスがどのように機能するかを確認するために、ループのいくつかの反復を手作業でトレースすることをお勧めします。シンセサイザーは各インスタンスに "dff_insts1"や "dff_insts2"のような一意のインデックス名を作成します。

ステップ17:VM 4.0:ビヘイビアVerilogの紹介

おめでとうございます。ここまで来たことで、「ビヘイビア」Verilogを使用してプロジェクトを比較的簡単に非常に複雑にすることができます。

これまでのところ私たちは主にVerilogで構造論理について議論してきました。これは、あなたの回路の振る舞いは一度私たちの論理演算子を使ってassignステートメントとlogicステートメントを使って設定され、ボード上でプログラムされたとき入力が何であるかを考慮せずに指定通りに振る舞うことを意味します。可能な限りすべての入力の組み合わせがまったく同じロジックで評価されます。動作ロジックを使用すると、特定の入力の状​​態に基づいて回路の動作を変更できます。

C / C ++などの非HDLプログラミング言語に見られるこの種のVerilogコードと同様の考え方は、制御ループおよびif / else / caseステートメントの形で見られます。

VM 4の残りの部分では、基本的な種類の動作Verilogとその実装方法について説明します。

ブロックステートメントと非ブロックステートメントの違いについても説明します。非常に重要だが見落とされがちな概念。私はかつて私のLogic Circuitsの研究室で彼のプロジェクトの動作スペックについて質問をしていた時、そして彼が課題をすべて読んでいれば簡単に答えられる質問に答えがなかった時に教えてくれました。彼がコードを書き始める前に、TAは「これは大きな問題です。非常に多くのエンジニアがコードを書くだけで、自分たちがしたことをなぜしたのかわからない」と答えました。これは私の頭の中で立ち往生し、いつ私はブロッキングとノンブロッキングステートメントを使うべきかについて言われただけで、なぜあるいはどう違うのかは言われなかったので、これは私が議論するのは非常に重要なトピックです。

ステップ18:VM 4.1:Always Block

誰かが同じ文中で「Verilog」と「Behavioral」という単語に言及した場合、あなたの脳はすぐに「常にブロックする」と考えるべきです。 alwaysブロックとC / C ++のような言語の間の真の翻訳はありませんが、その概念を理解するのは難しくありません。しかし、C / C ++と比較する必要がある場合は、alwaysブロックはwhileループのようになり、評価する条件はif文の混乱です。

一言で言えば、alwaysブロックは、トリガの条件が変わるたびに評価されるVerilogコードロジックのセクションです。

これは何を意味するのでしょうか? sw0とsw1の2つの入力を持つ回路の基本的なalwaysブロックを見てみましょう。

常に@(sw0、sw1)

ベギン

終わり

このブロックにボディコードはありませんが、今のところ無視しましょう。 「常に」進む「@」に注目してください(「常に」はVerilogキーワードです)。また、括弧内のステートメント "sw0、sw1"にも注意してください。

単純な真理値表を使用すると、2つの入力に対して最大4つの固有の状態(入力が両方ともHigh、両方がLow、もう一方がHigh)が存在することがわかります。

"always @(sw0、sw1)"は本質的に "いつでも入力がその状態を変更した場合(すなわち真理値表の行の変更)、次のコードを再評価します:"と言います。

これはC / C ++で書くのがあまり単純ではないでしょうが、Verilogではそれはとても単純です。リストされたトリガー(そのリストを機密リストと呼びます)のいずれかが論理1から論理に変更されると、alwaysブロックはそのスコープ内(「always」宣言に続くbeginステートメントとendステートメントの間)でコードを再評価します。ゼロまたは論理0から論理1へ。 一般的な論理回路コンポーネントを1つか2つ(またはまとめるといいでしょう)を考えてみてください。

古典的な例は、マルチプレクサ、またはマルチプレクサです。選択された入力のいずれかが変わると、マルチプレクサはどの入力を出力に渡すかを再決定する必要があります。次のチュートリアルモジュールでは、alwaysブロックの中にどのような種類のコードを記述できるかについて説明します。

Pro tip:Verilogモジュール(.vファイル)ごとに常にブロックされるのは1つだけです。別のブロックを作成するのではなく、感度リストを調整してください。

ヒント(重複):regの値はalwaysブロック内でのみ設定でき、wire型に値を代入することはできません。ただし、後でワイヤをregの値と等しくなるように割り当てることができます。

ステップ19:VM 4.2:ブロッキング対非ブロッキングVerilogステートメント

常にブロックを議論することからブロック/非ブロックに切り替えることは変に思えるかもしれませんが、これは新しい概念を導入するのに最適な時間です。

C / C ++でコードを書く場合、プログラムがそのパターンから逸脱するように指示されるまで(たとえば、特定の行にジャンプするまで)、コードは1行ずつ順番に実行されます。 1行が実行され、実行が終了して初めて次の行が実行されます。最も実用的な目的のために、私たちは単純なコンソールアプリケーションの遅れに気付きません。しかし、デジタル設計では、ある行を次の行に実行するまでのこの遅延は、非常に悪い結果となる可能性があります。 1ナノ秒未満の遅延でも問題が発生する可能性があります。正確にはグリッチ。

グリッチとは、1つの入力が一時的な状態になること、および入力間にパス間に等価なゲート遅延がない状態で少なくとも2つのパスがあることによる、回路の出力の瞬間的かつ望ましくない変化として正式に定義されます。

両方とも論理0の2つのスイッチ(sw0とsw1)があり、両方のスイッチが論理0(LED =〜(sw0&sw1))の場合、単一のLEDの結果の出力は論理1になります。望ましい出力しかし、sw1を反転させて論理High状態にするとします。 sw1電源からの電圧が、回路の最後のゲートに到達するまで、さまざまな経路の論理ゲートを通って「下流」に「波打つ」間に、ほんの一瞬だけLEDが論理1であったとしたらどうでしょうか。 ?理論的には論理0であるべきであるときにLEDが論理1であるところにはほんの一瞬である。あなたの回路はそれ自身の論理式のルールを破りました!

このモジュールの目的はロジックステートメントのグリッチを修正する方法をカバーすることではありませんが、「グリッチフリー」な回路に対してうまく形成された論理式でも、ブロッキングステートメントを使用するとグリッチが発生する可能性があるということです。ノンブロッキングステートメントを使用した、またはその逆。

これはどのように行われますか。また、間違ったVerilogステートメントが原因でこれが発生するのはなぜですか。

ブロッキングステートメントは、 "="演算子を使用する代入ステートメントです(必ずしも "assign"キーワードを使用する必要はありません)。ブロッキングステートメントは、C / C ++のコードのように、順番に実行されます。

ノンブロッキングステートメントは、 "<="演算子を使用する代入ステートメントです。ノンブロッキングステートメントは、他のノンブロッキングステートメントと同じコード範囲に置かれると、同時に実行されます。

上記の定義はとても重要なので、もう一度読んでください。

「Verilogコードはハードウェア記述言語なので、同じ物理回路に合成されて同じように動作するので、どのような種類のステートメントを書いてもかまいません」と学生は考えています。複雑なVerilogコンパイラはさまざまな信号をさまざまな場所に渡すためにさまざまなトランジスタパターンを作成する必要があるため、必ずしもそうとは限りません。また、このHASが同時に発生するか他の時間間隔で発生するかを指定する必要があります。回路に都合の良い時間に信号を送ることができるブロッキングステートメントを使用して回路を構築することがより効率的かもしれませんが、2つ以上の出力変更が本質的に同時に起こることがしばしば重要です。

一連のブロッキングステートメントが回路の動作に悪影響を与える可能性があることをご存知ですか。トップレベルの代入ステートメントは、ステートメントが一度だけ「実行」されるのでブロックされますが、「常に」条件を再評価しているかもしれないデコーダのようなものは同時に出力を変更する必要があります。エンジニアがデコーダの出力GSと出力EOの信号を異なるタイミングで変更した場合のエンジニアの問題を想像してみてください。

いくつかの簡単な例を見てください( "if"ステートメントは次のモジュールでカバーされています!):

//例1

常に@(sw0、sw1)

ベギン

if(sw0 == 1'b1)

ベギン

//これらすべての出力はそれらに割り当てられます

//同時に値

output1 <= 1'b0;

output2 <= 1'b1;

output3 <= 1'b0;

終わり

終わり

//例2

常に@(sw0、sw1)

ベギン

if(sw0 == 1'b1)

ベギン

//最初の出力が割り当てられ、次に次の2つが割り当てられます

//同時に割り当てられます

output1 = 1'b0;

output2 <= 1'b1;

output3 <= 1'b0;

終わり

終わり

//例3

常に@(sw0、sw1)

ベギン

if(sw0 == 1'b1)

ベギン

//最初の出力が割り当てられ、次の2つが割り当てられます

//次々に割り当てられる

output1 = 1'b0;

output2 = 1'b1;

output3 = 1'b0;

終わり

終わり

絶対に ブロッキングステートメントを構造ロジックブロック内に配置します。言い換えると、alwaysブロックの中に "="を決して置かないでください。 1つの出力だけを変更している場合でも、ブロッキングステートメントを使用することはお勧めできません。

常に "assign"キーワードを使用するときは、ブロッキングステートメントを使用してください。これらのステートメントは常にalwaysブロックの外側で行われます。使用例は、値をalwaysブロック内で変更するregを宣言し、次に出力ワイヤ "="をregの値に割り当てることです。

ヒント:通常は、次の形式を使ってregに値を与えます。 <= ;

追加のヒント(重複):型ブロックに値を代入することなく、alwaysブロック内でのみregの値を設定することができます。ただし、後でワイヤをregの値と等しくなるように割り当てることができます。

ステップ20:VM 4.3:VerilogのIf / Else

このチュートリアルモジュールは、ブロックステートメントと非ブロックステートメントについてモジュール4.2をしっかり理解した後で完成します。

VerilogのIf / Elseステートメントは、正しく使用すると非常に役立ちます。まず、VerilogのIf / Elseステートメントで従う必要があるいくつかの規則を見てください。

1.すべてのIf / Elseステートメントは、alwaysブロックの内側に配置する必要があります。

2. If / Elseステートメントによって設定された値はすべてreg型でなければなりません。

3.複数のregを割り当てている場合は、 "begin"と "end"を使用してIf / Elseの範囲を定義します。 1つだけの声明?それなら心配しないでください。しかしどちらの方法でも、If / Elseの各内部行をセミコロンで終了します(セミコロンは決して "begin"または "end"ステートメントの後に続きません)。

4.条件に2つの記号を含むコンパレータ演算子を使用します(例: "&&"と "=="と "!=")。

基本的にそれだけです。あなたのコードがあなたに問題を与えているのであれば、これら4つの規則をもう一度参照して、「私のコードはこれらの規則に従っていますか?」と自問してください。

Verilogでは、if、else、else ifの3つの基本的な条件文を使用します。ただし、これらを使用する前に、regに値を割り当てる方法について説明する必要があります。

regはそれ自身のソースを持つことができるワイヤー、すなわちそれ自身の価値を生み出すことができるワイヤーのようなものです。私達はまた私達がバスregsおよびsingle regsを持つことができることを知っている。

C / C ++の変数と同じようにregを使うことができます。明示的に変更されるまで値を保存します。

reg myReg;

myReg = 1'b1; //ブロッキング

myReg <= 1'b0; //ノンブロッキング

これにより、レジスタがロジックハイ電圧に設定され、その後ロジックロー電圧に設定されます。ただし、regsに割り当てられた値は、絶対に値を変更しない限り、alwaysブロックの内側にのみ存在するはずです。たとえば、論理ハイのワイヤが必要なだけの場合は、regにalwaysブロック以外の値を指定します。

regに値を代入する(これをassignキーワードと同じにしないでください)には、ステートメントの左側に次の形式が必要です。

そして右側(値)について:

'

regの幅は値の幅と正確に一致しなければならないため、これは重要です。 binary、hexadecimal、decimal(Verilogではそれぞれb、h、dと呼ばれています)など、regに割り当てることができる値にはさまざまな種類がありますが、Verilogを学んでいる人はBINARYのみを使用することを強くお勧めします。値それらは取り扱いが簡単で、トラブルシューティングも簡単です。

バイナリ値を使用するときは、regをすべてhighまたはすべてlowにしない限り、regの各ビットの各値(1または0)を入力する必要があります。

例:

my_reg <= 4'b0; //法的

my_reg2 <= 3'b010; //法的

my_reg3 <= 4'b01; //不正(幅の不一致、4ビットが予想される)

これらのステートメントは、例えば「4ビット2進ゼロ」または「3ビット2進2」のように、平易な英語で読まれます。

regに値を代入する方法がわかったので、VerilogでIf / Elseステートメントを使用する方法を見てみましょう。

モジュールtestMod(

aを入力してください、

入力b、

出力c);

reg Q;

常に@(a、b)// aまたはbの状態が変化した場合は、このコードを再実行してください。

ベギン

if(a == 1'b1 || b == 1'b0)

Q≦1'b0;

それ以外の

Q≦1'b1;

終わり

c = Qを割り当てる。

エンドモジュール

このコードをトレースして、VerilogでIf / Elseステートメントがどのように機能するのかを推測できますか。

もし "if"がトリガーされたときに複数の値を操作したいとしたらどうでしょうか(注:ifステートメントが2つの値を変更する場合、elseステートメントも浮動値を防ぐために2つの値を変更する必要があります)。

モジュールtestMod(

aを入力してください、

入力b、

出力1:0 c);

reg Q;

reg T;

常に@(a、b)// aまたはbの状態が変化した場合は、このコードを再実行してください。

ベギン

if(a == 1'b0)

ベギン

Q≦1'b0;

T <= 1'b0;

終わり

それ以外の場合(a == 1'b1 && b == 1'b1)

ベギン

Q≦1'b1;

T <= 1'b0;

終わり

それ以外の

ベギン

Q≦1'b1;

T <= 1'b1;

終わり

終わり

c [0] = Qを割り当てる。

c [1] = Tを割り当てる。

エンドモジュール

この例は、このチュートリアルシリーズで取り上げられているアイデアの多くを結び付けていると思います。私はまたElse If文を投げました。ただし、驚くことではありません。C/ C ++のように動作します。各条件のアクションが同じ出力を処理するようにしてください(この場合、QとTの両方がそれぞれの場合に処理されました)。直感的に中括弧であるべきだと思う場所のbeginおよびendキーワードに注意してください。

VerilogのIf / Else If / Elseステートメントに記載されているのはこれですべてです。デバッグの目的で、このチュートリアルモジュールの上部にあるデザインルールを必ず参照してください。

ステップ21:ここからどこへ行くか

FPGAデザインには何が必要かについて基本的な理解ができたことを願います。もっと複雑になります。しかし結局、FPGAデザインは学ぶ時間の価値があることがよくあります。

アプリケーションによっては、FPGAの方がはるかに高速で効率的であるため、電気工学または電子愛好家向けの研究/プロジェクトを進めるにつれて、マイクロコントローラおよびVerilogの知識が役立つことがあります。

Instructableコミュニティからの依頼で、私はもっとチュートリアルモジュール、特にcaseステートメントと条件付き表記に関するモジュールを投稿するかもしれません。

どんな質問ででも私にメッセージを送ってください。私の勉強は私を忙しくさせます、しかし、私は通常すぐにメッセージを送る時間を見つけるでしょう!