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: 26592 (from 2010/06/03),
today: 4,
yesterday: 4