2009年12月09日

ModelSimでビットマップのリードライト(完成)

以前、「ModelSimでビットマップのリードライト」で紹介したモジュールがやっと完成した。
これを使えば簡単にビットマップをテストベクタとして利用できるので画像処理なんかには便利だと思う。
また、FLI等は使わずVHDLの記述だけで実現しているので修正も出来るし応用も出来る。
という事で、他の人にも使って貰えればと思うのでソースコードを公開します。

ダウンロード

動作環境
OS:Windows系
シミュレータ:ModelSim(XEでも大丈夫だと思う)

使用方法
上記環境で、ModelSimにPathが通っていれば、解凍したファイルの「sim.bat」を実行すればサンプルが動きます。
このサンプルは、解凍したファイルの「read.bmp」を読み出しピクセルデータを反転して「write.bmp」に書き込むだけです。
後は適当にソースコードを見て下さい。

ドキュメントが少ないので申し訳ないが要望があれば整備します。



投稿時刻(20:54)│コメント(9)ModelSim 

2007年10月02日

VHDL TIPS 「物理タイプの使い方」

物理タイプとは、時間や電圧、電流、距離などの物理的な物を表すために使用するデータタイプだ。
VHDLには元々「time」が物理タイプとして定義されており内容はスタンダードパッケージに書かれている。
この「time」を代表とする物理タイプはpsやnsなどの単位を指定できるのが大きな特徴で、新しい単位を持ったデータタイプを作成する事も出来る。

以下の例は距離の物理タイプを新たに定義して、その距離を遅延時間に変換する関数も用意した。(遅延時間は1ns=15cmで計算)


library modelsim_lib;
use modelsim_lib.util.all;

package distance_pkg is

    -- 距離タイプ
    type distance is range 0 to integer'high
        units
            munit;

            um      = 10 munit;
            mm      = 1000 um;
            cm      = 10 mm;
            m       = 100 cm;
            km      = 1000 m;

            mil     = 254 munit;
            inch    = 1000 mil;
            ft      = 12 inch;
            yd      = 3 ft;
            fm      = 6 ft;
            mi      = 5280 ft;
            lg      = 3 mi;
        end units;

    -- 距離から遅延時間を算出
    function distance2time( trace_len : distance ) return time;

end distance_pkg;

package body distance_pkg is

    -- 距離から遅延時間を算出
    function distance2time( trace_len : distance ) return time is
        variable    resolution      : real;
        variable    delay_time      : time;
    begin
        -- シミュレーションレゾリューションの取得
        resolution := get_resolution;

        -- メートル−時間変換
        --1ns=15cm(150000um)
        --1000000fs=150000um
        --1fs=0.15um(1.5munit)
        delay_time := to_time( real( distance'pos(trace_len))
                                 /(1.5 * resolution * 1000000000000000.0));

        return delay_time;
    end;

end distance_pkg;


このように最小単位からの係数を変えることで、ミリやインチなどを混在して記述出来る事も便利な点だ。
また、距離を遅延時間に変換する関数は、「time」がシミュレーションのレゾリューションによって最小単位が変わる特殊な物理タイプのため、まずレゾリューションを取得してから変換している。

この距離パッケージを使うと、以前紹介したVHDL TIPS 「双方向バスの遅延モデル」の遅延値を入力する部分を、距離を入力する様に簡単に置き換えることが出来るので、ケーブルのモデリングなどに使用するとおもしろいと思う。



投稿時刻(08:58)│コメント(0)VHDL 

2007年09月26日

VHDL TIPS 「双方向バスの遅延モデル」

FPGAとメモリモデルを接続する場合等に「双方向バスでネットの遅延を挿入したい」と思った事が何度かある。
安直に「after文」でお互いを代入しても'X'になった以降はずっと'X'のままでうまくいかない。
ではどうすれば双方向バスに遅延を挿入する事が出来るのだろうか?
簡単な様で、これが結構難しい。色々と考えた末に、双方向バスの遅延モデルを作成できたので紹介する。

まずは、モデルの動作を確認した回路。
双方向の遅延モデルだが、テストで入力は使用しないので出力のみを入れた。
このPORTAとPORTBに対してテストベンチから波形を入力する。

linedelay_c

 



波形を入力するテストベンチはこれ。


library IEEE;
use IEEE.std_logic_1164.all;

entity TEST is
end TEST;

architecture BEHAVIOR of TEST is

    component LINEDELAY
        generic
        (
            linedelay       : time
        );
        port
        (
            PORTA           : inout std_logic;
            PORTB           : inout std_logic
        );
    end component;

    signal  PORTA   : std_logic;
    signal  PORTB   : std_logic;
    signal  PORTA_S : std_logic;
    signal  PORTB_S : std_logic;

begin

    C_LINEDELAY : LINEDELAY
    generic map
    (
        linedelay       => 10 ns
    )
    port map
    (
        PORTA           => PORTA_S,
        PORTB           => PORTB_S
    );

    process begin
        PORTA <= 'Z';
        PORTB <= 'Z';
        wait for 100 ns;
        PORTA <= '1';
        wait for 100 ns;
        PORTA <= '0';
        wait for 100 ns;
        PORTA <= 'Z';
        PORTB <= '1';
        wait for 100 ns;
        PORTB <= '0';
        wait for 100 ns;
        PORTB <= 'Z';
        wait for 100 ns;
        PORTA <= '1';
        PORTB <= '0';
        wait for 100 ns;
        PORTA <= '0';
        PORTB <= '1';
        wait for 100 ns;
        PORTA <= '1';
        wait for 100 ns;
        PORTA <= '0';
        PORTB <= '0';
        wait for 100 ns;
    end process;

    PORTA_S <= PORTA;
    PORTB_S <= PORTB;

end BEHAVIOR;


そして動作結果の波形はこんな感じ。

linedelay_w

 

PORTAから出力した値は、すぐにPORTA_Sに反映されるがPORTB_Sには遅延が入っている事が分かる。
逆にPORTBから出力した値は、すぐにPORTB_Sに反映されるがPORTA_Sには遅延が入っている。
また、同時にPORTA_SとPORTB_Sの値を変化させても遅延時間分のショートが'X'で再現されている。

次に肝心なモデルのソースがこれ。


library IEEE;
use IEEE.std_logic_1164.all;

entity LINEDELAY is
    generic
    (
        linedelay       : time
    );
    port
    (
        PORTA           : inout std_logic;
        PORTB           : inout std_logic
    );
end LINEDELAY;

architecture BEHAVIOR of LINEDELAY is

    signal  PORTA_X     : std_logic;
    signal  PORTB_X     : std_logic;

begin

    PROCESS
        VARIABLE last_time : time;
        VARIABLE transact : boolean;
    BEGIN
            WAIT ON PORTA'TRANSACTION, PORTB_X UNTIL (last_time /= NOW or PORTB_X'EVENT);

            if ( PORTA'TRANSACTION'EVENT ) then
                PORTA <= 'Z';
                transact := TRUE;
                last_time := NOW;
            else
                transact := FALSE;
            end if;

            WAIT FOR 0 ns;

            if ( transact = TRUE ) then
                PORTA_X <= transport PORTA after linedelay;
            end if;
            PORTA <= PORTB_X;

    END PROCESS;

    PROCESS
        VARIABLE last_time : time;
        VARIABLE transact : boolean;
    BEGIN
            WAIT ON PORTB'TRANSACTION, PORTA_X UNTIL (last_time /= NOW or PORTA_X'EVENT);

            if ( PORTB'TRANSACTION'EVENT ) then
                PORTB <= 'Z';
                last_time := NOW;
                transact := TRUE;
            else
                transact := FALSE;
            end if;

            WAIT FOR 0 ns;

            if ( transact = TRUE ) then
                PORTB_X <= transport PORTB after linedelay;
            end if;
            PORTB <= PORTA_X;

    END PROCESS;

end BEHAVIOR;


PORTA_XとPORTB_Xに、それぞれの双方向ポートの値をコピーし、それを遅延させて反対側のポートに代入している。
この双方向ポートの値をコピーする時、一度モデル側のドライバを'Z'にして本当の相手側ポートの値を取得する必要がある。
また、変な「WAIT文」、これは双方向ポートのトランザクションは時間単位あたり1度しか動作させたく無いのに対し反対側の双方向ポートをコピーした値のイベントは何回でも動作させるための条件だ。
双方向ポートのトランザクションを時間単位あたり1度しか動作させたくない理由は、自分でトランザクションを発生させているためこの条件が無いとループに陥ってしまうからである。
ただ、双方向ポートの一番最初のトランザクションで動作させているため、トランザクションが発生したデルタ時間の後のデルタ時間に値が変化するような信号ではうまく動作しない。
尚、以前紹介したVHDL TIPS 「アナログスイッチのモデリング」もそうだが、この手の手法はこの制限がつきまとってしまう。



投稿時刻(21:44)│コメント(3)VHDL 

2007年09月21日

