FC2カウンター FPGAの部屋 ISE11.1iのチュートリアル1(導入編)
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

ISE11.1iのチュートリアル1(導入編)

今回、ご縁があってトランジスタ技術2009年6月号の別冊付録”再確認!電子機器の開発ツール”の第4章4-1”高機能なディジタル回路を実現するFPGAの開発方法”と4-2”大規模ロジック回路を高速にシミュレーションするには”という題で書かせていただいた。
ここでは、FPGAはどんなものかということや、ISE10.1サービスパック3の使い方を書かせていただいた。今現在では、ISE11.1iが出ている。新しいISEでもう一度書いてみようと思い、ISE11.1iでチュートリアルを書き直すことにした。今回は都合によりVHDLを使用することにする。

最初にISE11.1iのダウンロード方法だが、Xilinxのダウンロードサイトからダウンロードしてほしい。やり方はだいたいわかると思うが、Product Download and Licensingのページで、State/ProvinceでSelect Oneがハイドされていると思うが、隣の四角にはNAを入れておかないとダウンロードができないようなので、注意が必要だ。Webインストールを選択しないで、全部をダウンロードすると、Xilinx_11.1_WebPack_SFD.tarがダウンロードできる。tarで固まっているので、何で解凍するか迷ったが、都合良くCygwinがインストールされていたので、これで解凍し、インストールした。なお、全部で2.67GBあるので、ネットの回線が細い方はDVDを頼んだほうが良いと思う。
ModelSim Xilinx Edition-IIIもダウンロードし、インストールする。その時にStarterバージョンを選ぶこと。やり方はかなり古いけど、この辺を参照

チュートリアルの回路はトラ技の付録に書いたのと一緒のサイコロ回路で、Spartan3 Starter Kitを使用する。やはり、簡単な回路は7セグメントLEDが良い。回路の説明が少なくてツールのやり方に専念できるので。。。
回路は下の図のように、電子サイコロのトップと、チャタリング除去回路、サイコロ・ステートマシン、7セグメントLED表示回路に分けられている。この詳しい解説はトラ技の方を読んでもらうことにする。
tutorial_1_081228.png

