51 / 63 · 概念
ALU と zero・negative・carry・overflow フラグ
共有加算器で ADD/SUB を構成し、キャリーと符号付きオーバーフローを分けて比較条件まで導きます。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
前提知識:2 の補数2 の補数 固定幅のビット列で符号付き整数を表す方式です。n ビットの範囲は −2^(n−1) から 2^(n−1)−1 です。 詳しく見る、加算器、組合せ MUXマルチプレクサ 選択信号に従い、複数の入力から一つを出力に接続する組合せ回路です。選択ビットと入力番号の対応を仕様で定めます。 詳しく見る、ビット幅拡張を理解していること。
結果と状態を一緒に作る ALU
ALU は演算ごとの回路結果を選び、その解釈に必要な状態ビットを出力します。この設計の入力 と結果 は ビットで、ADD・SUB・AND・OR・XOR を扱います。算術結果は常に下位 ビットへ切り詰めます。同じビット列を unsignedunsigned ビット列を非負整数として解釈する規則です。n ビットの範囲は 0 から 2^n−1 で、同じビット列の signed 解釈と異なる場合があります。 詳しく見る または 2 の補数 signed と読めるため、carry と overflow を別々に出します。符号解釈ごとに二つの加算器を作る必要はありません。
フラグは 、 です。Z は選択結果全体のリダクション NOR、N は最上位ビットです。N=1 は格納された列の signed 解釈が負という意味です。オーバーフローが起きた場合、無限精度で計算した実際の和の符号と異なることがあります。
キャリーとオーバーフローを分ける
加算では、拡張した和 の最上位ビットが C です。
V は両入力の符号が同じで結果の符号が変わったとき 1 です。各最上位ビットを とすると、
8 ビットで は R=0、C=1、V=0 です。signed なら で範囲内です。一方、 は R=0x80、C=0、V=1 です。unsigned の 128 は表せますが、signed の最大値 127 を超えます。C と V を一つにすると区別できません。
減算では W ビットの B だけを否定否定 0 を 1 に、1 を 0 に変える NOT 演算です。式の上の線は、その下の式全体に NOT を適用することを表します。 詳しく見るし、その後に上位 0 を付けて拡張します。つまり です。W+1 ビット全体を否定すると、上位キャリーの意味が変わります。この講義では C=1 をボローなしと定義するため、unsigned の は です。
符号の異なる値の減算でだけ signed オーバーフローが起こり得ます。比較命令は SUB の R を保存せず、フラグだけ使えます。signed は 、signed は です。例えば の格納結果は +127 ですが、V=1 が N だけを見たときの誤りを補正します。
下は 8 ビット ADD の例です。unsigned の結果は表せますが、signed の結果は範囲を超えます。同じ和で C=0 と V=1 が両立することを確認してください。
- A=7F · B=01: 符号付き 127 + 1
- 9 ビットの和 T: 0_10000000
- 8 ビットの結果 R: 80 → 符号付き −128
- 状態フラグ: 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 のインターフェース規則です。
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
endmoduleB の条件付き否定と初期キャリーにより、ADD/SUB を一つの加算構造で表します。各分岐で y を決めた後に Z,N を計算するため、論理演算にも一貫した状態が付きます。デフォルト代入は全出力経路を定義し、ラッチラッチ 制御信号が有効な間は入力に追従し、無効な間は値を保持する記憶素子です。エッジでのみ記憶するフリップフロップとは異なります。 詳しく見るを防ぎます。レジスタレジスタ 複数ビットの状態を記憶する回路です。このコースの同期式レジスタは、クロックエッジで定められた入力を記憶します。 詳しく見るはないのでクロック単位の計算遅延はありませんが、実信号の伝搬遅延はあります。演算選択 MUX とゼロ検出も、次レジスタのセットアップ予算内に収まる必要があります。
検証では 0・unsigned 最大値・signed 最大/最小値・境界の両側を組み合わせます。R は modulo の基準値、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 なので正しく真になります。