学問の小部屋

ここは学問の黒板です。

Intel社CPLDの立ち上げ2

FPGACPLDに関する一連の記事ではVHDLについての詳細な記述を展開するつもりはなく、現在のIntelXilinxそれぞれの環境における操作フローを主に解説する予定である。
前回の記事 http://d.hatena.ne.jp/kazima/20180419 では、Intel MAX-2 CPLDに対してプッシュボタンによるLED点灯というとても簡単な実例を示した。
FPGASDKは進歩が速く、web上の解説が古くなりやすい。前回の記事の流れからもわかるように、現在のIntel SDKは操作性が劣悪であり、初見ではどこを触ればよいのかわからないGUIは使用法の習得効率の悪化を招いていることが理由である。
今回は取り上げるのは以下の3点とする。

  • 前回のプログラム実装で宿題となった空きピン処理
  • 手を付けなかったタイミング制約
  • process文による簡単な同期処理
  1. 空きピン処理

本来、FPGAバイスの空きピン処理とは配線を接続しないハードウェアピンの処理を意味する。(グラウンドにつなぐのかプルアップするのかなど)その設定は
Assignments > Device GUIから Device and Pin Options ボタンを押し、Unused PinsのReserval all unused pinsで調整可能である。
本記事でいう空きピン処理は、EVMを使用する上で不要な7セグLEDやブザーをどのように処理するかを意味している。これは、既成のEVMはすでに様々な部品が接続されているので、出力ピンがグラウンドに落ちるとアクティブ状態になるときがあることが理由である。なお、本来の「空きピン処理」によりプルアップすることにより意図しないアクティブ状態は防げるが、基板上に配線したピンはVHDファイル内に記述するべきという思想からいって、VHDファイル内にすべて記述することにする。
ピンヘッダ以外の出力ピンは、LED、7セグLED、ブザーのみなので、サンプルファイルのようにport、出力状態を記述する。

  1. タイミング制約

Intel環境におけるタイミング制約は、.sdcファイルに記述する。タイミング制約は内部でクロックとして使用するピンや出力ピンに要求されるスピードを規定し、内部処理が間に合わないことをコンパイル段階で察知することができる。(出力ピンに対して記述するものであり、入力ピンに対しては記述しない)
sdcファイルはテキストエディタで編集して拡張子を変えて作ることもできるが、プロジェクト内でsdcファイルを生成するには 新規作成 > Other Files > Synopsys Design Constraints File を選択する。
sdcファイルの中には、外部端子に接続されている各ピンのクロックの周期をns単位に変換して記述する。のC-M240基板には50MHzの水晶が実装されているので、クロックは20ns周期で制約することになる。
sdcファイル内で右クリック > Insert Constraint > Create Clock を選択すると、以下のようなGUIが表示され、ある程度GUIで何を記述すればよいかがわかるようになっている。

GUIで指定した結果、あるいはテキスト編集で
create_clock -name LED0 -period 200.000 [get_ports {LED0}]
と記述したとすると、LED0という名前で(この部分は自由につけてよい)200ns周期での駆動が要求されるという制約をつけ、LED0という名前の出力ポートに紐づくという意味になる。
このとき、get_portsのtargetとして定義されている変数(今回はLED0)がVHDLファイル内とPin Plannerで定義されていないと、以下のような警告メッセージが出て正しく制約できていないことが示される。
Warning (332174): Ignored filter at t1.sdc(1): LED0 could not be matched with a port
Warning (332049): Ignored create_clock at t1.sdc(1): Argument is an empty collection
ここで、周期にデバイススペックが対応していないほど短い値(2ns)などを記述すると、タイミングが間に合わないことを示す以下のような警告メッセージが現れる。
Critical Warning (332148): Timing requirements not met
また、タイミング制約を記述していない出力ピンがあると、コンパイル後のタイミング解析部分で警告が表示される。Lチカ程度の処理であればタイミング制約は記述しなくても問題ないが、実際にMHzオーダーの高速信号を取り扱う上では、きわめて重要となる。
今回は、各出力ポートと操作用クロックとして使用するCLK、PB1に対して以下のように記述する。C-M240には50MHzの水晶が実装されているので、水晶に接続されているCLK端子には20nsを指定し、それ以外のLED駆動クロックには1us(1MHz)を指定した。(ファイル名はt1.sdcとした。混乱防止のためにsdcの名前はプロジェクト名と合わせるとよい。)

