読者です 読者をやめる 読者になる 読者になる

7セグメント・デコーダ

7セグメントLEDのデコーダをモジュール化した。
iValue(4bit)に応じた値 (0-F) を出力する。

デコード値はconstantで設定している。 kDigitがデコード値を格納した2次元配列になっていて、
例えばkDigit(3)は7セグメントLEDで3を表示する値となる。

iValueの値をconv_integerでintegerに変換するだけ。

library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.std_logic_misc.all;
    use IEEE.std_logic_arith.all;
    use IEEE.std_logic_unsigned.all;

entity Decoder7seg is
    port (
        CLOCK_50          : in    std_logic;
        RESET_N           : in    std_logic;     
        
        iEnable           : in    std_logic;
        iValue            : in    std_logic_vector( 3 downto 0);
        oEncValue         : out   std_logic_vector( 6 downto 0)
    );
end Decoder7seg;

architecture RTL of Decoder7seg is

    --  -- 0 --
    --  5     1
    --  -- 6 --
    --  4     2
    --  -- 3 --
    constant kDigit0 : std_logic_vector(6 downto 0) := "1000000";
    constant kDigit1 : std_logic_vector(6 downto 0) := "1111001";
    constant kDigit2 : std_logic_vector(6 downto 0) := "0100100";
    constant kDigit3 : std_logic_vector(6 downto 0) := "0110000";
    constant kDigit4 : std_logic_vector(6 downto 0) := "0011001";
    constant kDigit5 : std_logic_vector(6 downto 0) := "0010010";
    constant kDigit6 : std_logic_vector(6 downto 0) := "0000010";
    constant kDigit7 : std_logic_vector(6 downto 0) := "1111000";
    constant kDigit8 : std_logic_vector(6 downto 0) := "0000000";
    constant kDigit9 : std_logic_vector(6 downto 0) := "0010000";
    constant kDigitA : std_logic_vector(6 downto 0) := "0001000";
    constant kDigitB : std_logic_vector(6 downto 0) := "0000011";
    constant kDigitC : std_logic_vector(6 downto 0) := "1000110";
    constant kDigitD : std_logic_vector(6 downto 0) := "0100001";
    constant kDigitE : std_logic_vector(6 downto 0) := "0000110";
    constant kDigitF : std_logic_vector(6 downto 0) := "0001110";

    type tDigitSelect is array(0 to 15) of std_logic_vector(6 downto 0);

    constant kDigit : tDigitSelect := ( kDigit0, kDigit1, kDigit2, kDigit3,
                                        kDigit4, kDigit5, kDigit6, kDigit7,
                                        kDigit8, kDigit9, kDigitA, kDigitB,
                                        kDigitC, kDigitD, kDigitE, kDigitF
                                    );
    
    signal sEnable : std_logic;
    signal sValue  : std_logic_vector( 3 downto 0);
    
begin

    -- Retiming Input signal
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sEnable <= '0';
            sValue  <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            sEnable <= iEnable;
            sValue  <= iValue;
        end if;
    end process;

    -- Encode data
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            oEncValue <= (others => '1');
        elsif rising_edge(CLOCK_50) then
            if (sEnable = '0') then
                oEncValue <= (others => '1');
            else
                oEncValue <= kDigit(conv_integer(sValue));
            end if;
        end if;
    end process;

end RTL;

(DE0-CV) DE0拡張キットのLCDモジュールを使う (5)

(4)で作成したVHDLを使ってLCDモジュールを動かしてみました。
これがそのときの写真です。
f:id:m_keishi2006:20170507001545j:plain

( ^ω^)・・・薄くね?
これでもコントラストはGND直結の最大状態……。
まぁ、本来は5V動作のものを3.3Vで動かしているのだから当たり前か……。

一応、Vddを5Vにするとこんな感じ。ちゃんと映る。
(コントラスト調整済) f:id:m_keishi2006:20170507001802j:plain

でも、FPGAのIOは3.3Vなのでさすがに5Vで動かしているモジュールと直結するのは避けたい。
DE0はよくこんなの使ってるなぁ……。Writeだけしかしないこと前提にしてるのかな。

というわけで、LCDモジュールは動いたけど電源電圧に問題ありの状態。
間にレベルシフタなどを入れれば問題ないだろうけど、それは面倒くさい。

