SATAを簡単に扱えるように,と,設計したAxfmc6040という基板があります.FMCで他のFPGAボードに増設することで,メインロジックのリソースやらタイミングやらを考慮せずにSATAなディスクを扱えるようになります.
なのですが,バッファ用に2Gbitのメモリを持ったそこそこのボード.ということで,FPGAにMicroBlazeいれて,Linuxを動かしてみました.将来的にはSATAも使いたいな,とか思いながらも,今回は,Linuxを起動させるところまでの手順を紹介します.
また,最後のステップではLinuxの使用メモリ量を制限するためにpatch prom.c.patchをあてる話を紹介しています.
ターゲット環境
Axfmc6040はXC6SLX45Tと2GbitのDRAMを搭載したFPGAボードです.LEDが8個,プッシュボタンが1個ありますが,残念ながら,使い勝手の良いGPIOポートはありません.
そこで,CSIポートから適当にフィルムケーブルでGPIOポートを引き出し,秋月で販売されているUSB-UART変換モジュールでパソコンと接続することにしました.
ネットワークがないので,ちょっと寂しいですが,Linuxを動かすためのMicroBlaze with MMUの条件は満たすことができます.
手順
次のようなステップでターゲットボードにMicroBlazeを構築しLinuxを実行できるようにします.
- ISE 14.7 + EDK での作業
- EDKでベースシステムを作成
- ISEに戻って作業
- SDKへのエクスポート
- デバイスツリーを作る
- デバイスツリーの修正
- BuildrootでツールチェインとLinuxのコンパイル (ここからLinuxが楽.仮想マシンでもよい)
- Linuxの起動
- アプリケーションの実行
- Linuxで使用するメモリを制限する
1. ISE 14.7 + EDK での作業
まずは,ISEでプロジェクト作成します.Microblazeメインのシステムの場合,EDKで作ったシステムを直接トップにしてもよいのですが,後で,独自の回路やちょっとした回路をする際のことを考えて,ISEでプロジェクト管理したいので.
今回は,プロジェクト名 axfmc6040_linux_test とかして作成しました.以降,このプロジェクトのトップディレクトリを$WORKとします.プロジェクトの作成に,特に注意することはありませんが,間違えずに XC6SLX45T-3CSG324Cを選びましょう.
2. EDKでベースシステムを作成
ISEのHierarchyで右クリックしてNew Source→Embedded Processorと辿って,EDKプロジェクトを新規に作成します.名前は何でもいいのですがbase_systemとしました.
BSBウィザードに従ってシステムを作成します.ウィザードで設定するのは,
- REFCLKを33.333MHzにする
- CPU CLKを66MHzにする
- DRAM,UART,timerを追加
- UARTは,通信を115200に設定し,またinterruptを有効にする
くらいです.
BSBウィザードを終えたら,次の変更をします.
- MicroBlazeの設定
- Assembly Viewでmicroblaze_0をダブルクリックし,ダイアログを起動
- select configurationでLinux with MMUを指定
- キャッシュの設定ページでI$/D$ともにキャッシュ範囲を0xa0000000-0xafffffffにする
- DDR3メモリの設定
- Assembly ViewでMCB DDR3をダブルクリックし,MIGを起動
- 接続先はBANK3に設定(たぶんデフォルトでそうなっている)
- メモリを MT41J128M16XX-125 に設定(実際にはMT41K128M16JT-125だが代用)
- DDR3メモリ領域の設定
- Address PortタブでMCB DDR3のBase Addressを0xa0000000にSizeを256MBに
- クロックまわりの設定
- PortタブでCLKIN_P,CLKIN_Nを削除
- clock_generator_0のCLKINにexternal portを追加.名前をCLKINにする
- CLKOUT2にexternal portを追加(間違って登録済みのを消さないように!!)
- 割り込みポートの追加
- Portタブで右の方にある”Open Interrupt Control Dialog”をクリック
- timerとUARTをインタラプトに追加(connected interrupt(s)の方に移動)する
3. ISEに戻って作業
合成までの細々とした作業をします.
- EDKで作ったシステムのラッパーモジュールを作成
- ラッパーモジュールをベースにaxfmc6040_top.vhdを作成
- ラッパーモジュールをプロジェクトから取り除いてaxfmc6040_top.vhdを追加
- 動作監視用にCLKOUT2をクロックとするLチカ回路を追加
作成したトップモジュールはaxfmc6040_top.vhdです.
また,基板にあわせて,UCFを作成します.メモリはEDKで作ってくれたUCFに任せることにして,UART用のGPIOの設定などを.作成したのは axfmc6040_linux_test.ucf です.
あとは,いつも通り合成.
4. SDKへのエクスポート
HierarchyでEDKインスタンスを選択すると,Designタブにエクスポート用のアイコンが表示されますので,それをクリック.”Export Hardware Design To SDK with Bitstream”の方を選びます.
Eclipseが起動してきてワーキングスペースを訪ねられますので,今作成したばかりの$WORK/SDK/SDK_Export を指定します.
SDKが起動したら,bspを作成し,続けてHello Worldアプリを作成しましょう.
- File → New → Board Support Pacakgeでbspを作成
- デフォルトのまま,ダイアログに従って作成する
- File → New → Application ProjectでHello worldアプリを作成
- 名前は,たとえば,”hello_world”などとつける
- Target Software枠内のBoard Support PackageはUse existingを選び,上で作成したものを選択.
実行の手順は次の通りです.
- FPGAをコンフィギュレーションする
- メニューの”Xilinx Tools”→”Programming FPGA”をクリック
- “Program”ボタンをクリック.
- Software Configurationの”ELF File to 〜”のところはbootloopにしておきます
- プログラムを実行
- “Project Explorer”ペインで作成したアプリ(たとえばhello_world)を展開
- Binariesをさらに展開するとELFファイルがあるはず(たとえばhello_world.elf)
- ELFファイルを右クリック→”Run as”→”Launch on Hardware..”と辿る
ボーレートを115200に設定したターミナルを起動し,UARTに接続しておけば,Hello Worldの表示が確認できるはずです.
5. デバイスツリーを作る
作業用のフォルダとして,$WORK/dts を作成します.作成したら,
- $WORK/base_system/SDK/SDK_Export/hw/base_system.xml
- $WORK/base_system/SDK/SDK_Export/standalone_bsp_0/system.mss
の2つのファイルを$WORK/dtsにコピーします.
コピーしたsystem.mssのBEGIN OS〜ENDを次のように修正します.
BEGIN OS PARAMETER OS_NAME = device-tree PARAMETER OS_VER = 3.11.a PARAMETER PROC_INSTANCE = microblaze_0 END
$WORK/bsp/device-tree_v3_11_a/dataを作成し,device-tree_v2_1_0.mld,device-tree_v2_1_0.tclを置きます.どちらも,https://github.com/Xilinx/device-tree/tree/master/data に最新版があるのですが,うまくいかなかった(2015年3月26日時点)ので,フォーラムを参考に,少し古いバージョンをダウンロードしました.
ファイルの配置が終わったら,Xilinx環境変数設定済みのコマンドプロンプト起動し,$WORK/dtsに移動し,
libgen -hw base_system.xml -lp device-tree -pe microblaze_0 system.mss
を実行します.成功すると,
- $WORK/dts/microblaze_0/libsrc/device_tree_3_11_a/xilinx.dts
というファイルが作られます.
6. デバイスツリーの修正
生成されたxilinx.dtsを2箇所修正します.
- bootargsの修正
bootargs = “console=ttyUL0”;
linux,stdout-path = “/axi@0/serial@40600000”;
ここで,@40600000はEDKでRS232に設定されたアドレスに相当します.
- RS232のポート番号(port-numberの引数)が0でなければ0に変更します.
port-number = <0>;
7. BuildrootでツールチェインとLinuxのコンパイル
buildrootのホームページから最新版を持ってきます.
git clone git://git.buildroot.net/buildroot
ですね.できたディレクトリを$BUILDROOTとします.
環境設定スクリプトとして$BUILDROOT/configs/xilinx_axfmc6040_defconfigを以下の内容で用意します.
BR2_microblaze=y BR2_microblazeel=y BR2_TARGET_GENERIC_GETTY_PORT="ttyUL0" BR2_TARGET_GENERIC_GETTY_BAUDRATE_115200=y BR2_TARGET_GENERIC_GETTY_BAUDRATE="115200" BR2_TARGET_GENERIC_GETTY_TERM="vt100" BR2_TARGET_GENERIC_GETTY_OPTIONS="" # BR2_TARGET_ROOTFS_TAR is not set BR2_TARGET_ROOTFS_INITRAMFS=y BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="board/xilinx/axfmc6040/axfmc6040_defconfig" BR2_LINUX_KERNEL_USE_CUSTOM_DTS=y BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="board/xilinx/axfmc6040/xilinx_axfmc6040.dts" BR2_TARGET_GENERIC_HOSTNAME="axfmc6040" BR2_TARGET_GENERIC_ISSUE="Welcome to Axfmc6040"
カーネルコンパイル用の設定のひな形として
$BUILDROOT/board/xilinx/axfmc6040/axfmc6040_defconfigも用意します.作成したのはこちらaxfmc6040_defconfig.txt (拡張子の.txtは取り除いてください)
また,5.と6.で作成したxilinx.dtsを $BUILDROOT/board/xilinx/axfmc6040/xilinx_axfmc6040.dts に おいておきます.参考までに xilinx_axfmc6040.dts
必要なファイルが用意できたので,ビルドします.
make xilinx_axfmc6040_defconfig
make
buildrootはmake時に必要なファイルをインターネットからダウンロードしてきますので,一般には,インターネットに接続している必要があります.
buildrootでは,もっと沢山のコンフィギュレーションが可能です.
make menuconfig
とすればメニューから選んでぷちぷち設定できます.カーネルの設定は,同様に
make linux-menuconfig
とすればOKです.
8. Linuxの起動
ビルドが無事に完了すると,ルートファイルシステム込みのLinuxイメージ
$BUILDROOT/output/images/simpleImage.xilinx_axfmc6040
が作成されます.あとは,これをボードのDDRメモリ上にコピーして走らせれるだけです.
ISE環境パスの整ったDOSプロンプトやターミナルで,できあがったイメージのある$BUILDROOT/output/imagesの下に移動し,
xmd
でFPGAのデバッガポートにアクセスするプログラムを起動します.
connect mb mdm
でMicroBlazeのデバッグポートに接続し,
dow simpleImage.xilinx_axfmc6040
で,イメージをDDRメモリに書き込みます.書き込みにはしばらく時間がかかりますが,終わったら
con 0xc0000000
で,Linuxが起動するはずです.起動メッセージはシリアルターミナル上に表示されます.
ターミナルにログインプロンプトが表示されたら起動は完了です.ユーザ名rootでログインできます.パスワードはありません.
9. アプリケーションの実行
Linuxが動いたら,その上でアプリケーション・プログラムを実行したくなりますね.…さて,どうやってアプリケーションプログラムのバイナリをLinux上に転送しましょう.…今回はバイナリをuuencodeでテキスト化して転送することにしました.
ホストコンピュータの上でCでプログラムを書いたら,まずはコンパイル.クロスコンパイラはbuildrootが一緒に作ってくれています.
$(BUILDROOT)/output/host/usr/bin/microblazeel-buildroot-linux-gnu-gcc test.c
もちろん(?)stdio.hをインクルードしてprintfとかして大丈夫です.コンパイルしてa.outができたらuuencodeでテキスト化します.
uuencode < a.out > a.out.txt
これで送る方の準備はできました.続いて,MicroBlaze上のLinuxで
cat > a.out.txt
として待ち受けた状態で,できたa.out.txtを転送します.たとえば,TeraTermならファイル転送です.転送を終えたらCtrl-Dでcatを終了させます.
転送されたテキストをデコードすれば実行できます.MicroBlaze上のLinuxで,
uudecode < a.out.txt
chmod 755 a.out
./a.out
とします.
10. Linuxで使用するメモリを制限する
せっかくのFPGA上のプロセッサなのですから,Linux上で動作するソフトウェアとFPGA上のロジックを連携させたくなります.もちろんデバドラを用意すればよいのですが,簡単には,ソフトウェアとFPGAとで楽にデータを共有するプログラミングの方法として,/dev/memをmmapしてアクセスするという手法が知られていますね.
この手法を使う場合には,メモリ上にLinuxの管理外となる領域を用意してあげる必要があります.カーネルの起動オプションにmem=128Mとか書くアレです.
今回,bootloaderなしでLinuxを起動しているので起動オプションを渡せません.代替手段として,dtsあるいはカーネルにパラメタを埋め込むという方法がある…はずなのですが,MicroBlazeではどうやってもうまくパラメタを設定することができませんでした.
具体的には,カーネルが起動オプションのmem=を解析し設定するarch/microblaze/mm/init.cのmm_cmdline_setup()が呼ばれた時点で”mem=”という文字列が格納されているはずのcmd_lineが空文字になってしまっています.
ここになんとかしてカーネルパラメタを引き渡せるよう,arch/microblaze/kernel/prom.cにハックをしてみました.このハックではカーネルコンフィギュレーション時にスタティックに決定したパラメタを強制的にcmd_lineにコピーすることで,mm_cmdline_setup()時に”mem=”が有効になるようにしています.
次のようにして,メモリの使用量を制限できます.
- カーネルのコンパイルディレクトリ($BUILDROOT/output/build/linux-3.19.2)に移動
- patchをダウンロードするprom.c.patch
- patchを当てる
patch -p0 < prom.c.patch
- buildrootのルート($BUILDROOT)に戻る
- kernelパラメータを設定する
- make linux-menuconfig
- Processor type and features→Default kernel command stringにmem=128Mとか設定
- Exitを(何回か)選択して設定を完了
- make
makeが完了したらメモリの使用量を制限したLinuxを手にすることができます.
…なのですが,もし,こうすればpatchなんて当てなくてもいいよという方法があれば,是非教えていただきたいです!!
コメント