create_clock -name CLK -period 20.000 [get_ports {CLK}]
create_clock -name PB1 -period 1000.000 [get_ports {PB1}]

create_clock -name LED1 -period 1000.000 [get_ports {LED1}]
create_clock -name LED2 -period 1000.000 [get_ports {LED2}]
create_clock -name LED3 -period 1000.000 [get_ports {LED3}]
create_clock -name LED4 -period 1000.000 [get_ports {LED4}]
create_clock -name LED5 -period 1000.000 [get_ports {LED5}]
create_clock -name LED6 -period 1000.000 [get_ports {LED6}]
create_clock -name LED7 -period 1000.000 [get_ports {LED7}]
create_clock -name LED8 -period 1000.000 [get_ports {LED8}]

create_clock -name LED4_1 -period 1000.000 [get_ports {LED4_1}]
create_clock -name LED4_2 -period 1000.000 [get_ports {LED4_2}]
create_clock -name LED4_3 -period 1000.000 [get_ports {LED4_3}]
create_clock -name LED4_4 -period 1000.000 [get_ports {LED4_4}]
create_clock -name LED4_5 -period 1000.000 [get_ports {LED4_5}]
create_clock -name LED4_6 -period 1000.000 [get_ports {LED4_6}]
create_clock -name LED4_7 -period 1000.000 [get_ports {LED4_7}]
create_clock -name LED4_8 -period 1000.000 [get_ports {LED4_8}]

create_clock -name LED4_A -period 1000.000 [get_ports {LED4_A}]
create_clock -name LED4_B -period 1000.000 [get_ports {LED4_B}]
create_clock -name LED4_C -period 1000.000 [get_ports {LED4_C}]
create_clock -name LED4_D -period 1000.000 [get_ports {LED4_D}]
create_clock -name LED4_E -period 1000.000 [get_ports {LED4_E}]
create_clock -name LED4_F -period 1000.000 [get_ports {LED4_F}]
create_clock -name LED4_G -period 1000.000 [get_ports {LED4_G}]
create_clock -name LED4_DP -period 1000.000 [get_ports {LED4_DP}]

用意したsdcファイルは、Assignments > Settings >TimeQuest Timing Analyzer の中で以下のように指定あるいは読み込みが可能である。

  1. 同期回路