そのため、3.3Vで動く別のLCDモジュールを使うことにしました。
マルツ電波のこいつです。
https://www.marutsu.co.jp/pc/i/87076/

RTLはまったくいじっておりません。
f:id:m_keishi2006:20170507002345j:plain

ぶっちゃけLCDモジュールならどれでも同じように使えるので、例えば秋月電子
http://akizukidenshi.com/catalog/g/gP-04794/
とかでも良いです。

(DE0-CV) DE0拡張キットのLCDモジュールを使う (4)

VHDLにする

前回作成したタイミングチャートをVHDLにしました。

サンプルとしてQuartusのプロジェクトも置いておきます。
(Quartus Prime Version 16.1.0 Build 196 10/24/2016 SJ Lite Edition で確認) DE0_CV_LCD.zip - Google ドライブ

library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.std_logic_misc.all;
    use IEEE.std_logic_arith.all;
    use IEEE.std_logic_unsigned.all;

entity DE0_CV_LCD is
    port(

        --//////////// CLOCK //////////
        CLOCK_50         : in     std_logic;
        CLOCK2_50        : in     std_logic;
        CLOCK3_50        : in     std_logic;
        CLOCK4_50        : inout  std_logic;

        --//////////// KEY //////////
        KEY              : in     std_logic_vector( 3 downto 0);
        RESET_N          : in     std_logic;

        --//////////// LED //////////
        LEDR             : out    std_logic_vector( 9 downto 0);

        --//////////// for connecting an LCD //////////
        LCD_D            : out    std_logic_vector( 7 downto 0);
        LCD_RS           : out    std_logic;
        LCD_RW           : out    std_logic;
        LCD_EN           : out    std_logic 
    );
end DE0_CV_LCD;