まずは電子サイコロトップ(dice_top.vhd)を説明する。これは各下位モジュールをつなげているだけだ。Spartan3 Starter Kitの4つの7セグメントLEDはダイナミック点灯なので、ダイナミック点灯回路を追加する必要があるが、ここでは右はじの1つのLEDのみ使用するので、an_n出力をAN0のみ点灯するように固定値として代用している。(ダイナミック点灯回路についてはこちらを参照
roll入力はサイコロを振る動作をおこなうためのスイッチの入力でSpartan3 Starter KitのBTN0に割り当てる予定だ。

-- 電子サイコロ (dice_top.vhd)

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

entity dice_top is
    port(
        reset_sw :    in std_logic;
        clk :         in std_logic;
        roll :        in std_logic;
        an_n :        out std_logic_vector(3 downto 0);
        a_n :            out std_logic;
        b_n :         out std_logic;
        c_n :         out std_logic;
        d_n :         out std_logic;
        e_n :         out std_logic;
        f_n :         out std_logic;
        g_n :         out std_logic;
        dp_n :         out std_logic
    );
end dice_top;

architecture RTL of dice_top is
component reject_chatter
    port(
        reset_sw :    in std_logic;
        clk :        in std_logic;
        roll :        in std_logic;
        roll_sig :    out std_logic;
        roll_ena :    out std_logic
    );
end component;
component dice_state_machine
    port(
        reset_sw :    in std_logic;
        clk :         in std_logic;
        roll:            in std_logic;
        roll_ena :    in std_logic;
        spots :        out std_logic_vector(2 downto 0)
    );
end component;
component seven_seg_dec
    port(
        binary :    in std_logic_vector(2 downto 0);
        a_n :        out std_logic;
        b_n :        out std_logic;
        c_n :        out std_logic;
        d_n :        out std_logic;
        e_n :        out std_logic;
        f_n :        out std_logic;
        g_n :        out std_logic
    );
end component;

signal roll_sig : std_logic;
signal roll_ena : std_logic;
signal binary : std_logic_vector(2 downto 0);

begin
    an_n <= "1110"; -- AN0のみ点灯
    dp_n <= '1'; -- ドットの消灯
    
    inst_reject_chatter : reject_chatter port map(
        reset_sw => reset_sw,
        clk => clk,
        roll => roll,
        roll_sig => roll_sig,
        roll_ena => roll_ena
    );
    
    inst_dice_sm : dice_state_machine port map(
        reset_sw => reset_sw,
        clk => clk,
        roll => roll_sig,
        roll_ena => roll_ena,
        spots => binary
    );
    
    inst_seven_seg_dec : seven_seg_dec port map(
        binary => binary,
        a_n => a_n,
        b_n => b_n,
        c_n => c_n,
        d_n => d_n,
        e_n => e_n,
        f_n => f_n,
        g_n => g_n
    );
    
end RTL;


次にチャタリング除去回路の説明をする。スイッチにはチャタリングがある。スイッチには接点があって、スイッチを動かすという動作は、動く接点と止まっている接点をぶつけるようなものだ。つまり言ってみれば、勢いよく動く接点が止まっている接点にぶつかって、何度か跳ね返る現象だ。約数100us~数msくらいの間、接点の出力波形が乱れる。これはスイッチよっても違うし、ON、OFFによっても異なる。詳しい波形などはこちらを参照
このチャタリング除去回路では5ms間隔でrollの値(BTN0スイッチの値)をサンプルしてチャタリングを除去している(roll_sig)。本当はrollを一度、clkで同期化したほうが良かったと思うが、これで動作しているので良しとする。
さらに電子サイコロとしては、あまりサイコロの目が変わる速度が速いと7セグメントLEDの表示が8に固定されて見えてしまうので、少しちらついて変わっているなと分かるほうが良い。そのため、目をかえるためのイネーブル信号(roll_ena) を20msごとに1クロック分だけ出力している。
さらに、シミュレーションをする場合に50MHzで50000分周するとシミュレーション時間がとても長くなる。シミュレーションのときは現在コメントアウトしているfrequency_KHzに変えてシミュレーションを行うことにする。そのためにfrequency_KHzをconstantで定義してあるのだ。とても待っていられないため。人間の操作はハードウェアの動作に比べてとても遅いため、マンーマシンインターフェースをシミュレーションするのは時間とリソースがかかる。そのために楽にできるように工夫が必要となる。

-- スイッチのチャタリング除去とサイコロの表示変更タイミング20msをカウントする
-- reject_chatter.vhd

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

entity reject_chatter is
    port(
        reset_sw :    in std_logic;
        clk :        in std_logic;
        roll :        in std_logic;
        roll_sig :    out std_logic;
        roll_ena :    out std_logic
    );
end reject_chatter;

architecture RTL of reject_chatter is
constant frequency_KHz : integer := 50000; -- KHz単位でのクロック周波数(インプリメント用)
-- constant frequency_KHz : integer := 1; -- KHz単位でのクロック周波数(シミュレーション用)
constant divided_200Hz : integer := frequency_KHz * 5; -- 200Hzに分周するための分周比
signal sw_cnt : std_logic_vector(17 downto 0);
signal roll_cnt : std_logic_vector(1 downto 0);
signal roll_node : std_logic;

begin
    -- 200Hz, 5ms
    process(reset_sw, clk) begin
        if reset_sw='1' then
            sw_cnt <= (others => '0');
        elsif clk'event and clk='1' then
            if sw_cnt=(divided_200Hz-1) then
                sw_cnt <= (others => '0');
            else
                sw_cnt <= sw_cnt + 1;
            end if;
        end if;
    end process;
    
    process(reset_sw, clk) begin
        if reset_sw='1' then
            roll_node <= '0';
        elsif clk'event and clk='1' then
            if sw_cnt=(divided_200Hz-1) then
                roll_node <= roll;
            end if;
        end if;
    end process;
    roll_sig <= roll_node;
    
    -- 50Hz, 20ms
    process(reset_sw, clk) begin
        if reset_sw='1' then
            roll_cnt <= "00";
            roll_ena <= '0';
        elsif clk'event and clk='1' then
            if sw_cnt=(divided_200Hz-1) then
                if roll_cnt="11" then
                    roll_cnt <= "00";
                    roll_ena <= '1';
                else
                    roll_cnt <= roll_cnt + 1;
                    roll_ena <= '0';
                end if;
            else
                roll_ena <= '0';
            end if;
        end if;
    end process;
end RTL;



次にサイコロ・ステートマシン(dice_state_machine.vhd)だが、これはBTN0スイッチが押されている間(roll=1) のとき、roll_ena=1のタイミングで1~6の状態を遷移するステートマシンだ。

-- 1から6までのサイコロの目を表すステートマシン
-- dice_state_machine.vhd

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

entity dice_state_machine is
    port(
        reset_sw :    in std_logic;
        clk :         in std_logic;
        roll:            in std_logic;
        roll_ena :    in std_logic;
        spots :        out std_logic_vector(2 downto 0)
    );
end dice_state_machine;

architecture RTL of dice_state_machine is
type STATE is (st_one, st_two, st_three, st_four, st_five, st_six);
signal current_state, next_state : STATE;

begin
    process(reset_sw, clk) begin
        if reset_sw = '1' then
            current_state <= st_one;
        elsif clk'event and clk='1' then
            current_state <= next_state;
        end if;
    end process;
    
    process(current_state, roll, roll_ena) begin
        case current_state is
            when st_one =>
                spots <= "001";
                if roll='1' and roll_ena='1' then
                    next_state <= st_two;
                else
                    next_state <= st_one;
                end if;
            when st_two =>
                spots <= "010";
                if roll='1' and roll_ena='1' then
                    next_state <= st_three;
                else
                    next_state <= st_two;
                end if;
            when st_three =>
                spots <= "011";
                if roll='1' and roll_ena='1' then
                    next_state <= st_four;
                else
                    next_state <= st_three;
                end if;
            when st_four =>
                spots <= "100";
                if roll='1' and roll_ena='1' then
                    next_state <= st_five;
                else
                    next_state <= st_four;
                end if;
            when st_five =>
                spots <= "101";
                if roll='1' and roll_ena='1' then
                    next_state <= st_six;
                else
                    next_state <= st_five;
                end if;
            when st_six =>
                spots <= "110";
                if roll='1' and roll_ena='1' then
                    next_state <= st_one;
                else
                    next_state <= st_six;
                end if;
            when others =>
                spots <= "001";
                next_state <= st_one;
        end case;
    end process;
end RTL;



最後の7セグメントLED表示回路(seven_seg_dec.vhd) は、ステートマシンからのバイナリデータを7セグメントLEDのデータにデコードする回路だ。例えば7セグメントLEDにはセグメントが7つあり、例えば1はbセグメントとcセグメントを点灯させて、それ以外のセグメントは消灯させる。バイナリの1が来たら、そのように7セグメントLEDを点灯させるようにする。今回の回路では0で点灯、1で消灯となる。

--  7セグメントLEDデコーダ、0で点灯します。
-- seven_seg_dec.vhd


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

entity seven_seg_dec is
    port(
        binary :    in std_logic_vector(2 downto 0);
        a_n :        out std_logic;
        b_n :        out std_logic;
        c_n :        out std_logic;
        d_n :        out std_logic;
        e_n :        out std_logic;
        f_n :        out std_logic;
        g_n :        out std_logic
    );
end seven_seg_dec;

architecture RTL of seven_seg_dec is
begin
    process(binary) begin
        case binary is
            when "001" => -- 1
                a_n<='1'; b_n<='0'; c_n<='0'; d_n<='1'; e_n<='1'; f_n<='1'; g_n<='1';
            when "010" => -- 2
                a_n<='0'; b_n<='0'; c_n<='1'; d_n<='0'; e_n<='0'; f_n<='1'; g_n<='0';
            when "011" => -- 3
                a_n<='0'; b_n<='0'; c_n<='0'; d_n<='0'; e_n<='1'; f_n<='1'; g_n<='0';
            when "100" => -- 4
                a_n<='1'; b_n<='0'; c_n<='0'; d_n<='1'; e_n<='1'; f_n<='0'; g_n<='0';
            when "101" => -- 5
                a_n<='0'; b_n<='1'; c_n<='0'; d_n<='0'; e_n<='1'; f_n<='0'; g_n<='0';
            when "110" => -- 6
                a_n<='0'; b_n<='1'; c_n<='0'; d_n<='0'; e_n<='0'; f_n<='0'; g_n<='0';
            when others =>
                a_n<='1'; b_n<='0'; c_n<='0'; d_n<='1'; e_n<='1'; f_n<='1'; g_n<='1';
        end case;
    end process;
end RTL;


”ISE11.1iのチュートリアル2(プロジェクトの作成)”に続く。

2009/05/23 : 変更;
dice_state_machine.vからbram_mapのattributeを削除しました。

2009/06/18:変更;
Sim's blogさんで御指摘があったように process(clk) begin にreset_swが抜けていました。正しくは process(reset_sw, clk) beginです。修正しました。なお、以前のVHDLコードでもXSTでウォーニングは出ますが、動作は問題ありません。XSTで自動的に非同期リセット付きフリップフロップに推定してくれます。実は同期FFにするつもりが間違っていました。非同期でも問題はありません。訂正いたします。
  1. 2009年05月09日 12:05 |
  2. FPGAリテラシー及びチュートリアル
  3. | トラックバック:1
  4. | コメント:2

コメント

 こんばんわ。
>ご縁があってトランジスタ技術2009年6月号の別冊付録”再確認!
 お々!久しぶりに(もう何年も買ってないな~。これで育った世代なんですが)トラ技買ってみますね^^)!
  1. 2009/05/09(土) 20:43:10 |
  2. URL |
  3. くり #mQop/nM.
  4. [ 編集 ]

こんばんは。

私もトラ技を昔は買っていましたね。くりさんにとって、有用な情報があるかどうかはわかりませんが、よろしければ、ご購入ください。
  1. 2009/05/09(土) 21:30:17 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/1105-ef48f0de
この記事にトラックバックする(FC2ブログユーザー)

-

管理人の承認後に表示されます
  1. 2009/06/17(水) 20:05:00 |