Altifigence Academy

51 / 63 · 概念

ALU と zero・negative・carry・overflow フラグ

共有加算器で ADD/SUB を構成し、キャリーと符号付きオーバーフローを分けて比較条件まで導きます。

前提知識:2 の補数2 の補数 固定幅のビット列で符号付き整数を表す方式です。n ビットの範囲は −2^(n−1) から 2^(n−1)−1 です。 詳しく見る、加算器、組合せ MUXマルチプレクサ 選択信号に従い、複数の入力から一つを出力に接続する組合せ回路です。選択ビットと入力番号の対応を仕様で定めます。 詳しく見る、ビット幅拡張を理解していること。

結果と状態を一緒に作る ALU

ALU は演算ごとの回路結果を選び、その解釈に必要な状態ビットを出力します。この設計の入力 A,BA,B と結果 RR は WW ビットで、ADD・SUB・AND・OR・XOR を扱います。算術結果は常に下位 WW ビットへ切り詰めます。同じビット列を unsignedunsigned ビット列を非負整数として解釈する規則です。n ビットの範囲は 0 から 2^n−1 で、同じビット列の signed 解釈と異なる場合があります。 詳しく見る または 2 の補数 signed と読めるため、carry と overflow を別々に出します。符号解釈ごとに二つの加算器を作る必要はありません。

フラグは Z=(R=0)Z=(R=0)、N=RW−1N=R_{W-1} です。Z は選択結果全体のリダクション NOR、N は最上位ビットです。N=1 は格納された列の signed 解釈が負という意味です。オーバーフローが起きた場合、無限精度で計算した実際の和の符号と異なることがあります。

キャリーとオーバーフローを分ける

加算では、拡張した和 TT の最上位ビットが C です。

T={0,A}+{0,B},R=T[W−1:0],C=T[W]T=\{0,A\}+\{0,B\},\qquad R=T[W-1:0],\quad C=T[W]

V は両入力の符号が同じで結果の符号が変わったとき 1 です。各最上位ビットを a,b,ra,b,r とすると、

Vadd=¬(a⊕b)∧(a⊕r)V_{\mathrm{add}}=\neg(a\oplus b)\land(a\oplus r)

8 ビットで 255+1255+1 は R=0、C=1、V=0 です。signed なら −1+1=0-1+1=0 で範囲内です。一方、127+1127+1 は R=0x80、C=0、V=1 です。unsigned の 128 は表せますが、signed の最大値 127 を超えます。C と V を一つにすると区別できません。

減算では W ビットの B だけを否定否定 0 を 1 に、1 を 0 に変える NOT 演算です。式の上の線は、その下の式全体に NOT を適用することを表します。 詳しく見るし、その後に上位 0 を付けて拡張します。つまり T={0,A}+{0,¬B}+1T=\{0,A\}+\{0,\neg B\}+1 です。W+1 ビット全体を否定すると、上位キャリーの意味が変わります。この講義では C=1 をボローなしと定義するため、unsigned の A<BA<B は ¬C\neg C です。

Vsub=(a⊕b)∧(a⊕r)V_{\mathrm{sub}}=(a\oplus b)\land(a\oplus r)

符号の異なる値の減算でだけ signed オーバーフローが起こり得ます。比較命令は SUB の R を保存せず、フラグだけ使えます。signed A<BA<B は N⊕VN\oplus V、signed A≤BA\le B は Z∨(N⊕V)Z\lor(N\oplus V) です。例えば −128−1-128-1 の格納結果は +127 ですが、V=1 が N だけを見たときの誤りを補正します。

下は 8 ビット ADD の例です。unsigned の結果は表せますが、signed の結果は範囲を超えます。同じ和で C=0 と V=1 が両立することを確認してください。

127 + 1 で C と V を区別
  1. A=7F · B=01: 符号付き 127 + 1
  2. 9 ビットの和 T: 0_10000000
  3. 8 ビットの結果 R: 80 → 符号付き −128
  4. 状態フラグ: Z=0 · N=1 · C=0 · V=1

共有加算器と明示的な出力規則

次の RTL は W≥2 を前提とします。opcode 0~4 は ADD・SUB・AND・OR・XOR です。論理演算の C,V は 0 とします。未使用 opcode は R=0 なので Z=1、他のフラグは 0 です。これは特定 CPU の命令セットの模倣ではなく、この ALU のインターフェース規則です。

SystemVerilog
module alu #(parameter int W = 8) (
  input  logic [W-1:0] a, b,
  input  logic [2:0] op,
  output logic [W-1:0] y,
  output logic z, n, c, v
);
  logic sub;
  logic [W-1:0] bx;
  logic [W:0] sum;
  always_comb begin
    sub = (op == 3'd1);
    bx = b ^ {W{sub}};
    sum = {1'b0, a} + {1'b0, bx}
        + {{W{1'b0}}, sub};
    y = '0;
    c = 1'b0;
    v = 1'b0;
    case (op)
      3'd0, 3'd1: begin
        y = sum[W-1:0];
        c = sum[W];
        if (sub)
          v = (a[W-1] ^ b[W-1]) & (a[W-1] ^ y[W-1]);
        else
          v = ~(a[W-1] ^ b[W-1]) & (a[W-1] ^ y[W-1]);
      end
      3'd2: y = a & b;
      3'd3: y = a | b;
      3'd4: y = a ^ b;
      default: y = '0;
    endcase
    z = ~(|y);
    n = y[W-1];
  end
endmodule

B の条件付き否定と初期キャリーにより、ADD/SUB を一つの加算構造で表します。各分岐で y を決めた後に Z,N を計算するため、論理演算にも一貫した状態が付きます。デフォルト代入は全出力経路を定義し、ラッチラッチ 制御信号が有効な間は入力に追従し、無効な間は値を保持する記憶素子です。エッジでのみ記憶するフリップフロップとは異なります。 詳しく見るを防ぎます。レジスタレジスタ 複数ビットの状態を記憶する回路です。このコースの同期式レジスタは、クロックエッジで定められた入力を記憶します。 詳しく見るはないのでクロック単位の計算遅延はありませんが、実信号の伝搬遅延はあります。演算選択 MUX とゼロ検出も、次レジスタのセットアップ予算内に収まる必要があります。

検証では 0・unsigned 最大値・signed 最大/最小値・境界の両側を組み合わせます。R は modulo 2W2^W の基準値、C は unsigned の拡張和、V は signed の範囲超過として独立に比較します。R が正しくてもフラグ定義が誤っている場合があります。

負・ゼロ・unsigned キャリー・signed オーバーフローを分ける実例は、Arm の条件フラグの説明で確認できます。

自分で考えてみましょう

8 ビット ALU で (1) 0x7F+0x01、(2) 0xFF+0x01、(3) 0x00−0x01、(4) 0x80−0x01 の R,Z,N,C,V を求めてください。(4) の signed 比較で N だけでは誤る理由も説明してください。

解説を見る

(R,Z,N,C,V) の順で、(1) (0x80,0,1,0,1)、(2) (0x00,1,0,1,0)、(3) (0xFF,0,1,0,0)、(4) (0x7F,0,0,1,1) です。(3) は unsigned のボローがあり C=0、signed の −1 は表現可能です。(4) は unsigned 128−1=127 でボローなしなので C=1 です。signed では −128−1=−129 が範囲外で V=1 となり、格納結果の N=0 は実際の差の符号を表しません。signed A<B は N xor V=1 なので正しく真になります。

選択はこのブラウザに適用されます。フッターからいつでも変更できます。