Avnet Spartan-6LX9 MicroBoard でuClinuxを動かす


はじめに

秋月に Avnet Spartan-6LX9 MicroBoard(以下このボード)が¥8kくらいで売られています。
今は売り切れみたいですが...。このボードは8kと安価ながら、なかなか充実した構成です。
(2012/04/19 今は売り切れではありません)

・FPGA Spartan6 (LX9はこのシリーズのローエンドみたいですが...)
・64MB DDRメモリ
・16MB SPIフラッシュ
・Ethernet 100/10M
・オンボードUSB-Serial変換(uart コンソール用)
・オンボードUSB-JTAG 変換 (書き込み・デバッグ用)

拡張ポートはPModというのがついていますが、FPGAのコンフィグ次第で何にでもなるはずなので、ピン数さえ足りればいいでしょう。ボードが小さいので、この汎用ポートは他の名刺大以上のボードと比べると犠牲になっています。

このスペックだと3万円くらいしてもおかしくないです。パッケージにはボード搭載チップなどの広告も入っているので、おそらくプロモーション用としての位置づけで、殆ど原価+αで出していると思います。昔、Spartan3のボードを100ドルくらいで買ったときもそうでした。
アマチュアとしてはこれはお買い得です。オンボード USB-JTAG変換がついているので別途プログラマを買わなくても、このボードだけで開発が進められます。(ただし純正品に比較して遅いとのこと。)

CPU+αでちょっと作りたいものがあって、適当なCPUボードを探しているときに、このボードを見つけました。

事前にネットを探すと、FPGA内蔵CPUは難しいらしく、初心者には無理と言い切っているところもあります。
私はFPGA初心者なので、ちょっと引きますがダメもとでも安い(ここが重要)のでポチっといきました。

料理法を考える

ちょっと作りたいものにはネット接続が欲しいので、IPスタックをどうするかということになります。
今回はスタックまで自作したくないので、何らかのOSをもってくることになります。
microblaze(FPGAに載せるソフトCPU)でuClinuxが動くということなので、OSはuClinuxにします。
ということで、microblaze + α(自作IPコア) / uClinux という構成がほぼFixしました。
こういうシステム構成を考えるのは楽しいことです。

環境構築(FPGA開発)

パッケージ付属のISE(開発ツール)は古いので、どうせなら最新版にします。xilinxから落としてきます。
私はISE(アイエスイー)が読みにくいので(イセ)と言っています。母音の重なりは読みにくいです。
一式落とすと5GBくらいあります。tarなので、windowsだとちょっと戸惑うかもしれません。
ISEのほかに、いろいろ入れるものがあります。digilentやらavnetやらから落としてきてiseのフォルダにコピーしてやります。
いちいちメモっていないのですが、次のようなものを入れています。avnetにある手順pdf(Configuration Guide)に沿って進めます。
・digilent adept
・digilent_plugin
・avnetのボード定義ファイル(これを入れると、proj新設のときターゲットにこのボードが出てきて便利)

1つ嵌まるところ、digilent_pluginを選択するところですが、プルダウンに出ない場合は手で入力してください。

開発PCはCPUが速くてメモリも十分あるものを使ってください。
当方の開発PCはThinkpad T60p(Coreduo 2.16G)です。このスペックでも合成に7,8分はかかります。
後述のlinux開発環境をあわせると、結構なディスクスペースを用意しておく必要があります。

まずは練習から

FPGA/CPLDはもう何年も使っていません。
ツールが全く変わっていることが予想されるので、ここはチュートリアルからはじめました。
Avnetのサイトに、EDKチュートリアルがあるので、それを一式こなします。
これをこなすと、FPGAへの組み込みシステム(CPU他)、カスタムIPの追加等の手順がだいたいわかります。
ツールの使い方もわかってきます。なお、最初はEDKがパッケージと別ライセンスであると気づかず、EDKライセンスエラーで止まって?? でした。
もっと言うと、EDKが何なのか理解していませんでした。

EDKは本来別に買わなければならないのですが、1ヶ月試用版でしのぎます。
ちょっと内緒ですが 1ヶ月経ったらまた試用ライセンスを作って更新します。
ここらへんは、ちゃんと業務で使うなら購入してください。
このボードのスペックからしてEDKなしではもったいないので、ベンダーにも是非ノーサポート・デバイス限定EDK付で 販売してほしいと思います。

