EZ-FPGAライブラリを使用して、任意の周波数を生成出来るクロックジェネレータを作成してみます。
EZ-FPGAボードに搭載されるPLLを使用して30MHz〜90MHzまでの周波数を作成します。90MHz以上の周波数が必要になる場合はSPARTAN2に内蔵されているDLLを使用してさらに2倍にします。30MHz以下の周波数が必要な場合はFPGAのロジックを使用して分周を行います。
PC(ホスト)のキーボードから入力した周波数を上記のルールに沿って、周波数を出力するアプリケーションです。
ちょっとした実験などでクロックが必要な場合や、クロックアップや部品のマージンテストなどで周波数を変化させてテストしたい場合などに重宝すると思います。
<FPGA側の処理>
FPGAで必要な処理は、PLL、DLL、分周器、ホストとのインターフェイスになります。この中のPLLとホストインターフェイス部分が、EZ-FPGAライブラリのpll.vとjtagmem.vとして提供されています。
PLLの仕組みについて簡単に解説しておきます。PLLはVCOと呼ばれる電圧によって周波数を変化させることが出来る装置を使用します。例として24MHzのEZ-USBの出力クロックから30MHz(clk_b)を作成することを考えてみましょう。最初に24MHz(clk_a)を24(div_a)で分周して1MHzを作ります。目的の30MHzも30(div_b)で分周すると1MHzになります。このそれぞれの分周した周波数を比べ、VCOにかける電圧を調整します。調整の結果一致したとき、目的の30MHzが出力されていることになります。この状態をロックしたといいます。
クロックジェネレータのトップレベルモジュール(ezfpga.v)ソースコード
PLLの機能はpll.vとしてモジュールで提供されています。このモジュールの組み込みは030-054行目です。div_a、div_bのパラメータを外部から入力することで周波数を決定します。
PLLで作成したクロックをSPARTAN2のDLLを使用して2倍にします。DLLの組み込み込みは055-072行目です。PLLのロックが外れるとリセットをかけるようにしています。
30MHz以下の周波数を分周する処理は073-089行目で行います。
そしてこの3つのクロック(PLL、DLL、分周器)のどれを出力するかは、レジスタsel_clkの値で選択されます。(090-096行目)
PLLのパラメータ(div_a、div_b)、分周器のパラメータ(v_div)、クロック選択(sel_clk)の値はPCから設定する必要があります。EZ-FPGAライブラリではPCとのインターフェイスとしてFPGA側に8bitのスタティックメモリーを仮想的に作ることで実現しています。もちろんPBSRAMやSRAMのような物理メモリーを実際に接続することも可能です。つまりFPGA側に用意してあるメモリーに対してPCのプログラムがリード・ライトを実行する形になります。このメモリーインターフェイスを提供するのがjtagmem.vです。
097-105行目で組み込んでいます。実際のjtagmemはアドレスが31bitですが、今回のクロックジェネレータではやり取りするパラメータの数が少ないですからアドレスバスは3bitのみを使用しています。
109-120行目の処理でPCからメモリーに書き込みがあった場合の処理を記述しています。各アドレスに対して必要なパラメータを割り当てます。これでPCからの書き込みでFPGAにパラメータ(値)がセットできます。
123-124行目の処理はFPGAのPLLとDLLのロック状態をPCに返す処理です。アドレスは無視して直接入力データバスに接続しています。
FPGAの外部とのI/Oをライブラリシンボルを使用して定義します。
ライブラリシンボル | ||
シンボル | 意味 | 行 |
IBUF | 入力バッファ | |
IBUFG | グローバルクロック入力 | 019,022 |
OBUF | 出力バッファ | 074 |
OBUFT | 3ステート出力バッファ | 040 |
IOBUF | 入出力バッファ |
グローバルクロック(GCK)の入力をIBUFGで定義します。(019、022行)
053行目のOBUFT_S_2は3ステートバッファで、_Sの意味はslew_rateがSlowで、_2の意味はdrive_strengthが2mAということです。この3ステートバッファのアウトプット(
.O )、インプット( .I )、ステート入力( .T )をそれぞれ指定します。
071行目ではロック状態をOBUFを使用して定義しています。
<PC(ホスト)側の処理>
EZ-FPGAライブラリはezfpga.h、ezfpga.cで構成されています。
ezfpga.hの先頭部分に下記の3つのシンボルが定義されています。この定義を0と1で切り替えることによりEZ-FPGAライブラリの動作を変えることができます。
クロックジェネレータでは全て"1"で使用します。
#define EZFPGA_STATIC_LIB | 0:ダイナミックライブラリ(DLL)使用 |
1:スタティックライブラリ(LIB)使用 | |
#define EZFPGA_CONSOLE_FUNC | 0:コンフィグ状況をコンソール表示しない |
1:コンフィグ状況をコンソール表示する | |
#define EZFPGA_JMEM_FUNC | 0:JTAGメモリーインターフェイスを使用しない |
1:JTAGメモリーインターフェイスを使用する |
DLLを使用する場合はezfpga.cの先頭部分で定義されているDLL_NAMEにDLLの存在するパスを指定しておきます。
クロックジェネレータのソースコード(main.c)
007行目で"ezfpga.h"をインクルードします。
009-015行目でFPGAをコンフィグするbitデータを指定しています。EZFPGA_EXTERN_CFGを0にすると、clkgenf.cに展開されているbitデータを使用します。(このデータはxbit2c.exeツールを使用して作成)
1にするとWebPackが生成する"fpga\ezfpga.bit"ファイルを直接入力してコンフィグを行います。従ってFPGAのデバッグ中は1を指定することでWebPackで修正したbitファイルをダイレクトに使用できます。
028行目でezfpga_init()でEZ-FPGAを初期化します。引数にはDEVICE_USB2(0)を指定します。インタフェースとしてパラレルケーブルを指定する場合はDEVICE_XPAR(0x378)などになります。
034行目でFPGAのコンフィグを行います。コンフィグ状況を表示する関数ezfpga_config_show()関数を使用します。表示しない場合はezfpga_config()になります。ここでbitストリームに対するファイル名、あるいはbitストリームそのものを指定します。これで自動的にFPGAはコンフィグされます。
148行目でjmem_write()関数を使用してFPGAにあるJTAGメモリーに書き込みを行います。アドレス、データ、バイト数です。クロックジェネレータではFPGAには物理的なメモリーは実装されておらず、単にPLLや分配器へのパラメータの受け渡しになります。実際のパラメータは128-147行目でbufにセットされます。
151行目ではjmem_read()関数を使用してFPGAにあるJTAGメモリーから読み込みを行います。クロックジェネレータではPLLとDLLのロックステイタスが読み込めます。
<使用方法>
ダイレクトに指定する場合
例)対話モード
clkgen 33M
clkgen 12K
例)<ピン配置>
clkgen
周波数を聞いてきますので(input frequency(EX. 10.2K or 66M).)入力します
CN1-7:ロックインジケータ(ロックでL)
CN1-8:クロック出力
<ダウンロード>