xiangze's sparse blog

機械学習、ベイズ統計、コンピュータビジョンと関連する数学について

FPGA(DE2)でHello World

組み込み系やハードウェア開発の最初の一歩としてボード上のLEDをチカチカさせるというのがありますが、
あまりもプリミティブすぎて面白みがない、モチベーションがわきづらいという意見があります。
そこでソフトウェアの言語や環境の入門でよくある"Hello World!"をFPGAボード上のLCDに表示させてみました。
今回はTerasic社が販売しているDE2ボードを用いました。

Altera社製のFPGA Cycloneを用いてLSI向けに作成した回路の検証や付属するインターフェース(PS/2経由のマウス、キーボード入力、VGA画像出力,USB,Ethernet, GPIOを用いたモーター制御など)を使用したアプリケーションの開発を行うことができます。
基盤に取り付けられている文字を表示することのできるLCDモジュール(GDM1602A)に文字を表示させる簡単な回路とプログラムを作成しました。
通常FPGAで電子回路を設計する場合にはHDL(ハードウェア記述言語)を用いるのですが、Altera社は自社のFPGA向け設計ツールとして
FPGA内に配置される独自CPU NIOSとAvalonバスを用いたQsysを提供しています。
Qsys内で提供されている回路部品(IP)を使う限りであればGUIでの設定のみで設計が完了してしまいます。

手順

以下のような作業になります。

  • 開発ツール(Quartus)のダウンロード、インストール
  • Qsysでの設計
  • 論理合成、配置配線
  • Nios Software Build Tools for Eclipseを用いたソフトウェアのコーディングとビルド
  • 回路情報の書き込み
  • ソフトウェアの書き込みと実行

この流れはQsysを使ってみたで紹介されているものと基本的には同一です。
Windows7 64bitで行いました。

開発ツール(Quartus)のダウンロード、インストール

AlteraのWebサイト
https://www.altera.com/download/software/quartus-ii-we/ja
からQuartus Web Editionをダウンロード、インストールしてください。今回使用するQsys,Quartus,NIOSs Software Build ToolsとEclispeに加えHDLシミュレータのModelSimがインストールできます。
Linux版、Windows版ともに3GB以上あって大きいです。

Qsysでの設計

QsysはAltera社のIDE Quartusに付属したツールであるため、Quartus上から立ち上げることになります。
アイコンをクリックすると出てきます。

以下のIPを使用します。
左側のツリーが表示されたペインの文字をクリックすることで追加することができ、右側のペインに表示されます。

  • Avalon ALTPLL (PLL システム全体のクロックを生成するのに必要)
  • Nios (CPU,無償版の NIOS/eを使用する)
  • System ID Peripheral (Peripherals → Debug and Performance 文字通りSystemのIDを保持する)
  • JTAG UART (Interface Protocols → Serial デバッグ通信用)
  • SDRAM Controller (Memories and Memory Controllers → SDRAM NIOSのプログラムやデータを格納する)
  • LCD Controller (Peripherals →Display → Character LCD LCD表示用)

基本的には"Qsysを使ってみた"で説明されているとおりにIPの追加、クロック、リセットの接続、NIOSとの接続、割り込みvectorの接続(あれば)を行います。
接続は線が縦横に交差している部分の白い丸をクリックするだけで簡単です。
LCD Controllerを今回新しく追加しました。他のIP同様クロック、リセットを接続し、Base Addressをかぶらないように設定しましょう。

