1. 基礎的な数値表現
1.1 10進数と16進数の相互変換
10進整数を16進数に変換する方法は主に2つある。
- 方法1:10進数を2進数に変換(除2取余法)し、その2進数を下位から4ビットずつ区切り、各グループを16進数に置き換える。
- 方法2:10進数を直接16で割り続け、余りを下位から並べる(除16取余法)。
例:3010 → 1E16
1.2 原码・反码・補数表現
原码:最上位ビットが符号(0: 正、1: 負)、残りが絶対値。
8ビットの場合の範囲:−127 〜 +127
反码:正数は原码と同じ。負数は符号ビット以外を反転。
範囲は原码と同じ。
補数(2の補数):正数はそのまま。負数は原码の符号ビット以外を反転し、1を加算。
8ビットの場合の範囲:−128 〜 +127(ゼロの重複なし)
1.3 グレイコード
グレイコードは隣接する数値間で1ビットしか変化しない符号化方式であり、状態遷移時の誤動作を抑制できる。
変換規則:自然2進数 B[4:0] からグレイコード G[4:0] へは、
G[i] = B[i+1] ^ B[i](ただし B[5] = 0)で計算可能。
| 自然2進数 | 5ビットグレイコード |
|---|---|
| 00000 | 00000 |
| 00001 | 00001 |
| 00010 | 00011 |
| 00011 | 00010 |
| 00100 | 00110 |
| 00101 | 00111 |
| 00110 | 00101 |
| 00111 | 00100 |
| 01000 | 01100 |
| 01001 | 01101 |
| 01010 | 01111 |
| 01011 | 01110 |
| 01100 | 01010 |
| 01101 | 01011 |
| 01110 | 01001 |
| 01111 | 01000 |
| 10000 | 11000 |
| 10001 | 11001 |
| 10010 | 11011 |
| 10011 | 11010 |
| 10100 | 11110 |
| 10101 | 11111 |
| 10110 | 11101 |
| 10111 | 11100 |
| 11000 | 10100 |
| 11001 | 10101 |
| 11010 | 10111 |
| 11011 | 10110 |
| 11100 | 10010 |
| 11101 | 10011 |
| 11110 | 10001 |
| 11111 | 10000 |
1.4 IEEE 754 単精度浮動小数点数
32ビット形式:1ビット符号 + 8ビット指数部(バイアス127) + 23ビット仮数部(暗黙の1あり)。
表現範囲:約 ±1.2×10−38 〜 ±3.4×1038、有効桁数:約7桁(10進数)。
浮動小数点 vs 固定小数点:
- 浮動小数点:広いダイナミックレンジと高精度だが、演算回路が複雑。
- 固定小数点:回路が単純で高速だが、範囲と精度に制限あり。
2. Verilogによる算術回路設計
2.1 ストップウォッチ回路
10 MHzクロック入力、クリア・スタート/ストップ制御付きの時分秒カウンタ(XX:XX:XX)。各桁は4ビットBCD出力。
設計方針:階層的カウンタモジュールと2状態(START/STOP)のFSMを組み合わせ、ゲーテッドクロックでカウント制御。
module bcd_counter (
input clk,
input rst_n,
input clear,
input [3:0] limit,
output reg full,
output reg [3:0] count
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= 4'd0;
full <= 1'b0;
end else if (clear) begin
count <= 4'd0;
full <= 1'b0;
end else if (count == limit) begin
count <= 4'd0;
full <= 1'b1;
end else begin
count <= count + 1;
full <= 1'b0;
end
end
endmodule
module stopwatch_top (
input clk, rst_n, clear, start_stop,
output [3:0] hr_h, hr_l, min_h, min_l, sec_h, sec_l
);
reg counting;
wire clk_en = counting & clk;
// 状態遷移
always @(posedge clk or negedge rst_n) begin
if (!rst_n) counting <= 1'b0;
else if (clear) counting <= 1'b0;
else if (start_stop) counting <= ~counting;
end
// カウンタチェーン(秒下位 → 秒上位 → 分下位 → ...)
bcd_counter sec_low (.clk(clk_en), .limit(4'd9), .count(sec_l), /*...*/);
bcd_counter sec_high(.clk(sec_low.full), .limit(4'd5), .count(sec_h), /*...*/);
bcd_counter min_low (.clk(sec_high.full), .limit(4'd9), .count(min_l), /*...*/);
bcd_counter min_high(.clk(min_low.full), .limit(4'd5), .count(min_h), /*...*/);
bcd_counter hr_low (.clk(min_high.full), .limit(4'd9), .count(hr_l), /*...*/);
bcd_counter hr_high (.clk(hr_low.full), .limit(4'd9), .count(hr_h), /*...*/);
endmodule
2.2 超高速加算器(Carry-Lookahead)
16ビット符号付き整数の加算を17ビット出力で実現。キャリールックアヘッド構造により遅延を最小化。
設計:4ビットCLAブロック(Carry4)を5段階で階層化し、グループ伝搬/生成信号を再帰的に計算。
module cla4 (
input [3:0] p, g,
input cin,
output P, G,
output [2:0] carries
);
assign P = &p;
assign G = g[3] | (p[3]&g[2]) | (p[3]&p[2]&g[1]) | (p[3]&p[2]&p[1]&g[0]);
assign carries[0] = g[0] | (p[0]&cin);
assign carries[1] = g[1] | (p[1]&g[0]) | (p[1]&p[0]&cin);
assign carries[2] = g[2] | (p[2]&g[1]) | (p[2]&p[1]&g[0]) | (p[2]&p[1]&p[0]&cin);
endmodule
module fast_adder_16 (
input [15:0] a, b,
output [16:0] sum
);
wire [15:0] prop = a ^ b;
wire [15:0] gen = a & b;
wire [15:0] carry;
assign carry[0] = 1'b0;
cla4 stage0(.p(prop[3:0]), .g(gen[3:0]), .cin(carry[0]), .carries(carry[3:1]));
cla4 stage1(.p(prop[7:4]), .g(gen[7:4]), .cin(carry[4]), .carries(carry[7:5]));
cla4 stage2(.p(prop[11:8]), .g(gen[11:8]), .cin(carry[8]), .carries(carry[11:9]));
cla4 stage3(.p(prop[15:12]), .g(gen[15:12]), .cin(carry[12]), .carries(carry[15:13]));
cla4 top(.p({prop[15],prop[11],prop[7],prop[3]}), .g({gen[15],gen[11],gen[7],gen[3]}),
.cin(1'b0), .carries({carry[12],carry[8],carry[4]}));
wire [15:0] s = prop ^ carry[15:0];
wire ovf = (a[15] & b[15] & ~s[15]) | (~a[15] & ~b[15] & s[15]);
assign sum = {ovf ? ~s[15] : s[15], s};
endmodule
2.3 高速乗算器(Booth符号化 + Wallaceツリー)
Radix-4 Booth符号化により部分積を8個生成し、Wallaceツリー(CSA階層)で効率的に加算。
Boothエンコーダは3ビット窓で −2, −1, 0, +1, +2 の係数を判定。部分積は符号拡張とシフトで生成。
module booth_encoder (
input [2:0] bits,
output is_neg, is_zero, is_one, is_two
);
assign is_neg = bits[2];
assign is_zero = (bits == 3'b000) || (bits == 3'b111);
assign is_two = (bits == 3'b100) || (bits == 3'b011);
assign is_one = !(is_zero || is_two);
endmodule
module partial_prod_gen (
input [15:0] multiplicand,
input is_neg, is_zero, is_one, is_two,
output [31:0] product
);
wire [31:0] base = is_zero ? 32'd0 :
is_one ? {{16{multiplicand[15]}}, multiplicand} :
{{15{multiplicand[15]}}, multiplicand, 1'b0};
assign product = is_neg ? (~base + 1) : base;
endmodule
Wallaceツリーは複数段のCSA(キャリーセーブ加算器)で部分積を縮約し、最終段で通常加算器で合計。
2.4 バレルシフタ(循環シフト)
32ビット入力に対し、左/右循環シフトを5ビットシフト量で制御。ログベースアーキテクチャで高速化。
module barrel_shifter_32 (
input [31:0] din,
input dir, // 0: 左, 1: 右
input [4:0] shift,
output [31:0] dout
);
wire [31:0] stage0 = dir ? (shift[0] ? {din[0], din[31:1]} : din) :
(shift[0] ? {din[30:0], din[31]} : din);
wire [31:0] stage1 = dir ? (shift[1] ? {stage0[1:0], stage0[31:2]} : stage0) :
(shift[1] ? {stage0[29:0], stage0[31:30]} : stage0);
wire [31:0] stage2 = dir ? (shift[2] ? {stage1[3:0], stage1[31:4]} : stage1) :
(shift[2] ? {stage1[27:0], stage1[31:28]} : stage1);
wire [31:0] stage3 = dir ? (shift[3] ? {stage2[7:0], stage2[31:8]} : stage2) :
(shift[3] ? {stage2[23:0], stage2[31:24]} : stage2);
assign dout = dir ? (shift[4] ? {stage3[15:0], stage3[31:16]} : stage3) :
(shift[4] ? {stage3[15:0], stage3[31:16]} : stage3);
endmodule