練習の結果、microblazeでLEDやSWをいじるところまではできました。
前述、FPGA内蔵CPUは難しい...とのことですが、ここまではさほど難しさはありません。
ツールに助けられているんだとは思います。気をよくして、Linuxを載せることを考え始めます。
SDKはクロス環境を含んでいるので、linuxを考えなければこれだけでソフト開発までこなせてしまいます。

Linux...

Microblaze linuxで探ると、petalinuxに行き着くと思います。しかし、petalinuxは昔はフリー
提供されていたらしいのですが、今は商業ソフトになっていて入手できません。注意深く探す
と昔のものがダウンロードできますが、当時とEDKのバージョンが違うのでそのままでは動きません。
ここで2つの方法、petalinuxの古いものを現状のEDKで動くよう修正するか、petalinuxを捨てて
別の方法をとるか、という選択になります。後者にしました。
xilinxも linuxリソースを提供しているようなのですが、mmu前提とのことなので、このボードでは
規模オーバーで論理合成できませんでした。(フィッティングできたという人もいるみたいですが)
 

環境構築(linux開発)

えーっとここは、atmark technoさんで出しているFPGAボード 「朱雀」の環境をそのまま使わせてもらうことにしました。
これは vmware 仮想マシンの中に クロス環境が入っているので、クロス環境をいちいち作らなくてもすみます。
uCのソース tar ballも 「朱雀」のものを使わせてもらうことにします。
「朱雀」はmicroblazeのuClinux環境なので、 このボードと殆ど同じです。
実は uCの本家から取ってきたソースを試して みたのですが、ソースツリー内の既存ターゲットでさえmakeが素で通らず、手っ取り早く[朱雀」の ものを使わせてもらいます。
atmark techonoさんの製品 armadilloはかつて何点か購入しているので、 その辺は大目に見てもらいましょう。
(もちろんGPLなので、使って構わないんですけど)
もし、似たようなことする場合はatmark techonoさんに感謝して使ってください。
間違っても「朱雀」 と関係ない質問などしないこと。

Linuxビルド

ボードごとのフォルダをソースツリー内に用意します。
基本手順はlinux-2.4.x/Documentation/microblaze/AddingPlatforms.txtにあります。
コピーしたフォルダ内をFPGAへ作りつけたハードの環境(アドレスほか)をlinux側とあわせて編集します。
それだけでは足りない部分があるので、ちょこちょこ修正していきます。
トップディレクトリで make menuconfig make dep make でカーネル、ユーザランドを作ります。
うまくいくと、linuxフォルダに linux (elf)が、imageフォルダに linux, ユーザランド込みのイメージが出来ます。
これをボードへ転送するわけです。

SDKで hello world でも作っておきます。これをターゲット上でrunすると、SDKがターゲット と接続され、コンソールが使えるところまで確認できます。
デバッガxmdもターゲットと接続されます。

FPGAのbitデータの転送をあらかじめやっておくのを忘れないように。

ちなみに手動でxmd をターゲットと接続する場合、 connect mb mdm です。

そこで、xmd console で stop としてcpuを止めます。
カーネルを転送する場合、linux(elf)を PCのどこかへコピーしておいて パスをあわせて dow linux でいいです。
イメージの場合、dow image.elf はうまくいきません。実はエラーも出ないので、うまくいかないことがわからないのですが、xmdでは.romfsセクションを正しくロードできないらしいです。
最初、ここがわからず 悩みました。
カーネルロード時間とimageロード時間が変わらないのでへんなことに気づくかと思います。

ここでは dow -data image.bin 0x80000000 としてイメージロードします。
かなり時間がかかります。
rwr pc 0x80000000 でPCをあわせて、con 0x80000000 で実行します。
elfロードしないとrunは使えないみたいです。

まずは、kernelだけのロード(dow linux)からスタートしてください。こちらのほうがサイズも小さく、 最初はカーネルをあげることが必要ですので。

当然最初は全く動かない

うんともすんとも言いません。いろいろやっているうちに少しずつわかってくるというものです。
実は、このプロセスが多量に考え、想像することを要し、人材育成上大切なんだと思います。

■AXIはダメ
チュートリアルのウィザードでも何気にAXIバスを選択しているので、当然AXIでシステム 設計をすすめます。
が、全く動きません。数日間悩みました。
この頃からデバッガxmdを本格的に使い始めますので、メモリやレジスタのダンプが出来るようになってきます。
とりあえずロードしたカーネルの先頭ワードをダンプ、開発環境上で逆アセンブルしたものと 比較...全然違う。
逆になっているではないか!。
早速 microblaze little endianでぐぐると、どうやら元々big endianであったmicroblazeは、AXIを選択するとlittle endianになるとのこと。
これでは動くわけ有りません。


AXIはarmのバスなんだって。IPコアの汎用性を持たせるために、組み込み市場を席巻しているarmに近づいたんだろう。
ここからは勝手な想像なんだけど、おそらく microblaze はそのうちディスコンになって、armにリプレースされると思う。
FPGAベンダーが独自のcpuを出しても、周辺IPやサポートを考えると商売にならないよね。

kernel2.6からlittleサポートしているらしいのですが、まともに動かないという情報もあります。
AXIをやめてPLBで設計しなおすことにします。ということでprojectを作り直し。

■アドレスは自動生成せず、コピーしたuclinux-auto のデフォルト達にあわせる。
XPSに自動生成機能 があるので、一意なアドレス空間を割り当ててくれます。
理屈上はこのアドレスをlinux側に もって行けば良いはずですが、なかなか動きません。
素直にlinux先行者の決めたアドレスに しましょう。そのほうが早く幸せになれます。
後からわかってきますが、一部のドライバは、 アドレスをハードコードしたりしてます。

■さて、このあたりまで来ると、バナーが表示できるくらいになります。

Linux version 2.4.32-uc0 (atmark@atde) (gcc version 3.4.1 ( Xilinx EDK 9.1 Build EDK_J.19 121007 )) #1 Sun Nov 20 02:25:57 JST 2011
On node 0 totalpages: 16384
zone(0): 16384 pages.
zone(1): 0 pages.
zone(2): 0 pages.
CPU: MICROBLAZE
Kernel command line:
Console: xmbserial on UARTLite
Calibrating delay loop...

おそらくここで止まって悩むんじゃないかと思います。
これはタイマー割り込みが入っていないことが原因です。

こういった場合、CPUが何処で止まっているか探らないといけません。先のうんともすんとも言わない場合もです。
次のようにします。
stopでcpuを止めます。
rrd でレジスタを読みます。
pcを見て実行中のアドレスを読み取ります。

linuxカーネルを逆アセンブルしたものを容易します。
mb-objdump -da linux
mmuを使っていないので、全部物理アドレスでわかりやすい!。
armデバッグのときはmmu onにしたら混乱したもんなぁ。

これで止まっているアドレスが何処かわかります。アセンブラとCソースの対比は勉強がてら
やってください。有る程度アセンブラが見られないと、先へ進めなくなりますよ。

タイマー割り込みのチェックポイントは次のとおりです。
xps timer -> intc -> CPU
・タイマーは0番の割り込みであること。(最優先)
・intc->CPUにちゃんと接続されて、割り込み極性、レベル・エッジがあっていること。
どうやらこの当りがNGだったみたいです。(後からではもうどうNGだったか特定できませんが)
intc入力の割り込み極性、レベル・エッジ設定はxpsで変更できないみたいなので、そのままとしました。
割り込みの入力ポートはXPSでは2のまま変更できません。
少なくとも、timer, uart, etherの3つは必要なので、mhsファイルを手修正しました。
固定されているということは2のままでも良いのかもしれません。

ここがクリアすると、おそらく root filesystemマウント不可能のpanicにに出会うと思います。
ここまで来たらカーネルはほぼOKです。ユーザランドとそれの適切なドライバの組み込みがあれば起動します。

Linux version 2.4.32-uc0 (atmark@atde) (gcc version 3.4.1 ( Xilinx EDK 9.1 Build EDK_J.19 121007 )) #24 2011年 12月 10日 土曜日 04:24:31 JST
On node 0 totalpages: 16384
zone(0): 16384 pages.
zone(1): 0 pages.
zone(2): 0 pages.
CPU: MICROBLAZE
Kernel command line:
Console: xmbserial on UARTLite
Calibrating delay loop... 33.07 BogoMIPS
Memory: 64MB = 64MB total
Memory: 61648KB available (968K code, 2204K data, 44K init)
Dentry cache hash table entries: 8192 (order: 4, 65536 bytes)
Inode cache hash table entries: 4096 (order: 3, 32768 bytes)
Mount cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer cache hash table entries: 4096 (order: 2, 16384 bytes)
Page-cache hash table entries: 16384 (order: 4, 65536 bytes)
POSIX conformance testing by UNIFIX
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Microblaze UARTlite serial driver version 1.00
ttyS0 at 0xffff2000 (irq = 1) is a Microblaze UARTlite
Starting kswapd
led(1.0.0): Sp6Lx9 LED Driver at 0xFFFFA200
dipsw(1.0.0): Sp6Lx9 DIPSW Driver at 0xFFFFA000
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
eth0: PHY detected at address 0.
eth0: using fifo mode.
eth0: Xilinx EMACLite #0 at 0xC0000000 mapped to 0xC0000000, irq=2
Avnet Spartan6 LX9 Microboard MTD mappings:
Flash 0x1000000 at 0xff000000
flash: id=0x20BA18
flash: Found an alies 0x1000000 for the chip at 0x0, Micron N25Q128 device detect.
Creating 8 MTD partitions on "flash":
0x00000000-0x01000000 : "Flash/All"
0x00000000-0x00100000 : "Flash/FPGA"
0x00100000-0x00120000 : "Flash/Bootloader"
0x007f0000-0x00800000 : "Flash/Config"
0x00120000-0x007f0000 : "Flash/Image"
0x00120000-0x00420000 : "Flash/Kernel"
0x00420000-0x007f0000 : "Flash/User"
0x00800000-0x01000000 : "Flash/Jff2"
FLASH partition type: spi
uclinux[mtd]: RAM probe address=0x80127e8c size=0x1f2000
uclinux[mtd]: root filesystem index=8
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 4096 bind 8192)
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
VFS: Mounted root (romfs filesystem) readonly.
Freeing init memory: 44K
Mounting proc:
Mounting var:
Populating /var:
Running local start scripts.
Mounting /etc/config:
Populating /etc/config:
Clock: old time 1970/01/01 - 00:00:01 GMT
Clock: new time 1970/01/01 - 02:47:28 GMT
flatfsd: Created 6 configuration files (632 bytes)
Setting hostname:
Setting up interface lo:
Starting inetd:
Starting thttpd:

