3 / 24 · 実習
実習:8ビットALUの参照モデル
独立した実行ファイルでADD、SUB、AND、XOR、キャリー、ボローを検査します。
レッスンは無料で読めます。受講登録すると進捗を保存できます。
数値の契約を実行可能にする
この実習ではJavaScriptの整数で組み合わせALUをモデル化します。オペランドは0から255、演算は0から3です。出力yは常に8ビットに収まります。フラグはADDキャリーまたはSUBボローを表し、論理演算ではゼロです。このインターフェースではaがbより小さいときボローを1とします。他のアーキテクチャでは減算フラグの定義が異なることもありますが、ここでは契約を明示しています。
ローカルフォルダを作るか、Digital Design Studioで空のプロジェクトを手動で作成し、alu-model.mjsを追加してください。次のファイルを保存します。Node.jsがインストールされた端末でフォルダに移動し、node alu-model.mjsを実行します。DDSでファイルを編集することと、JavaScriptを実行することは別の操作です。パッケージのインストールや自動インポート機能は必要ありません。
import assert from 'node:assert/strict';
function alu(a, b, op) {
if (op === 0) {
const wide = a + b;
return { y: wide & 255, flag: Number(wide > 255) };
}
if (op === 1) return { y: (a - b) & 255, flag: Number(a < b) };
if (op === 2) return { y: a & b, flag: 0 };
if (op === 3) return { y: a ^ b, flag: 0 };
throw new RangeError('operation');
}
assert.deepEqual(alu(250, 10, 0), { y: 4, flag: 1 });
assert.deepEqual(alu(3, 5, 1), { y: 254, flag: 1 });
assert.deepEqual(alu(170, 204, 2), { y: 136, flag: 0 });
assert.deepEqual(alu(170, 204, 3), { y: 102, flag: 0 });
let checks = 0;
for (let a = 0; a < 256; a++) {
for (let b = 0; b < 256; b++) {
const add = alu(a, b, 0), sub = alu(a, b, 1);
assert.equal(add.y + 256 * add.flag, a + b);
assert.equal(sub.y - 256 * sub.flag, a - b);
assert.equal(alu(a, b, 2).y | alu(a, b, 3).y, a | b);
checks += 3;
}
}
console.log(`ALU PASS: ${checks} identities`);250+10ではy=4とcarry=1から260を復元できます。3-5ではy=254とborrow=1から-2を復元できます。170の2進表現は10101010、204は11001100で、ANDは136、XORは102になります。ループは全65536組のオペランドを調べ、各組について3つの恒等式を検査します。成功すると196608件の検査が報告されます。
参照モデルではRTLと異なる方法を使ってください。ハードウェアが9ビット加算器からキャリーを取り出す場合、このモデルは数学的な範囲を検査します。これにより共通の実装ミスを減らせます。この検査はHDLのX/Z値、伝搬タイミング、クロック動作を評価しません。RTL実装時には同じベクトルをシミュレータで再利用してください。データだけでなくフラグも必ず比較します。折り返した結果のビットが正しくても、境界フラグの誤りは隠れている場合があります。
自分で考えてみましょう
SUBが常にflag=0を返す場合、どの数値的な恒等式が成立しなくなりますか。a=0、b=1を手計算で確認し、実装を元に戻してください。
解説を見る
yは255です。flag=0ではy-256×flagが-1ではなく255となり、減算結果の復元に失敗します。borrow=1なら255-256=-1です。yだけを検査すると、この誤りを見落とします。