VisualStudioでVHDL編集する (2)


VisualStudioでVHDL編集する (1)

V3SのTrialライセンスは1ヶ月間のみ有効 1ヶ月後は使えないので、それ以上使いたいならライセンスを買う必要がある。
ここではPrivateライセンスの購入方法を解説する。

Privateライセンスを買う

V3Sのライセンス体系はこのようになっている。
f:id:m_keishi2006:20170717121726p:plain

Privateライセンスは$42で、だいたい\5,000以下で買うことができる。
有効期限は1年間。
これを高いと思うか安いと思うかは人それぞれ・・・。
お金を出すか出さないか判断するためにも、Trialライセンスで十分に試そう。

Privateライセンスのところの「Purchase」をクリックすると下記の画面になる。
f:id:m_keishi2006:20170717164756p:plain

名前とメールアドレス、そして使用するPCのMACアドレスを入力する。 ライセンスはMACアドレスに紐付いているので、ここで登録したMACアドレスのPC以外からの利用はできない。

必要情報を入力して次に進むとこの画面になる。
f:id:m_keishi2006:20170717165106p:plain

入力は日本語で大丈夫だった。
クレジット、PayPalAmazonのいずれかで支払うことができる。
自分はPayPalで支払った。

お金を支払うと、ViDE-Softwareから「v3s-private-license.lic」というファイルがメールで送られてくる。
これをPCのどこかに格納する。

ライセンスをV3Sで読み込む

VisualStudioを起動し、V3S - Preference - Licensing を開く。
Path to license file をチェックして、「v3s-private-license.lic」の場所を読み込ませる。
License Summary がチェックマークになったら読み込み成功。
これで1年間、V3Sを利用できるようになった。
f:id:m_keishi2006:20170717165937p:plain
※画像は色々と情報を消してます。

MACアドレスを変更する。

MACアドレス変更にはSerial Numberが必要です。

MACアドレスを変更にはライセンスのシリアルナンバーが必要である。
シリアルナンバーは Preference - Licensing の Serial Number から確認可能。
v3s-private-license.licをテキストエディタで開いてもSerial Numberが書いてある。

MACアドレスを変更するということはPCを変更するということ。
gmail等でライセンスファイルを受け取った人は良いが、PCにしか保存していない人は、
シリアルナンバーがわからずにMACアドレス変更に苦労するかもしれない。
ライセンスファイルが送られてきたメールはできればクラウド上に保管しよう。

MACアドレス変更はとってもかんたん
ここのMACアドレス変更URLから変更しよう。

注意!
かんたんにMACアドレスを変更できるのは1回のみ。 2回目からはViDE-Softwareに連絡しなきゃならない。

VisualStudioでVHDL編集する (1)

V3SというVisualStudioのアドインを使う。

開発元はViDE-Software

ライセンス形態はこんな感じ。
自分は1ヶ月のTrialライセンスを使ってみて、良さそうだと感じたのでPrivateライセンスを購入した。
f:id:m_keishi2006:20170717121726p:plain

Trialライセンスを使う

  1. VisualStudioをインストールする

V3SはVisualStudioのアドインなので、当然ながらVisualStudioをインストールしないと使えない。
VisualStudioをインストールしよう。
V3SはVisualStudioのバージョンごとに提供されており、2017-07-17では

VS2010, VS2012, VS2013, VS2015, VS2017

に対応したバージョンが提供されている。

なお、アドインはVisualStudio Expressでは利用できないので注意。
Community版を使用しよう。(Pro版が使える環境ならそちらで)

※4月にインストールしたときはVS2017だとプロジェクトを開いたときにVisualStudioが落っこちて使えなかった。
 Updateされたのかわからないが、ひとまずVS2015バージョンで説明する。

VisualStudioのインストールは解説記事がたくさんあるので説明するつもりはない。
つもりだったが、VisualStudio2017がリリースされてから以前のバージョンをインストールするのが面倒になっていたので
(自分の覚え書きも兼ねて)簡単に説明する。

VisualStudioのダウンロードページを開く
ダウンロード | IDE、Code、Team Foundation Server | Visual Studio

最新版のVisualStudioであれば VisualStudio Communityの「無償ダウンロード」をクリックすればインストーラが手に入るので
そのままインストールすればOK。

それより以前のバージョンをお探しの場合はここでは見つからないので、下の方にグリグリスクロールして「以前のバージョン」をクリックする。
f:id:m_keishi2006:20170717123504p:plain

表示されたページの「Visual Studio (MSDN) サブスクリプションにサインインする」をクリックする。
f:id:m_keishi2006:20170717124033p:plain

Microsoftアカウントでサインインするように促されるのでサインインする。
もしMicrosoftアカウントを持っていない人は作成する必要がある。

・・・すごく厭らしい。まぁ、VisualStudio利用者はMicrosoftアカウントを持っている人が多いと思うが。。

ちなみに、「MSDN サブスクリプションをお持ちでない方へ」というところの「今すぐ無料で参加する」ではインストーラは手にはいらないので注意。
そもそもそれもMicrosoftアカウントを要求してくる。

