FPGAの基礎知識----第3章 第5節 機能説明-組み合わせロジック

セクション 5 機能説明 - 組み合わせロジック

5.1 プログラムステートメント

5.1.1 assign ステートメント

assign ステートメントは、連続した代入ステートメントです。一般に、1 つの変数の値は、中断することなく別の変数に代入されます。2 つの変数は、接続として使用されるワイヤで接続されているのと似ています。assign ステートメントの基本的な形式は次のとおりです。

assign a = b (論理演算子) c ...;

assign ステートメントの機能は組み合わせ論理のカテゴリに属し、その適用範囲は次のように要約できます。

(1)継続的な割り当て

(2)接続;

(3)ワイヤ型変数に値を代入します。ワイヤは実際の接続線に相当するワイヤネットワークです。割り当てを使用して直接接続したい場合は、ワイヤ型変数を使用します。ワイヤ型変数の値は変化します。いつでも。

複数の assign 連続代入ステートメントは独立して並列に実行されることに注意してください。

5.1.2 always ステートメント

Always ステートメントは条件付きループ ステートメントであり、実行メカニズムは、以下で詳細に説明するセンシティブ変数テーブルと呼ばれるイベントを駆動することによって実現されますalways ステートメントの基本的な形式は次のとおりです。

常に @(機密イベント)begin

プログラムステートメント

終わり

always は「常に、常に」を意味し、@ の後にイベントが続きます。全体は常に、機密イベントの条件が満たされたときに、「プログラム ステートメント」を 1 回実行することを意味します。機密イベントが満たされるたびに、「プログラム ステートメント」が 1 回実行されます。(機密イベントの機密条件が変化した場合、always 条件ループ文の内容を実行します。

画像-20211029113455362

このプログラムの意味は、信号 a、信号 b、または信号 d が変化したときに、次のステートメントを 1 回実行することです。このステートメントを実行するときは、まず sel 信号が 0 かどうかを判定し、0 であれば 3 行目のコードを実行します。sel が 0 でない場合は、コードの 5 行目を実行します。a、b、c のいずれかが 1 回変更された場合、2 行目から 5 行目は 1 回だけ実行され、2 回目は実行されないことに注意してください。

ここで、sel 信号の変更だけでは 2 行目から 5 行目のコードが実行されるわけではないことに注意してください。これは通常、設計者のアイデアと一致しません。たとえば、一般的な設計者の考え方は、sel が 0 の場合、c の結果は a+b であり、sel が 0 でない場合、c の結果は a+d です。しかし、トリガ条件が変化しない場合、sel が 0 から 1 に変化しても、c の結果は a+b のままです。したがって、これは標準的なデザイン思考ではありません。

したがって、設計者のアイデアに従ってコードを再設計します。信号 a または信号 b または信号 d または信号 sel が変化したときに、2 行目から 5 行目を実行します。このようにして、sel の信号値が 0 の場合、c の結果は a+b でなければならず、sel が 0 でない場合、c の結果は a+d でなければならないことが保証されます。したがって、sel を機密リストに追加するには、コードは次のようになります。

画像-20211029113540684

敏感な信号が多い場合、敏感な信号を見逃しやすくなります。この状況を回避するには、代わりに「*」を使用します。この「*」は「プログラム文」中のすべての条件信号、つまりa、b、d、sel(cは含まない)を指しており、この書き方も推奨されており、具体的なコードは以下の通りです。

画像-20211029113600878

このように、条件付き信号変更の結果が即座に変化するalways文を「組み合わせ論理」と呼びます。

画像-20211029113618567

上記のコード感度リストは **「ポーズジ clk」です。ポーズジは立ち上がりエッジを意味します**。つまり、clk が 0 から 1 に変化するとき、プログラム コードは 2 行目から 5 行目まで 1 回実行され、それ以外の時間では c の値は変化しません。clk が 0 から 1 に変化しない場合、a、b、d、sel が変化しても c の値は変化しないことに注意してください。

画像-20211029113640663

上記のコードの機密リストは「negedge clk」であることがわかります。negedge は立ち下がりエッジを表しますつまり、clk が 1 から 0 に変化するとき、プログラム コードは 2 行目から 5 行目まで 1 回実行され、それ以外の時間では c の値は変化しません。clk が 1 から 0 に変化しない場合、a、b、d、sel が変化しても c の値は変化しないことに注意してください。

画像-20211029113709007

上記のコードの機密リストは、「posedge clk または negedge rst_n」です。つまり、clk が 0 から 1 に変化するとき、または rst_n が 1 から 0 に変化するときに、プログラム コードが 1 回実行されます (つまり 2 行目)。 ~ 8、その他 c の値はその時点では変化しません。このような信号のエッジトリガー、つまり信号が常に立ち上がりエッジまたは立ち下がりエッジでのみ変化することを「シーケンシャルロジック」と呼び、信号 clk がこのときのクロックとなります。注: 信号がクロックであるかどうかを識別するには、名前を確認するのではなく、信号が配置されている場所を確認する必要があります。センシティブ リストに配置され、エッジ トリガーされるものだけがクロックです。信号 rst_n はリセット信号であり、名前によって判断されるのではなく、センシティブ リストに入れられ、同じエッジによってトリガーされます。より重要なことは、「プログラム ステートメント」が最初に rst_n の値を判断するということです。 rst_n の優先順位が最も高く、リセットに使用されます。

設計時には次の点に注意してください

1. *組み合わせロジックの always ステートメント内の機密変数は、完全に記述するか、" " に置き換える必要があります。

2.組み合わせ論理デバイスの代入はブロッキング代入「=」を採用し、順序論理デバイスの代入ステートメントは非ブロッキング代入「<=」を採用します

具体的な理由については、「ブロッキング割り当てと非ブロッキング割り当て」のセクションを参照してください。

5.2 番号体系

5.2.1 デジタル表現

Verilog でのデジタル表現で最も一般的に使用される形式は、 <bit width>'<radix><value> (4'b1011 など) です。ビット幅: 定数に含まれるビット数を表す 10 進整数 (オプション)。たとえば、4'b1011 の 4 はビット幅であり、一般的に理解されているのは 4 本のワイヤです。そのような項目がない場合は、定数の値から推測できます。たとえば、'b1011 はビット幅 4 を推測し、'b10010 はビット幅 5 を推測します。

Radix : 値が何進数であるかを示します。2 進数、10 進数、8 進数、16 進数を表す場合は、それぞれ b、B、d、D、o、O、h、または H を指定できますこのエントリがない場合、デフォルトは 10 進数になります。たとえば、2 進数の 4'b1011 は、10 進数の 4'd11、16 進数の 4'hb、8 進数の 4'o13、または基数なしの 11 と書くことができます。つまり、2進数が同じであれば、10進数で書いても、8進数で書いても、16進数で書いても、同じ数になります。

Value : 基数によって決定される定数の実数値を表す ASCII コードの文字列。基数が b または B として定義されている場合、値は 0、1、x、X、z、または Z になります。塩基が o または O として定義されている場合、値は 2、3、4、5、6、7 になります。塩基が h または H として定義されている場合、値は 8、9、a、b、c、d、e、f、A、B、C、D、E、F になります。基数 d または D の場合、値の符号は 0 ~ 9 の任意の 10 進数にすることができますが、x や z は使用できません。たとえば、4'b12 は誤りです。b はバイナリを意味し、値は 0、1、x、または z のみであり、2 は含まれません。32'h12 は 32'h00000012 と等しくなります。つまり、値が完全に書き込まれていない場合、上位ビットは 0 で埋められます。

5.2.2 バイナリが基本

