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: 19629 (from 2010/06/03), today: 1, yesterday: 0