VHDL TIPS 「属性"DRIVING_VALUE"の使用法」

VHDLでは出力ポートに割り当てた値を内部で参照する事は出来ない。
所が、VHDL93でコレを可能にするアトリビュート「DRIVING_VALUE」が追加された。

以下に使用例を紹介する。


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_signed.all;

entity COUNT is
    port
    (
        CLK     : in    std_logic;
        COUNT   : out   std_logic_vector(7 downto 0) := (others => '0')
    );
end COUNT;

architecture rtl of COUNT is

begin

    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            COUNT <= COUNT'DRIVING_VALUE + '1';        -- 内部参照
        end if;
    end process;

end rtl;


これは単純な8ビットのカウンタで、クロックを入力するとカウントアップするだけの回路だ。通常は内部にカウント信号を定義してその信号をカウントアップし、出力ポートに割り当てるような記述になると思うが「DRIVING_VALUE」を使用すると出力ポートの値を直接参照してカウントアップし、そのまま出力ポートに代入する事が出来る。

便利と言えば便利だが、自分は出力ポートに出ている信号は内部参照されていないことを前提にソースコードを追いかけるので、出来るだけ使用しないようにしている。
また、使用しない決定的な理由として、XSTではサポートしていないと言う事がある。

それでも、シミュレーションモデル等で出力ポートを直接チェックしたい時には便利に使えるかもしれない。



投稿時刻(21:34)│コメント(0)VHDL 

2007年09月11日

VHDL TIPS 「shared variableの使用法」

VHDL93から「共有変数」が使用可能になりVHDL2002で使用方法が変更になった。
XSTはVHDL93をサポートしており限定的ではあるが、共有変数を使用できるみたいなのでVHDL93とVHDL2002両方の使い方を紹介する。
(ただし今回、紹介する例はXSTで合成出来ない。合成するためには同じアーキテクチャ内から共有変数にアクセスする必要がある。)

まず、VHDL93の例


package shared_pkg is
    shared variable SH_INT      : integer;
end shared_pkg;

 

library IEEE;
use IEEE.std_logic_1164.all;

library work;
use work.shared_pkg.all;

entity a is
    port
    (
        CLK             : in    std_logic;
        DATA           : out   integer
    );
end a;

architecture a of a is
    component b
        port
        (
            CLK             : in    std_logic
        );
    end component;
begin
    u_b : b port map( CLK );
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            DATA <= SH_INT;
        end if;
    end process;
end a;

 

library IEEE;
use IEEE.std_logic_1164.all;

library work;
use work.shared_pkg.all;

entity b is
    port
    (
        CLK             : in    std_logic
    );
end b;

architecture b of b is
begin
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            SH_INT := SH_INT + 1;
        end if;
    end process;
end b;


この例では、2つのアーキテクチャからパッケージで宣言した共有変数にアクセスしている。

次に、VHDL2002の例
package shared_pkg is     -- プロテクトデータタイプ
    type SH_TEST is protected
        -- 書き込み関数
        procedure write ( data : in integer );
        -- 読み出し関数
        impure function read return integer;
    end protected SH_TEST;

    -- 共有変数をプロテクトタイプで宣言
    shared variable SH_INT      : SH_TEST;

end shared_pkg;

package body shared_pkg is

    type SH_TEST is protected body
        -- 実データ
        variable buf : integer := 0;
        -- 書き込み関数
        procedure write ( data : in integer ) is
        begin
            buf := data;
        end;
        -- 読み出し関数
        impure function read return integer is
        begin
            return buf;
        end;
    end protected body SH_TEST;

end shared_pkg;

 

library IEEE;
use IEEE.std_logic_1164.all;

library work;
use work.shared_pkg.all;

entity a is
    port
    (
        CLK             : in    std_logic;
        DATA            : out   integer
    );
end a;

architecture a of a is
    component b
        port
        (
            CLK             : in    std_logic
        );
    end component;
begin
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            DATA <= SH_INT.read;
        end if;
    end process;
    u_b : b port map( CLK );
end a;

 

library IEEE;
use IEEE.std_logic_1164.all;

library work;
use work.shared_pkg.all;

entity b is
    port
    (
        CLK             : in    std_logic
    );
end b;

architecture b of b is
begin
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            SH_INT.write( SH_INT.read + 1 );
        end if;
    end process;
end b;


VHDL2002では単純にアクセスするのでは無く、プロテクトタイプを宣言しデータにアクセスするための関数を定義してその関数を通してデータにアクセスする。
丁度、C++のクラスでプライベートのデータにパブリックのメソッドからアクセスするようなイメージだ。

今回、共有変数の使用例を紹介したがXSTでは同じアーキテクチャ内からでしかアクセス出来ない等の制限があり余り利用価値がない。
もし別のアーキテクチャからアクセス出来ればデバッグ時に信号を引き出す手段として有効利用できたと思う。私はコレがやりたかったのでとても残念に思う。

尚、今回の例は変数の使い方としては良くない例で、同じ時間に違うプロセスからリードライトしているのでシミュレータによってはタイミングが変わるかもしれない。あくまでも共有変数の動作を確認する目的で作成している。

2010/2/19:今更ながら少し修正、VHDL93版の"DATA <= SH_INT;"でイベントが発生しないのでCLKイベントにしました。


投稿時刻(20:52)│コメント(4)VHDL 

2007年09月10日

配置と配線の指定

XilinxのFPGAで配置と配線を指定し再インプリメントしてもそれを固定する方法を紹介する。

全体の流れは、
・配置配線を指定したい回路を適当にインプリする
・FPGAエディタで配置配線を編集する
・配置配線の指定を行うUCFファイルを出力する
このような感じになる。

画像を織り交ぜて解説すれば分かりやすいのだが、大変なのでテキストのみでご勘弁を。
それでは、詳細のオペレーションを紹介する。

その一(配置)
’枌崘枩指定したい回路を作成し使用するFPGAにインプリする。
■藤丕韮船┘妊タでそのデザインを開く。
ここで、UCF等で配置固定していなければ当然適当に配置されている。
で枌峪慊蠅靴燭ぃ丕稗里よびSLICEで適当に配置されてしまったSITEと配置指定先のSITEを選択してSWAPボタンを押す。
イ海譴韮咤稗圍鼎入れ替わるので、これを繰り返し目的の配置を完了する。

その二(配線)
仝従適当に配線されていて配線指定したいネットを選択しUNROUTEボタンを押す。これでラッツネットになる。
▲薀奪張優奪箸離疋薀ぅ个らレシーバまで配線したい順番に選択して行き最後に
ROUTEボタンを押す。これで選択した順番に配線される。
ここで注意点:
・スイッチボックスは選択しても配線出来ないので配線に使用するネットを直接選択して行く。
・レシーバが複数あっても順番に指定すればOK、配線指定しなくても良いレシーバは選択に含めず配線指定のネットを配線してからドライバ、レシーバを選択してAUTOROUTEボタンで配線する事もできる。
・配線リソースが表示されていなければ選択も出来ないのでツールバーのレイヤーで配線が表示されるようにしておく。
これで配線が出来るので、これを繰り返し目的の配線を完了する。

その三(制約生成)
.瓮縫紂爾Tools→Directed Routing Constraintsを選択してダイアログを出す。
▲瀬ぅ▲蹈阿離螢好箸ら配置配線指定を行いたいネットを選ぶ。
出力するUCF Fileを指定する。
Placement Constraint TypeでUse Absolute Location Constraintを選択する。
Applyを押す。
Δ海譴如配置配線を固定するUCFが生成されたので再インプリすればFPGAエディタで編集した回路が再現出来る。

これは単純な例だが、これの応用で色々出来ると思う。

2007/09/11 - Directed Routing制約には対応デバイスに制限がある。
使用不可:Virtex/VirtexE/Virtex2ProX/Spartan2/SpartanE/PLD
使用可能:Virtex2/Virtex2Pro/Virtex4/Virtex5/Spartan3



投稿時刻(21:43)│コメント(0)Xilinxツール 

2007年09月04日

VHDLでFPGAのバージョン管理

皆さんはFPGAのバージョン管理はどうしていますか?
「たまにエラー出るけどこれいつのバージョンか分からない!」
「バージョンレジスタを付けたけど更新するの忘れた!」
なんて事ありませんか?
私はよくあります...

そこで!論理合成するたびに自動的にインクリメントしてくれる信号を作ってみました。これは合成時に値が決定するので回路のリソースは消費しませんし、自動なので更新を忘れることもありません。

使い方は、FPGAのトップから本パッケージの関数を呼び出すだけです。
以下に使用例を紹介します。


-- バージョン取得パッケージの参考例
library IEEE;
use IEEE.std_logic_1164.all;

-- FPGAのトップファイルにこのパッケージを宣言する。
library work;
use work.ver_get.all;