サインインしたら、検索窓で「Community 2015」とでも検索すればCommunity 2015のインストーラが手に入る。
インストールするのは Visual Studio Community 2015 with Update 3 とかで良いんじゃないかな。
その他のバージョンが欲しい場合は2015のところを必要なバージョンに変更すれば良い。

  1. V3Sをインストールする。

V3SはVisualStudioのMarketplaceからダウンロードできる。 下記からインストールしているVSのバージョンを選んでダウンロードし、インストールする。

marketplace.visualstudio.com

インストールしているVSバージョンと違うものはインストールできないようになっている。

インストール後、VisualStudioを起動してメニューバーに「V3S」が見えたらインストールできている。
f:id:m_keishi2006:20170717125434p:plain

  1. V3Sを使ってみる。

「新しいプロジェクト」のウィザードに、「V3S-Extension」が追加されているので選ぶ。
適当にプロジェクトを作成する場所を選んでOKを押す。 f:id:m_keishi2006:20170717125844p:plain

こんなウィザードが出てくる。
f:id:m_keishi2006:20170717130212p:plain

AlteraのProjectファイルやXilinxのProjectファイルから、登録されているソースを自動で読み込んでくれる機能を持っている。 ただ、この機能を利用するとVisualStudioプロジェクトを作成する場所を間違うとVisualStudioが落っこちるので注意。 (そのうち解説できればいいな~)

ここではSample Projectを選ぶ。
SampleProjectが作成されると、ソリューションエクスプローラーにソースファイルが表示される。
f:id:m_keishi2006:20170717130721p:plain

試しにspi.vhdを開いてみるとこんな感じで表示される。
f:id:m_keishi2006:20170717130931p:plain

シンタックスハイライトだけでなく、マウスオーバーすると定義が表示される。
これで「あれ、この信号は何bitだったっけ?」とか「このrecordタイプの信号って何があったっけ?」とか悩まずに済む。
f:id:m_keishi2006:20170717131220p:plain
f:id:m_keishi2006:20170717131543p:plain

その他にもVisualStudioのショートカット機能やスニペット機能を使える。

設定を変えたい場合はメニューバーで V3S - Preference から。 Code Coloring で色を変更できる。

なお、Verilogシンタックスハイライトしか対応していない。
VHDL相当の機能は開発中らしい。

とりあえずTrial版の説明はここまで。
次回はPrivate版の購入方法を解説する。

VisualStudioでVHDL編集する (2)

PC購入

使用していたPC(HP Z400)がOSの修復中から起動しなくなったので新しいPCを購入。
(セーフモードですら起動しなくなった)

CADとかするだろうということでWorkstationなんぞを使っていたけれど、ぶっちゃけCADなんかやらなかった。
今回は、CPUはCore i7とし、GPUモロモロを旧PCから流用するつもりでドスパラで組んだ。
流用するのはGPU(GTX970とQuadro2000)、HDD(1TBx2でRAID構成だった。今回はRAIDにせず計2TBで使用)
メモリは残念ながら再利用不可だった。再利用しようと思って刺せなくて気が付いた。
旧PCはPC3-10600で、新PCはPC4-19200。会社のPCがPC3-10600だったと思うので会社に持っていこう。

覚書として新PCのスペックを記載。

項目 内容
OS Windows10 Pro 64bit
メモリ 16GB DDR4 SDRAM(PC4-19200/8GBx2/デュアルチャネル)
電源 玄人志向 700W 静音電源 (80PLUS TITANIUM / KRPW-TI700W/94+)
CPU インテル Core i7-7700K (クアッドコア/HT対応/定格4.20GHz/TB時最大4.50GHz/L3キャッシュ8MB)
SSD 500GB SSD
HDD1 2TB HDD
HDD2(*) 1TB HDD
HDD3(*) 1TB HDD
光学ドライブ ブルーレイドライブ (読み書き対応)
GPU1(*) GeForce GTX970
GPU2(*) Quadro 2000
Officeソフト Microsoft® Office Home and Business 2016 (Word/Excel/PowerPoint/Outlook/OneNote)

約21万円だった。
※HDD2,HDD3,GPU1,GPU2は旧PCから流用

HDD2とHDD3が認識しない……。
ディスクの管理でも出てこないので、たぶん電源をつながなきゃならないのだと思う。
SATAケーブルしかつないでない。旧PCがそうだったから……)

Quadro2000からちゃんと映像出力されない。マウスカーソルしか見えない。
なんか直近のWindows10のアップデートからこんな感じ。 旧PCで何とかしようと電源のON/OFFくりかえしてたらOSがぶっ壊れた。。のでもう諦めることにする。

せっかくCore i7-7700Kを積んでいるので、GTX1080Tiとか載せてみたい。
そんなお金ないけど。

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)


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

(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)


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

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モジュールを使う (5)

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


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

タイミングチャート

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

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モジュールを使う (4)