Menu

Verilog 連続代入:assign文

assignの動作。常に真である関係を記述すること、何を駆動できて何を駆動できないか、手続き的なコードと比べたときに光るパターン。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

assignは永続的な真理を記述する

wire宣言はsignalを作ります。assign何がそれを駆動するか を記述します。関係は連続的で、右辺が何であれ、左辺はそれに等しく、シミュレートされる時間の全時点でそうです:

wire y;
assign y = a & b;

この2行が暗示する2つのこと:

  • yは回路内のwireとして存在する。
  • yは常にabのビット単位ANDである。どちらかの入力を変えれば、yは追従する。

クロックも更新トリガとなるイベントもありません。シミュレータはaまたはbの変化を検知し、yを汚れたものとマークし、式を再評価します。ハードウェアでは、これがAND gateにマップされます。組み合わせ、stateless、瞬時です。

暗黙的な形式

宣言と代入を1行にまとめられます:

wire y = a & b;

これは上の2行と同じです。このスコープ外では誰も気にしないインラインwireに便利です。多くのスタイルガイドは、宣言を式のすぐ隣に置くため、暗黙的な形式を好みます。

assignが駆動できるもの

assignのターゲットは ネット型 でなければなりません。プレーンVerilogではwire(または稀なtriwandwor)です。regはだめです。誤ってターゲットをregとして宣言した場合:

reg y;
assign y = a & b;   // エラー: assignでregを駆動できない

コンパイラが教えてくれます。ターゲットをwireに変えるか、regが正当なターゲットであるalways @(*)ブロックにロジックを移動します。

右辺は値に評価される何でも構いません。リテラル、signal、parameter、演算子の式、関数呼び出し。複数の入力幅を混在させることもでき、標準の幅拡張ルールが適用されます。

assignalwaysの使い分け

両方とも組み合わせ論理を生成できます。選択は主にコードの読みやすさです:

  • assignは関係が単一の式の場合に最適。 加算器、?:で構築する単純なmux、マスク、パリティビット、1行で書けるあらゆるもの。
  • always @(*)は手続き的な文が必要な場合に最適。 多分岐のcase文、ネストしたif/else if、名前付き中間regの恩恵を受けるもの。Always Blockで扱います。

同じ4-to-1 muxを両方の書き方で:

両moduleはほぼ同じmultiplexerに合成されます。assign版は1行、always版は6行。4ケースなら近いですが、16ケースになるとcaseブロックの方が明らかに読みやすいです。

よくあるパターン

単純な組み合わせ論理

assign sum   = a + b;
assign carry = a[7] & b[7];
assign equal = (data == 8'hFF);

1つの式、1つのwire。assignの主役です。

2-to-1 mux

assign out = sel ? a : b;

単一の条件式。合成ツールは単一の2-to-1 muxに変換します。「aとbから選ぶ」の最もきれいな書き方です。

ビット詰め込み

assign status = {error, overflow, ready, busy, 4'b0};

assignの右辺での連結は、フラグをステータスバイトに詰め込む方法です。結果は連続的に計算され駆動されます。

Tri-state出力

assign data_pin = output_enable ? data_out : 1'bz;

output_enableが1のとき、ピンを駆動します。0のとき、ハイインピーダンスに解放します。複数のドライバがwireを共有する可能性のあるチップピンでの定番パターンです。

並行性:複数のassignは順序ではない

何度も関係するリマインダーです。同じmodule内の複数のassign文は すべて並列に動作します 。順序ではありません:

assign y = a & b;     // 常に存在
assign z = a | b;     // こちらも独立に常に存在

ファイル内の順序は無関係です。両方の式が同時に真です。合成ツールはAND gateをORの左に置くか右に置くかもしれませんが、関係ありません。両方のgateは連続的に発火します。

順序的な見た目の挙動が欲しいなら、alwaysブロック(おそらくクロック付き)に手が伸びます。それは別の章です。

複数ドライバ:バスのパターン

wireは複数のassignでターゲットにできますが、tri-stateバス以外ではほぼ常にこれを避けたいでしょう。wireを争う2つのドライバは未定義動作を生みます:

assign y = a;
assign y = b;   // 悪い - 2つのドライバ、シミュレータは片方を選ぶかxにする

正当なパターン:各ドライバが非アクティブ時にzに解放し、任意の時点でアクティブなのは多くとも1つだけ。

assign bus = device_a_active ? data_from_a : 1'bz;
assign bus = device_b_active ? data_from_b : 1'bz;

任意の時点で、2つの三項演算子のうち多くとも1つだけが非z値を生成するので動きます。wireの実際の値は、解放していないドライバの値です。

内部ロジック(チップピンやチップ内共有バス以外のどこでも)では、1つのwireに1つのドライバ。多ドライバのバグはデバッグが厄介です。

assignにできないこと

assignが間違ったツールである例:

  • ストレージ。 assignは組み合わせの関係を記述します。flip-flopを導入できません。値をクロックサイクル間で記憶する必要があれば、always @(posedge clk)ブロックの出番です。
  • 多段階の手続きロジック。 assign内でif/elsecaseを書けません。最も近いのはチェーン化した?:ですが、3分岐を超えると醜くなります。
  • 手続きブロック内からregisterを駆動する。 regターゲットはassignではなく手続き的代入が必要です。

限界を知ることが、いつalwaysに切り替えるかを判断する方法です。

次に読むもの

これでVerilogの構造的な側面を完全に見ました。moduleの宣言、インスタンス化、組み合わせ論理をassignで配線する。次の章は手続きブロックに入ります。時間と順序が重要になり始めるinitialalways構文です。

よくある質問

Verilogの連続代入とは?

assign target = expression;は永続的で連続的な関係を宣言します。targetは常にexpressionに等しい、ということです。式中のsignalが変わるたびに、シミュレータは右辺を再評価し、targetを更新します。クロックもイベントもなく、関係はあらゆる時点で真です。

Verilogでassignのターゲットにできるものは?

assignwireを駆動できますが、regは駆動できません。ターゲットはネット型でなければなりません。代わりにalwaysブロック内で何かに代入したい場合は、それをregとして宣言してください。xregなら、コンパイラはassign x = ...を拒否し、xwireならalways内のx = ...を拒否します。

assignとalways blockのどちらを使うべき?

シンプルな組み合わせ論理(1つの式が入り、1つのsignalが出るだけで、if/elseが不要)にはassignを使います。ロジックが手続き的な文(caseif/else ifチェーン、forループ)を必要とする場合はalways @(*)を使います。両者とも組み合わせハードウェアを生成します。選択は可読性の問題です。

同じwireに複数のassignを行えますか?

各ドライバが非アクティブ時にwireをzに解放するtri-stateバスをモデル化している場合のみです。2つのassignが同時にwireを確定値に駆動しようとすると競合します。シミュレータは片方を選ぶかもしれないし、signalをxにするかもしれず、ツール次第です。通常の組み合わせ論理では、1つのwireに1つのドライバです。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める