CQ出版社のFPGAボードを使ってカウンターを製作

icon 項目のみ表示/展開表示の切り替え

概要

CQ出版社のFPGAボードで学ぶ論理回路設計と付属しているFPGAボードを使って、カウンターを作ってみました。

付属するFPAGの仕様

Altera社 EP1K10(フリップフロップ576個)

7セグメントLED*7

単体LED*6子

プッシュスイッチ*4個

使用するソフト

Altera QuartusⅡ 2.2SP Web Editon(本に付属)

記述言語 VHDL

設計仕様

ボードの32MHzのクロック(CLK)を640000*50分周し1Hz(CLK2)を得ます。

これを3桁のBCDカウンタでカウントします。

各桁のBCD値はデコーダを使って7セグメントを駆動します。

SW4を押すとD4が点灯し、カウンターをリセットします。

SW1~SW4は、チャタリング除去のため、32MHzのクロック(CLK)を64000分周した(20ms)でサンプリングし、スイッチの状態が変化し40ms安定したときにスイッチの状態を取り込みます。

block.svg

がプロセスに対応します。コンピュータと違って各プロセスは同時に動くことができます。(時分割ではない)

回路の構成は、標準ロジックを使う感じでAND,OR,XOR,NOTや、IF文やCASE文、クロックのエッジでフリップフロップを記述することもできます。足し算や引き算などは自動的に回路を作成してくれます。

QuartusⅡ 2.2 SP2のインストール

インストールするパソコンには、ネットワークカード(MACアドレス)とインターネットにつなげることが必要です。

ライセンスファイルの認証にMACアドレスとハードディスクのシリアル番号が使われるので、ネットワークカードを差し替えても無理でしょう。ライセンスファイルの所得に金は要りません。(プロバイダーや電話料金は別途必要)ライセンスファイルの取得のホームページは英語なので覚悟してください。

ソフトウェアインストール後にQuartusのメニューのTools Options で取得したライセンスファイルを登録します。

FPGAの書き込みにはパラレルポートと付属のドライバ ByteBlasterMVをインストールする必要があります。

プロジェクトファイルの作成

メニューのFile - New Project Wizard で作成します。エンティ名を指定するとき、後述のVHDLソースのstop_watchと一致している必要があります。

ダイアログの2番目がエンティ名(後述のVHDLソースを使用する場合はstop_watch)

VHDLソースファイルの作成

メニュー FIle - New でVHDLを指定するとエディット画面がでます。ここで、回路の記述をします。

拡張子VHDになります。VHDLファイルは、ここからダウンロードてください。

以下はVHDLファイルの説明です。

-- 下記の3行は、Cでいうと#includeに相当し、指定されたファイルを読み込んで内容を挿入します。標準的なデータータイプや演算等の記述がされています。おまじないと思ってください。

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

-- 下記の8行は、FPGAの入出力ピンを定義しています。

entity stop_watch is
 port(CLK : in std_logic;
 SW : in std_logic_vector(3 downto 0);
 D4,D5,D6,D7,D8,D9 : out std_logic;
 LED1 : out std_logic_vector(7 downto 0);
 LED2 : out std_logic_vector(7 downto 0);
 LED3 : out std_logic_vector(7 downto 0));
end
stop_watch;

-- 下記の行は実際のFPGAの内部を定義します。プロセス間で共有する変数を定義します。

architecture RTL of stop_watch is
signal count_a : std_logic_vector(19 downto 0);
signal count_b : std_logic_vector(5 downto 0);
signal count1 : std_logic_vector(3 downto 0);
signal count2 : std_logic_vector(3 downto 0);
signal count3 : std_logic_vector(3 downto 0);
signal reset : std_logic;
signal sw_e : std_logic_vector(3 downto 0);
signal sw_v : std_logic_vector(3 downto 0);
signal CLK1 : std_logic;
signal CLK2 : std_logic;
signal c1 : std_logic;
signal c2 : std_logic;
signal c3 : std_logic;

begin

-- 下記の行は640000分の1に分周するカウンタを定義しています。

-- このカウンタはresetが1の時クリアされ、CLKの立ち上がり時に1カウントアップします。

-- カウンタが640000になったときクリアすると同時にCLK1を1にセットします。

