01
引き算を、足し算で行う
コンピュータの中に「引き算器」という独立した回路はない。減算 a − b は a + (−b) と読み替えて、加算器で計算する。 2進数で −b を表すのに使うのが 2の補数(全ビット反転して 1 を足す)である。
b0000_0011= 3
~b1111_1100全ビット反転(1の補数)
~b + 11111_1101= −3 の2の補数表現
a − ba + (~b) + 1→ cin に 1 を渡すだけ
演習Aの加算器から変えるのは 2箇所だけである。b を ~b にする、cin を 1 にする。
それだけで同じ回路が引き算をする。これがハードウェア設計で「部品を使い回す」ということである。
02
ボロー(桁借り)の意味
符号なし8ビットでは、a < b のとき結果が負になり、8ビットでは正しく表せない。 このことを知らせる1ビットの信号が borrow(桁借り)である。 9ビット目に桁上がりが出なかったときが、ちょうど桁借りが起きたときにあたる。
| a | b | diff = a − b | borrow | 意味 |
|---|---|---|---|---|
| 10 | 3 | 7 | 0 | 正常 |
| 0 | 0 | 0 | 0 | ゼロ |
| 3 | 10 | 249 | 1 | 桁借り(−7 が 249 に見える) |
| 255 | 1 | 254 | 0 | 正常 |
03
作るモジュールの仕様
- module
- subtractor8
- input
- a [7:0] — 被減数 / b [7:0] — 減数
- output
- diff [7:0] — 差 / borrow — 桁借りが起きたら 1
04
書いて、その場でチェック
演習Aと同じ形で書ける。9ビットで受けてから分ける方法でも、9ビットの計算をそのまま {borrow, diff} で受ける方法でも構わない。
module subtractor8 (
input [7:0] a,
input [7:0] b,
output [7:0] diff,
output borrow
);
endmodule
Ctrl / ⌘ + Enter でも実行
答え合わせ(自分で試してから開く)
2の補数を自分で組み立てる書き方(教科書どおり)と、引き算演算子に任せる書き方。どちらも同じ回路になる。
// 書き方 1:加算器の使い回しがはっきり見える形 wire [8:0] result; assign result = {1'b0, a} + {1'b0, ~b} + 1'b1; assign diff = result[7:0]; assign borrow = ~result[8]; // 桁上がりが出なかった = 桁借り
// 書き方 2:9ビットの引き算をそのまま2つに分ける assign {borrow, diff} = a - b;
書き方2で borrow が正しく出るのは、9ビットで計算すると a < b のとき 9ビット目が 1 になるからである。 書き方1では逆に、9ビット目が 0 のときが桁借りなので ~ が要る。同じ現象を別の側から見ている。
05
考えてみよう
- a=5, b=200 のとき diff と borrow はどうなりますか。手で計算してから、チェッカーの結果と照らし合わせてみましょう。
- 演習Aの加算器とこの減算器を並べると、回路として何が同じで何が違いますか。
- assign diff = a - b; と書くだけでも動く。それでも 2の補数を自分で書いてみる意味は何でしょうか。
- 足し算と引き算を1つのモジュールで切り替えるには、どんな入力を足せばよいでしょうか。(次の演習C・Dにつながる)
発展:本物のツール(EDA Playground)でも動かす 任意
実際のシミュレータで波形も見たい人は、EDA Playground に設計を貼り付けて実行できる(Gmail アカウントが必要)。 手順は 使い方ガイド へ。
TESTBENCH(左パネル)
module tb_subtractor8; reg [7:0] a, b; wire [7:0] diff; wire borrow; subtractor8 uut (.a(a), .b(b), .diff(diff), .borrow(borrow)); initial begin $dumpfile("dump.vcd"); $dumpvars(0, tb_subtractor8); $monitor("a=%0d b=%0d | diff=%0d borrow=%0d", a, b, diff, borrow); a = 8'd10; b = 8'd3; #10; a = 8'd0; b = 8'd0; #10; a = 8'd3; b = 8'd10; #10; a = 8'd255; b = 8'd1; #10; a = 8'd100; b = 8'd100; #10; $display("--- テスト完了 ---"); $finish; end endmodule