2進からBCDへの変換回路 の履歴(No.1)
更新概要†
marsee さんから頂いたお題で、通常の2進数表現符号なし整数を BCD (二進化十進表現) に変換する回路を書いてみました。
http://ja.wikipedia.org/wiki/%E4%BA%8C%E9%80%B2%E5%8C%96%E5%8D%81%E9%80%B2%E8%A1%A8%E7%8F%BE
ソース†
LANG:verilog(linenumber)
// 2進数を BCD に直す回路
// bin に変換元の符号なし整数をセットして start に1を立てる
// 次クロックで busy が立つので、降りるまで待って bcd を読む
// BITS により、任意ビット数数値を変換可能
// 計算時間は BITS * 6 クロック程度
module bin2bcd #(
parameter BITS = 10
) (clk, rst, start, busy, bin, bcd);
// log 16 = 1.20412 < 1205/1000 を使って BCD 表現に必要な桁数を求める
localparam BCD_DIGITS = ( BITS * 1205 / 1000 - 1 ) / 4 + 1;
localparam BCD_DIGITS_BITS
= BCD_DIGITS < 2 ? 1 :
BCD_DIGITS < 4 ? 2 :
BCD_DIGITS < 8 ? 3 :
BCD_DIGITS < 16 ? 4 :
BCD_DIGITS < 32 ? 5 :
BCD_DIGITS < 64 ? 6 :
BCD_DIGITS < 128 ? 7 : 8;
input wire clk;
input wire rst;
input wire start;
output wire busy;
input wire [BITS-1:0] bin;
output wire [BCD_DIGITS*4-1:0] bcd;
// 割り算用レジスタ
reg [BITS-1:0] numerator; // 分子
reg [BITS+4-1:0] denominator; // 分母
reg [3:0] quotient; // 商
// 10^n << 3 を保持する ROM
reg [BITS+4-1:0] denominators[0:BCD_DIGITS-1];
integer i;
initial // ROM の初期化
for (i=0; i<BCD_DIGITS; i=i+1)
denominators[i] = ( 10 ** i ) * ( 2 ** 3 );
// ステートマシンは state, digit を変数として動作する
reg [2:0] state;
reg [BCD_DIGITS_BITS-1:0] digit;
localparam stIdle = 0;
localparam stDivide0 = 1;
localparam stDivide1 = 2;
localparam stDivide2 = 3;
localparam stDivide3 = 4;
localparam stDivide4 = 5;
localparam stNext = 6;
// 計算結果を保持するレジスタ
reg [3:0] bcd_internal[0:BCD_DIGITS-1];
always @(posedge clk)
if (rst) begin
state <= stIdle;
end else
case (state)
stIdle: begin
numerator <= bin;
digit <= BCD_DIGITS - 1;
if (start)
state <= stDivide0;
end
stDivide0: begin
// 分母に 10^n << 3 を用意する
denominator <= denominators[digit];
state <= stDivide1;
end
stDivide1, stDivide2, stDivide3, stDivide4: begin
// 割り算して商を得る
if (numerator >= denominator) begin
quotient <= { quotient, 1'b1 };
numerator <= numerator - denominator;
end else begin
quotient <= { quotient, 1'b0 };
end
denominator <= denominator >> 1;
if (state != stDivide4) begin
state <= state + 1;
end else begin
state <= stNext;
end
end
stNext: begin
// 結果を格納して次へ
bcd_internal[digit] <= quotient;
if (digit==0) begin
state <= stIdle;
end else begin
digit <= digit - 1;
state <= stDivide0;
end
end
endcase
// 出力に繋ぐ
assign busy = state != stIdle;
generate
genvar j;
for (j=0; j<BCD_DIGITS; j=j+1) begin: bcd_connection
assign bcd[j*4 +: 4] = bcd_internal[j];
end
endgenerate
endmodule
テストベンチ†
LANG:verilog(linenumber)
module bin2bcd_test;
// ここを変えれば異なるビット数についてもテスト可能
parameter BITS = 10;
localparam BCD_DIGITS = ( BITS * 1205 / 1000 - 1 ) / 4 + 1; // log 16 = 1.20412
// Inputs
reg clk;
reg rst;
reg start;
reg [BITS-1:0] bin;
// Outputs
wire busy;
wire [BCD_DIGITS*4-1:0] bcd;
// Instantiate the Unit Under Test (UUT)
bin2bcd #(BITS) uut (
.clk(clk),
.rst(rst),
.start(start),
.busy(busy),
.bin(bin),
.bcd(bcd)
);
// value を入力して expect が出てくることを確かめる
task test_with;
input [BITS-1:0] value;
input [BCD_DIGITS*4-1:0] expect;
begin
bin = value;
start = 1;
@(posedge clk);
start = 0;
@(negedge busy);
if (bcd!==expect)
$display("*** ERROR: expecting %x (%d) but found %x.",
expect, value, bcd);
end
endtask
// クロック
always #5 clk = !clk;
initial begin
// Initialize Inputs
clk = 0;
rst = 1;
start = 0;
bin = 0;
// Wait 100 ns for global reset to finish
#100;
// Add stimulus here
rst = 0;
repeat(10) @(posedge clk);
// 境界値を重点的に
test_with(0, 'h0);
test_with(255, 'h255);
test_with(123, 'h123);
test_with(128, 'h128);
test_with(127, 'h127);
test_with(64, 'h64);
test_with(63, 'h63);
test_with(100, 'h100);
test_with(99, 'h99);
test_with(10, 'h10);
test_with(9, 'h9);
end
endmodule
コメント†
Counter: 28508 (from 2010/06/03),
today: 1,
yesterday: 4