Sp6Lx9Microbrd login:

■root/rootでログイン
広大な..というわけでなく箱庭のようなuCユーザランドが広がっています。
ここまでくるとlinuxとして動いているし、コマンドも打てるので満足感が出てきます。
夜な夜ないじっていたら、ここらで一杯いきたくなるでしょう。
残念ながらまだネットワークは動きません。

■ネットワークNGを探る
pingなどしても100% lossしますし、この状態で(ボードのbitコンフィグそのままに)linuxを ダウンロードしなおして実行すると ether driverで止まります。
転送完了ビットが落ちないためですが、理由はよくわかりません。おそらく、何らかの理由でPHY側からTX clockが供給されていないと推測してます。

ちょっと面白いのですが、cpuを止めても内部のペリフェラルは動いている(当たり前だが)ので、例えば シリアルのレジスタにデバッガで値を書き込むとちゃんとコンソールに文字が出てきます。
こんな ことからも簡単に周辺部品のデバッグが出来ます。

It works !



SDKでオンボードペリフェラルのテストを作った際のソース類を持ってきて、ちょっとmodしたところ動きました。
やはりPHYが絡んでいるようです。
突拍子なく止まったことがあるので、まだどこかおかしいかもしれません。
(2011.11.30)どうやら、割り込みハンドラの中でdev_kfree_skb()すると固まることがわかりました。dev_kfree_skv_irq()はおそらくIRQセーフなので、これを使えば良いのでしょうか、それに気づく前に割り込み外でdev_kfree_skb()するように変更しました。このほうがロック頻度が低くて高効率なはずです。本来、割り込みハンドラは少しでも早くリターンしたいはずなので。PC->このボードへの転送はめちゃくちゃ遅くて20kB/sくらい。逆は500kB/sくらいあります。なぜ?。ドライバ内でのむだ時間では無いようです。とりあえず安定して動いているので、速度の問題には追及しないことにします。
ifconfig eth0 した直後はパケット受信できないようです。ボード側からpingを打つなり、一度でも送信すると、以後は正常に動作します。理由は不明なのですが、ダミーの送信を入れさえすれば最初から受信することがわかりましたので、ドライバのopen時、割り込み有効にした後で0バイトの送信を入れています。これでifconfigした直後から受信に反応します。
(2012.04.19)受信が遅いのは、データ取りこぼし→再送が発生しているのが一因。つまり、回路規模縮小のためEthernetフレーム1個しかハード的に受けられないので、割り込みでフレームを拾ったりしている間にTCP window制御でどどーっと流れてきたフレームを取りこぼしてしまう。それで再送が頻繁に起きている。Ethernet liteの設定でPingPong=1にするとダブルフレームになって改善出来るのだろうけど、規模的に難しい。それだけでは説明がつかない遅さなんだけど。