デジタル回路では、チップ A がチップ B にデータを送信する場合 (0 または 1 の情報を送信するなど)、チップ A とチップ B をピンを介して接続すると、チップ A がピンの出力を High または Low に制御します。レベル、0 と 1 は高レベルと低レベルで表されます。チップ B がこのピンが Low レベルであることを検出した場合は 0 を受信したことを意味し、チップ B がこのピンが High レベルであることを検出した場合は 1 を受信したことを意味します。

画像-20211029114040717

逆に、ローレベルで1を受信したことを示し、ハイレベルで0を受信したことを示すことは可能でしょうか。もちろん、チップ A とチップ B が事前に合意している限り、チップ A がデジタル 1 を送信したい場合は、ピンを Low レベルに設定します。チップ B はピンがローレベルであることを検出し、デジタル 1 を受信したことを示し、通信が完了します。

画像-20211029114120140

ピンにはハイとローの 2 つの状態があり、それぞれデジタル 0 と 1 の 2 つの状況を表すことができます。チップ A がチップ B に 0、1、2、3 の数字を送信したい場合はどうなるでしょうか?

チップ A とチップ B を 2 つのピン、つまり a と b の 2 つの線で接続できます。両方のラインがローレベルの場合はデジタル 0 を送信することを意味し、a がハイレベルで b がローレベルの場合はデジタル 1 を送信することを意味し、a がローレベルで b がハイレベルの場合はデジタル 2 を送信することを意味します。ラインが高い場合は、数字の 3 を送信することを意味します。

画像-20211029114157781

同じ原理に従って、チップ A がデータ 4、5、6、7 をチップ B に送信したい場合は、別の行を追加するだけです。3 本のワイヤには合計 8 つの状態があり、8 つの数値を表すことができます。要約すると、線の異なるレベルの状態は異なる意味を表すことができ、異なる状態の数と同じだけ多くの数値を表すことができます。

チップ A が +1、-1、0、+2 などの数値をチップ B に送信したい場合、ここで正と負をどのように表現するかを考えてみましょう。前の考え方を参照すると、線のハイレベルとローレベルの意味がチップの両側で事前に合意されており、この場合、単一の線を使用してシンボルを表現できます。正の数、高レベルは負の数を意味します。

画像-20211029114327334

上図に示した 3 本の線のうち、線 c は正と負を表すために使用されます。0 は正の数を表し、1 は負の数を表します。行 a と行 b を使用して値を表します。例として 3'b111 を取り上げます。これは 10 進数 7 として解釈できます。また、符号付き数値の元のコード「-3」として解釈することもできます。符号付き数値の補数 "-1" として解釈されますが、それがどのように解釈されるかは、エンジニアの 2 進数の定義によって異なります。この定義が回路間の通信に影響を与えない限り、問題は発生しません。したがって、数字の「0」と「1」は文字通りの数値の意味を表すだけでなく、正の記号や負の記号などの他の意味も表すことができます。同様に、デジタル回路では、2 進数は、8 進数、10 進数、16 進数、符号付き数、符号なし数、10 進数などの他の数体系の基礎となります。FPGA 設計において、10 進数と符号付き数の計算方法が分からない最も根本的な原因は、これらのデータに対応する 2 進数が分からないことであり、対応する 2 進数を理解していれば、多くの問題は解決できます。

以下の例を通して、生徒たちにこの概念をより深く理解してもらいましょう 多くの初心者は、FPGA で 10 進数の計算を実現するにはどうすればよいかよく尋ねます。「0.5+0.25」を例にしますと、0.5+0.25の結果が0.75になることはよく知られていますが、0.5、0.25、0.75を2進数でどう表現するか考えてみてはいかがでしょうか?固定小数点 10 進数、浮動小数点 10 進数、さらには上で説明したように、数行を使用して自分で定義するなど、このような表現方法は多数あるため、具体的な表現方法はエンジニアの実践に依存します。普通にできるので問題ありません。以下の表に示すように、エンジニアが 3 本のワイヤを使用して、バイナリ値で表される 10 進値を定義するとします。

バイナリ値 意味 バイナリ値 意味
3'b000 0.1 3'b100 0.25
3'b001 0.5 3'b101 0.3
3'b010 0.75 3'b110 0.8
3'b011 0.2 3'b111 0

バイナリ値の意味が自由に定義できることを示すために、数値の順序は逆になっています。では、なぜこれらの種類の小数だけを使用するのでしょうか? これは、想定しているシステムにはこれらの数値しかなく、より多くの数値を表現したい場合は行数を増やせば済むためです。上記の定義を完了すると、「0.5+0.25」を実現するのは非常に簡単です。これは、実際には 3'b010 が得られることを期待して、3'b001 と 3'b100 を「加算」したものです。ただし、テーブル内で 3'b001 + 3'b100 を直接使用すると、結果は「101」となり、望ましい結果ではなくなり、コードは次のように記述できます。

画像-20211029114740441

もちろん、これは書き方の一つであり、対応する機能が実現でき、結果が正しいのであれば、どのような書き方をしても問題ありません。

ここで疑問が生じるかもしれません。0.1+0.8 は 0.9 であるはずですが、上の表には 0.9 の表記がありません。これは実際には、設計者が定義したテーブルの欠陥であるか、設計者はこの状況は起こらないと考えています。ここで言いたいのは、対応する 2 進数が定義されていれば、多くの関数は簡単に設計できるということです。

もちろん、実際のエンジニアリングでは、通常は合意された慣例に従っており、別の方法を見つける必要はありません。たとえば、次の表は、一般的に使用される固定小数点 10 進数の定義です。

バイナリ値 意味 バイナリ値 意味
3'b000 0.0 3'b100 0.5
3'b001 0.125 3'b101 0.625
3'b010 0.25 3'b110 0.75
3'b011 0.3725 3'b111 0.8725

このとき、0+0.5=0.5、つまり 3'b000 と 3'b100 を足すと、3'b100 が得られることが期待されます。バイナリ 3'b000+3'b100 を直接使用して 3'b100 を取得できることがわかります。同様に、0.125+0.75=0.8725、つまり 3'b001 と 3'b110 を追加すると、3'b111 が得られることが期待されます。バイナリ 3'b001+3'b110 を直接使用して 3'b111 を取得できることがわかります。

0.5+0.75=1.25という計算を実現したい場合、この時点で1.25は表現範囲を超えていることが分かり、信号のビット幅を増やすか、小数点以下の桁のみを表現することでこの問題を解決できます。小数点以下の桁のみが表現されている場合、結果は 0.25 になります。つまり、3'b010 が得られると予想され、3'b100 と 3'b110 を加算します。3'b100 + 3'b110 = 4'b1010 を 3 ビットで表現すると 3'b010、つまり 0.25 であることを見つけるのは難しくありません。上記のことから、固定小数点の計算は複雑ではなく、固定小数点とバイナリ値の関係を定義した後、直接計算を実行できることがわかります。

5.2.3 不定状態

前述したように、デジタル回路にはハイレベルとローレベルしかなく、それぞれ1と0を表します。ただし、1'bx、1'bz など、x と z はコード内で頻繁に使用されます。では、x と z のレベルは何でしょうか? 答えは、この 2 つに対応する実際のレベルは存在しないということです。x と z は、デザイナーの意図、またはシミュレーターやシンセサイザーにこのコードを解釈する方法を指示するためのシミュレーション目的のものです。

Xの状態は不定状態と呼ばれるもので、合成ツールの設計者にレベルは問わない、0でも1でも良いということを伝えるための条件判断によく使われます。

画像-20211029114938488

上記の例から、判定条件は din== 4'b10x0 であることがわかります。これは din== 4'b1000||din==4'b1010 と同等です。ここで、「||」は「or」です。シンボル。

画像-20211029115013142

