第10回: 実習(2): FPGAへの論理回路の実装(2)

今回は、以前の演習でも使ったEDX-001に、順序回路を 実装してみましょう。 Xilinx ISEを使った論理回路設計から論理合成、書き込みの手順は 第4回の演習と同様ですので、 一通り見返しておき、必要なところは適宜そちらを参照して進めてください。

演習:カウンタ回路

まずは順序回路の例として、カウンタを作ってみましょう。 EDX-001では、FPGA(XC2S100)の18番ピンに、1kHzのクロック信号が 与えられていますので、これを基準としてカウンタを動作させることにします。

1kHzのクロック信号でカウンタを動作させると、1kHzの周期、 つまり1/1000秒ごとにカウンタの値が増加していくことになります。 これをLEDなどで表示しても、速すぎてわかりません。 そこで少し工夫をして、12ビットのカウンタを使い、 その上位の8ビット分だけを、LEDで表示させてみることにします。

これによって、LEDの一番下位の桁(LSB)は1/1000秒×24=16[ms]ごと にカウントが増えていきます。 これでもまだ速すぎてわかりませんが、 LEDの一番上位の桁(MSB)では1/1000秒×211=2048[ms]=約2秒 ごとにカウントが増えることになり、これであれば目で見てわかりそうです。

カウンタ回路の設計・書き込み


まずプロジェクトをつくります。 名前はcounterとしておきましょう。


続いて、カウンタを記述するVHDLファイルを作ります。 名前はcounter(counter.vhd)としておきましょう。


続いて入出力を定義します。 以下の3つの信号を定義しておきます。 CLKはクロック、RSTはリセット、LED(7)〜LED(0)は、 EDX-001上の8個のLEDにつながる出力です。


カウンタをcount.vhdにVHDLで記述しておきます。 この例では、カウンタの内部に12ビットのカウント値Qをもち、 その上位8本だけを、最後の方で出力LEDに接続しています。 (EDX-001のLEDが負論理(0で点灯)なので、notをつけて接続しています)

またリセット信号RSTはクロックとは非同期にしてあります。 ちなみにこのリセット信号RSTは、EDX-001のスイッチAにつなぐように 次のピン定義ファイルに記述します。 つまりスイッチAを押すとリセットがかかるようにします。


User Constrain内のEdit Constrainsを選んで ピン定義ファイルを作成し、上記のように記述しておきます。 ちなみにRSTは、EDX-001上のスイッチAがつながっているP50(50番ピン)に つなげるように指定しています。 (このP50は、第4回の演習ではPSW_Aという名前にしていた信号です)

あとは論理合成から書き込みファイルの生成へ進み、 counter.bitができたら、copyコマンドで転送して動作させてみましょう。

シミュレーション

順番が前後してしまいましたが、シミュレーションを行うことも もちろんできます。


左上の画面でBehavioal Simulationを選び、 テストベンチのファイル(test_counter.vhd)を新規に追加し、 上記のように記述しておきます。 最初の100ns間だけRST=0としてリセットをかけています。 また後半のprocess文では、RST信号を変化させているのとは 独立に、50nsごとにCLKの値を反転しています。 つまりCLKは、50nsごとに0→1→0→1→・・・となりますので、 変化の周期は100ns、つまり10MHzのクロック信号、ということになります。 (これは実際に与える1kHzよりは1000倍速いですが、 今回は気にしないことにします)


シミュレーションを実行すると、このようになります。 counterの出力であるLEDの値の波形が現れますが、 FFで変化していません。 これは、LEDはカウンタの上位8ビットだけをつないであったので、 まだ変化が現れていないため、です。 (CLKの16発目ではじめてLEDが変化するはず)


そこで、カウンタの内部にあるカウント値であるQの値も シミュレーションで確認してみることにしましょう。 左側のSim Hierarchyの画面内で、シミュレーション対象の回路counterを 開き、その中にあるq[11:0]を波形の画面へドラッグしておきます。


それから再度シミュレーションを行うと、qの値が CLKの立ち上がりごとに増えていっているのがわかります。

演習:分周回路とカウンタ

今度は、カウンタに与えるクロック信号をもっとゆっくりにして、 カウンタの値を直接LEDでみてみることにしましょう。


これには、1kHzのクロック信号CLKを直接カウンタに与えるのではなく、 それから1秒周期のクロック信号CLK1Sを作り、それを カウンタに与えるようにしてみます。 CLKのように速いクロックからCLK1Sのように遅いクロックを作る回路を 分周回路(divider)と呼びます。 今回使う分周回路にはdiv1sという名前をつけておくことにします。