■ブートローダ/SPIフラッシュ
これもSuzaku用のBBootをそのまま使わせてもらいました。SuzakuではBBoot->Hermit->linuxとブートローダを2個使って 起動しています。今回はオンボードJTAG接続でflashに書き込めることから、高機能ローダーのHermitは省き、BBootから直接linuxを起動しています。linuxが起動すると、割り込みベクタなどがBRAM領域にコピーされるため、何もケアしないとBBootが上書きされ壊れます。そうなるとUser resetで再起動できなくなるため、0x08〜0x7Fを空けています。このボードのFlashはQuad-SPI(正しい表現か不明)で従来よりバス幅を広げて高速化したものがついています。どうやらSpartanがコンフィグするときはこのモードのようです。一瞬でコンフィグされます。しかしながら、MicroblazeのSPI IPはこれに対応していないので、通常の2線式です。余ったピンはHレベルにする必要があります。MTD PartitionはSuzakuと同じにして、残り8Mbをjffs2用として残しましました。NORなので、こういう使い方をしていいのかわかりませんが。SPIのlinux側のドライバはSuzakuにパッチしてます。SPI FlashのデータシートはMicronとNDA契約しないと落とせないみたいです。チップID(0x20BA18)は現物あわせです。

■GPIOドライバ
Suzakuのものを使わせてもらいました。別物(Platform>GPIO Deiver)もありますが、read/writeではなく、iocontrolで使うようになっていま すし、よくわからない(misc何とかに包含されている)ので素直にできているSuzakuドライバを使いました。作動中にLEDをパカパカするアプリ(/bin/blink_led)を作ってユーザランドに入れています。