externalの行のExportの列をクリックし、名前を入力すると、それがLCDへの信号名となります。
Generateボタンを押すとこのシステムがFPGAで合成可能なVerilogファイルとして生成されます。
ここでは紹介しませんが、HDLの編集、回路のシミュレーションを含めた開発、検証はQuartus上で行うことができます。
回路シミュレーションのためのファイルを生成することもできますが、今回は必要ありません(デバッグ時に重要です)。
その後HDL Example タブのExample HDLの記述をコピーしてください。

    Qmodule u0 (
        .reset_reset_n                     (<connected-to-reset_reset_n>),                     //                      reset.reset_n
        .sdram_0_wire_addr                 (<connected-to-sdram_0_wire_addr>),                 //               sdram_0_wire.addr
        .sdram_0_wire_ba                   (<connected-to-sdram_0_wire_ba>),                   //                           .ba
        .sdram_0_wire_cas_n                (<connected-to-sdram_0_wire_cas_n>),                //                           .cas_n
        .sdram_0_wire_cke                  (<connected-to-sdram_0_wire_cke>),                  //                           .cke
        .sdram_0_wire_cs_n                 (<connected-to-sdram_0_wire_cs_n>),                 //                           .cs_n
        .sdram_0_wire_dq                   (<connected-to-sdram_0_wire_dq>),                   //                           .dq
        .sdram_0_wire_dqm                  (<connected-to-sdram_0_wire_dqm>),                  //                           .dqm
        .sdram_0_wire_ras_n                (<connected-to-sdram_0_wire_ras_n>),                //                           .ras_n
        .sdram_0_wire_we_n                 (<connected-to-sdram_0_wire_we_n>),                 //                           .we_n
        .pio_0_external_connection_export  (<connected-to-pio_0_external_connection_export>),  //  pio_0_external_connection.export
        .sdram_clk_clk                     (<connected-to-sdram_clk_clk>),                     //                  sdram_clk.clk
        .altpll_0_areset_conduit_export    (<connected-to-altpll_0_areset_conduit_export>),    //    altpll_0_areset_conduit.export
        .altpll_0_locked_conduit_export    (<connected-to-altpll_0_locked_conduit_export>),    //    altpll_0_locked_conduit.export
        .altpll_0_phasedone_conduit_export (<connected-to-altpll_0_phasedone_conduit_export>), // altpll_0_phasedone_conduit.export
        .clk_in_clk                        (<connected-to-clk_in_clk>),                        //                     clk_in.clk
        .charlcd_0_external_data           (<connected-to-charlcd_0_external_data>),           //         charlcd_0_external.data
        .charlcd_0_external_E              (<connected-to-charlcd_0_external_E>),              //                           .E
        .charlcd_0_external_RS             (<connected-to-charlcd_0_external_RS>),             //                           .RS
        .charlcd_0_external_RW             (<connected-to-charlcd_0_external_RW>)              //                           .RW
    );

HDLへの組み込みと論理合成、配置配線

Verilog HDLで書かれたTOPファイルにQsysでコピーしたインターフェースの記述を追加、編集して組み込みます。
TOPファイルとはHDLで作成する回路の階層の最上位のモジュールを記述したファイルのことで、Quartusのassignment->Settings->GeneralのTop-level entityで指定するものです。
今回(DE2を使う限りはいつでも)はTerasicの提供するサンプルのものをそのままコピーして使い、不必要な下位モジュールに関する記述を削除するのが便利かと思います。