ただし、デザイン内で「din == 4'b10x0」と記述するよりも、din== 4'b1000||din == 4'b1010 と直接記述する方が、より直接的かつ単純であるため、より適切です。

シミュレーションの過程では、一部の信号は不定状態となるため、設計者は不定状態が妥当であるかどうかを慎重に分析する必要があります。0 か 1 かが本当に気にならない場合は、そのままにしておいても問題ありません。ただし、すべての信号が不定の状態にならず、0 か 1 かを明確に記述し、設計に「考える」手間を加えないようにすることをお勧めします。

5.2.4 ハイインピーダンス状態

Z 状態は一般にハイ インピーダンス状態と呼ばれ、設計者がこの信号を駆動しない (0 でも 1 でもない) ことを意味し、通常はトライステート ゲート インターフェイスで使用されます。

画像-20211030112458042

上図はトライステートバスの応用例で、図中の接続バスはCPUとFPGAの入出力を兼ねており、双方向インターフェースとなっています。一般的なハードウェア回路では、このラインにプルアップ抵抗(弱プルアップ)またはプルダウン抵抗(弱プルダウン)が接続されます。

CPU も FPGA もバスを駆動していないとき、ポイント A は High のままです。FPGA がバスを駆動せず、CPU がバスを駆動する場合、ポイント A の値は CPU によって決定されます。CPU がバスを駆動せず、FPGA がバスを駆動する場合、点 A の値は FPGA によって決定されます。ただし、FPGA と CPU は同時にバスを駆動することはできません。そうしないと A のレベルが不確実になります。通常、FPGA と CPU がバスを駆動するときは、事前に取り決められた合意に従って動作します。

画像-20211030112556798

上図は一般的な I2C タイミングです。I2C のバス SDA はスリーステート信号です。I2C プロトコルでは、上記の時間のうち、どの時間をマスターデバイスが駆動し、どの時間をスレーブデバイスが駆動するかが規定されており、両者は合意に従う必要があり、同時に駆動することはできません。では、FPGA はどのようにして設計内で「駆動しない」動作を実現するのでしょうか? これは、FPGA 内にスリーステート ゲートがあるためです。

画像-20211030112615735

トライステートゲートはハードウェアの一部であり、上図はその典型的な構造です。トライステート ゲートには、上図に示すように、ライト イネーブル wr_en、ライト データ wr_data、リード データ rd_data、および外部デバイスに接続されるトライステート信号 data の 4 つのインターフェイスがあります。

ライトイネーブル信号が有効な場合、トライステートゲートは wr_data の値をトライステートラインデータに割り当てます。このとき、データの値は wr_data によって決定されます。 0の場合はデータの値は0、wr_dataの場合は1の場合はデータの値は1です。ライトイネーブル信号が無効の場合、wr_data 値がどのような値であっても、外部データ値には影響しません。つまり、駆動されません。

Verilog では、上記の機能は次のコードによって実現されます。

画像-20211030112644309

シンセサイザーはこれら 2 行のコードを認識すると、それがスリーステート ゲートに合成されることを認識します。これが高インピーダンス z の役割です。また、ハードウェア上のトライステート線の使用はピンを減らすためであり、FPGA 内の配線を減らす必要がないため、トライステート信号を使用する意味がないことがわかります。したがって、自分で「考える」手間を加える必要がないため、設計時には FPGA 内部のハイ インピーダンス状態「z」を使用しないことをお勧めします。もちろん、ハイインピーダンス状態で設計すればエラーは報告されず、機能も実現可能です。

一般にハイインピーダンス状態「z」は「バスを駆動していない」動作を意味しますが、実際にはデジタル回路にはハイかローのレベルがあり、それ以外のレベルはありません。

5.3 算術演算子

画像-20211030112732563

画像-20211030145125938

算術演算子には、加算「+」、減算「-」、乗算「*」、除算「/」、剰余「%」があり、よく使用される算術演算子は主に加算「+」、減算「-」、乗算「*」です。 。

除算と剰余演算子は単純なゲート ロジックで構築されておらず、対応するハードウェア回路が比較的大きいため、一般的に使用される演算には除算と剰余演算子が含まれていないことに注意してください。加算と減算は最も単純な演算であり、乗算は複数の加算演算に分解できるため、加算、減算、乗算に対応する回路は比較的小規模です。割り算は異なりますが、乗算、シフト、加算、減算を複数回行う割り算の手順を生徒が思い出せるため、割り算に相当する回路が複雑になり、設計者もVerilogを設計する際に注意が必要になります。注意。

5.3.1 加算演算子

まず加算演算子を学習します。記号「+」は Verilog コードで直接使用できます。

画像-20211030112934610

その回路図は次のとおりです。

画像-20211030112947325

シンセサイザーは加算演算子を認識し、上に示したような回路に変換できます。2 進数の加算演算は 10 進数の加算演算と似ており、10 進数は 10 ごと、2 進数は 2 ごとです。バイナリ加算の基本演算は次のとおりです。

画像-20211030113004606

5.3.2 減算演算子

減算演算子である記号「-」は、Verilog コードで直接使用できます。

画像-20211030113035316

その回路図は次のとおりです。

画像-20211030113049315

シンセサイザーは減算演算子を認識し、それを上記の回路に直接変換できます。

2 進数の減算演算は 10 進数の減算演算に似ており、借入の概念もあります。10 進法では 1 を 10 として借用し、2 進法では 1 を 2 として借用します。1 ビット減算の基本的な演算は次のとおりです。

画像-20211030113108106

5.3.3 乗算演算子

乗算演算子、記号「*」は Verilog コードで直接使用できます。

画像-20211030113141837

その回路図は次のとおりです。

画像-20211030113153342

シンセサイザーは乗算演算子を認識し、それを上記の回路に直接変換できます。2 進数の乗算演算は 10 進数の乗算演算と似ており、計算プロセスも同じです。1 ビット乗算の基本的な演算は次のとおりです。

画像-20211030113213925

複数の桁間の乗算は、10 進数の計算プロセスと同じです。たとえば、2'b11 * 3'b101 の計算プロセスは次のようになります。

画像-20211030113232235

5.3.4 除算演算子と剰余演算子

除算演算子は Verilog コードで直接記号「/」を使用できますが、剰余演算子は「%」です。

画像-20211030142720756

除算回路の概略図は次のとおりです。

画像-20211030142733602

剰余回路の概略図は次のとおりです。

画像-20211030142749890

シンセサイザーは除算演算子と剰余演算子を認識できますが、これら 2 つの演算子には多数の乗算、加算、減算演算が含まれるため、FPGA 内の除算器の回路が非常に大きくなり、シンセサイザーを直接変換できない場合があります。図に示す回路。

ここで疑問が生じるかもしれません。なぜ除算と剰余には多くのリソースが必要なのでしょうか? 122 を 11 で割った場合を例として、小数の割り算と余りの処理を分析してみましょう。

画像-20211030142812957

上記の演算を実行するプロセスでは、複数のシフト、乗算、減算、その他の演算が必要になります。つまり、除算演算を実行するには複数の乗算器と減算器が使用され、比較的大きなハードウェア リソースが必要になります (2 項演算の場合も同様です)。

したがって、設計コードでは除算や剰余は一般的には使用されません。アルゴリズムでの除算と剰余演算を回避するさまざまな方法があります。したがって、デジタル信号処理、通信、画像処理などでは、乗算、加算、減算などの演算は多く見られますが、除算や剰余演算はほとんど見られません。しかし、シミュレーションテストでは除算と剰余が使用できます。これはシミュレーションテストにのみ使用され、回路に合成する必要がないため、当然のことながらリソースの占有量を気にする必要はありません。

5.3.5 経験の概要

ビット幅の問題