entity top is
    generic
    (
        -- シミュレーションを行う場合は、
        -- テストベンチから「SIM_MODE」を1にして呼び出す。
        -- 合成の時はディフォルトの0が自動的に割り当たる。
        SIM_MODE            : integer := 0
    );
    port
    (
        VER_OUT         : out   std_logic_vector(31 downto 0)
    );
end top;

architecture top of top is

    -- この呼び出しで論理合成する毎にインクリメントされた値を返す。
    constant    VERSION : std_logic_vector(31 downto 0)
                                := get_version( SIM_MODE, "ver.txt" );

begin

    VER_OUT <= VERSION;

end top;


動作の仕組みは、論理合成時に指定されたバージョンファイルからバージョンデータを読み出しインクリメントしたバージョンデータを書き込むだけです。
詳しくはパッケージファイルにコメントを書いていますのでそれを見てください。

パッケージはココ
参考ファイルはココ
バージョンファイルの参考例はココ
からダウンロードできます。

「もっと良い方法しってるよ!」って人は是非教えてください。

投稿時刻(20:14)│コメント(6)VHDL 

2007年08月31日

書評「VHDL」

delta_out.gif

タイトル「VHDL」
著者「Douglas L. Perry」
出版社「アスキー出版局」
発行日「1996年11月1日」
ISBN「4−7561−1906−9」




これは、私が初めて購入したVHDLの本だ。
当時VHDLの本は選択肢が少なくこの本が一番ぶ厚く内容が濃いと思ったので選んだ。ところが、最初から読み進めても全く理解出来ない!仕方がないので初心者向けの入門書を別に購入して勉強した。そして数件の開発を経験しVHDLの理解が深まったところでもう一度この本を読むと「真のVHDLの姿」を思い知らされた。それからこの本は一時も手放せないリファレンス本となり今でも参照させてもらっている。
また、英語が得意でない人にとっては、英語のLRMを読む前にこれを読むと言った使い方も出来ると思う。

まとめると、この本は初心者向けではないが、上級者を目指す中級者にとってはとても良い本だと思う。是非購入を進めたい!...が絶版となっていたと思う。Amazonには数点在庫があったのでこれが最後のチャンスかもしれない。

おすすめ度:★★★★★



投稿時刻(19:01)│コメント(4)書評 

2007年08月29日

VHDL TIPS 「postponed processの使用法」

前回のシミュレーション・サイクルで少し触れた実行遅延プロセスを紹介する。

実行遅延プロセスはVHDL93で追加された機能で、その時刻の全てのデルタサイクルが終わった後で実行されるプロセスである。

動作をサンプルソースで見てみる事にする。
以下のソースは4ビットの同期カウンタを通常のプロセスと実行遅延プロセスでセットアップチェックを行い動作を比較した例だ。


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

entity PP_TEST is
end PP_TEST;

architecture behavior  of PP_TEST is

    signal  CLK     : std_logic;
    signal  COUNT   : std_logic_vector(3 downto 0) := (others => '0');

