2016年6月11日土曜日

FPGAでLチカ

FPGAでLチカというタイトルをネットで見る時がある。
マイコンやFPGAの動作確認等の目的でLEDを点滅させることをLチカと言うが、「FPGAでLチカ」と言う場合にどういうやり方がFPGAらしいのか、あるいは、FPGAならではと言えるんだろうか?と考えてみた。

まずは思いつくのは、

reg [xx:0] cnt;

always @(posedge clk)
begin
  cnt <= cnt + 1;
end

assign LED = cnt[**];

見たいな感じでクロックでカウンタを回して、カウンタの上位の方の、LEDの点滅が視認できる程度で変化するビットでLEDを駆動する方法だろうか。でも、これだと当たり前すぎて面白くない。

あるいは、FPGAにソフトマクロのCPUを載せて、

while (1) {
   LEDport = 1;
   msleep(n);
   LEDport = 0;
   msleep(n);
}

のようなプログラムを実行させてLチカする方法もあるだろう。また、リアルタイムOSを動作させてタスクを定期的に起床させてそのタスクでLEDを点滅させるという方法もあるだろう。
でも、これらの方法はFPGAでLチカというよりはマイコンでLチカだし、FPGAならではの方法とは言い難い。

こういうのはどうだろう。

FPGAの出力をCRで遅らせて、FPGAに戻しそれを反転してまたCRに戻す。CR発振回路だ。
FPGA側のRTLは以下のような記述になる。

assign CR_out = ~CR_in;
assign LED = CR_in;

LEDの点滅周期はCとRの時定数で決まる。
この方法はアナログちっくで、ソフトウェアでは実現できない。でも、CとRという外付け部品が必要になるのでFPGAならではの方法とはちょっと言えないかも知れない。

で、最後にこういうのを思いついた。
FPGA内にリングオシレータを実装する方法だ。

この方法の場合、CR等の外付け部品は勿論のこと、クロック源の水晶発振器さえも不要でFPGAの内部リソースのみで実現できる。LEDの点滅周期は直列になっているバッファと配線を含むパスの伝搬時間の2倍の時間となる。
RTL記述は例えば以下のように書けるだろう。

wire [LENGTH-1:0] ring;

assign ring = {ring[LENGTH-2:0], ~ring[LENGTH-1]};
assign LED = ring[LENGTH-1];

あるいは、バッファのプリミティブセルをインスタンスして、

wire [LENGTH-1:0] ring;

BUFFER BUFFER[LENGTH-1:0] (
    .A({ring[LENGTH-2:0], ~ring[LENGTH-1]),
    .Z(ring)
);

assign LED = ring[LENGTH-1];

しかしながら、これらの記述では旨くいかない。回路合成で最適化されて1つのバッファのみの回路になるか、または、エラーになるかだ。実際に試してみたところLatticeの場合が前者でXilinxのISEの場合は後者だった。
そこで、トランスペアレントラッチとして記述し、実動作ではゲートを開きっぱなしにすることにした。

wire [LENGTH-1:0] ring;

assign ring = (~EN)? {ring[LENGTH-2:0],~ring[LENGTH-1]}:ring;
assign LED = ring[LENGTH-1];

このようにringをラッチと見せかけることで合成ツールに最適化されなくなった。

この回路を以前自作した MY FPGA BOARDのFPGA (Lattice XO2 7000HE)に実装してみた。
実際のRTL記述は以下のようにした。 7000HEはSLICE内のregisterの数が6864個なのでringの長さは6864にしている。また、ENはinputとしてPINに出しているが、実際は何も接続せずPINの属性をPULLDOWNにしてLに固定している。

リソースの使用率はきっちり100%になった。


physical view
全てのSLICEのregisterが数珠つなぎになっている筈だ。

SLICEの中身を見てみると…

ラッチが生成されているのが判る。

FPGAに書いて動かしてみた。
D3を点滅信号で駆動しているが、肉眼では視認できない。点滅周期が速すぎるようだ。

オシロスコープで信号を見てみたところ、約90KHzの信号が出力されていた。

そこで、ringの段数を減らす代わりに分周器を入れてringで生成したクロックを分周してLEDを駆動するようにしてみた。

registerが4個未使用になってしまった。

で、FPGAに書いて動かしてみた。今度は視認出来るほどの点滅周期になった。

オシロスコープで測ると約2.7Hzだった。

水晶発振器等の外付けのクロック源を使わず、FPGAのSLICEだけを使ってLチカ出来た。
他にも方法があるだろうか? しかし、上に書いた全ての方法がFPGAで実現可能であり、この多様性というか懐の深さがFPGAならではと言えるのかも知れない。

最高周波数は?
ringオシレータの周波数はゲートの段数によって決まる。。。ということは段数を最小にした場合、どの位の周波数になるんだろう? ということで試してみた。

周波数は約580MHzとなった。


このringオシレータの周波数はFPGA内部素子の遅延に依存しているので、コア電圧や温度の変動の影響を受ける筈で安定度は余り期待は出来ないかも知れない。が、手軽にFPGA内の全てのSLICEを活性化できるので、FPGAボード等を作った場合の電源の供給能力の評価や、FPGAの診断等に応用できるかも知れない。

ということで、久しぶりにFPGAを触るのでリハビリを兼ねてこんなことをやってみたのだった。




0 件のコメント:

コメントを投稿

自作CPUで遊ぶ 25

まだ制御ソフトが完成していないので今まではスピンドルを移動するために一々簡単なプログラムを書いて移動させていたのだが、非常に面倒なのでCNCペンダント的なものを作ることにした。 右側の縦に2つ並んでいるスイッチ...