2013/02/16

7SEG表示のためのBCD変換

花粉が飛んでますので引き籠り^^;  で、仕事は進みますねT_T
今日は、2進数カウンタ(ハードウェアでいう普通のカウンタですが)でエラーカウントした結果を周期的に7SEG-LED(7セグ)に10進数で表示するにはどうするか? です。
例えば、10(dec)は2進数で"1010b"で7SEGでは"Ah"でも良いのですが、10進数の"10d"と表示したいということです。

本当は元のデータは32bit(0~4,294,967,296)の表示が必要なのですが、規模が大きそうなのでとりあえず16bit(0~65,535)で作ります。
2進数16bitだと、7SEG-LEDに表示するには5個(5桁→0~65535)のLEDがあれば良いですね。1の位から万の位まで5桁なので。ちなみに16進数表示だと4個(0000~FFFF)の7SEG-LEDでOK。
※ 32bitだと10個必要ですが10個とかの7SEGはあまり無いので浮動小数点表示とかで・・考えるの大変なのでやめた。これはNiosIIとかのソフトウェアに任せた方が良さそう。


(1) 仕様
  (a) 入力は2進数16bitの符号なしとする(10進:0~65,535、16進:0000~FFFF)
  他には無いか・・


(2) RTL
verilogで設計してみました。(FlipFlop出力信号にsr_を付けてますが気にしなくてOKです)
//-----------------------------------------------------------------------------
// Title         : bin2dec
// Project       : none
//-----------------------------------------------------------------------------
// File          : bin2dec.v
// Author        : shio
// Created       : 2013/02/16
// Last modified : 2013/02/16
//-----------------------------------------------------------------------------
// Description   : 16bit(16進数4桁)のバイナリ値を10進数5桁の値に変換する
//                 16bitカウント値を10進数にして7SEG-LEDに表示するためのBCD変換
// 
//-----------------------------------------------------------------------------
// Copyright (c) 2013 by shio This model is the confidential and
// proprietary property of shio and the possession or use of this
// file requires a written license from shio.
//------------------------------------------------------------------------------
// Modification history :
// 2013/02/16 shio     created
//-----------------------------------------------------------------------------
module bin2dec
  (/*AUTOARG*/
  // Outputs
  DIGIT5, DIGIT4, DIGIT3, DIGIT2, DIGIT1, DIGITS,
  // Inputs
  CLK, XRST, LOAD, BINDT
  );

  // Ports
  input         CLK   ; // clock
  input         XRST  ; // reset
  input         LOAD  ; // ロード
  input  [15:0] BINDT ; // バイナリデータ(変換元)
  output  [3:0] DIGIT5; // セグメント5(万の位)
  output  [3:0] DIGIT4; // セグメント4(千の位)
  output  [3:0] DIGIT3; // セグメント3(百の位)
  output  [3:0] DIGIT2; // セグメント2(十の位)
  output  [3:0] DIGIT1; // セグメント1(一の位)
  output [19:0] DIGITS; // セグメント5..1(見やすいようにマージ)

  // Signals
  reg  [15:0] sr_bindt ;
  reg   [3:0] sr_digit5;
  reg   [3:0] sr_digit4;
  reg   [3:0] sr_digit3;
  reg   [3:0] sr_digit2;
  reg   [3:0] sr_digit1;
  reg         sr_end   ; // 変換が終了したことを示すフラグ
  reg  [19:0] sr_digits;

  // 各位毎に減算の繰返し
  always @(negedge XRST or posedge CLK)begin
    if(!XRST)begin
      sr_bindt   <= {16{1'b0}};
      sr_digit5  <= {4{1'b0}};
      sr_digit4  <= {4{1'b0}};
      sr_digit3  <= {4{1'b0}};
      sr_digit2  <= {4{1'b0}};
      sr_digit1  <= {4{1'b0}};
      sr_end     <= 1'b0;
    end
    else begin
      if(LOAD==1'b1)begin
        sr_bindt   <= BINDT;
        sr_digit5  <= {4{1'b0}};
        sr_digit4  <= {4{1'b0}};
        sr_digit3  <= {4{1'b0}};
        sr_digit2  <= {4{1'b0}};
        sr_digit1  <= {4{1'b0}};
        sr_end     <= 1'b0;
      end
      else if(sr_bindt>=16'd10000)begin
        sr_bindt  <= sr_bindt - 16'd10000;
        sr_digit5 <= sr_digit5 + 1'b1;
      end
      else if(sr_bindt>=16'd1000)begin
        sr_bindt  <= sr_bindt - 16'd1000;
        sr_digit4 <= sr_digit4 + 1'b1;
      end
      else if(sr_bindt>=16'd100)begin
        sr_bindt  <= sr_bindt - 16'd100;
        sr_digit3 <= sr_digit3 + 1'b1;
      end
      else if(sr_bindt>=16'd10)begin
        sr_bindt  <= sr_bindt - 16'd10;
        sr_digit2 <= sr_digit2 + 1'b1;
      end
      //else if(sr_bindt>=16'd1)begin
      //  sr_bindt  <= sr_bindt - 16'd1;
      //  sr_digit1 <= sr_digit1 + 1'b1;
      //end
      // 1の位は直接代入して終了信号を生成
      else begin
        sr_digit1 <= sr_bindt;
        sr_end    <= 1'b1;
      end
    end
  end

  // 終了信号でラッチ
  always @(negedge XRST or posedge CLK)begin
    if(!XRST)begin
      sr_digits = {20{1'b0}};
    end
    else begin
      if(sr_end==1'b1)begin
        sr_digits = {sr_digit5,sr_digit4,sr_digit3,sr_digit2,sr_digit1};
      end
    end
  end
    
  // 端子出力
  assign DIGIT5 = sr_digit5;
  assign DIGIT4 = sr_digit4;
  assign DIGIT3 = sr_digit3;
  assign DIGIT2 = sr_digit2;
  assign DIGIT1 = sr_digit1;
  assign DIGITS = sr_digits;

endmodule


(3) シミュレーション
テストベンチは以下の通り。ModelSim ALTERA STARTER EDITION 10.0c で確認。
//-----------------------------------------------------------------------------
// Title         : bin2dec
// Project       : none
//-----------------------------------------------------------------------------
// File          : tb_top.sv
// Author        : shio
// Created       : 16.02.2013
// Last modified : 16.02.2013
//-----------------------------------------------------------------------------
// Description   : 
// 
//-----------------------------------------------------------------------------
// Copyright (c) 2013 by shio This model is the confidential and
// proprietary property of shio and the possession or use of this
// file requires a written license from shio.
//------------------------------------------------------------------------------
// Modification history :
// 16.02.2013 : created
//-----------------------------------------------------------------------------
`timescale 1ps/1ps

module tb_top;
  
  //--------------------------------------------------------------------------------
  // 定数宣言
  //--------------------------------------------------------------------------------
  parameter  C_CYC_CLK  =  10.000ns;        // クロック(100MHz)
  parameter  C_RST_TIME = 105.000ns;        // リセット時間
  
  
  //--------------------------------------------------------------------------------
  // シグナル宣言
  //--------------------------------------------------------------------------------
  // クロックとリセットの生成
  bit          s_CLK  ;
  bit          s_XRST ;
  bit          LOAD   ;
  bit   [15:0] BINDT  ;
  logic  [3:0] DIGIT5 ;
  logic  [3:0] DIGIT4 ;
  logic  [3:0] DIGIT3 ;
  logic  [3:0] DIGIT2 ;
  logic  [3:0] DIGIT1 ;
  logic [19:0] DIGITS ;


  //--------------------------------------------------------------------------------
  // クロックとリセットの生成
  //--------------------------------------------------------------------------------
  // マスタクロック(初期値+OFFSET+繰り返し)
  initial begin 
    s_CLK = 0;
    forever #(C_CYC_CLK/2) s_CLK = ~s_CLK;
  end
  
  // リセット(fork~joinで並列処理)
  initial
    fork
      #0          s_XRST = 0;
      #C_RST_TIME s_XRST = 1;
    join
  
  
  //--------------------------------------------------------------------------------
  // タスク
  //--------------------------------------------------------------------------------


  //--------------------------------------------------------------------------------
  // 外部端子設定
  //--------------------------------------------------------------------------------
  initial begin
    // リセット解除待ち
    repeat(20) @(posedge s_CLK);

    // message
   $display("");
    $display("****************************************");
    $display("* test                                 *");
    $display("****************************************");

    // 1st-BINDT
    $display("1st-BINDT");
    BINDT = 65535;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(40) @(posedge s_CLK);

    // 2nd-BINDT
    $display("2nd-BINDT");
    BINDT = 0;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(10) @(posedge s_CLK);

    // 3rd-BINDT
    $display("3rd-BINDT");
    BINDT = 64999;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(40) @(posedge s_CLK);

    // 4th-BINDT
    $display("4th-BINDT");
    BINDT = 10;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(10) @(posedge s_CLK);

    // 5th-BINDT
    $display("5th-BINDT");
    BINDT = 100;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(10) @(posedge s_CLK);

    // 6th-BINDT
    $display("6th-BINDT");
    BINDT = 10000;
    repeat(2) @(posedge s_CLK);
    #(1ns); LOAD = 1;
    #(C_CYC_CLK);LOAD = 0;
    repeat(10) @(posedge s_CLK);

   // sim終了
    repeat(10) @(posedge s_CLK);
   $display("### Simulation end ###");
    $finish;
  end


  //--------------------------------------------------------------------------------
  // インスタンス
  //--------------------------------------------------------------------------------
  //----------------------------------------
  // DUTs
  //----------------------------------------
  bin2dec u_bin2dec
    (
     .CLK  ( s_CLK  ),
     .XRST ( s_XRST ),
     .*
     );
  
endmodule
シミュレーション結果です。
LOAD=1でBINDT[15:0]をロード。その後、万の位から順に1CLK毎に処理される。
1の位は直接代入で良く、1CLKで処理終わり、ついでに終了フラグsr_endを生成。
20bitのBCDデータとしてはsr_endでラッチ。(これは要らないと思うけど一応見やすいので)

注) バス信号でtb_top/BINDT[15:0]とDIGITS[19:0]はHex表示、それ以外はunsigned(符号無し10進)表示にしています。sr_digit5~1の見ない部分は0,1,2.. とインクリメントしています。

レイテンシが入力する値により変わるので、最大の遅延を考慮して固定遅延後にラッチしてもいいかもしれませんね。そもそも7SEGを点灯する程度の速度ならラッチも不要か・・。
7SEGは1個当り100us~1ms程度で周期的に点灯させれば良いのかな? (10ms周期だと人間の目にはちらついて見えないようなので)

レイテンシは、万の位+千の位+百の位+十の位となる。
65535の場合は6+5+5+3=19CLK。0だと1CLK。10,100,1000,10000は2CLK。
最大は、64999で28CLKなので、30CLK後にラッチしても良いのかな~。


(3) その他

方法は他にもあって、減算を除算+余りで作ればレイテンシは最大5CLKとかになりますね・・。
また、0~BINDTまで回るカウンタを回し、10,100,1000,10000のイネーブルを作成、各位毎にカウントアップさせる方法もあります。最大65535CLKもかかるけど。

16bitくらいならレイテンシも回路規模も大したことない感じ。
32bitでは、レイテンシは69CLKくらいですが回路規模が大きいのかな?
大きなFPGAなら大丈夫かも。DE0-Nanoでインプリしても良いのですが・・そのうちということで^^;

0 件のコメント:

コメントを投稿