コードを記述するときは、信号のビット幅に注意する必要があり、最終的な結果は、「=」記号の左側にある信号のビット幅に依存し、下位ビットを保存し、上位ビットを破棄します。例えば:

画像-20211030142922330

信号 c のビット幅は 1 ビットなので、演算結果は最終的に最下位 1 ビットを確保するため、c の値は 1'b0 になります。dのビット幅は2ビットなので、演算結果の下位2ビットは確保できるので、dの値は2'b10となります。eのビット幅は3ビットなので、演算結果の下位3ビットは確保できるので、eの値は3'b010となる。「1」はデフォルトで32ビットで、1+1の結果も32ビットですが、fのビット幅は3ビットしかないため、演算結果の下位3ビットは予約できるので、fの値は3'b010です。

減算演算についても同様で、次のコードを例に挙げます。

画像-20211030142952611

「0-1」から得られるバイナリ値は「1111111111….」ですが、保存される結果は「=」記号の左側の信号のビット幅に依存します。c のビット幅は 1 で、最下位の 1 ビットは予約されているため、c の値は 1'b1 になります。d のビット幅は 2 ビットなので、結果では下位 2 ビットが予約され、d の値は 2'b11 になります。e のビット幅は 3 ビットなので、結果では下位 3 ビットが予約され、e の値は 3'b111 になります。f のビット幅は 4 ビットなので、演算結果の下位 4 ビットは予約できるため、f の値は 4'b1111 となります。

乗算コードを記述するときは、信号のビット幅にも注意する必要があります。最終的な結果は、「*」記号の左側にある信号のビット幅によって異なります。下位ビットを保存し、上位ビットを破棄します。 :

画像-20211030143016965

「2'b11 * 3'b101」で得られるバイナリ値は「4'b1111」ですが、保存される結果は「*」の左側の信号のビット幅によって異なります。c のビット幅は 1 で、最下位の 1 ビットは予約されているため、c の値は 1'b1 になります。d のビット幅は 2 ビットなので、結果では下位 2 ビットが予約され、d の値は 2'b11 になります。e のビット幅は 3 ビットなので、結果では下位 3 ビットが予約され、e の値は 3'b111 になります。f のビット幅は 4 ビットなので、演算結果の下位 4 ビットは予約できるため、f の値は 4'b1111 となります。h、信号は 5 ビットで、5 ビット信号に 4'b1111 が割り当てられ、結果は上位ビットが 0 で埋められるため、結果は 5'b01111 になることに注意してください。

補体の起源

FPGA がさまざまなアルゴリズムを実装する場合、最も重要なことは計算結果の正確性を保証することであり、そうでなければすべてが無意味になります。加算演算子と減算演算子を分析すると、結果を保存するための信号ビット幅が妥当かどうかが正確さに大きな影響を与えることがわかります。

たとえば、次のような加算演算です。

画像-20211030143139276

上の表から、キャリーが保存されていない場合、加算が発生したときに計算結果が正しくなくなり、キャリーが保存されている場合にのみ計算結果が正しいことがわかります。これから結論を導き出すことができます。加算を使用する場合、結果の正確性を保証するには、キャリーを保存する必要があります。つまり、結果のビット幅を拡張する必要があります。

たとえば、2 つの 8 ビット数値を加算する場合、結果は 1 ビット拡張する必要があり、ビット幅は 9 ビットに設定されます。

画像-20211030143209482

次に、次の表に示すように、減算演算を分析してみましょう。

画像-20211030143329162

表および 2'b00-2'b01 では、結果は 2'b11、対応する 10 進数値は 3 ですが、予期される結果は「-1」であることに注意してください。同様に、2'b01 - 2'b11、結果は 2'b10、対応する 10 進数値は 2、期待される結果は「-2」であるため、上記の結果は正しくありません。

期待される結果が正または負の場合、正または負の結果を区別するために符号ビットを追加できます。業界で合意された表現方法は、最上位ビットが 0 の場合は正の数を意味し、最上位ビットが 1 の場合は負の数を意味します。符号ビット以降の値は下位 2 ビットで表され、結果は次のようになります。

画像-20211030143414060

上の表から、符号ビットを追加した後も、演算結果が期待を満たさないといういくつかの問題が依然として存在することがわかります。たとえば、表の 2'b00-2'b01 の場合、結果は 3'b111、対応する 10 進数値は -3 ですが、予期される結果は「-1」です。したがって、上記の結果は依然として正しくありません。

ここで、次のように 2 進数「000~111」を再変換します

a.正の数: 変更されないまま

b.負の数: 符号ビットは変更されず、値が反転されて 1 が追加されます

つまり、正の数「+1」の場合、以前は「001」で表されていましたが、現在も「001」で表されます。負の数「-1」の場合、以前は「101」で表現していましたが、現在は「111」で表現しています。負の数「-3」は、以前は「111」で表されていましたが、現在は「101」で表されています。この表現は補数表現です

補数コードで表現した後、結果を分析してみましょう。

画像-20211030145353945

上の表の結果はすべて正しく、期待と一致していることがわかります。このプロセスではコードはまったく変更されませんでしたが、データの定義を変更することで正しい結果が得られました。

前の説明では、加数、加数、減数、および被減数の演算では符号付き数値を使用しませんでした。次に、符号付き数値の 2 の補数を使用してこれを再表現します。加数、被加数、減数、被数がすべて 2 ビット (範囲は -2 ~ 1) であると仮定すると、桁上げと借用の理由を考慮して、結果は 3 ビット (範囲は -4 ~ 3) で表されます。 )。結果のビット幅が 3 ビットになるため、次の表に示すように、減数と被減数は両方とも 3 ビットで表現されるように拡張されます。

画像-20211030145455123

画像-20211030145507551

操作手順の概要は次のとおりです。

  1. 「人間の常識」によれば、結果の最大値と最小値によって結果の信号ビット幅が決まると考えられます
  2. 加数、減数などのデータのビット幅を拡張して、結果のビット幅を一定にします。
  3. バイナリの加算と減算で計算を実行します。

上記の方法で得られるのは補数コードの結果です。実際、FPGA やコンピュータ システムでも、すべてのデータは補数形式で格納されます補数コードについて詳しく知りたい場合は、関連資料を参照してください。

5.4 論理演算子

画像-20211030145547059

画像-20211030145556624

Verilog HDL 言語には 3 つの論理演算子があります。

(1) &&: ロジックと;

(2) | | : 論理和;

(3) !: 論理否定

5.4.1 論理積

「&&」は二項演算子で、a && b のように 2 つのオペランドが必要です。

(1) 1ビット論理積

画像-20211105212729170

A と B が両方とも 1 の場合、C は 1、それ以外の場合、C は 0 になります。

対応するハードウェア回路図は次のとおりです。

画像-20211105212800939

(2) 複数ビットの論理積

画像-20211105212816535

A も B も 0 でない場合、C は 1 になり、それ以外の場合は 0 になります。

画像-20211105212839123

5.4.2 論理和

「||」は二項演算子で、 a||b などの 2 つのオペランドが必要です。
(1) 1ビットの論理和

画像-20211105213147969

A と B のいずれか 1 つは 1、C は 1、それ以外の場合は C は 0 です。

対応するハードウェア回路図を次の図に示します。

画像-20211105213306687

(2) 複数ビットの論理和

画像-20211105213322593

A と B のいずれかが 0 以外の場合、C は 1 になり、それ以外の場合、C は 0 になります。

対応するハードウェア回路図を次の図に示します。

画像-20211105213347308

5.4.3 論理否定

「!」は単項演算子であり、! (a>b) のようにオペランドを 1 つだけ必要とします。

画像-20211105213442864