architecture RTL of DE0_CV_LCD is
    
    --------------------------------------------------
    -- 初期化開始待機用信号
    signal sWakeUpCounter : std_logic_vector(20 downto 0);
    signal sModuleEnable  : std_logic;
    --------------------------------------------------

    --------------------------------------------------
    -- クロックカウント用信号
    signal sClockCounter : std_logic_vector(17 downto 0);
    signal sMaxCount     : std_logic_vector(sClockCounter'range);
    --------------------------------------------------

    --------------------------------------------------
    -- 状態カウント用信号
    -- kStopStateまでsStateCountをカウントし続ける
    signal sStateCount   : std_logic_vector( 4 downto 0);
    constant kStopState  : std_logic_vector(sStateCount'range) := conv_std_logic_vector(20, sStateCount'length);
    --------------------------------------------------

    
    --------------------------------------------------
    -- sStateCountの値に対応したLCD_RS, LCD_RW, LCD_Dの出力
    -- 2次元配列に格納している値をLCD_RS, LCD_RW, LCD_Dに割り当てる
    
    signal sCommandOfLCD : std_logic_vector( 9 downto 0);
    type tPatternOfLCD     is array( 0 to 19) of std_logic_vector(sCommandOfLCD'range);

    constant kFunstionSet    : std_logic_vector(sCommandOfLCD'range) := "0000111100";
    constant kDisplayInit    : std_logic_vector(sCommandOfLCD'range) := "0000001000";
    constant kDisplayControl : std_logic_vector(sCommandOfLCD'range) := "0000001111";
    constant kDisplayClear   : std_logic_vector(sCommandOfLCD'range) := "0000000001";
    constant kEntryModeSet   : std_logic_vector(sCommandOfLCD'range) := "0000000110";

    -- HELLO WORLD! の値
    constant kH  : std_logic_vector(sCommandOfLCD'range) := "1001001000";
    constant kE  : std_logic_vector(sCommandOfLCD'range) := "1001000101";
    constant kL  : std_logic_vector(sCommandOfLCD'range) := "1001001100";
    constant kO  : std_logic_vector(sCommandOfLCD'range) := "1001001111";
    constant kSp : std_logic_vector(sCommandOfLCD'range) := "1000100000";
    constant kW  : std_logic_vector(sCommandOfLCD'range) := "1001010111";
    constant kR  : std_logic_vector(sCommandOfLCD'range) := "1001010010";
    constant kD  : std_logic_vector(sCommandOfLCD'range) := "1001000100";
    constant kEx : std_logic_vector(sCommandOfLCD'range) := "1000100001";

    constant kInitPattern : tPatternOfLCD :=
                            ( kFunstionSet, kFunstionSet , kFunstionSet , kFunstionSet   ,
                              kDisplayInit, kDisplayClear, kEntryModeSet, kDisplayControl,
                              kH , kE  , kL , kL ,
                              kO , kSp , kW , kO ,
                              kR , kL  , kD , kEx
                            );

    --------------------------------------------------

    --------------------------------------------------
    -- sStateCountの値に対応したEの周期 (t_cycE)
    -- 初期化処理やコマンドによって異なるため、
    -- 2次元配列によってコマンドごとに時間を決めている

    type tWaitPatternOfLCD is array( 0 to 19) of std_logic_vector(sClockCounter'range);

    constant kWait4500us : std_logic_vector(sClockCounter'range) := "110110111011101000";
    constant kWait200us  : std_logic_vector(sClockCounter'range) := "000010011100010000";
    constant kWait40us   : std_logic_vector(sClockCounter'range) := "000000011111010000";

    constant kInitWaitPattern : tWaitPatternOfLCD :=
                                ( kWait4500us , kWait200us  , kWait40us, kWait40us,
                                  kWait40us   , kWait4500us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us,
                                  kWait40us   , kWait40us , kWait40us, kWait40us
                                );

    --------------------------------------------------
    
    --------------------------------------------------
    -- LCD_RS, LCD_RW開始/終了時間
    constant kRsStart  : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(  0, sClockCounter'length);
    constant kRsEnd    : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(128, sClockCounter'length);
    --------------------------------------------------
    -- LCD_EN開始/終了時間
    constant kEnStart  : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 32, sClockCounter'length);
    constant kEnEnd    : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 96, sClockCounter'length);
    --------------------------------------------------
    -- LCD_D開始/終了時間
    constant kComStart : std_logic_vector(sClockCounter'range) := conv_std_logic_vector( 64, sClockCounter'length);
    constant kComEnd   : std_logic_vector(sClockCounter'range) := conv_std_logic_vector(128, sClockCounter'length);
    --------------------------------------------------
    
begin
    
    --------------------------------------------------
    -- 初期化処理待機用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sWakeUpCounter <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            -- sModuleEnable='0'の間、
            -- sWakeUpCounterをカウントアップし続ける
            if (sModuleEnable = '0') then
                sWakeUpCounter <= sWakeUpCounter + '1';
            end if;
        end if;
    end process;
    
    -- sWakeUpCounterがすべて'1'となったらsModuleEnable='1'
    sModuleEnable <= and_reduce(sWakeUpCounter);

    --------------------------------------------------

    --------------------------------------------------
    -- クロックカウント用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sClockCounter <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sModuleEnable = '0' or sStateCount = kStopState) then
                -- 初期化待機中、もしくは状態が既定の値に達したら
                -- カウントアップを止める
                sClockCounter <= (others => '0');
            elsif (sClockCounter = sMaxCount) then
                sClockCounter <= (others => '0');
            else
                sClockCounter <= sClockCounter + '1';
            end if;
        end if;
    end process;
    
    -- 各状態に対応したsClockCounterのMax値を代入
    sMaxCount <= kInitWaitPattern(conv_integer(sStateCount));

    --------------------------------------------------

    --------------------------------------------------
    -- 状態カウント用
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            sStateCount <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter = sMaxCount) then
                sStateCount <= sStateCount + '1';
            end if;
        end if;
    end process;
    
    --------------------------------------------------

    --------------------------------------------------
    -- LCD_EN, LCD_D, LCD_RS, LCD_RW出力

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_EN <= '0';
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kEnStart and sClockCounter < kEnEnd) then
                LCD_EN <= '1';
            else
                LCD_EN <= '0';
            end if;
        end if;
    end process;

    
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_D <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kComStart and sClockCounter < kComEnd) then
                LCD_D <= kInitPattern(conv_integer(sStateCount))( 7 downto 0);
            else
                LCD_D <= (others => '0');
            end if;
        end if;
    end process;

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            LCD_RW <= '0';
            LCD_RS <= '0';
        elsif rising_edge(CLOCK_50) then
            if (sClockCounter > kRsStart and sClockCounter < kRsEnd) then
                LCD_RW <= kInitPattern(conv_integer(sStateCount))(8);
                LCD_RS <= kInitPattern(conv_integer(sStateCount))(9);
            else
                LCD_RW <= '0';
                LCD_RS <= '0';
            end if;
        end if;
    end process;
    
    --------------------------------------------------

    
end architecture;

(DE0-CV) DE0拡張キットのLCDモジュールを使う (3)

タイミングチャート

データシートからタイミングを確認します。

DE0拡張キットのLCDモジュールに使われているのはST7066UというICです。
ST7066Uは基本的に一般的に使われるHD44780と互換性があるようですが、
AC特性が改善されているようです。

HD44780より早く動作させることができるみたいですが、お遊びで使う分には
他のLCDモジュールでも動かせるほうが良いのでHD44780をベースに
タイミングを確認します。

HD44780のデータシートはこちらからダウンロードできます。
HD44780 データシート(PDF) - Hitachi Semiconductor

まず文字を表示するだけなので、Write Operationを実装します。
Write Operationのタイミングは下図の通りです。
大事な箇所にタイミングの仕様を記載しています。 f:id:m_keishi2006:20170506221313p:plain

それぞれの意味は次の通りです。
Eの変化(立ち上がり/立ち下がり)を起点として考えるとわかりやすいです。

シンボル 意味
tAS RSとRWの変化開始は、短くともEの立ち上がりより60nsは早くなくてはならない
tAH RSとRWの変化終了は、短くともEの立ち下がりより20nsは遅くなくてはならない
PWEH EがHighとなる時間は、短くとも450nsなくてはならない
tDSW Dataの変化開始は、短くともEの立ち下がりより195nsは早くなくてはならない
tH Dataの変化終了は、短くともEの立ち下がりより10nsは遅くなくてはならない
tcycE Eの周期(立ち上がりから次の立ち上がりまで)は、短くとも1000nsはなくてはならない

この情報を踏まえて、FPGAでは下図のようなタイミングで信号を出力するように設計します。
LCD_RSがRS, LCD_ENがE、LCD_DがDB0 to DB7に対応します。WriteではRW=0固定のため記載していません。
f:id:m_keishi2006:20170506232238p:plain

1clock=20nsです。
sClockCounter(18bit)が1clockごとに1加算されます。この値でLCD_RS,LCD_EN,LCD_Dを制御します。 LCD_RSはsClockCounter=0x000でHighとし、sClockCounter=0x070でLowにします。
LCD_ENはsClockCounter=0x010でHighとし、sClockCounter=0x050でLowにします。
LCD_DはsClockCounter=0x030でHighとし、sClockCounter=0x070でLowにします。

sWakeUpCounterとsModuleEnableは、電源投入後に初期化処理を待機するためのものです。 初期化処理の開始は、電源電圧が2.7Vを超えてから40ms以上待つ必要があります。 21bitのカウンタがフルカウントしたらsModuleEnable=‘1'となり、初期化処理を開始します。 (21bitカウンタのフルカウントにかかる時間は、221 *20ns=約42msです)

次の記事では、これをVHDLに記述します。

(DE0-CV) DE0拡張キットのLCDモジュールを使う (2)

プロジェクト作成

TerasicのサイトにDE0-CVのQuartusIIプロジェクトを生成してくれるツールがあります。
下記の[CD-ROM]-[DE0-CV CD-ROM]をダウンロードし解凍します。
Terasic - All FPGA Main Boards - DE0-CV Board

解凍したファイルの[Tools]-[SystemBuilder]にある DE0_CV_SystemBuilder.exe を起動します。
GUIで選択した端子が設定されたQuartusIIプロジェクトを作成可能です。
下記は今回使う分だけ端子を設定したところ。これで「Generate」を押すとプロジェクトが作成されます。
GPIO-0 Header を None から GPIO default にしてください。 f:id:m_keishi2006:20170430185130p:plain

作成されたプロジェクト。.htmは端子を一覧で見ることができるファイルです。
.qsfはデバイスやピンアサインなどのQuartusIIのプロジェクト設定が書かれたファイルです。 .sdcはクロック制約。凝ったことしないなら特に編集は不要です。
.vがVerilogのソースファイルです。
ただし、今回はVHDLで作ります。拡張子を.vから.vhdに変更します。 f:id:m_keishi2006:20170430185717p:plain

プロジェクトを開いてソースファイルを追加

.qsfをダブルクリックするとQuartusIIプロジェクトが開きます。

最初はソースファイルが設定されていないので、ソースを追加します。
Project NavigatorのドロップダウンリストからFilesを選択します。 f:id:m_keishi2006:20170430190018p:plain

ディレクトリマークのFilesを右クリックして「Add/Remove Files in Project…」をクリックします。
f:id:m_keishi2006:20170430190534p:plain

File name: の右にある「…」をクリックしてソースファイルを選択すると追加されます。
※拡張子は.vから.vhdに変更しています。 OKを押してウィンドウを閉じます。
f:id:m_keishi2006:20170430190748p:plain

端子名を変更

GPIOは下図のようなピン配置になっています。
このうち、LCDとしてGPIO_0_D10~GPIO_0_D20までの11本の端子を使用します。
f:id:m_keishi2006:20170430214242p:plain

qsfファイルを開きます。qsfファイルにはピンアサインが記載されています。
GPIOは36本ありますが、使うのはGPIO[10]~GPIO[20]までなので、他のGPIOの端子は消してしまいます。
※VisualStudioで編集しているので、QuartusIIの編集画面とは異なります。
f:id:m_keishi2006:20170430214511p:plain

GPIO[10]~GPIO[20]を残して、他のGPIOの端子は消しました。
さらに、GPIO[10]~GPIO[17]をLCD_D[0]~LCD_D[7]に、GPIO[18]~GPIO[20]をそれぞれRS,WR,ENにリネームしました。
f:id:m_keishi2006:20170430214838p:plain

ソースコード(VHDL)の編集

DE0_CV_SystemBuilder.exe で作成された VerilogファイルをVHDLの記述に置き換えます。
下図のようになりました。
f:id:m_keishi2006:20170430215835p:plain

これをベースにしてLCDで文字を表示させる記述を作っていきます。

(DE0-CV) DE0拡張キットのLCDモジュールを使う (1)

2017.05.06追記
DE0拡張キットのLCDモジュールは5V動作品です。
しかし、DE0-CV (DE0も) は3.3V動作のため、使用しないほうが良いと考えます。

一応、電源電圧だけ5VとしてIOは3.3Vでデータライトして動作はしますが、
LCDからのデータリード時に過電圧になる可能性があります。
使用するには間にレベルシフタを入れて5V⇔3.3V変換できるようにする必要があります。
(DE0の回路図を見ると直接つないじゃってるんですよね…大丈夫なのかな)

あと、なぜかバックライトが点きません。

もしLCDを使用するのであれば、3.3V品を別途購入することをお勧めします。
ただ、現状で調べたところDE0にそのままつなげられる一列端子のLCDモジュールはないようです。

DE0を使う場合はちょっと考える必要がありますが、
DE0-CVの場合はそもそもGPIOから信号を引き回してLCDにつなぐ必要があるため二列端子で良いです。
私は、マルツ電波のこのLCDモジュールを使うことにしました。
LCDモジュール(白文字発光・バックライト有・3.3V) TC1602E-13A Linkman製|電気部品ならマルツオンライン

よほど変なものでない限り、LCDモジュールの制御はどれでも同じなので3.3V品ならどれでも良いです。

以下、ひとまず拡張キットのLCDモジュールについて書きます。


LCDモジュールについて

拡張キットはこれ。
Altera DE0 開発・学習ボード 拡張キット II - Soliton Wave Shop

LCD単体はこれ。
16×2 LCDモジュール - Soliton Wave Shop

LCDモジュールのデータシートはキットになぜか添付されていない。
モジュールにシルク印刷されている1602AとLCDというキーワードでデータシートがヒットした。
https://www.openhacks.com/uploadsproductos/eone-1602a1.pdf

ピンアサイン

f:id:m_keishi2006:20170430093734p:plain

周辺回路

DE0のschematicに書いてあるものをそのまま使う。
DE0でバックライトを操作できるようになっているのでトランジスタを使っているみたい。(図の左上)
手持ちにトランジスタがあればそのまま再現する。なければバックライトはVCCと直つなぎにする。
f:id:m_keishi2006:20170430094724p:plain

LCD制御

このLCDモジュールに載っているICはST7066Uというものらしい。
LCDモジュールのデータシートに載っている表とかはST7066Uのデータシートをコピー&ペーストしたものっぽい。
詳細な制御方法はこっちを見たほうが良い。
http://www.newhavendisplay.com/app_notes/ST7066U.pdf

(DE0-CV) 約1秒で7segLEDをカウントアップ(0-F)する記述

お勉強メモ DE0-CVでやってますけど、もちろんDE0でも使えます。

library IEEE;
    use IEEE.std_logic_1164.all;
    use IEEE.std_logic_misc.all;
    use IEEE.std_logic_arith.all;
    use IEEE.std_logic_unsigned.all;

entity CountUp7seg is
    port(

        --//////////// CLOCK //////////
        CLOCK_50       : in     std_logic;

        --//////////// SEG7 //////////
        HEX0           : out   std_logic_vector(6 downto 0);
        HEX1           : out   std_logic_vector(6 downto 0);
        HEX2           : out   std_logic_vector(6 downto 0);
        HEX3           : out   std_logic_vector(6 downto 0);
        HEX4           : out   std_logic_vector(6 downto 0);
        HEX5           : out   std_logic_vector(6 downto 0);

        --//////////// KEY //////////
        KEY            : in    std_logic_vector(3 downto 0);        
        RESET_N        : in    std_logic;

        --//////////// LED //////////
        LEDR           : out   std_logic_vector(9 downto 0);

        --//////////// SW //////////
        SW             : in    std_logic_vector(9 downto 0)
    );
end CountUp7seg;

architecture rtl of CountUp7seg is
    
    --  -- 0 --
    --  5     1
    --  -- 6 --
    --  4     2
    --  -- 3 --
    constant kDigit0 : std_logic_vector(6 downto 0) := "1000000";
    constant kDigit1 : std_logic_vector(6 downto 0) := "1111001";
    constant kDigit2 : std_logic_vector(6 downto 0) := "0100100";
    constant kDigit3 : std_logic_vector(6 downto 0) := "0110000";
    constant kDigit4 : std_logic_vector(6 downto 0) := "0011001";
    constant kDigit5 : std_logic_vector(6 downto 0) := "0010010";
    constant kDigit6 : std_logic_vector(6 downto 0) := "0000010";
    constant kDigit7 : std_logic_vector(6 downto 0) := "1111000";
    constant kDigit8 : std_logic_vector(6 downto 0) := "0000000";
    constant kDigit9 : std_logic_vector(6 downto 0) := "0010000";
    constant kDigitA : std_logic_vector(6 downto 0) := "0001000";
    constant kDigitB : std_logic_vector(6 downto 0) := "0000011";
    constant kDigitC : std_logic_vector(6 downto 0) := "1000110";
    constant kDigitD : std_logic_vector(6 downto 0) := "0100001";
    constant kDigitE : std_logic_vector(6 downto 0) := "0000110";
    constant kDigitF : std_logic_vector(6 downto 0) := "0001110";

    type tDigitSelect is array(0 to 15) of std_logic_vector(6 downto 0);
    
    constant kDigit : tDigitSelect := ( kDigit0, kDigit1, kDigit2, kDigit3,
                                        kDigit4, kDigit5, kDigit6, kDigit7,
                                        kDigit8, kDigit9, kDigitA, kDigitB,
                                        kDigitC, kDigitD, kDigitE, kDigitF
                                      );
    
    -- clock = 50MHz (20ns)
    -- 1s = 50,000,000 cycle
    -- log2(50,000,000) = 25.5 := 26bit
    signal count1s    : std_logic_vector(25 downto 0);
    signal countdigit : std_logic_vector( 3 downto 0);
    
begin
    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            count1s <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            count1s <= count1s + '1';
        end if;
    end process;

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            countdigit <= (others => '0');
        elsif rising_edge(CLOCK_50) then
            if and_reduce(count1s) = '1' then
                countdigit <= countdigit + '1';
            end if;
        end if;
    end process;

    process (CLOCK_50, RESET_N) is
    begin
        if RESET_N = '0' then
            HEX0 <= (others => '1');
            HEX1 <= (others => '1');
            HEX2 <= (others => '1');
            HEX3 <= (others => '1');
            HEX4 <= (others => '1');
            HEX5 <= (others => '1');
        elsif rising_edge(CLOCK_50) then
            HEX0 <= kDigit(conv_integer(countdigit));
        end if;
    end process;

end architecture;