■その他
SPI Flashへの書き込みはIMPACTよりsfutil(Digilentより落っことしてくる)のほうが圧倒的に早いです。IMPACTだとだいたい45分くらいかかるのに対して、sfutilだと3分くらいです。IMPACTはmcsを作るだけにとどめて、書き込みはsfutilを使うべきでしょう。

BRAMへBBootを入れるのには、data2memを使います。これでBBoot入りのbitファイルを作り、linuxイメージとともにIMPACTでmcsファイルにします。
data2memはイセに入っています。Xilinx\13.2\ISEh_DS\ISE\bin\nt
data2mem -bm edkBmmFile_bd.bmm -bd Z:\BBoot\bboot.elf -bt mb_system_top.bit -o b output.bit

ボードの消費電流は実測150〜160mA(5V)でした。

以上でこのボードでのlinux搭載は完了です。
ちょっと作りたいものの開発はこれからやっていきます。

資材置き場

作成途中なので完全ではありません。
flash用mcsファイル(FPGAコンフィグ/BBoot/ucLinux 全部入り)
MHSファイル
uClinux-distパッチパッチのほとんどはXilinx SDKのサンプルから持ってきたxemacliteドライバの差分です。自分でいじっている部分は遙かに少ないです。
BBoot
・合成後のProject Status

[戻る]