オペランド a については、a が true かどうかを判定する必要があり、true の場合は {} 内の演算を実行し、false の場合は演算を終了します。
以下の表は論理演算の真理値表であり、aとbの値が異なる組み合わせの場合に、さまざまな論理演算で得られる値を示しています。

画像-20211105213506720

論理演算子の最終結果は、論理的に true または論理的に false、つまり 1 または 0 のみになります。一般に、論理演算子を判定条件として使用する場合、論理積演算は 1 ビット幅の数値 2 つだけであり、2 つの式が同時に真である場合にのみ真となり、一方が偽の場合は、それは間違っている可能性があります。

オペランドがマルチビットの場合、オペランドは全体としてみなされ、オペランドの各ビットが 0 の場合は論理 0 の値、オペランドに
1 があれば論理 1 の値となります。

画像-20211105213540379

4'b0111 も 4'b1000 も 0 ではないため、0 でなければ論理的に真とみなされ、上記のコードは次のコードと等価です。

画像-20211105213557611

つまり、結果は、a は論理的に真、b は論理的に真、c は論理的に偽になります。

5.4.4 経験の概要

(1)論理演算子の優先順位

論理演算子の中で、「&&」と「||」は算術演算子より優先順位が低く、「!」は両眼論理演算子より優先順位が高くなります。

例は次のとおりです。

画像-20211105213736040

(2)論理演算子の両辺が 1 ビットの信号に対応

経験両辺の論理演算子が1ビット信号に対応

画像-20211105213859092

上記のコードに注目してください。ここで、a と b は両方ともマルチビット信号であり、2 つのマルチビット信号が論理 AND 演算されていることを示しています。このコードの正しい理解は、a が 0 に等しくなく、b が 0 に等しくない場合、d の値は 1 であるということです。しかし、長年の実務経験を持つエンジニアであっても、上記のコードが意味する意味を直観的に理解することは困難です。0 に等しくない場合は論理的に真を意味し、0 に等しい場合は論理的に偽を意味するという概念は見落とされがちです。

したがって、上記のコードに誤りはありませんが、設計者は設計上の技術を誇示する目的でコードを書くべきではなく、このような書き方は設計ミスを招きやすいものです。たとえば、本来は assign d を表現している可能性があります。 = a & b ですが、結局のところ、設計が十分に直感的ではなかったために上記のコードが書かれ、設計上の問題が発生しました。したがって、自分や他の人がコードを見たときにすぐにそのコードの意味を理解できるように、直感的かつ分かりやすく設計することが非常に重要です。そのため、上記のコードは次の形式で記述することをお勧めします。

画像-20211105213942868

(3)優先順位を区別するための複数使用の括弧

体験2優先順位を覚えようとせず、かっこをもっと使用してください

実際のところ、エンジニアは仕事の優先順位をすべて覚えているわけではなく、優先順位をすべて覚えていてもエンジニアの作業効率が大幅に向上するわけではありません。設計では、次のコードが表示される場合があります。

(1) a < b && c > d ;

(2) a = = b | | c = = d ;

(3)!| a > b 。

演算子の優先順位を覚えていないと、上記 3 つの例のような状況に遭遇したときに、必ずある程度の時間をかけて考えを整理し、どの部分を最初に判断するかを考えることになります。エンジニアが優先順位を覚えている場合は、これらのコードの動作を伝達してチェックする必要もあります。人間は間違いを犯しやすく、これらの間違いはしばしば見落とされ、チェックするのが困難です。

したがって、プログラムの可読性を向上させ、演算子の優先関係を明確に表現するには、より多くの括弧を使用して設計することをお勧めします。上記の 3 つの例は次のように記述できます。

1)( a < b) &&( c > d);

2)( a = = b) | |( c = = d);

3)(!a) | |( a > b)。

(4)論理 NOT の使用を減らす

体験3「論理と」と「論理または」をより多く使用し、論理の使用を減らします。

「論理的および」を中国語に訳すと「および」、「論理的または」を中国語に訳すと「または」になります。信号フラグがあるとします。0 はアイドル、1 はビジーを意味します。"(!(flag== 0) && a== 4' b1000)" は、「アイドル時には反対の状態をとり、a が 4' b1000 に等しい場合に条件が true になります」と解釈されます。これは非常に読みにくいので、このコードを読むときは、もう少し頭を動かして、もう少し考える必要があります。コードをより直観的にするために、上記の例を「flag== 1 && a==4' b1000」として記述することをお勧めします。これは、「ビジーかつ a が 4' b1000 に等しいとき」という条件を意味します。それは本当です。

5.5 ビット単位の論理演算子

画像-20211105215609897

画像-20211105215735525

注: ~ ^、^ ~ (バイナリ XOR、NOR): (NOR 演算と同等)。

Verilog HDL 言語には次のビット演算子があります。

~ (単項 NOT): (NOT 演算と同等)

& (バイナリ AND): (AND 演算と同等)

| (バイナリ or): (OR 演算と同等)

^ (バイナリ XOR): (XOR 演算と同等)

これらの演算子は、入力オペランドの対応するビットをビット単位で演算し、ベクトルの結果を生成します。以下の図の各真理値表は、さまざまなビット単位の論理演算子のビット単位の演算の結果を示しています。

画像-20211106195003983

5.5.1 単眼のビットごとの AND

演算子の後の単項ビット単位 AND 演算子 & は、論理演算が必要な信号です。これは、信号の各ビット間の AND 演算を意味します。例えば

Reg[3:0] A、C;

C=&A を代入します。

上記のコードは C = A[3] & A[2] & A[1] & A[0] と等価です。A=4'b0110 の場合、C の結果は 0 になります。

5.5.2 単眼のビットごとの OR

単眼のビットごとの OR 演算子 | の後の演算子は、論理演算が必要な信号であり、信号がビット間で OR 演算されていることを示します。例えば

reg[3:0] A、C;

C=|A を代入します。

上記のコードは C = A[3] | A[2] | A[1] | A[0] と等価です。A=4'b0110 の場合、C の結果は 1 になります。

5.5.4 両眼のビットごとの AND

両眼のビット単位 AND 演算子 & では、信号が演算子の左側と右側に配置されます。これは、対応する位相 AND 演算が 2 つの信号に対して実行されることを意味します。例えば

reg[3:0] A、B、C;

C = A & B を代入します。

上記のコードは、C[0] = A[0] & B[0]、C[1] = A[1] & B[1]、C[2] = A[2] & B[2] と同等です。 ]、C[3] = A[3] & B[3]。A=4'b0110、B=4'b1010 の場合、C の結果は 4'b0010 になります。

オペランドの長さが等しくない場合は、長さの小さい方のオペランドの左端に 0 が埋め込まれます。例えば、

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

C = A & B を代入します。

上記のコードは次と同等です: C[0] = A[0] & B[0]、C[1] = A[1] & B[1]、C[2] = 0& B[2]、C[ 3] = 0 &0。

5.5.5 両眼のビットごとの OR

両眼のビットごとの OR 演算子 | では、信号が演算子の左側と右側に配置され、対応する位相 OR 演算が 2 つの信号に対して実行されることを示します。例えば

reg[3:0] A、B、C;

C = A | を割り当てる B;

上記のコードは次と同等です: C[0] = A[0] | B[0]、C[1] = A[1] | B[1]、C[2] = A[2] | B[2 ]、C[3] = A[3] | B[3]。A=4'b0110、B=4'b1010 の場合、C の結果は 4'b1110 になります。

オペランドの長さが等しくない場合は、長さの小さい方のオペランドの左端に 0 が埋め込まれます。例えば、

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

C = A | を割り当てる B;

上記のコードは次と同等です: C[0] = A[0] | B[0]、C[1] = A[1] | B[1]、C[2] = 0 | B[2]、C [3] = 0|0。