process(reset,CLK) begin
if(CLK'event and CLK='1') then
if(count_a=640000) then
count_a<="00000000000000000000";
CLK1<='1';
else
CLK1<='0';
count_a<=count_a+1;
end if;
end if;
end process;

-- 下記の行は50分の1に分周するカウンタを定義しています。

-- このカウンタはresetが1の時クリアされ、CLK1の立ち上がり時に1カウントアップします。

-- カウンタが50になったときクリアすると同時にCLK2を1にセットします。

process(reset,CLK1) begin
if(reset='1') then
count_b<="000000";
elsif(CLK1'event and CLK1='1') then
if(count_b=50) then
count_b<="000000";
CLK2<='1';
else
CLK2<='0';
count_b<=count_b+1;
end if;
end if;
end process;

-- SW1~SW4のチャタリングを除去する

-- CLK1の立ち上がりごとにスイッチの状態をサンプルし、前々回(re2)から前回(re1)

でキーの状態が変化し、前回(re1)から今回(SW)が変化していない時にキーの状態が確定したものとみなします。

-- キーが変化したときにsw_eの各ビットが1にセットされます。 キーの状態はsw_vにセットされます。

process(CLK1)
variable re2 : std_logic_vector(3 downto 0);
variable re1 : std_logic_vector(3 downto 0);
variable t : std_logic_vector(3 downto 0);
begin
if(CLK1'event and CLK1='1') then
t:=(re2 xor re1);
sw_e<=t and not(re1 xor SW);
sw_v<=SW;
re2:=re1;
re1:=SW;
end if;
end process;

-- スイッチの状態に応じてLEDを点灯させます。

process(SW_e(3)) begin
if(SW_e(3)'event) then
D4<=sw_v(3);
reset<=not sw_v(3);
end if;
end process;

process(SW_e(2)) begin
if(SW_e(2)'event) then
D5<=sw_v(2);
end if;
end process;

process(SW_e(1)) begin
if(SW_e(1)'event) then
D6<=sw_v(1);
end if;
end process;

process(SW_e(0)) begin
if(SW_e(0)'event) then
D7<=sw_v(0);
end if;
end process;

-- BCDカウンターとデコーダの記述です。各桁ともほぼ同じコードです。

-- CLK2の立ち上がりによりカウントアップします。10に達するとクリアされます。

-- resetによりクリアされます。

process(reset,CLK2) begin
if(reset='1') then
count1<="0000";
elsif(CLK2'event and CLK2='1') then
if(count1=9) then
count1<="0000";
c1<='1';
else
count1<=count1+1;
c1<='0';
end if;
end if;
end process;

process(count1) begin
if(count1(0)='0') then
D8<='1';
D9<='0';
else
D8<='0';
D9<='1';
end if;
end process;

process(count1) begin
case count1 is
--abcdefg.
when "0000" => led1<="00000011"; -- aaaa
when "0001" => led1<="10011111"; -- f b
when "0010" => led1<="00100101"; -- f b
when "0011" => led1<="00001101"; -- gggg
when "0100" => led1<="10011001"; -- e c
when "0101" => led1<="01001001"; -- e c
when "0110" => led1<="01000001"; -- dddd .
when "0111" => led1<="00011111";
when "1000" => led1<="00000001";
when "1001" => led1<="00001001";
when "1010" => led1<="11000101";
when "1011" => led1<="11000001";
when "1100" => led1<="11100101";
when "1101" => led1<="10000101";
when "1110" => led1<="01100001";
when "1111" => led1<="01110001";
end case;
end process;


process(C1) begin
if(reset='1') then
count2<="0000";
elsif(C1'event and C1='1') then
if(count2=9) then
count2<="0000";
c2<='1';
else
count2<=count2+1;
c2<='0';
end if;
end if;
end process;

process(count2) begin
case count2 is
--abcdefg.
when "0000" => led2<="00000011"; -- aaaa
when "0001" => led2<="10011111"; -- f b
when "0010" => led2<="00100101"; -- f b
when "0011" => led2<="00001101"; -- gggg
when "0100" => led2<="10011001"; -- e c
when "0101" => led2<="01001001"; -- e c
when "0110" => led2<="01000001"; -- dddd .
when "0111" => led2<="00011111";
when "1000" => led2<="00000001";
when "1001" => led2<="00001001";
when "1010" => led2<="11000101";
when "1011" => led2<="11000001";
when "1100" => led2<="11100101";
when "1101" => led2<="10000101";
when "1110" => led2<="01100001";
when "1111" => led2<="01110001";
end case;
end process;

process(C2) begin
if(reset='1') then
count3<="0000";
elsif(C2'event and C2='1') then
if(count3=9) then
count3<="0000";
c3<='1';
else
count3<=count3+1;
c3<='0';
end if;
end if;
end process;

process(count3) begin
case count3 is
--abcdefg.
when "0000" => led3<="00000011"; -- aaaa
when "0001" => led3<="10011111"; -- f b
when "0010" => led3<="00100101"; -- f b
when "0011" => led3<="00001101"; -- gggg
when "0100" => led3<="10011001"; -- e c
when "0101" => led3<="01001001"; -- e c
when "0110" => led3<="01000001"; -- dddd .
when "0111" => led3<="00011111";
when "1000" => led3<="00000001";
when "1001" => led3<="00001001";
when "1010" => led3<="11000101";
when "1011" => led3<="11000001";
when "1100" => led3<="11100101";
when "1101" => led3<="10000101";
when "1110" => led3<="01100001";
when "1111" => led3<="01110001";
end case;
end process;

end rtl;

VHDLファイルをコンパイル

上記のファイルから実際の回路のデータを作成します。

アイコンのをクリックします。

ピン配置の定義

VHDLファイル内の信号名とICのピン番号を対応させます。

メニュー Assignments - Assign Pins

ピン番号を選び Pin nameの欄に信号名を入力し Addボタンをクリックします。

VHDLファイルをコンパイル

再びコンパイルします。

FPGAに書き込む

プリンターポートにFPGAボードを接続し、FPGAボードにACアタブタから電源を供給してください。

メニュー Tools - Programmer

Programmin Hardwaer の欄に ByteBlasterMV が表示されている必要があります。

Add Device ボタンをクリックしコンパイル時に作成された*.sofファイルを指定してください。

Startで書き込まれます。