2017年1月2日月曜日

ド素人からはじめるFPGA ~ Lチカ 篇~


FPGAプログラミング大全 Xilinx編を入手してお勉強開始してみました。

当然だけどFPGAがないと読み進められないので、

も入手。

開発環境としては、WindowsかLinuxが必須で、無償のVivado Design Suite HL WebPACK™ Editionを使用する事になります。環境構築も含め書籍内に丁寧に記載されていますし、Xilinx社のHPを含めネットにも詳細な情報が数多くあるので簡単にインストール出来るかと思います。

私が準備した環境は以下の構成

Ubuntu Server 16.04.1 LTS
Vivado HLx 2016.4: WebPACK Edition
リモートアクセスするための母艦となるMac


Linuxではケーブルドライバを手動インストールする必要がありVivado 2016.4では、
/opt/Xilinx/Vivado/2016.4/data/xicom/cable_drivers/lin64/install_script/install_drivers/install_drivers

を実行する事でドライバのインストールが完了します。

あとは、FPGAプログラミング大全 Xilinx編のサポート情報からFPGAボード用の設計データをダウンロードしておきましょう。

以上で全ての準備は完了となるので、本を読み進めサンプルコードを実行していくだけで、FPGAの動作の概略やVivadoの使い方が学習出来ます。

とは言え、この本は『触って学ぶ』事に焦点を当てて編纂されており、”FPGAとは?”、"Verilogとは?"などの入門書や入門サイトにありがちな内容にはなっていません。
なので、とりあえず動かして学習したい人には最適な書籍であると同時に、マニュアルを最初から最後まで読まないと触れないタイプの人には向いていないかもしれません。私は前者の触らないと理解出来ない人なので非常に助かっています。

各章の最後に、学んだ事を再確認するための課題が提示されるのですが、私自身も"Verilog”すら理解しないまま進めてきたので課題で躓く事になりました。

ここでは、躓いた部分を振り返った備忘録として書きとどめていこうと思っています。

私の場合の問題点は、Verilog HDLは何となく、XDCはぼんやりとしか理解出来てなかった事で、その箇所を再認識するためにLチカよりも簡単な回路を組みつつ理解を深めてみたいと思います。

最初に組んでみるのは、FPGAを使う必要性すらないボタンを押すとLEDが点灯するだけのものです。

使用するのはLEDとタクトスイッチだけで、
LEDが、LD0(M14)、LD1(M15)、LD2(G14)、LD(D18)
タクトスイッチが、BTN0(R18)、BTN1(P16)、BTN2(V16)、BTN3(Y16)
の各一つづつ(ここでは、LD0とBTN0)が対象となります。
※()の中の表示はポート番号。

書籍を見習い、作って確認してみましょう。
制約ファイルled_ctrl.xdcを作成して以下のように編集します。
#LED
set_property PACKAGE_PIN M14 [get_ports {LED}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED}]

#BTN
set_property PACKAGE_PIN R18 [get_ports {BTN}]
set_property IOSTANDARD LVCMOS33 [get_ports {BTN}]
LD0(M14)、BTN0(R18)の二つのポートを使用した構成です。

set_property - プロパティを設定するコマンド
PACKAGE_PIN ポート - パッケージピンを指定する際のコマンド
[get_ports { ~ }] - ポートをポートオブジェクトとして指定
IOSTANDARD LVCMOS33 - Low Voltage CMOSの略で数字はレベル電圧を設定

同様の設定を以下のようにも書けます。
#LED
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { LED }];

#BTN
set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { BTN }];

続けて回路led_ctrl.v(Verilog HDL)を作成し編集も行ってみましょう。
動作はタクトスイッチ(BTN0)を押すと、LED(LD0)が点灯するだけと非常に単純です。
module led_ctrl (
    input BTN,
    output LED
);
assign LED = BTN;
endmodule

あとはコンパイル、コンフィグレーションすれば上記動作を確認出来るかと思います。

ド素人向けに、単純でベタなLチカを作ってみます。

XDCで設定を行うのは、クロック発振器とLEDの二つだけです。
#Clock signal
set_property PACKAGE_PIN L16 [get_ports {CLK}]
set_property IOSTANDARD LVCMOS33 [get_ports {CLK}]
create_clock -period 8.000 -name sys_clk_pin -waveform {0.000 4.000} -add [get_ports {CLK}]

#LED
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { LED }];
クロック発振器は、書籍での設定をそのまま使わせてもらい、LEDは先ほど設定したものと同じ内容です。
この制約ファイルをled_blink.xdcとして保存しておきます。

回路はネットで見つけたものをベースにしています。
led_blink.vとして作成し、以下の様に記述します。
module led_blink(
    input CLK,
    output LED
);