5.5.6 両眼のビットごとの XOR

両眼のビットごとの XOR 演算子 ^ では、信号が演算子の左側と右側に配置されます。これは、2 つの信号に対して対応する位相 XOR 演算を実行することを意味します。XOR は 0 0=0,1 1=0,0^1=1、つまり同じは 0、差は 1 を指します。例えば

reg[3:0] A、B、C;

C = A ^ B を代入します。

上記のコードは次と同等です: C[0] = A[0] ^ B[0]、C[1] = A[1] ^ B[1]、C[2] = A[2] ^ B[2 ]、C[3] = A[3]
^ B[3]。A=4'b0110、B=4'b1010 の場合、C の結果は 4'b1100 になります。

オペランドの長さが等しくない場合は、長さの小さい方のオペランドの左端に 0 が埋め込まれます。例えば、

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

C = A | を割り当てる B;

上記のコードは次と同等です: C[0] = A[0] ^ B[0]、C[1] = A[1] ^ B[1]、C[2] = 0 ^ B[2]、C [3] = 0^0。

5.5.7 経験の概要

論理演算子とビット演算子の違い

論理演算子には &&、||、! が含まれ、ビット演算子には &、|、~ が含まれますでは、論理演算子とビット演算子の違いは何でしょうか? 論理 AND "&&" とビット単位の AND "&" を比較すると、論理 AND 演算子の動作が次のようになります。結果は論理的に真か論理的に偽の 2 つだけです。、つまり 1 または 0、そして「&」はビット単位の演算子です。2 つのマルチビット幅のデータ操作の場合ビット演算子の場合、2 つの数値がビット単位で AND、OR、または NOT 演算されます。

画像-20211107101136767

上記の演算の結果は、a=1'b1、b=1'b1、c=1'b0、d=4'b0000、e=4'b1111、f=4'b1000 となります。

5.6 関係演算子

画像-20211107101351496

画像-20211107101503199

関係演算子は次のとおりです: > (より大きい)、< (より小さい)、>= (以上)、<= (以下)、== (論理的に等しい)、および ! = (論理的に等しくない)。

関係演算子は true (1) または false (0) に評価されます。オペランドの 1 つが x または z の場合、結果は x になります例: 23 > 45 : 結果は false ( 0 ) になります。52 < 8'hxFF: 結果は x です。

オペランドの長さが異なる場合、短い方のオペランドの最上位ビット方向 (左) にゼロが埋め込まれますたとえば、'b1000 >= 'b01110 は 'b01000 >= 'b01110 と同等であり、これは false (0) です。

論理的等価と不等号の比較では、一方のオペランドに x または z が含まれている限り、比較結果は不明 (x) になります。たとえば、 Data = 'b11x0; Addr = 'b11x0; then Data == Addr の場合、比較は行われます。結果は不確実です。つまり、結果は x です。

5.7 シフト演算子

Verilog HDL には、「<<」 (左シフト演算子) と「>>」 (右シフト演算子) という 2 つのシフト演算子があります。

以下に 2 つの使用法をそれぞれ説明します。

画像-20211107102901190

画像-20211107102911492

5.7.1 左シフト演算子

Verilog HDL では、「-」<< は左シフト演算子を表します。その一般的な表現は次のとおりです。

A << n;

このうち、Aはシフト対象のオペランド、nは左シフト対象のビット数を表します。この式の意味は、オペランド A を n ビット左にシフトすることです。左シフト演算は論理シフトであり、シフトアウトされた空きを埋めるために 0 を使用する必要があります。つまり、下位ビットに 0 が埋められます。n ビット左にシフトするには、n 個の 0 を埋める必要があります。

画像-20211107103016873

上記のコードは 2 ビット左シフトされ、下位ビットに 2 つのゼロが埋められるため、上記のコードの実行結果は次のようになります: a = 4'b1100。
左シフト演算の注意点は3つあります:
(1)左シフト演算は論理リソースを消費せず、AND ゲートや NOT ゲートも必要なく、単にラインを接続するだけです

画像-20211107103040064

上記のコードは、信号 b を 2 ビット左にシフトして c に割り当てます。対応するハードウェア回路は次のとおりです。

画像-20211107103103978

(2)左シフト演算はビット幅に応じて結果を格納する必要がある

学習プロセス中に次のコードを見たことがあるかもしれません: 4'b1001<<1=4'b0010 および 4'b1001<<1=5'b10010

オペランドも 1 ビット左にシフトされた 4'b1001 であるのに、結果が 4'b0010 と 5'b10010 になるのはなぜですか? これは、左シフト演算後、結果を格納するために使用されるビット数に依存するためです。

画像-20211107103702482

上記のコードでは、a が 4 ビットなので 4 ビットしか保存できないため、b を 1 ビット左にシフトして 4 ビットの a に代入し、シフトされたビットを 0 で埋めた結果は a = 4'b0010; となります。

画像-20211107103721433

上記のコードでは、a は 5 ビットなので、5 ビットの結果を格納できるため、b を 1 ビット左にシフトして 5 ビット a に代入し、シフトされたビットを 0 で埋めた後の結果は a = 5'b10010 になります。 ; ( 3**)
left シフト演算のオペランドには定数または信号**を指定できます。同様に、左シフト演算の定数であるシフト番号も信号にすることができます。

画像-20211107103747278

上記コードではcntは1クロックごとに1ずつインクリメントされ、3ビットなので値は0~2となります。a は 4'b1 を cnt ビットだけ左にシフトしたものです。cnt が 0 に等しい場合、0 ビット左にシフトし、a は 4'b1 に等しくなります。cnt が 1 に等しい場合、1 ビット左にシフトし、a は 4'b10 に等しくなります。類推すると、 a の各クロック変更は次のようになります。

画像-20211107103805477

なお、シフト数が信号の場合、集積回路は単純な接続ではなく、下図のようなセレクタが集積化される場合もありますただし、それでも、このハードウェア回路によって消費されるリソースはまだ比較的小さいです。

画像-20211107103822414

5.7.2 右シフト演算子

Verilog HDL では、「>>」を使用して右シフト演算子を表します。その一般的な表現は次のとおりです。

A>>n;

このうち、Aはシフトするオペランド、nは右シフトするビット数を表します。このコードの意味は、オペランド A を n ビット右にシフトすることです。

右シフト演算の注意点は3つ:
(1)右シフト演算は論理シフトであり、シフトアウトされた空きを埋めるには 0 が必要です。つまり、上位ビットを 0 で埋めるには、何個の 0 を埋める必要があるかは、信号のビット幅
によって異なります結果を保存します。

画像-20211107103914795

4'b0111 2ビット右シフトした結果は2'b01ですが、aは6ビットなので、6ビットに2ビットを代入するには上位ビットに0を足す必要があるので、0を4つ足す必要があります。したがって、上記のコードを実行した結果は次のようになります。

a = 6'b0001
(2)左シフト演算と同様に、右シフト演算はロジック リソースを消費せず、AND ゲートや NOT ゲートも必要なく、単にライン
を接続するだけです。

画像-20211107103936593

上記のコードは信号 b を 2 ビット左にシフトして a に割り当てます。対応するハードウェア回路は次の図に示されています。

画像-20211107103951964

(3) 左シフト演算のオペランドは定数または信号にすることができます。同様に、右シフト演算のシフト番号は定数または信号のいずれかになります

画像-20211107104010745

上記コードではcntは1クロックごとに1ずつインクリメントされ、3ビットなので値は0~2となります。a は 4'b1000 を cnt ビット右にシフトしたものです。cnt が 0 に等しい場合、0 ビット右にシフトすると、a は 4'b1000 に等しくなります。cnt が 1 に等しい場合、1 ビット右にシフトすると、a は 4'b0100 に等しくなります。類推すると、a の各クロックの変化は下図のようになります。