サンプルはDE2のCDまたは
http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=53&No=30&PartNo=4
にあります。
<>括弧の中の部分はTOPファイルにある信号名で置き換えることでTOPファイルの信号と接続できます。
以下が記述例です。

   Qmodule u0 (
        .reset_reset_n                     (KEY[0]),                     //                      reset.reset_n
        .sdram_0_wire_addr                 (DRAM_ADDR ),                 //               sdram_0_wire.addr
        .sdram_0_wire_ba                   (DRAM_BA	  ),                   //                           .ba
        .sdram_0_wire_cas_n                (DRAM_CAS_N),                //                           .cas_n
        .sdram_0_wire_cke                  (DRAM_CKE  ),                  //                           .cke
        .sdram_clk_clk                     (DRAM_CLK  ),                     //                  sdram_clk.clk
        .sdram_0_wire_cs_n                 (DRAM_CS_N ),                 //                           .cs_n
        .sdram_0_wire_dq                   (DRAM_DQ	  ),                   //                           .dq
        .sdram_0_wire_dqm                  (DRAM_DQM  ),                  //                           .dqm
        .sdram_0_wire_ras_n                (DRAM_RAS_N),                //                           .ras_n
        .sdram_0_wire_we_n                 (DRAM_WE_N ),                 //                           .we_n
        .pio_0_external_connection_export  (LEDG),  //  pio_0_external_connection.export
        .altpll_0_areset_conduit_export    (1'b0),    //    altpll_0_areset_conduit.export
        .altpll_0_locked_conduit_export    (),    //    altpll_0_locked_conduit.export
        .altpll_0_phasedone_conduit_export (),  // altpll_0_phasedone_conduit.export
	.clk_in_clk                        (CLOCK_50),                         //                     clk_in.clk
        .charlcd_0_external_data           (LCD_DATA),           //         charlcd_0_external.data
        .charlcd_0_external_E              (LCD_EN),              //                           .E
        .charlcd_0_external_RS             (LCD_RS),             //                           .RS
        .charlcd_0_external_RW             (LCD_RW)              //                           .RW
		  );

	  assign		        		LCD_BLON=SW[0];//1'b1; 
	  assign		        		LCD_ON=SW[1];//1'b1; 

LCD_ON,LCD_BLONはQsysで追加したIPに対応する信号がないので外部スイッチを接続しました。コメントアウトのように常に1でも良いです。

またFPGAのIOとTOPファイルのinput,outputの対応関係を定義する必要があります。通常これはQuartusのassignment editerで編集するかqsfファイルを直接編集するかして行います。
自分でボードを設計し、FPGAチップを実装する場合にはTOPファイルとqsfファイル内のIO assignmentを編集しなければいけませんが、ここでもTerasicの提供するサンプルのものをそのままコピーして使えます。

論理合成、配置配線もQuartus上で行います。この工程はFPGA内の領域をどのように使うかを計算するもので提供されるツールはFPGAベンダーごとに異なります。
紫の三角()ボタンを押すと自動的に論理合成、配置配線が行われます。

ソフトウェアのコーディングとビルド

NIOS上のソフトウェア開発はEclipse上で行います。
C言語で記述することができ、OS(linux)を搭載することもできるようですが、今回は簡単なプログラムなのでOSなしでやります。
LCDの制御ですが、Qsysで設定したアドレスに初期化シーケンスを送った後に
必要がありますが、すでにこの部分を実装されている方がいらっしゃったのでそちらを使わせていただきます。
http://users.ece.gatech.edu/~hamblen/DE2/DE2_demonstrations/DE2_NET/hello_led_0/
LCD.h
LCD.c

コードは以下のようになります。

#include <stdint.h>
#include "sys/alt_stdio.h"
#include "system.h"
#define LCD_16207_0_BASE LCD_0_BASE
#include "LCD.h"

void HelloLCD(int i){
  char Text[] = "Hello FPGA!    ";
  LCD_Show_Text(Text+i);
  LCD_Line2();
}

int main(){ 
    volatile uint32_t t;
    alt_putstr("Hello from Nios on DE2!\n");
    int i=0;
    LCD_Init();

    /* Event loop never exits. */
	while (1){
		for(t = 0; t < 50000; t++) ;
		HelloLCD(i);
		i=(i+1)%16;
	}
  return 0;
}

Nios Software Build Tools for Eclipseを管理者権限で立ち上げた後新しいプロジェクトを作成します。
そこでプロジェクト名の他にSOPC information file nameが訊かれるので、先ほどQsysで作成した *.sopcというファイルを指定し、またコードのひな形としては"Hello World small"を選びます。

生成されたcファイルに上記のコードを貼付け、LCD.h,LCD.cもプロジェクトに追加します。
ビルドはRunボタンを押すと行われます。

回路情報の書き込み

USBケーブルでFPGAボードとPCをつないで回路情報を書き込みます。まずUSBのドライバをインストールしなければいけません。

インストール後QuartusのProgrammerの機能を使って書き込みます。Qsysの左隣のアイコンがProgrammerのものです。
まずHardware setupボタンをおしてSelected hardware でUSB blasterを指定してください。*sofファイルを指定し、Programのチェックを入れてStartボタンを押すと書き込みが開始されます。

ソフトウェアの書き込みと実行

RunボタンのNios II Hardwareを選択するとビルドのあと自動的にバイナリコードFPGAボードへの書き込みが行われます。
この際に上記回路情報の書き込みが終わっておりかつ、Programmer,Quartusを終了させていなければ行けません。
書き込みが成功するとプログラムが実行され、Eclipseのコンソールにalt_putstrで指定したデバッグ用のメッセージ(Hello from Nios on DE2!)が表示され、LCDにも文字が表示されます。

わかりづらいですが2行目は文字が動いています。

はまった点

Downloading ELF process failed
というエラーは原因が複数有り、"Qsysを使ってみた"で説明されているとおりTarget ConnectionのタブでRefresh Connectionsのボタンを押してFPGAとPCの中心ができた状態(赤い表示がなくなる)にする必要があります。

感想

結局面倒なわりに見た目があまり派手ではない現状はあまり改善していません。
しかし多彩なデバイスの搭載されたボード、開発環境の提供と先人の知識の集積でFPGAでの開発がスムーズになりつつあるのも事実です。
DE2ボード、またそれにカメラ、タッチパネルを接続したVEEK(The Video & Embedded Evaluation Kit)ではデモアプリも充実しています。これらを用いればもっといろいろなことができるようです。

普通にTerasicの人がほとんど同じようなことをやっている動画があることにブログを書いている途中に気づきました。
http://www.youtube.com/watch?v=gBknFw511s0
こちらではLCDをファイルとしてみてNIOSから文字列書き込みを行っています。

あとよく考えてみれば同じことはLCDのあるマイコンボードを使えばもっと簡単にできます。最近ではRubyなどのスクリプト言語arduinoをつかった組み込みプログラミング開発が流行りつつあるように思います。

東芝情報システムの提供しているmrubyの評価ボードはAlteraのCyclone III上のNIOSを使っているらしいです。

  • Webアプリでプログラミングができる環境mbed

Reference

FPGAボードを用いたTrialは多くの方が行い、記録を残されています。
ここでは主にAltera社のCycloneシリーズを用いたものを紹介します。

XilinxFPGAでのIPライブラリSUSUBOX

Qsysの前身SOPC builderでNIOSと自作回路を接続、制御したり、NIOSにuClinuxを入れたりする方法が書かれています。

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?

FPGA ボードで学ぶ組込みシステム開発入門 ?Altera編?