reg [26:0] cnt;
reg state;

assign LED = state;

always @ ( posedge CLK ) begin
    cnt <= cnt + 1'h1;
    state <= cnt[26];
end

endmodule
このソースコードでは、27番目のbitを取得して点滅処理を行うようになっています。
27番目のbitが1になる状態は、2の26乗で求められますが、
2^26 = 67,108,864

の様に10進数だと何だかわからない数値なので、2進数にしてみます。
100000000000000000000000000 - 27bit

これだと先頭の数値が1で残りの桁が全て0になっている事が確認できるかと思います。

この値をZYBOのクロック周波数(125MHz)で割ってやると、
67,108,864 / 125,000,000 = 0.536870912

先頭数値が1になるまでの時間が求められます。
このソースコードの場合は、LEDが点灯するまでの時間が約0.5秒である事がわかります。
また、2の26乗の値に継続して"+1'h1"を加算し続けると、
111111111111111111111111111 - 27bit

に達し、これに更に"+1'h1"を加算すると桁上げが発生して
000000000000000000000000000 - 27bit

に戻り、先頭数値が1から0に変わります。
1になるまでと、0になるまでの時間は同じなので、
0.536870912 * 2 = 1.073741824

の様に倍の値として計算して、"1.07..."、約1秒が0→1→0の時間である事が求められます。
ソースコードでは1が点灯、0が消灯とし、1サイクルとして扱っています。
このサイクルの周波数を計算してみます。
1 / 1.073741824 = 0.93132257461

結果、約0.9Hzで動作している事がわかります。

ちなみに、このサイクルは電子計算機の世界ではクロックや周波数、クロック周波数と言いHzの単位を用いて表します。
ZYBOは125MHzで動作しており、1Hzは0.000000008秒(8ナノ秒)でが、上記のように
26bitのカウンタを使った処理を行う事で、1Hz=1秒の状態を作り出す事が可能となります。
この様な処理を分周と言います。

ここまでの内容を踏まえて、書籍の処理も再確認してみます。
先のサンプルでは、26bitのカウンタを用いて27bit目のデータを用いてLEDの状態を設定していましたが、書籍内のソースコードでは23bitの変数値で分周して15Hzのクロック周波数を作り出し全てのbitが1となる"23'h7fffff"を判定する事でLEDを動作させています。

23bitで変数を設定すると、
00000000000000000000000 - 23bit

のような形になります。ここに"+1'h1"を繰り返し加算し、
7fffff(16進数) = 8388607(10進数)

の数値、つまり二進数で表す値、
11111111111111111111111 - 23bit

を判定します。
この状態は26bitのサンプルと同様で、更に"+1'h1"を行うと桁上げが発生して、
100000000000000000000000 - 23bit

と、23bit範囲は0に戻ります。(赤字部分は24bit箇所なので23bit変数に含まれません)
led_blink.vでは、消灯、点灯、消灯を1つのサイクルとして構成していましたが、 こちらでは0、判定値(7fffff)のみのサイクルとなります。

このサイクルは約15Hzとなっていたので計算して確認してみましょう。
計算には判定値(7fffff)を10進数にしたものを用いて、1サイクルの時間を求めます。
8388607 / 125,000,000 = 0.067108856

1サイクルの時間約0.067秒から、周波数を求めると
1 / 0.067108856 = 14.9011629702

約15Hzと求めるべき値が出てきました。

この様に分周もled_blink.vでの方法や書籍内の方法など、同じ処理を行う場合にも色々あるわけですね。ネットに転がっていたものよりは、書籍内の方法をしっかり理解する事が最適なのだとは思います。私には、まだまだ理解出来るだけの能力もないですけど。。

私の場合は、この辺りが何となく理解出来てから課題はすんなり解けました。
FPGAに興味を持たれて偶然にもこのブログを見られた方も、疑問と感じる箇所を実際に動かすことを優先に色々と試してみる事が良いかもしれません。

オマケ:led_blink.vもIF文を使ったものに作り変えてみました。
module led_blink(
    input CLK,
    output LED
);

reg [25:0] cnt;
reg state;

assign LED = state;

always @ ( posedge CLK ) begin
    cnt <= cnt + 1'h1;
    if ( cnt == 26'h3FFFFFF )
        state <= state + 1'h1;
end

endmodule

正直言って、ハードウェアのド素人には準備運動の序盤からハードル高い状況です。
HDLもハードウェアとも、プログラムとも微妙に異なっていて双方を理解していないと最適な答えは出ないのだと思い知らされます。
ただ、触って動く事を体感出来る事で得られる事も多いので、もう少し頑張って読み進めてみたいと思います。

0 件のコメント:

コメントを投稿