画像-20211107104037650

左シフト演算と同様に、右シフト演算でもシフト番号が信号の場合、集積回路は単純な接続ではなく、下図のようなセレクタを合成する場合があります。ただし、この場合でも、そのようなハードウェア回路によって消費されるリソースは依然として比較的小さいです。

画像-20211107104055622

5.7.3 経験の概要

左シフトを掛ける

FPGA では、この種の計算は大量のハードウェア リソースを占有する必要があり、演算速度も比較的遅いため、乗算演算はできるだけ避ける必要があります。乗算を使用する必要がある場合は、2 の N 乗を試してください。これにより、デザインで左シフト演算を使用して乗算演算を実現できるため、ハードウェア リソースが大幅に削減されます。

乗数が2のN乗の定数の場合、乗算はシフト演算で実現できる。たとえば、a 2 は a<< 1 と同等、a 4 は a<< 2 と同等、a*8 は a<< 3 と同等です。乗数が2のN乗の定数でなくても、シフト演算により実装を簡略化できる。例えば:

画像-20211107104134562

上記のコードの b と c は両方とも a*127 を実現できますが、最初の行では 1 つの乗算が使用され、2 番目の行では 1 つの減算器のみが使用されます

画像-20211107104147477

上記のコードでは、b と c は両方とも a*67 を実現できますが、最初の行では乗算が 1 つ消費され、2 行目では加算器が 2 つだけ使用されるため、リソースが節約されます。

上の 2 つの例の乗算器はすべて定数であることがわかりますが、この種の乗算も設計時に最適化を考慮するための時間と労力がかかるのでしょうか? 実際には、包括的なツールが非常に強力になったため、その必要はありません。ツールが乗数が定数であると判断した場合、上記のプロセスに従って自動的に最適化されます。つまり、定数による乗算は乗数を消費しません。本質的にリソースなので、安心して使用できます。

ただし、乗数が定数でない場合は乗算の使い方に注意が必要です。信号を 2 の N 乗に関連する形式に変換してみます。たとえば、後でデータを拡張して計算する必要がある場合、通常の考え方に従ってデータを 100 倍に拡張するのではなく、直接 128 倍に拡張します(従来の考え方に従って小数を100倍や1000倍に拡張したり、2のn乗に従って増幅したりすることは不可能であり、これによりリソース空間の占有が減少します。)。

右シフトを使用して除算演算を実装します

FPGAの設計では除算は極力避ける必要があり、除算計算に「/」を使うことすら厳禁です。これは、除算器が乗算器に比べて膨大なリソースを占有し、多くの場合、1 クロック サイクル内に結果が得られないためです。また、割り算を使用する必要がある場合は、割り算を 2 の N 乗で割る形式に変換するようにしてください。右シフト操作を使用する除算演算を実現し、ハードウェアリソースを大幅に削減します

除数が2のN乗定数の場合、シフト演算により除算が実現できます。たとえば、a/2 は a>>1 と同等、a/4 は a>>2 と同等、a/8 は a>>3 などと同等です。

左シフトとは異なり、除数が2のN乗定数でない場合は、シフト演算だけでは実装を簡略化できません。要約すると、FPGA 設計では除算はできる限り避けるべきです。

左シフトを使用したワンホットエンコーディング

ワンホット コードは、ワンホット コードとも呼ばれ、1 つのビットだけが 1 で、他のビットはすべて 0 であるコード システムですたとえば、8'b00010000、8'b1000000 などです。

ワンホット コードは設計において非常に役立ち、ステート マシンの状態を表すために使用してステート マシンをより堅牢にすることができます。また、複数選択回路で使用して、そのうちの 1 つを選択することを表すこともできます。

左シフト演算を使用すると、ワンホット コードを簡単に生成できます。たとえば、4'b1 << 1 となる 4'b0010 を生成できます。同様に、1 つのビットが 0、他のビットが 1 であるコード体系も生成できます。たとえば、4'b1011 を生成するには、~(4'b1 <<2) となります。左シフトを使用して、他の望ましい数値結果を生成することもできます。

たとえば、5'b00111 を生成するには、(5'b1<<3)-1 となります。

たとえば、5'b11100 を生成するには、~((5'b1<<2)-1) となります。

5.8 条件演算子

画像-20211107192312809

画像-20211107192334644

5.8.1 三項演算子

Verilog HDL 構文の **条件演算子 (?: )** には 3 つのオペランド (つまり、三項演算子) があり、その形式は一般に次のように表現されます。

画像-20211107192358180

その意味は、「条件式」が真(論理 1)の場合は「真の式」を実行し、「条件式」が偽(論理
0)の場合は「偽の式」を実行します。つまり、condition_expr が true (値が 1) の場合は true_expr を選択し、condition_expr が
false (値が 0) の場合は false_expr を選択します。condition_expr が x または z の場合、結果は次のロジックに従って true_expr と false_expr のビット単位の演算の値になります: 0 と 0 は 0 を生成し、1 と 1 は 1 を生成し、それ以外の場合は x を生成します。

適用例は次のとおりです。

画像-20211107192422886

上の式では、s が true の場合は t を r に代入し、s が false の場合は u を r に代入します。

対応するハードウェア回路図を以下に示します。

画像-20211107192454340

条件演算子の使用には次の注意点があります

(1)条件式の機能は、図 1.3-8 に示すように、実際にはマルチプレクサと似ています。また、if-else ステートメントに置き換えることもできます。

画像-20211107192527485

(2)条件演算子は、データ フロー モデリングでの条件割り当てに使用できます。この場合、条件式は制御スイッチとして機能します例えば:

画像-20211107192550223

このうち、式 Marks > 18 が true の場合、Grade_A には Student の値が割り当てられ、Marks > 18 が false の場合、
Grade_C には Student の値が割り当てられます。

対応するハードウェア回路図を以下に示します。

画像-20211107192607991

(3)条件演算子は入れ子にすることもでき、それぞれの「true 式」と「false 式」自体を条件
例えば:

画像-20211107192719206

上記のコードの意味は、式 M == 1 が true の場合、CTL が true であるかどうかを判断し、CTL が true の場合、A を OUT に割り当て、false の場合、B を OUT に割り当て、M = = 1 の場合は、が false の場合、CLT が true かどうかを判断し、CLT が true の場合は C を OUT に割り当て、false の場合は OUT に D を割り当てます。

対応するハードウェア回路図は次のとおりです。

画像-20211107192740274

5.8.2 if ステートメント

「if」ステートメントの構文は次のとおりです。
if(condition_1)

procedural_statement_1;
{else if(condition_2)

procedural_statement_2};
{else

プロシージャル_ステートメント_3};

その意味は、条件 1 が満たされると、残りの条件が満たされるかどうかに関係なく、procedural_statement_1 が実行され、procedural_statement_2 も procedural_statement_3 も実行されません。

条件 1 が満たされず、条件 2 が満たされた場合、procedural_statement_2 が実行され、procedural_statement_1 も procedural_statement_3 も実行されません。

条件 1 が満たされず、条件 2 も満たされない場合、procedural_statement_3 が実行され、procedural_statement_1 も processedural_statement_2 も実行されません。

例を挙げて説明しましょう。

if(合計 < 60) 開始

グレード = C;

合計_C = 合計_C + 1;
end
else if(Sum < 75) begin

グレード = B;

合計_B = 合計_B + 1;
終了、
そうでない場合は開始

グレード = A;

合計_A = 合計_A + 1;
終わり


条件式は常に括弧で囲む必要があり、次の例に示すように、
if - if - else 形式を使用すると曖昧になる可能性があることに注意してください