まずdiv1sをcomponentとして使い、それから出力されてくる クロック信号CLK1Sを使ったカウンタ回路をつくっておきます。 先ほどつくったcounterを改造すればよいでしょう。 ついでに、せっかくなので、カウント値Qを、そのまますべてLEDへ つないでおき、それにあわせてカウント値Qも8ビットにしておきます。 またCLK1Sをsignalとして宣言しておきます。


同様に分周回路div1sを、新しいVHDLファイルとして、 counter.vhdに対して新規に追加をしておきます。 名前はdiv1s.vとしておけばよいでしょう。 その中身の分周回路は、上記のように記述しておきます。

分周回路も、実はカウンタです。 ただしカウント値に応じて、分周後のクロック出力であるCLK1Sを 変化させています。 この例では、CLKの立ち上がりごとに、 Q=999ならばQ=0、CLK1S=1とし、 そうでなければQに1を加えてCLK1S=0としています。

これにより、CLKの立ち上がりごとにQは0→1→・・・→999→0→1→・・・ と変化して意気、999→0のときだけCLK1S=1、それ以外ではCLK1S=0と なります。 したがってCLK1Sは、CLKの1000発ごとに0→1となることになり、 CLKの1/1000の周波数となります。 今回はCLKは1kHzでしたので、CLK1Sは1Hz、つまり1秒周期になります。

この調子で行けば、どのような周波数のクロックでも作れそうですね。

ちなみにdiv1sの内部のカウント値Qが10ビットとなっていますが、 その理由を考えておきましょう。


ちなみにdiv1s.vhdを作成すると、counterの中に インスタンスi0として呼び出されていることが、 Sources画面でも確認できます。

演習:分周回路とカウンタと7セグメントLED

上記の2種類の回路を参考に、 7セグメントLED(1桁だけでよい)を、自分で決めた速さで 増加させていく回路を記述し、動作させてみましょう。 なお7セグメントLEDの各LEDのピン配置は、 EDX-001のマニュアルのp.3の3.7を参考にしましょう。 (SA0のみ0とし、SA1〜SA3=1としておけばよいです)

各回路の要点をまめておきます。 (設計例(VHDLファイル・ucfファイル一式)

counter (counter.vhd)

entity counter is
    Port ( CLK : in  STD_LOGIC;
           RST : in  STD_LOGIC;
           LED : out STD_LOGIC_VECTOR (7 downto 0);
           SG  : out std_logic_vector(7 downto 0);
           SA  : out std_logic_vector(3 downto 0));
end counter;

architecture Behavioral of counter is
    signal Q: std_logic_vector(3 downto 0);
    signal CLK1S: std_logic;
    signal SGb: std_logic_vector(7 downto 0);
    component div1s
        port (
            CLK: in std_logic;
            CLK1S: out std_logic;
            RST: in std_logic
        );
    end component;
    component dec7seg
        port (
            D: in std_logic_vector(3 downto 0);
            SG: out std_logic_vector(7 downto 0);
            SA: out std_logic_vector(3 downto 0)
        );
    end component;
    
begin
    i0: div1s port map(CLK=>CLK, CLK1S=>CLK1S, RST=>RST);
    i1: dec7seg port map(D=>Q, SG=>SGb, SA=>SA);

    process (CLK1S, RST) begin
        if (RST = '0') then
            Q <= "0000";
        elsif (CLK1S'event and CLK1S = '1') then
            if (Q = 9) then
                Q <= "0000";
            else
                Q <= Q + 1;
            end if;
        end if;
    end process;
    SG <= not SGb;
    LED <= "1111" & not Q;
end Behavioral;

seg7dec (seg7dec.vhd)

entity dec7seg is
    Port ( D : in  STD_LOGIC_VECTOR (3 downto 0);
           SG : out  STD_LOGIC_VECTOR (7 downto 0);
           SA : out  STD_LOGIC_VECTOR (3 downto 0));
end dec7seg;

architecture Behavioral of dec7seg is

begin
    process (D) begin
        case D is
            when "0000" => SG <= "00111111";
            when "0001" => SG <= "00000110";
            when "0010" => SG <= "01011011";
            when "0011" => SG <= "01001111";
            when "0100" => SG <= "01100110";
            when "0101" => SG <= "01101101";
            when "0110" => SG <= "01111101";
            when "0111" => SG <= "00100111";
            when "1000" => SG <= "01111111";
            when "1001" => SG <= "01101111";
            when others => SG <= "00000000";
        end case;
    end process;
    SA <= "1110";
end Behavioral;

戻る