begin

    -- クロック生成
    process begin
        CLK <= '0';
        wait for 10 ns;
        CLK <= '1';
        wait for 10 ns;
    end process;

    -- CLKでCOUNTをインクリメント
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            COUNT <= COUNT + '1';
        end if;
    end process;

    -- COUNTのセットアップチェック
    process ( CLK ) begin
        if ( CLK = '1' ) then
            assert ( COUNT'last_event >= 5 ns )
                report "Setup Time was not suitable."
                severity WARNING;
        end if;
    end process;

    -- COUNTのセットアップチェック
    postponed process ( CLK ) begin
        if ( CLK = '1' ) then
            assert ( COUNT'last_event >= 5 ns )
                report "Setup Time was not suitable.(postponed)"
                severity WARNING;
        end if;
    end process;

end behavior;


これを実行すると実行遅延プロセスのアサーションだけアクティブになる。
これは、先ほども書いたが全てのデルタ遅延が処理された後でカウンタのセットアップチェックを行うためセットアップ時間が0となり5ns以上では無いのでアサートする。
一方、通常のプロセスはカウンタの値が更新される1つ前のデルタサイクルでセットアップチェックを行うためセットアップ時間が10nsとなり5ns以上なのでアサートされない。

ここで注意として実行遅延プロセスでクロックイベント等を評価した場合、デルタサイクルが無い場合を除き真が返ってくることはない。
また、実行遅延プロセスでデルタサイクルを発生させる事は禁止になっている。
簡単に言うと、実行遅延プロセスで「if ( CLK'event and CLK = '1' )」と書いても既にクロックイベントは終了しているのでこのIF文は実行される事は無い。と言う事と、実行遅延プロセスで「A <= B;」等と信号の代入を行ってはダメと言う事である。

投稿時刻(12:55)│コメント(0)VHDL 

2007年08月28日

シミュレーション・サイクル

VHDLにおけるシミュレーション・サイクルを以下に紹介する。


1.TcにTnを代入
ここで、アクティブなドライバや開始するプロセスがない、または
シミュレーションの最大時刻ならばシミュレーション終了

2.信号を更新する。

3.更新された信号にセンシティブな全てのプロセスを実行する

4.以下の中で最も近い時刻をTnに入れる
・シミュレーションの最大時刻
・ドライバがアクティブになる次の時刻
・プロセスが再開する次の時刻
この処理後、TcとTnが同じならばデルタサイクルなので
デルタサイクルを進めて1に戻る

5.実効遅延プロセスを実行して1に戻る

Tc:現在のシミュレーションサイクルの時刻
Tn:次のシミュレーションサイクルの時刻


VHDLシミュレータはこの処理を繰り返し行いシミュレーションを進めている。
これを理解出来るとデルタ遅延や実行遅延プロセス等の理解も早いと思うので是非理解したい所だ。
実行遅延プロセスについては次の機会に書こうと思う。



投稿時刻(21:48)│コメント(0)VHDL 

2007年08月27日

セレクタを2個並べてD−FF

基本的な内容をひとつ

セレクタを2個並べてD−FFを作る
・2入力1出力のセレクタを2段並べる
・最初のセレクタの出力を最後のセレクタの入力0に接続
・それぞれのセレクタ出力を自分の入力1に接続
・CLK入力を最初のセレクタのセレクトに入力
・CLK入力を最後のセレクタのセレクトに反転入力
・最初のセレクタの入力0はD入力
・最後のセレクタの出力はQ出力

出来上がり!



投稿時刻(21:15)│コメント(0)雑記 

2007年08月23日

Hello、 world!

ここ数日、外出などで忙しかったためBlogが更新出来なかった。
平日1件のペースを維持したいが、かなり大変な事だと感じた。FPGAの部屋さんは、これを数年続けているのだから本当に尊敬する。

なつたんさんの所でVHDLのShortCodingのランキングサイトが紹介されていた。
早速VHDLでHelloWorldに参加!このおもしろさにはまってしまった。1時間ほど考えたが、これ以上小さくするのは無理なのかな?って事でechoにも参加!これ以降の問題は疲れたので終了!
やっぱりシンプルな問題がおもしろい。



投稿時刻(12:51)│コメント(2)VHDL 

2007年08月16日

VHDL TIPS 「rangeでパラメータ制約」

モジュールのパラメータチェックは「assert」を使用する事が多いが、単純な範囲制約であれば「range」を使用する事も出来る。
「range」を使用すると静的パラメータであればコンパイル時にチェックし、動的パラメータであれば範囲外になった瞬間Fatalになる。(ModelSim)

以下にサンプルソースを紹介する。
このサンプルは「integer」データタイプのシフトレジスタでシフト数と最大データ値を指定する事が出来る。
ただし、範囲制約として、シフト値は1〜10(ディフォルト5)、入出力データは指定された最大データ値(ディフォルト128)を入れている。


library IEEE;
use IEEE.std_logic_1164.all;

-- integerデータタイプの遅延モジュール
entity INT_DLY is
    generic
    (
        -- 遅延クロック数
        COUNT   : in    integer range 1 to 10 := 5;
        -- データの最大値
        DMAX    : in    integer := 128
    );
    port
    (
        CLK     : in    std_logic;
        DIN     : in    integer range 0 to DMAX;
        DOUT    : out   integer range 0 to DMAX
    );
end INT_DLY;

architecture behavior of INT_DLY is

    -- integerの配列定義
    type int_vector is array ( natural range <> ) of integer;

    -- 遅延をシフトレジスタで実現するためのレジスタ配列
    signal  DELAY   : int_vector(COUNT-1 downto 0);

begin

    -- integerのシフトレジスタ
    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            DELAY(0) <= DIN;
            for i in 1 to COUNT-1 loop
                DELAY(i) <= DELAY(i-1);
            end loop;
        end if;
    end process;
    DOUT <= DELAY(COUNT-1);

end behavior;


このモジュールをCOUNT=11で呼び出した場合、コンパイル時にエラーとなる。
また、DMAX=128でコンパイル、シミュレーションしDINが0〜128を超えた瞬間にFatalとなりシミュレーションがストップする。

このように「range」で範囲制約を行うと使用する側から見て制約が解りやすく、ライブラリ等の共有モジュールには入れておきたい。



投稿時刻(21:26)│コメント(0)VHDL 

2007年08月13日

VHDL TIPS 「std_matchの使用法」

例えば2つのSTD_LOGICを単純に比較すると'1'や'0'の時は何も疑問なく結果が出ると思う。ただSTD_LOGICは'U'や'-'などの状態も存在する。
これらが含まれた比較を行った場合はどうなるか?
 シミュレーションでは単純に=(イコール)で比較した場合は正確に同じ状態でないとイコールとはならない。
 合成ツール(XST)では'-'や'X'はドントケアとして処理し最適化される。
この事から、シミュレーションと実機の動作不一致が発生する。これを回避するために「numeric_std」に含まれる「std_match」ファンクションが使用できる。
std_matchは'-'をドントケアとするだけでなく'U'などの不定値が一致した場合でもイコールとしないような動作をして実機と一致させている。
(XSTは'X'をドントケア、std_matchは不定値として処理するので動作が一致していないが、numeric_stdを参考にテーブルを少し変えるだけで簡単に新たなファンクションを作成できる)

しかし、そもそも'-'(ドントケア)と比較する事自体、意味が無いなんて思われるかもしれない。それはその通りなのだが、データがベクタタイプになると意味が出てくる。例えば、あるビットだけ比較しなくて良い場合などはその部分を'-'をドントケアとして比較すれば一度に記述する事が出来る("0-01--10"等)

以下にstd_matchの動作を紹介する。


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

entity test is
end test;

architecture behavior of test is

    signal  result_match    : boolean := FALSE;
    signal  result_equ      : boolean := FALSE;
    signal  testdata        : std_logic_vector(2 downto 0) := (others => '0');

begin

    -- 固定値
    testdata(2) <= '1';
    testdata(0) <= '0';

    -- std_logicの全ての状態を繰り返す
    process begin
        testdata(1)  <= 'U';
        wait for 10 ns;
        testdata(1) <= 'X';
        wait for 10 ns;
        testdata(1) <= '0';
        wait for 10 ns;
        testdata(1) <= '1';
        wait for 10 ns;
        testdata(1) <= 'Z';
        wait for 10 ns;
        testdata(1) <= 'W';
        wait for 10 ns;
        testdata(1) <= 'L';
        wait for 10 ns;
        testdata(1) <= 'H';
        wait for 10 ns;
        testdata(1) <= '-';
        wait for 10 ns;
    end process;


    -- イコール(=)で比較した場合
    process begin
        if ( testdata = "1-0" ) then
            result_equ <= TRUE;
        else
            result_equ <= FALSE;
        end if;
        wait on testdata;
    end process;

    -- std_matchを使った場合
    process begin
        if ( std_match(testdata, "1-0")) then
            result_match <= TRUE;
        else
            result_match <= FALSE;
        end if;
        wait on testdata;
    end process;

end behavior;


これの結果は、

delta_out.gif

 

 

このように、単純に=(イコール)で比較した場合は完全に一致した"1-0"の場合だけTRUEになり、std_matchを使用した場合はtestdata(2)とtestdata(0)だけの比較になるので常時TRUEになる。



投稿時刻(20:08)│コメント(0)VHDL 

2007年08月09日

イベントとトランザクション

VHDLにはトランザクションという陰の存在がある。
イベントもトランザクションの一部で、イベントを伴うトランザクションと伴わないトランザクションがあると考える事が出来る。

イベントは値が変化した場合に発生する。しかしトランザクションは値の代入が行われるだけで発生する。
以下の例では信号Aに対してイベントは発生しないがトランザクションは発生する。
しかし信号Bはイベントもトランザクションも発生しない。これは、Bへの代入が発生するためには信号Aに対してトランザクションでは無くイベントが必要になるためである。


    process begin
        wait for 1 ns;
        A <= '1'
    end process;
    B <= A;


もう少し複雑な例をModelSimでシミュレーションして動作を見てみることにする。
以下にサンプルソースを紹介する。
これは、単純な2入力のORで1本の入力は'1'固定、もう1本は5ns毎に'1'/'0'を繰り返している。ただし'1'の固定値もトランザクションを発生させるために5ns毎に代入を繰り返している。
そして各信号のイベントとトランザクションをカウントして変化を確認する。

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

entity test is
end test;

architecture behavior of test is

    signal  DATA1       : std_logic := '1';
    signal  DATA2       : std_logic := '1';
    signal  DATA3       : std_logic := '1';
    signal  DATA1_TR    : integer := 0;
    signal  DATA1_EV    : integer := 0;
    signal  DATA2_TR    : integer := 0;
    signal  DATA2_EV    : integer := 0;
    signal  DATA3_TR    : integer := 0;
    signal  DATA3_EV    : integer := 0;

begin

    process begin
        wait for 5 ns;
        DATA1 <= '1' after 1 ns;
        wait for 5 ns;
        DATA1 <= '1' after 2 ns;
        wait for 5 ns;
        DATA1 <= '1' after 1 ns;
        wait for 5 ns;
        DATA1 <= '1' after 2 ns;
    end process;

    process begin
        wait for 5 ns;
        DATA2 <= '0' after 1 ns;
        wait for 5 ns;
        DATA2 <= '1' after 2 ns;
        wait for 5 ns;
        DATA2 <= '0' after 2 ns;
        wait for 5 ns;
        DATA2 <= '1' after 1 ns;
    end process;

    DATA3 <= DATA1 or DATA2;

    -- トランザクション発生でカウントアップ
    process begin
        wait on DATA1'transaction;
        DATA1_TR <= DATA1_TR + 1;
    end process;
    process begin
        wait on DATA2'transaction;
        DATA2_TR <= DATA2_TR + 1;
    end process;
    process begin
        wait on DATA3'transaction;
        DATA3_TR <= DATA3_TR + 1;
    end process;

    -- イベント発生でカウントアップ
    process begin
        wait on DATA1;
        DATA1_EV <= DATA1_EV + 1;
    end process;
    process begin
        wait on DATA2;
        DATA2_EV <= DATA2_EV + 1;
    end process;
    process begin
        wait on DATA3;
        DATA3_EV <= DATA3_EV + 1;
    end process;

end behavior;


これの結果は、

delta_out.gif

 

 

 

 

 

この様になる。一つずつ見ていくと、
DATA1とDATA3のイベントは変化がない。これは先ほどの説明の通り。
DATA2のイベントとトランザクションは同じで、これは通常の動作。
DATA1とDATA2のトランザクションはディレイ値は違うが代入時に変化する。
DATA3のトランザクションはDATA2のイベントが発生した時に代入が発生するのでそのタイミングで変化する。
頭が混乱しそうになるがじっくり考えると理解できるとおもう。



投稿時刻(22:10)│コメント(2)VHDL 

2007年08月06日

ModelSimでビットマップのリードライト

ModelSim+VHDLでビットマップのリードライトを行えるモジュールを作成中だが、思ったように時間がとれないので難航している。
とりあえずパッケージだけ出来たので公開。(ボディは半分くらい)これを作るのに「textio.vhd」はとても参考になる。一度目を通すことをおすすめしたい。
これが出来るとModelSimXE Starterでビットマップを直接扱うことが出来る。
私の仕事上、画像ファイルを扱うことが多いのでこれが出来るとModelSimのライセンスが空いていない時にもシミュレーションが出来るようになる。ModelSimSEだとFLIが使えるので前に作ったコレでも出来るが...


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

package bmp_pkg is

    ----------------------------------------------------------------------------
    -- データタイプ定義
    ----------------------------------------------------------------------------
    -- ファイルヘッダ定義
    type    BITMAPFILEHEADER    is record
        bfType              :   bit_vector(15 downto 0);
        bfSize              :   bit_vector(31 downto 0);
        bfReserved1         :   bit_vector(15 downto 0);
        bfReserved2         :   bit_vector(15 downto 0);
        bfOffBits           :   bit_vector(31 downto 0);
    end record;

    -- インフォヘッダ定義
    type    BITMAPINFOHEADER    is record
        biSize              :   bit_vector(31 downto 0);
        biWidth             :   bit_vector(31 downto 0);
        biHeight            :   bit_vector(31 downto 0);
        biPlanes            :   bit_vector(15 downto 0);
        biBitCount          :   bit_vector(15 downto 0);
        biCompression       :   bit_vector(31 downto 0);
        biSizeImage         :   bit_vector(31 downto 0);
        biXPelsPerMeter     :   bit_vector(31 downto 0);
        biYPelsPerMeter     :   bit_vector(31 downto 0);
        biClrUsed           :   bit_vector(31 downto 0);
        biClrImportant      :   bit_vector(31 downto 0);
    end record;

    -- パレット定義
    type    RGBQUAD             is record
        rgbBlue             :   bit_vector(7 downto 0);
        rgbGreen            :   bit_vector(7 downto 0);
        rgbRed              :   bit_vector(7 downto 0);
        rgbReserved         :   bit_vector(7 downto 0);
    end record;

    -- パレットのポインタ定義
    type    RGBQUADARRAY        is array( natural range <> ) of RGBQUAD;
    type    RGBQUADACCESS       is access RGBQUADARRAY;

    -- バイトのポインタ定義
    subtype BYTE                is bit_vector(7 downto 0);
    type    BYTEARRAY           is array( natural range <> ) of BYTE;
    type    BYTEACCESS          is access BYTEARRAY;

    -- ビットマップ全体を定義
    type    BMP_INFO            is record
        bmp_fh              :   BITMAPFILEHEADER;
        bmp_ih              :   BITMAPINFOHEADER;
        col_tbl             :   RGBQUADACCESS;
        pix_data            :   BYTEACCESS;
    end record;

    -- ファイルタイプ定義
    type    BIN             is file of character;

    ----------------------------------------------------------------------------
    -- 公開サブプログラム定義
    ----------------------------------------------------------------------------
    -- ビットマップオープン(ファイル指定)
    procedure open_bmp (
                            filename        : in    string;
                            bmp_info_data   : out   BMP_INFO;
                            result          : out   boolean
                        );
    -- ビットマップオープン(空データ)
    procedure open_bmp (
                            x_size          : in    std_logic_vector(31 downto 0);
                            y_size          : in    std_logic_vector(31 downto 0);
                            bitcount        : in    integer;
                            bmp_info_data   : out   BMP_INFO;
                            result          : out   boolean
                        );
    -- ビットマップ保存
    procedure save_bmp (
                            filename        : in    string;
                            bmp_info_data   : inout BMP_INFO;
                            result          : out   boolean
                        );
    -- ビットマップクローズ
    procedure close_bmp (
                            bmp_info_data   : inout BMP_INFO;
                            result          : out   boolean
                        );
    -- ビットマップリード
    procedure read_bmp (
                            x_pos           : in    std_logic_vector(31 downto 0);
                            y_pos           : in    std_logic_vector(31 downto 0);
                            pix_data        : out   std_logic_vector(31 downto 0);
                            bmp_info_data   : inout BMP_INFO
                        );
    -- ビットマップライト
    procedure write_bmp (
                            x_pos           : in    std_logic_vector(31 downto 0);
                            y_pos           : in    std_logic_vector(31 downto 0);
                            pix_data        : in    std_logic_vector(31 downto 0);
                            bmp_info_data   : inout BMP_INFO
                        );

end bmp_pkg;




投稿時刻(21:15)│コメント(0)ModelSim 

2007年08月03日

VHDL TIPS 「ディファード定数の使用法」

ディファード定数という難しそうな名前の余り意味の無い機能がある。
これは、パッケージで定数の宣言だけしておき実際の値はパッケージボディで割り当てるという機能だ。通常はパッケージとパッケージボディは1:1で記述すると思うのでディファード定数にすると余計にややこしくなる。
パッケージボディでは無くアーキテクチャで値を割り当てる事が出来れば、定数宣言を強制できるという意味で、まだ使えたのかもしれないが、これは出来ずあくまでもパッケージボディ内に限られている。
無理に使おうと思えば、パッケージだけライブラリのようにしてパッケージボディはパッケージユーザに作らせ定数宣言を強制させたい場合くらいだろうか。
ただ、この場合もXSTはサポートしていないのでシミュレーションだけにしておいた方が良い。

以下に使用例を紹介する。


package test_pkg is
    -- ディファード定数
    constant    const_int   : integer;
end test_pkg;

package body test_pkg is
    -- ディファード定数の定数決定
    constant    const_int   : integer := 10;
end test_pkg;




投稿時刻(20:28)│コメント(0)VHDL 

2007年08月02日

ModelSim TIPS 「ModelSimでバイナリファイルの操作」

以前、バイナリファイルの読み込みと書き込みを紹介したが、その時、「ファイルを最後まで読み出した事が分からず最後の次を読み出そうとした瞬間にFatalエラーとなってしまう。」と書いた。
しかし、EOF(エンドオブファイル)を検出出来る関数があることを知ったので、それを使って以前のサンプルを書き直す事にする。

以下のソースコードはバイナリファイルを読み込みSTD_LOGIC_VECTORに変換してからバイナリファイルに書き込みを行う。要はファイルのコピーを行う。
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;

entity bin_rw is
end bin_rw;

architecture behavior of bin_rw is

    type    BIN is file of character;
    file    FILEIN      :   BIN open READ_MODE  is "binary_in.bin";
    file    FILEOUT     :   BIN open WRITE_MODE is "binary_out.bin";

    signal  BYTEDATA     :   std_logic_vector(7 downto 0);

begin

    process
        variable    FREAD_CHAR   :   character;
        variable    FWRITE_CHAR  :   character;
    begin
        -- ファイルの終わりを検出するまでループ
        while ( endfile( FILEIN ) = FALSE ) loop
            -- ファイルリード
            read( FILEIN, FREAD_CHAR );

            -- std_logic_vectorに変換(今回は変換する意味は無いが、参考のため)
            BYTEDATA <= conv_std_logic_vector( character'pos(FREAD_CHAR), 8 );

            -- デルタ遅延を入れてBYTEDATAを更新
            wait for 0 ns;

            -- characterに変換(今回は変換する意味は無いが、参考のため)
            FWRITE_CHAR := character'val( conv_integer(BYTEDATA) );

            -- ファイルライト
            write( FILEOUT, FREAD_CHAR );
        end loop;
        wait;
    end process;

end behavior;



今回は、whileでEOFを検出するまで無限ループしているのでFatalエラーとなることはない。ただし、ループ中にデルタ遅延を入れているので大きなファイルの場合、Modelsimのリミットでストップされてしまうかもしれない。この場合は、ウエイトの値に1以上を入れるかModelsimのメニューから「Simulate」→「Runtime Option」→Defaultsタブの「Iteration Limit」の値を大きくする。



投稿時刻(19:23)│コメント(2)ModelSim 

2007年07月31日

VHDL TIPS 「incomplete typeの使用法」

VHDL TIPS 「access typeの使用法」に関連して「incomplete type」を紹介する。

ソフトウェアでは、複数のデータを管理する時、次のデータポインタを自分のデータ内に含めてチェーンさせるような事がよくある。
これを表現するには、ポインタとデータパッキングの考え方が必要だと思うが、VHDLではaccess typeとrecord typeがあるので、この点では問題はない。
ただ、VHDLではaccess typeでポインタを定義する時には、record typeの定義が必要で、record typeでデータパッキングを定義する時には、access typeの定義が必要になる。この矛盾を回避する時に「incomplete type」を使用する。

使用方法は簡単で、あとで使用する名前を先にtype宣言してしまうだけである。

以下に定義部分だけの例を紹介する。


    -- 後で定義するrecord typeの名前だけを先に定義する(incomplete type)
    type    DATA_PACK;
    -- 仮に定義した名前でポインタを定義
    type    PACK_PTR  is access DATA_PACK;
    -- 仮に定義した名前に実際の定義を割り当て
    type    DATA_PACK is record
        next_pack   :   PACK_PTR;
        header      :   std_logic_vector(7 downto 0);
        data        :   std_logic_vector(31 downto 0);
    end record;



投稿時刻(21:00)│コメント(0)VHDL 

2007年07月30日

VHDL TIPS 「パッシブプロセスの使用法」

VHDLにはパッシブプロセスという物がある。
パッシブプロセスとはエンティティ内にあるプロセスの事で、主にシミュレーションでインターフェースのチェック等を行う事が出来る。
同様のことがアーキテクチャ内でも可能だがエンティティ内で行うことにより複数のアーキテクチャをコンフィギュレーションで選択する場合などに共有できるメリットがある。

以下の例では、パッシブプロセスでデータのセットアップ/ホールドをチェックしている。


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

entity PASSIVE_TEST is
    generic
    (
        SETUP   : in    time := 10 ns;
        HOLD    : in    time := 5 ns
    );
    port
    (
        CLK     : in    std_logic;
        DIN     : in    std_logic;
        DOUT    : out   std_logic
    );

begin
-- XSTはパッシブプロセスをサポートしていない
-- pragma translate_off
    process
        variable    PREV_DIN        : std_logic := 'U';
        variable    PREV_DIN_TIME   : time      := 0 ns;
        variable    PREV_CLK_TIME   : time      := 0 ns;
    begin
        -- CLKかDINに変化があれば次に進む
        wait on     CLK, DIN;

        -- DINに変化があった場合
        if ( DIN /= PREV_DIN ) then
            PREV_DIN_TIME   := now;
            PREV_DIN        := DIN;
            if ( now - PREV_CLK_TIME < HOLD ) then
                assert false
                report "Hold Time was not suitable."
                severity WARNING;
            end if;
        end if;

        -- CLKに変化があった場合
        if ( CLK'event and CLK = '1' ) then
            PREV_CLK_TIME   := now;
            if ( now - PREV_DIN_TIME < SETUP ) then
                assert false
                report "Setup Time was not suitable."
                severity WARNING;
            end if;
        end if;

    end process;
-- pragma translate_on
end PASSIVE_TEST;

-- ビヘイビア1
architecture BEHAVIOR1 of PASSIVE_TEST is
begin

    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            DOUT <= DIN;
        end if;
    end process;

end BEHAVIOR1;

-- ビヘイビア2
architecture BEHAVIOR2 of PASSIVE_TEST is
begin

    process ( CLK ) begin
        if ( CLK'event and CLK = '1' ) then
            DOUT <= DIN after 2 ns;
        end if;
    end process;

end BEHAVIOR2;


この様にパッシブプロセスとしてセットアップ/ホールドをチェックする事で、アーキテクチャ「BEHAVIOR1 」と「BEHAVIOR2 」で共有する事ができる。
ただし注意点として、パッシブプロセス内では信号(signal)定義や代入を行うことが出来ないのでprocess内でvariableを使用するかコンカレント文で直接ポートをチェックするしか無い。また、XilinxのXSTではサポートされていないのでtranslate_offする必要もある。

以下に上記例のコンフィギュレーションを含めたテストベンチを紹介する。

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

entity PASSIVE_TEST_TB is
end PASSIVE_TEST_TB;

architecture behavior of PASSIVE_TEST_TB is

    component PASSIVE_TEST
        generic
        (
            SETUP   : in    time := 10 ns;
            HOLD    : in    time := 5 ns
        );
        port
        (
            CLK     : in    std_logic;
            DIN     : in    std_logic;
            DOUT    : out   std_logic
        );
    end component;

    signal  CLK     : std_logic := '1';
    signal  DIN     : std_logic := '0';
    signal  DOUT    : std_logic := '0';

begin

    U_PASSIVE_TEST : PASSIVE_TEST
    generic map
    (
        SETUP   => 10 ns,
        HOLD    => 5 ns
    )
    port map
    (
        CLK     => CLK,
        DIN     => DIN,
        DOUT    => DOUT
    );

    process begin
        CLK <= '1';
        wait for 50 ns;
        CLK <= '0';
        wait for 50 ns;
    end process;

    process begin
        DIN <= '0' after 3 ns;      -- Hold Error;
        wait for 200 ns;
        DIN <= '1' after 91 ns;     -- Setup Error;
        wait for 200 ns;
    end process;

end behavior;

-- ビヘイビア1を選択
configuration PASSIVE_TEST_CONFIG1 of PASSIVE_TEST_TB is
    for behavior
        for U_PASSIVE_TEST : PASSIVE_TEST use entity work.passive_test(BEHAVIOR1);
        end for;
    end for;
end PASSIVE_TEST_CONFIG1;

-- ビヘイビア2を選択
configuration PASSIVE_TEST_CONFIG2 of PASSIVE_TEST_TB is
    for behavior
        for U_PASSIVE_TEST : PASSIVE_TEST use entity work.passive_test(BEHAVIOR2);
        end for;
    end for;
end PASSIVE_TEST_CONFIG2;


これをModelsimでシミュレーションすると、両方のコンフィギュレーションでアサーションが有効であることが分かる。



投稿時刻(22:06)│コメント(0)VHDL 

2007年07月27日

シミュレーションデルタ

VHDLを理解する上で「シミュレーションデルタ」は重要な項目だと思う。
しかし、理解せずに設計を行っても、設計/シミュレーションが問題なく行える事が多く存在自体を知らない人もいる(かつての私...)。
しかし、これを理解出来ていないとシミュレーションと実機の動作不一致を引き起こす事がある。

以下はその例だ。


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

entity DD_TEST is
end DD_TEST;

architecture behavior  of DD_TEST is

    signal  CLK0    : std_logic;
    signal  CLK1    : std_logic;
    signal  COUNT   : std_logic_vector(3 downto 0) := (others => '0');
    signal  CNT_OK  : std_logic_vector(3 downto 0) := (others => '0');
    signal  CNT_NG  : std_logic_vector(3 downto 0) := (others => '0');

begin

    -- クロック生成
    process begin
        CLK0 <= '0';
        wait for 10 ns;
        CLK0 <= '1';
        wait for 10 ns;
    end process;

    -- クロックを複製
    CLK1 <= CLK0;

    -- CLK0でCOUNTをインクリメント
    process ( CLK0 ) begin
        if ( CLK0'event and CLK0 = '1' ) then
            COUNT <= COUNT + '1';
        end if;
    end process;

    -- COUNTをCLK0で1段遅らせる
    process ( CLK0 ) begin
        if ( CLK0'event and CLK0 = '1' ) then
            CNT_OK <= COUNT;
        end if;
    end process;

    -- COUNTをCLK1で1段遅らせる
    process ( CLK1 ) begin
        if ( CLK1'event and CLK1 = '1' ) then
            CNT_NG <= COUNT;
        end if;
    end process;

end behavior ;



同じ「COUNT」を「CLK0 」と「CLK1 」で1クロック遅延させたのだが結果は以下のように「CNT_OK」と「CNT_NG 」で異なる。

delta_out.gif

 

 


実機では期待通りの動作を行うのでシミュレーションと実機の動作不一致が起こっている。

なぜこのような結果になるかというと、
・CLK0とCLK1は同じように見えるが、信号の代入によってシミュレータの内部では1デルタサイクルのずれ(デルタ遅延)が発生している。
・CLK0でインクリメントしたCOUNTも、CLK0に比べて1デルタサイクルずれている。
ここでCNT_NGはCNT1の立ち上がりエッジ(CLK0の立ち上がりエッジ処理の次のデルタサイクル)で処理されるが、シミュレーションサイクルの処理規定により、この時既にCOUNTが更新されてしまっている。

前例は、故意に動作不一致を起こすコードを記述したが、次の例ではVHDLの記述制約のために動作不一致を起こした例を紹介する。


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

library unisim;
use unisim.vcomponents.all;

-- クロックバッファを入れてクロックイネーブルを生成するモジュール
entity CLKGEN is
    port
    (
        CLK_IN      : in    std_logic;
        CLK_OUT     : out   std_logic;
        CKE         : out   std_logic
    );
end CLKGEN;

architecture behavior  of CLKGEN is

    signal  CLK_BUFF    : std_logic;
    signal  COUNT       : std_logic_vector(3 downto 0) := (others => '0');

begin

    -- Xilinxのグローバルクロックバッファ
    U_BUFG : BUFG port map ( I => CLK_IN, O => CLK_BUFF );
    -- VHDLの制約(portでoutに指定した信号は内部で参照出来ない)のため、
    -- クロックイネーブルを作るクロックを一度、ローカル信号に入れる。

    -- クロックの出力
    CLK_OUT <= CLK_BUFF;

    -- クロックイネーブルを作るためのカウンタ
    process ( CLK_BUFF ) begin
        if ( CLK_BUFF'event and CLK_BUFF = '1' ) then
            COUNT <= COUNT + '1';
        end if;
    end process;

    -- クロックイネーブル
    process ( CLK_BUFF ) begin
        if ( CLK_BUFF'event and CLK_BUFF = '1' ) then
            if ( COUNT = (COUNT'range => '0') ) then
                CKE <= '1';
            else
                CKE <= '0';
            end if;
        end if;
    end process;

end behavior ;


このコンポーネントは、あるFPGAのクロック関連の回路を生成するために作られたものだ。このように関連性のある回路を一つのコンポーネントにまとめる事は頻繁に行われていると思う。
ソースコード中のコメントにもあるように、VHDLではポートに出力する信号は内部で参照出来ない制約がある。そのためこの例の場合、出力するクロックと内部で使用するクロックが1デルタサイクルずれて、このコンポーネントから出力するクロックでクロックイネーブルを使用するとシミュレーションと実機の動作不一致を起こす。(ただしこの例ではクロックイネーブルの位相は動作に関係ないので事実上問題はないが)

ちなみに、以前紹介したソースコードで
   wait for 0 ns;
のような記述をしたが、これはデルタ遅延を発生させている。



投稿時刻(21:19)│コメント(2)VHDL 

2007年07月26日

慣性遅延と伝播遅延

VHDLには慣性遅延と伝播遅延の遅延モデルがある。

慣性遅延はその名前の通り慣性を持っており、慣性の値未満の幅しか持たない信号は出力には現れない。逆に慣性の値以上の幅を持った信号は、その信号幅はそのまま遅延時間後に出力に現れる。
このような振る舞いをするため、同期回路等では1クロックより大きい遅延を入れてしまうと1パルス幅の信号は出力に現れなくなってしまう
慣性遅延は以下のように記述し、ごく一般的に使用する。

B <= A after 1000 ns;

伝播遅延は、慣性遅延と違ってどのような信号でも遅延時間後に入力がそのまま出力に現れる。
伝播遅延は以下のように記述し、配線やディレイラインのモデリングに使用する。ただしクロックに同期した信号の配線モデリングは1クロック以内の遅延値が前提だと思うので慣性遅延でも問題は起きないが、最近の高速シリアル信号等は伝播遅延を使用しないとモデリングが大変になる。(昔、小さい慣性遅延をいくつも入れた記述を見たことがある...)

B <= transport A after 1000 ns;

シミュレータ側から見ると慣性遅延は1つのイベントバッファを使いイベントが重なると上書きされるような動作を行い、伝播遅延はイベントを無限のFIFOに入れるような動作を行う。
このため遅延させる信号の最小イベント間隔が遅延時間以上の場合は慣性遅延と伝播遅延の結果に違いはない。



投稿時刻(22:06)│コメント(5)VHDL 

2007年07月24日

VHDL TIPS 「access typeの使用法」

VHDLにはアクセスタイプと言うC言語のポインタのような機能がある。
ほとんど使用する機会は無いと思うがおもしろい機能なので紹介したい。

C言語のポインタが分かる人なら以下のサンプルを見て理解できると思う。
このサンプルは4Bank構成のメモリで最初はメモリ領域を確保せず、Bank毎に最初にアクセスがあったときに初めてメモリ領域を確保している。また、リセットを入れると全てのメモリ領域を開放する。
(記述としてはジェネリックを使わず固定値にしていたり、メモリとしても非効率かつ無意味な機能があったりするが、あくまでアクセスタイプのサンプルなので...)


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

-- 16Wordx8Bitx4Banksの同期メモリ
entity test_accs is
    port
    (
        CLK         : in    std_logic;                      -- クロック
        RST         : in    std_logic;                      -- リセット
        ADDR        : in    std_logic_vector(3 downto 0);   -- アドレス(16Word=4Bit)
        BA          : in    std_logic_vector(1 downto 0);   -- バンクアドレス(4Bank=2Bit)
        CS          : in    std_logic;                      -- チップセレクト
        WE          : in    std_logic;                      -- ライトイネーブル
        INDATA      : in    std_logic_vector(7 downto 0);   -- ライトデータ
        OUTDATA     : out   std_logic_vector(7 downto 0)    -- リードデータ
    );
end test_accs;

architecture behavior of test_accs is

    -- データ幅8Bitのサブタイプ
    subtype BUSWIDS     is std_logic_vector(7 downto 0);
    -- 16Wordのサブタイプ
    subtype ARRAYNUM    is std_logic_vector(15 downto 0);
    -- 1Bankのデータタイプ
    type    CELLARRAY   is array(ARRAYNUM'range) of BUSWIDS;
    -- 1Bankのアクセスタイプ(ポインタ)
    type    BNKACCESS   is access CELLARRAY;
    -- 1Bankのアクセスタイプの4配列(ポインタの配列)
    type    BNKARRAY    is array(3 downto 0) of BNKACCESS;

begin

    process
        -- アクセスタイプの4配列をインスタンス
        variable    BANK        : BNKARRAY := (others => NULL);

        variable    BA_INT      : integer;
        variable    ADDR_INT    : integer;
    begin

        -- クロックの立ち上がりエッジで動作する
        wait until CLK = '1';

        -- データ型変換
        BA_INT := conv_integer(BA);
        ADDR_INT := conv_integer(ADDR);

        -- チップセレクトがイネーブルで選択されているバンクに
        -- メモリが割り当たっていない場合
        if ( RST = '0' and CS = '1' and BANK(BA_INT) = NULL ) then
            -- 選択されているバンクにメモリを確保する
            BANK(BA_INT) := new CELLARRAY;
            -- 確保したメモリ領域を0クリアする
            for i in ARRAYNUM'range loop
                BANK(BA_INT)(i) := BUSWIDS'(others => '0');
            end loop;
        end if;

        -- チップセレクトがイネーブルでライトイネーブルがイネーブルの場合
        if ( RST = '0' and CS = '1' and WE = '1' ) then
            -- メモリにデータを書き込む
            BANK(BA_INT)(ADDR_INT) := INDATA;
        end if;

        -- チップセレクトがイネーブルの場合
        if ( RST = '0' and CS = '1' ) then
            -- メモリからデータを読み出す
            OUTDATA <= BANK(BA_INT)(ADDR_INT);

        -- チップセレクトがイネーブルでない場合
        else
            -- データを弱いHIGH(プルアップ)にする
            OUTDATA <= (others => 'H');
        end if;

        -- リセットがイネーブルの場合
        if ( RST = '1' ) then
            -- メモリ割り当てを解放する
            for i in BNKARRAY'range loop
                if ( BANK(i) /= NULL ) then
                    deallocate(BANK(i));
                    BANK(i) := NULL;
                end if;
            end loop;
        end if;

    end process;

end behavior;



まとめとして、サンプルコードでのアクセスタイプは、
    type    BNKACCESS   is access CELLARRAY;
でアクセスタイプを定義して、
        variable    BANK        : BNKARRAY := (others => NULL);
でアクセスタイプのインスタンスを作成し、
            BANK(BA_INT) := new CELLARRAY;
でメモリ領域を割り当て、
                deallocate(BANK(i));
でメモリ領域を開放する。
以上のような流れで使用している。

これに関連して今度は「incomplete type」を紹介しようと思う。



投稿時刻(12:32)│コメント(0)VHDL 

2007年07月22日

ModelSim TIPS 「ModelSimでバイナリファイルの書き込み」

今回は、バイナリファイルの書き込みをテストした。

以下のソースコードはバイナリファイルを読み込みSTD_LOGIC_VECTORに変換してからバイナリファイルに書き込みを行う。要はファイルのコピーを行う。


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

entity bin_rw is
end bin_rw;

architecture behavior of bin_rw is

    type    BIN is file of character;
    file    FILEIN      :   BIN open READ_MODE  is "binary_in.bin";
    file    FILEOUT     :   BIN open WRITE_MODE is "binary_out.bin";

    signal  BYTEDATA     :   std_logic_vector(7 downto 0);

begin

    process
        variable    FREAD_CHAR   :   character;
        variable    FWRITE_CHAR  :   character;
    begin
        for i in 0 to 255 loop
            read( FILEIN, FREAD_CHAR );
            BYTEDATA <= conv_std_logic_vector( character'pos(FREAD_CHAR), 8 );

            wait for 0 ns;

            FWRITE_CHAR := character'val( conv_integer(BYTEDATA) );
            write( FILEOUT, FREAD_CHAR );
        end loop;
        wait;
    end process;

end behavior;


これを実行すると「binary_in.bin」と同じ内容の「binary_out.bin」ファイルが生成される。(ソースコードと「binary_in.bin」を置いておく)

ただ前回同様に、これはModelSimでの動作を確認しただけで保証されている訳ではなく、他のシミュレータでは動作しない可能性がある。
また、ファイルを最後まで読み出した事が分からず最後の次を読み出そうとした瞬間にFatalエラーとなってしまう。これを回避するにはファイル中にそれが分かる情報を入れるか事前に読み出すサイズを決めるなどの方法が考えられる。
2007/08/02 - EOFを検出する方法を発見 ModelSim TIPS 「ModelSimでバイナリファイルの操作」を参照。

(今まで「printf」を使ったソースコードを紹介していたが文字列パラメータの最後に¥nが入っていなかった。これが入っていないと表示が出てこない。どうやらBlogに貼り付けたときに抜けてしまっていたようだ。)


投稿時刻(01:40)│コメント(2)ModelSim 

2007年07月21日

秀丸の強調VHDLキーワード更新

秀丸の強調VHDLキーワードファイルを更新した。
更新点は、キーワード数点の追加と強調色データの追加だ。
前回同様、
ファイルはココからダウンロード出来る。
Verilogもココからダウンロード出来る。



投稿時刻(16:35)│コメント(0)VHDL 

2007年07月20日

ModelSim TIPS 「ModelSimでバイナリファイルの読み込み」

以前、ModelSim TIPS 「ModelSimでビットマップ操作」を書いたが、これはModelSimのFLI機能を使っていた。
今回は、FLIを使用せずVHDLだけでバイナリファイルを操作する方法を紹介する。

以下のソースコードはバイナリファイルを読み込みSTD_LOGIC_VECTORに変換してTranscriptウィンドウに表示する動作を行う。


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

library c;
use c.stdio_h.all;

entity test_bread is
end test_bread;

architecture behavior of test_bread is

    type    BIN is file of character;
    file    FILEPOINT   :   BIN open READ_MODE is "binary.bin";

    signal  PRINTD      :   std_logic_vector(7 downto 0);

begin

    process
        variable    FREAD_CHAR  :   character;
    begin
        for i in 0 to 255 loop
            read( FILEPOINT, FREAD_CHAR );
            PRINTD <= conv_std_logic_vector(character'pos(FREAD_CHAR), 8 );
            wait for 0 ns;
            printf( "0x%02x ", PRINTD );
        end loop;
        wait;
    end process;

end behavior;


表示するために「for」を使ったり「library c;」の「printf」を使用しているがバイナリファイルの読み込みには一切関係ない。
重要な部分は、
   type    BIN is file of character;
    file    FILEPOINT   :   BIN open READ_MODE is "binary.bin";
これだけだ。
(動作させるには読み出す対象の"binary.bin"ファイルも必要)

FLIを使用していないのでもしかするとModelSimXEでも動作するかもしれない。この要望は結構多いので後で実験してみようと思う。
2007/07/20 - ModelSimXE Starterでの動作が確認できた。

ただ、これはModelSimでの動作を確認しただけで保証されている訳ではなく、他のシミュレータでは動作しない可能性がある。
また、ファイルを最後まで読み出した事が分からず最後の次を読み出そうとした瞬間にFatalエラーとなってしまう。これを回避するにはファイル中にそれが分かる情報を入れるか事前に読み出すサイズを決めるなどの方法が考えられる。
2007/08/02 - EOFを検出する方法を発見 ModelSim TIPS 「ModelSimでバイナリファイルの操作」を参照。

近いうちに、この機能を使ってビットマップのリードライトが出来るモジュールを作って公開しようと思う。



投稿時刻(13:27)│コメント(2)ModelSim 

2007年07月18日

ヒートシンク選定

V5に使用するヒートシンクを選定する事にした。
選定するための資料として以前はUG112にパッケージの特性データが記載されていたがデバイス個別の資料Webで簡単に選択できるようになった。
今回はWebから以下の結果が得られた。


Device Family Virtex-5
Device Name XC5VLX50T
Package Name FF665
Unit C/Watt
   
JA (Still Air) 12.1
JA (250 LFM) 7.8
JA (500 LFM) 6.6
JA (750 LFM) 6.1
JB 3.2
JC 0.19


 最初はとりあえず無風時で計算してみる。
計算は次の通り、
Tj(ジャンクション温度)=(θja(ジャンクションと周囲の間の熱抵抗)×P(消費電力))+Ta(環境温度)
なので、環境温度はシステムの動作保証範囲上限の40℃、消費電力は電源の選定から7Wと出ているので
Tj=12.1×7+40
Tj=124.7℃
これではFPGAの最大動作保証範囲の85℃を大幅に超えてしまっている。

ヒートシンクの選定はこの結果を85℃にするための熱抵抗を持った物を選択すれば良い。
具体的には、最大動作保証範囲の85℃とシステムの動作保証範囲上限の40℃と消費電力7Wから
θja=(Tj−Ta)/P
θja=(85−40)/7
θja=6.43
となり、熱抵抗を6.43以下にすれば良いことが分かる。
ただしこのθjaはθjc(ジャンクションとケース間の熱抵抗)を含みさらにヒートシンクをつける場合はθjs(ケースとヒートシンク間の熱抵抗)も含むのでこれを引いて
θsa(ヒートシンクとケース間の熱抵抗)=θja−θjc−θjs
θsa=6.43−0.19−0.1(この値は適当)
θsa=6.14
これで熱抵抗が6.14のヒートシンクを選択すれば良いことが分かった。

今回は、アルファ社製のヒートシンクを選ぶことにして型番「N54−15B」辺りが適当という結果になった。



投稿時刻(19:32)│コメント(4)Xilinxデバイス 

2007年07月16日

電源の選定

電源の選定をする時には消費電力の見積もりが必要になる。
経験から大体は予想できるのだが、それを確認する為にもツールを活用している。
今回は、あまり詳細な見積もりは必要ないのでISEに付属のXPowerは使用せずWebからExcelファイルをダウンロードして見積もりしてみた。
まずExcelファイルのダウンロードだが、最近のデバイスはスプレッドシートが用意されている、ただ少し前のデバイスはウェブ消費電力ツールを利用しないといけない。今回必要なのはV4とV5なのでダウンロードした。

早速実行してV4は約6W、V5は約7Wとの結果がでた。これは全体の消費電力なので熱対策を行うデータとして利用する。個別の電源はそれぞれV4とV5の共有や基板のレイアウト、配線、層構成、層数等を考慮して決定する。
この時、私が注意している事は、
・DC−DCコンバータは最大消費電力の約50%程度を上限として使用する。
・LDOは最大消費電流の約80%程度を上限とし熱が、自己発熱が50度を超えると分散や放熱する事を検討する。
・VccauxはなるべくLDOを使用する。
・MGT、PLL等のアナログ電源は必ずLDOを使用する。
・なるべく低い電圧の方から順番に立ち上げる。
等である。
他にもアプリケーションによっては様々な検討を行う必要があると思う。

電源の検討は多電源、低電圧の今はとても重要な項目で、電源が不安定だとクロックやChipScopeさえも信用できなくなってしまい、何もデバッグ出来ないまま改版なんて事もあり得る。



投稿時刻(11:37)│コメント(0)Xilinxツール 

2007年07月13日

ModelSim TIPS 「Utilパッケージ(ファンクション使用例)」

今度は、ModelSim標準Utilパッケージのファンクション使用例を書いてみた。
ソースコードは次のようになる。


library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

library modelsim_lib;
use modelsim_lib.util.all;

library c;
use c.stdio_h.all;

entity mti_func_test is
end mti_func_test;


architecture behavior of mti_func_test is
begin

    process
        variable    resolution      : real;
        variable    alignment_time  : time;
        variable    current_time    : real;
        variable    wait_time       : real;
    begin
        -- シミュレーションレゾリューションの取得
        resolution := get_resolution;
        printf( "Resolution    = %f\n", resolution );

        -- 半端なウエイト
        wait for 12345678 fs;

        -- 半端なウエイトをアライメント
        wait_time := to_real(12345678 fs);
        -- 現シミュレーション時間の取得
        current_time := to_real(now);

        printf( "Current Time  = %f\n", current_time );
        printf( "Wait Time     = %f\n", wait_time );

        -- 半端なウエイトをアラインメント
        alignment_time := to_time(12345.678);
        wait for alignment_time;

        -- 現シミュレーション時間の取得
        current_time := to_real(now);

        printf( "Current Time  = %f\n", current_time );

        wait;
    end process;

end behavior;


これをModelSimでシミュレーションするとTranscriptウィンドウに次のように表示される。

シミュレーションレゾリューションが1nsの場合


# Resolution    = 1.000000e-009
# Current Time  = 1.200000e+001
# Wait Time     = 1.200000e+001
# Current Time  = 1.235800e+004

シミュレーションレゾリューションが1psの場合


# Resolution    = 1.000000e-012
# Current Time  = 1.234600e+004
# Wait Time     = 1.234600e+004
# Current Time  = 2.469200e+004

シミュレーションレゾリューションが1fsの場合


# Resolution    = 1.000000e-015
# Current Time  = 1.234568e+007
# Wait Time     = 1.234568e+007
# Current Time  = 1.235802e+007

2回目の「Current Time」は「alignment_time := to_time(12345.678);」の単位がシミュレーションレゾリューションによって変化するため、このような結果になる。

投稿時刻(12:38)│コメント(0)ModelSim 

2007年07月12日

ModelSim TIPS 「Utilパッケージ(ファンクション解説)」

ModelSimには標準でUtilパッケージが入っている。
このパッケージには6種類のプロシージャと3種類のファンクションが含まれており以下のライブラリ宣言で使用する事が出来る。(ModelSimのバージョンによっては入っていない物もある)


--ModelSimのUtilパッケージ
library modelsim_lib;
use modelsim_lib.util.all;

ファンクションの使用方法
・get_resolution
 用途:シミュレーションのレゾリューションを取得する。
 引数:なし
 戻り:
        型:real
        説明:シミュレーションのレゾリューション
 例:
        variable resolution : real;
        resolution := get_resolution;
        シミュレーションのレゾリューションが「1ps」の場合「1e-12」が戻る。

・to_real
 用途:シミュレーションのレゾリューションにアラインメントされた実数値を取得する。
 引数:
        名前:timeval
        型:time
        説明:アラインメントしたい時間値
 戻り:
        タイプ:real
        説明:シミュレーションのレゾリューションにアラインメントされた実数値
 例:
        variable alignment_real : real;
        alignment_real := to_real(123.45 ns);
        シミュレーションのレゾリューションが「1ps」の場合「123450」が戻る。

・to_time
 用途:シミュレーションのレゾリューションにアラインメントされた時間値を取得する。
 引数:
        名前:realval
        型:real
        説明:アラインメントしたい実数値
 戻り:
        タイプ:time
        説明:シミュレーションのレゾリューションにアラインメントされた時間値
 例:
        variable alignment_time : time;
        alignment_time := to_time(123.45);
        シミュレーションのレゾリューションが「1ps」の場合「123ps」が戻る。

今回はファンクションの使用方法のみでプロシージャはいずれ書こうと思う。



投稿時刻(22:56)│コメント(0)ModelSim 
最新コメント
カテゴリ別表示
月別表示