Q = 0;
それ以外

Q = D;

ここで質問があります: 最後の else はどの if ステートメントに属しますか? それは最初の if の条件 (Clk) に属しますか? それとも 2 番目の if の条件 (Reset) に属しますか? これは Verilog HDL で else を組み合わせることで行われます。最も近い none Else の if ステートメントが
解決に関連付けられます。この例では、else は内部の if ステートメントに関連付けられています。

if ステートメントの別の例を次に示します:
if(Sum < 100)

合計 = 合計 + 10;
if(ニッケル_イン)

デポジット = 5;
エルセイフ (ダイム_イン)

デポジット = 10;
else if(四半期_In)

デポジット = 25;
それ以外

デポジット = エラー;
提案

1.条件式は括弧で囲む必要があります

2. if - if ステートメントの場合は、以下に示すように、ブロック ステートメント begin — end を使用してください。
if (Clk) が始まる

if(リセット)

Q = 0;

それ以外

Q = D;
終わり

上記の 2 つの提案は、コードをより明確にし、エラーを防ぐことです。

5.8.3 case ステートメント

case ステートメントは多方向の条件分岐形式であり、その構文は次のとおりです:
case(case_expr)
case_item_expr{case_item_expr} :procedural_statement
. . . . . .
[default:procedural_statement]
endcase

case ステートメントでは、最初に条件式 case_expr が評価され、次に各分岐項目が順番に評価および比較され、条件式の値に一致する最初の分岐のステートメントが実行されます1 つのブランチ内に複数のブランチ項目を定義でき、これらの値は相互に排他的である必要はありません。デフォルトのブランチは、ブランチ式でカバーされていない他のすべてのブランチをオーバーライドします。

画像-20211107193238224

記述上の提案: case ステートメントのデフォルト項目は、ラッチの生成を防ぐために記述する必要があります。

5.8.4 Select ステートメント

Verilog 構文には一般的に使用される選択ステートメントがあり、その構文は次のとおりです。

vect[a +: b] または vect [a -: b]

vect は変数名、a は開始位置、プラス記号またはマイナス記号は昇順または降順を表します, b は昇順または降順の幅を表します

vect[a +: b] は vect[a : a+b-1] と等価です。 vect の間隔は a から始まり、a を超える方向に b 回カウントします。vect[a -: b] は vect と等価です。 [a : a -b+1] の場合、 vect の区間は a から a が小さい方向に b 回カウントされます。a は定数または変数の数値にすることができますが、b は定数である必要があります。

例 1 : vect[7 +: 3]; ここで、開始位置は 7、+ は昇順を表し、幅は 3 です。つまり、7から7より大きい方向に3つの数字を数えます。同等の形式は、vect[7 +: 3]== vect[7 : 9] です。

例 2 : vect[9 -: 4]; このうち、開始位置は 9、- は降順を表し、幅は 4 です。つまり、9から9より小さい方向に4つの数字を数えます。同等の形式は、vect[9 -: 4]== vect[9 : 6] です。

実際に使用されるこの構文の最も一般的な形式は、 a を変数数値として使用することです。たとえば、
次の関数を含むコードを設計する必要があります。

cnt==0 の場合は data[15:8] に din[7:0] を代入し、cnt==1 の場合は data[7:0] に din[7:0] を代入します。

設計時には、data[15-8 cnt -: 8] <= din[7:0] のように書くことができます(このとき、15-8 cntを全体として考える必要があり
、これは変更に伴い発生します) cnt の変更)、コードの合理化が完了します。

選択ステートメントのハードウェア回路構造は次の図に示されており、本質的にはセレクターです。cnt==0 の場合、data[15:8] のラッチを選択し、din[7:0] を data[15:8] に割り当て、data[7:0] のラッチは出力を変更しないままにします。 =1、data[7:0]のラッチを選択し、din[7:0]をdata[7:0]に割り当て、data[15:8]のラッチが出力Changeを維持します。

画像-20211107193442661

経験上の結論: 実際のプロジェクトでは、選択ステートメント vect[a +: b] または vect [a -: b] の形式をコード記述に使用でき、設計コードを簡素化するのに役立ちます。

5.8.5 経験の概要

if ステートメントと case ステートメントは Verilog の 2 つの非常に重要なステートメントであり、if ステートメントと case ステートメントには一定の相関関係と
相違点があります。ほぼ同じ機能を実現できるということは同じですが、以下では両者の違いを中心に紹介します。
if ステートメントの各分岐間には優先順位があり、合成された回路はカスケード構造に似ています。case ステートメントの各分岐は等しく、合成された回路はマルチプレクサーですしたがって、複数の if else-if 文を組み合わせた論理回路の遅延は、case 文よりも若干大きくなる場合があります初心者の場合、Veriolg を学習する最初の段階で if else-if ステートメントを使用することがよくあります。これは、この構文の方が表現が簡単だからです。ただし、実行速度がより重要なプロジェクトでは、case ステートメントを使用する効果が高くなります。if ステートメントと case ステートメントを使用して、同じ機能回路の総合的な結果を記述し、具体的なケースを通して比較してみましょう。

まず、if ステートメントを使用して記述されたコードです。

画像-20211107193601401

画像-20211107193618227

合成された RTL ビューを以下に示します。

画像-20211107193643645

上図の RTL 図からわかるように、この回路には 2 対 1 マルチプレクサが 2 つ含まれており、右側の優先順位が左側よりも高くなります (q の値が直接関係しているため)。右のデバイス接続での 2 対 1 選択)、en[0] が 1 でない場合、en[1] の判定が継続されます。つまり、if文で合成された回路が優先されます。

次に、case ステートメントを使用して記述されたコードを分析します。

画像-20211107193718179

合成された RTL ビューは次のとおりです。

画像-20211107193742651

見てわかるように、case文で記述された論理コードで合成された回路は並列であり、優先順位がなく、回路の動作速度に影響を与えません。

RTL ビューでは、2 つのステートメントによって合成された回路には大きな違いがありますが、現在の開発ツールは十分に賢いため、回路はレイアウトと配線中に自動的に最適化され、最終的に回路にマッピングされます。 FPGA 内部にあり、基本的に両者に違いはありません

画像-20211107193823156

画像-20211107193833956

最後に、if ステートメントと case ステートメントの違いと関係をまとめます。

If文が優先され、if文の条件が成立しない場合のみelse以降が実行されます。case ステートメントは並列であり、優先順位がありません。これは、2 つによって合成された RTL ビューで明確に観察できます。ただし、現在のシミュレーションおよび合成ツールは十分強力であるため、if...else... ステートメントと case... ステートメントの最終的な合成結果は実際には違いはなく、単に 2 つの異なる実装方法であるため、基本的には違いはありません。 2 つの違いの違いを考慮する必要があります。機能に影響を与えないことを前提に、if/case文のリソース消費の違いや回路の最適化を考慮するなど、設計者は局所的な最適化作業を行う必要がありません。関数に影響を与える (つまり、シーケンス制約によるエラーを報告する) という前提の下でのみ、プロンプトに従って回路を最適化します。

5.9 連結演算子

画像-20211107193940242

画像-20211107193949282

連結演算は、小さな式を結合して大きな式を形成する演算であり、その形式は次のとおりです。

{式1、式2、. .,exprN} ;

スプライシング文字はハードウェア リソースを消費せず、単に線の組み合わせを変更するだけです。次の例を参照してください。

画像-20211107194014357

非固定長定数の長さが不明であるため、非固定長定数の連結は許可されません。したがって、以下に示すコードは文法的ではありません。{Dbus,5}; //非固定長定数の連結演算は許可されません

おすすめ

転載: blog.csdn.net/Royalic/article/details/121196365