今回のサンプルファイルには、まず論理演算子による2つのボタンが同時に押されたときにLEDが光る回路を加えた。
architecture文の中の信号線の定義
signal tmp : std_logic;
およびAND計算と代入
tmp <= NOT(NOT(PB0) AND NOT(PB1));--2つのpinのAND処理をsignalに割り当てる
により、実装している。この部分は、コードを読めば直感的に読み取れるであろう。
さらに、入力クロックに応じて駆動される論理回路を同期回路という。VHDLではprocess文で表現し、クロック信号の立ち上がり、立ち下りのタイミングで従属的に同期している論理回路を駆動する。今回は、プッシュボタンPB1のオンオフ動作を駆動クロックの代わりとし、ボタンを押した回数を数えるカウンタを実装する。クロックの立ち上がりのタイミングで駆動するには
if(PB1'event and PB1 = '1') then
とif-then文で記述し(記述法にはrising_edgeという書き方もある)、その中にボタンを押しただけバッファに値を加算する処理を入れれば、2進カウンタを構築できる。バッファとなる信号は、architecture文内にsignal文で定義する。今回は8bitカウンタを
signal counter : std_logic_vector(7 downto 0);--8bitカウンタ
と定義する。
さらに、このカウンタに対してPB1に対する同期カウンタをprocess文で記述すると、プッシュボタンがactive lowであることを考慮してNOT演算による反転を加え、以下のように記述する。

process(PB1) begin
if(PB1'event and PB1 = '1') then
counter <= counter + NOT(PB1);
end if;
end process;

この記述は直感的に理解しやすいが、ひとつ大きな問題がある。論理回路はロジックゲートの組み合わせで実現するので基本的に信号入力部には0か1の状態しか取れず、C言語プログラムのように気軽に加算や減算をすることはできない。そこで
use ieee.std_logic_unsigned.all;
という記述をVHDLファイルの先頭に加えると、あたかもC言語プログラムのような四則演算が扱えるようになり、"+"演算子vhdファイル内で使用できる。
以上の操作で今回の課題3つを適用したt2.vhdとt1.sdcが生成できる。t2.vhdの全貌は以下のようになる。
(ボタンのAND操作とカウンタはどちらもLED1への出力に設計しており、これらのアルゴリズムの違いは今回の本質ではないので、コンパイル時に切り替えて対応する)

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

entity t2 is
port (
PB0 : in std_logic;--プッシュボタン入力0 DEV_CLR
PB1 : in std_logic;--プッシュボタン入力1 K1
PB2 : in std_logic;--プッシュボタン入力2 K2
PB3 : in std_logic;--プッシュボタン入力3 K3
PB4 : in std_logic;--プッシュボタン入力4 K4

LED1 : out std_logic;--LED出力(active low)
LED2 : out std_logic;--LED出力(active low)
LED3 : out std_logic;--LED出力(active low)
LED4 : out std_logic;--LED出力(active low)
LED5 : out std_logic;--LED出力(active low)
LED6 : out std_logic;--LED出力(active low)
LED7 : out std_logic;--LED出力(active low)
LED8 : out std_logic;--LED出力(active low)

LED4_1 : out std_logic;--7seg LED出力(active low)
LED4_2 : out std_logic;--7seg LED出力(active low)
LED4_3 : out std_logic;--7seg LED出力(active low)
LED4_4 : out std_logic;--7seg LED出力(active low)
LED4_5 : out std_logic;--7seg LED出力(active low)
LED4_6 : out std_logic;--7seg LED出力(active low)
LED4_7 : out std_logic;--7seg LED出力(active low)
LED4_8 : out std_logic;--7seg LED出力(active low)

LED4_A : out std_logic;--7seg LED出力(active low)
LED4_B : out std_logic;--7seg LED出力(active low)
LED4_C : out std_logic;--7seg LED出力(active low)
LED4_D : out std_logic;--7seg LED出力(active low)
LED4_E : out std_logic;--7seg LED出力(active low)
LED4_F : out std_logic;--7seg LED出力(active low)
LED4_G : out std_logic;--7seg LED出力(active low)
LED4_DP : out std_logic;--7seg LED出力(active low)

CLK : in std_logic; --XTAL入力 50MHz
BUZZER : out std_logic --BUZZER出力(active low)

);
end t2;

architecture rtl of t2 is

    • 内部信号線の定義

signal tmp : std_logic;
signal counter : std_logic_vector(7 downto 0);--8bitカウンタ

begin
--プッシュボタンカウンタ
process(PB1) begin
if(PB1'event and PB1 = '1') then

    • counter <= counter(6 downto 0) & CLK;

counter <= counter + NOT(PB1);
end if;
end process;
tmp <= NOT(NOT(PB0) AND NOT(PB1));--2つのpinのAND処理をsignalに割り当てる

    • LED1 <= tmp;

LED1 <= counter(7);--カウンタの最上位ビットを出力する
LED2 <= counter(5);
LED3 <= counter(4);
LED4 <= counter(3);
LED5 <= counter(2);
LED6 <= counter(1);
LED7 <= counter(0);
LED8 <= counter(0);

LED4_1 <= '1';
LED4_2 <= '1';
LED4_3 <= '1';
LED4_4 <= '1';
LED4_5 <= '1';
LED4_6 <= '1';
LED4_7 <= '1';
LED4_8 <= '1';

LED4_A <= '1';
LED4_B <= '1';
LED4_C <= '1';
LED4_D <= '1';
LED4_E <= '1';
LED4_F <= '1';
LED4_G <= '1';
LED4_DP <= '1';

BUZZER <= '1';
end rtl;

以上を実装すると、意図せず7セグLEDが光ったりブザーが鳴ったりするのを防ぎ、さらにボタンを押した回数によるLED発光が可能になる。

上に示したt2.vhdには、次回の伏線として以下のコメント文を残しておいた。

    • counter <= counter(6 downto 0) & CLK;

次回はシフトレジスタの記述法(シリアル-パラレル変換)について述べる。
空きピン処理、クロック同期処理、カウンタ作成、シフトレジスタ作成は論理回路設計の最も基本的な部分なので、仮に既存IPを組み合わせて使う設計者も読み書きできるようにしてくとよい。