adm5120のu-bootをビルドしてみた


10年以上前の有線ルーターで使われていたadm5120というMIPS系のSOCのu-bootをビルドしてみました。

adm5120は元々ADMtek(台湾)という会社が2003年くらいに開発した製品でしたが、ADMtekがInfineon(ドイツ)の会社に買収され、後半はInfineonの製品として販売されていました。MIPS 4KのSOCの先駆けでEthernet Switchも内蔵していました。クロックは175MHzとちょっと遅いです。

チップは下に324本の足が出てるタイプ(BGA)のADM5120と横に208本出ているタイプ(PQFP)のADM5120Pがあります。データーシートはネットで手に入ります。

有線ルーターはadm5120を使う前にはSAMSUNG S3C4510(ARM)やZyDASのZD2001(MIPS)やMICRELのKS8695X(ARM)を使った製品もありましたが、adm5120を使った製品が一番長く作られていたのではないかと思われます。2010年くらいからは蟹に変わったようです。今考えると有線系ルーターSOCの完成形だったようにも思えます。

10年以上前に佐藤益弘さんがコードを作られていました。

これは既存のブートに重ねる形で作られていました。

ほぼ同時期に佐藤さんのコードからスタンドアローン版をRobert Delienさんが作られていたようです。

スタンドアローン版をUrJTAGでFlashの先頭から書きたかったのですが、Robertさんのコードはpatchだけでオリジナルのソースがどれかわからなかったので、佐藤さんのページのアーカイブファイルとガッチャンコする事しました。

Robertさんのコードから利用した部分は二つです。

  • lowlevel_initというメモリの初期化をしているアセンブラ(board/bbr4/lowlevel_init.S)
  • PL010というシリアルのコード(drivers/serial_pl010.c)

佐藤さんのコードではlowlevel_initは空でした。これはオリジナルのブートローダーが処理しているからです。シリアルのコードは佐藤さんも書かれている(cpu/mips/adm5120_serial.c)のですが何故か動かず、既存のコードにパッチをあてたRobertさんのコードを使いました。

何かのtoolchainのmipselのgcc3なLinuxバイナリをLinuxエミュレーションのFreeBSDでビルドしました。このソースが作られた時期を考えるとgcc3が良いような気がしました。

U-Boot 1.1.4 (Jan 11 2019 - 19:53:20)

DRAM:   8 MB
Top of RAM usable for U-Boot at: 80800000
Reserving 183k for U-Boot at: 807d0000
Reserving 2624k for malloc() at: 80540000
Reserving 44 Bytes for Board Info at: 8053ffd4
Reserving 36 Bytes for Global Data at: 8053ffb0
Reserving 128k for boot params() at: 8051ffb0
Stack Pointer at: 8051ff98
Now running in RAM - U-Boot at: 807d0000
flash_protect ON: from 0xBFC00000 to 0xBFC235FF
protect on 0
protect on 1
protect on 2
protect on 3
protect on 4
protect on 5
protect on 6
protect on 7
protect on 8
protect on 9
flash_protect ON: from 0xBFC30000 to 0xBFC3FFFF
protect on 10
Flash:  2 MB
In:    serial
Out:   serial
Err:   serial
Net:   adm0
Hit any key to stop autoboot:  0 
BBR-4MG/HG #

ソース一式はここに置きました。

ビルドはこのスクリプトでやってます。

#!/bin/sh

export PATH="/foo/buildroot-gcc342/bin:${PATH}"

CROSS=mipsel-linux-

gmake distclean
gmake HOSTCC=cc CROSS_COMPILE=${CROSS} bbr4_config
gmake HOSTCC=cc CROSS_COMPILE=${CROSS}

v0.1のタグうってバイナリもアップしてあります。

SHA256(u-boot.bin)= 3bdd92c9ece8f0797b2b665a0dfa7a0ae8d8bb81f79c70bf3e4b8f7cf76bc599

バイナリが128Kをちょっと越えたくらいになっていてenvの保存領域が0x30000から0x3ffffになります。0x40000以降が自由に使える領域になります。ちょっと機能削って、128Kに収まると64K分余計に使えるのですが。

もちろんこれを焼くと、ルーターとしての機能はなくなりますので、注意してください。

UrJTAGでのオペレーションはこのようにしてます。

jtag> cable ft2232
Connected to libftd2xx driver.
jtag> detect
IR length: 5
Chain length: 1
Device Id: 00000000000000000000000000000001 (0x0000000000000001)
  Unknown manufacturer!
chain.c(149) Part 0 without active instruction
chain.c(200) Part 0 without active instruction
chain.c(149) Part 0 without active instruction
jtag> include admtek/adm5120/adm5120
ImpCode=01000001010000000100000000000000 41404000
EJTAG version: 2.6
EJTAG Implementation flags: R4k DINTsup ASID_8 NoDMA MIPS32
Processor entered Debug Mode.
jtag> detectflash 0x3fc00000
Query identification string:
        Primary Algorithm Command Set and Control Interface ID Code: 0x0003 (Int
el Standard Command Set)
        Alternate Algorithm Command Set and Control Interface ID Code: 0x0000 (n
ull)
Query system interface information:
        Vcc Logic Supply Minimum Write/Erase or Write voltage: 2700 mV
        Vcc Logic Supply Maximum Write/Erase or Write voltage: 3600 mV
        Vpp [Programming] Supply Minimum Write/Erase voltage: 11400 mV
        Vpp [Programming] Supply Maximum Write/Erase voltage: 12600 mV
        Typical timeout per single byte/word program: 32 us
        Typical timeout for maximum-size multi-byte program: 0 us
        Typical timeout per individual block erase: 1024 ms
        Typical timeout for full chip erase: 0 ms
        Maximum timeout for byte/word program: 512 us
        Maximum timeout for multi-byte program: 0 us
        Maximum timeout per individual block erase: 8192 ms
        Maximum timeout for chip erase: 0 ms
Device geometry definition:
        Device Size: 2097152 B (2048 KiB, 2 MiB)
        Flash Device Interface Code description: 0x0001 (x16)
        Maximum number of bytes in multi-byte program: 1
        Number of Erase Block Regions within device: 2
        Erase Block Region Information:
                Region 0:
                        Erase Block Size: 8192 B (8 KiB)
                        Number of Erase Blocks: 8
                Region 1:
                        Erase Block Size: 65536 B (64 KiB)
                        Number of Erase Blocks: 31
jtag> flashmem 0x3fc00000 u-boot.bin noverify
Manufacturer: Intel
Chip: Unknown (0x88C3)!
program:

block 0 unlocked
erasing block 0: 0
addr: 0x3FC01000

実はかなり以前に佐藤さんのu-bootを使ってFreeBSDを起動しててみたりしていたのですが、Flashの使える領域がオリジナルブートの制約があり窮屈だったので試しにu-bootスタンドアローン版をビルドしてUrJTAGで書き込んだところ、その後Flashが見えなくなってしまって、壊してしまったと思っていたのが、解決策のページがあったのがこれをやってみようと思ったきっかけでした。

ビルドは1分もかからないのですがUrJTAGで128Kちょっとを焼くのに30分くらいかかり安易にトライアンドエラーが出来ず根気のいる作業でした。

Etherが使えるブートが出来ればあとは気軽にトライアンドエラーできます。

FreeBSD 12Rの前にadm5120のコードは消されてしまいました。adm5120は8Mとかのターゲットがほとんどで、しかたがなかったのですが。

追記:このブートは割り込みを使わない実装になっています。そのため、アプリで割り込みを使うためにはCP0のStatusのEXLとERLを0にする必要がありました。

いろいろ調べていたらadm5120を使った製品でCFEをブートに使ったケースがありましたが、ソースは見つかりませんでした。

memory controller

SDRAMの接続はおそらくarmのPL172のIPが使われているのではないかと思われます。lowlevel_init.Sの中に初期化コードがあります。JTAGでレジスタをタンブしたところ、オリジナルと同じ設定になっています。パフォーマンスが出ないのは初期化パラメーターがチューニングされていない可能性があるのではないかと睨んでいます。

LEDデバッグ

uartが使える前に落ちていると、どこまで処理されているのか分からず困ります。そのような時はLEDデバッグが役に立ちます。下記のコードで赤いLEDが消灯します。電源投入時は0となっていて点灯しているので、処理が到達しているかの確認が出来ます。

*(volatile unsigned *)(0xb20000b8) = 0x08080000;

このSOCはGPIO専用のピンとEtherのLEDと兼用のピンがあります。LEDの回路は以下のようになってました。

A B LED
H L 左点灯
L L 右点灯
* H 消灯

UrJTAGを使ったデバッグ

adm5120の一つ目のuartは0x12600000にあります。これのUART Flag(0x18)を確認するのは以下のようになります。

jtag> peek 0x12600018
bus_read(0x12600018) = 0x97 (151)

1バイトアクセスが0x12600000で2バイトアクセスが0x32600000で4バイトアクセスが0x52600000になります。

uartの初期化が終わっていれば下記の方法でuartから1バイト送信ができます。

jtag> poke 0x12600000 0x55

佐藤さんのコードでは何故かこの方法で送信が出来ない状態でした。おそらくなんらかオリジナルのブートの初期化に依存しているのではないかと思われますが、よく分かりませんでした。

mruby動きました

BBR-4MG/HG # setenv serverip 10.10.10.3
BBR-4MG/HG # setenv ipaddr 10.10.10.102
BBR-4MG/HG # saveenv
BBR-4MG/HG # tftp 0x80300000 main.bin.bz2.uboot
Using adm0 device
TFTP from server 10.10.10.3; our IP address is 10.10.10.102
Filename 'main.bin.bz2.uboot'.
Load address: 0x80300000
Loading: #################
done
Bytes transferred = 84701 (14add hex)
BBR-4MG/HG # bootm
## Booting image at 80300000 ...
   Image Name:   mruby VM image
   Created:      2019-01-12   8:32:30 UTC
   Image Type:   MIPS Linux Kernel Image (bzip2 compressed)
   Data Size:    84637 Bytes = 82.7 kB
   Load Address: 80010000
   Entry Point:  80010000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Starting kernel ...

index:0
index:1
index:2
index:3
index:4
index:5
index:6
index:7
index:8
index:9

Flashに焼いてみました。

BBR-4MG/HG # tftp 0x80300000 main.bin.bz2.uboot
Using adm0 device
TFTP from server 10.10.10.3; our IP address is 10.10.10.102
Filename 'main.bin.bz2.uboot'.
Load address: 0x80300000
Loading: #################
done
Bytes transferred = 84701 (14add hex)
BBR-4MG/HG # protect off 0xbfc40000 0xbfe00000
............................
Un-Protected 28 sectors
BBR-4MG/HG # erase 0xbfc40000 0xbfe00000

............................ done
Erased 28 sectors
BBR-4MG/HG # cp.b 0x80300000 0xbfc40000 ${filesize}
Copy to Flash... flash_write_cfiword, cnt=542465, src=80300000
done
BBR-4MG/HG # 

起動後に実行されるbootcmdが元のままなので正しく設定します。

BBR-4MG/HG # setenv bootcmd bootm 0xbfc40000
BBR-4MG/HG # saveenv

RAM 8MでFlash 2MのターゲットはOSを動かすにはちょっときついのですが、mrubyには最適なので、格好の餌食です。:-)

いろいろな人の英知があってでできたことで、感謝しております。

admbm-mrubyの使い方

プリコンパイルしたVMをリリースタグに付けてあるので、以下の操作で試せます。

まずadmbm-mrubyのリリースタグの最新のmain.bin.bz2.ubootをダウンロードします。

mrubyのmrbcでスクリプトをコンパイルして出来たmrbを追加すれば出来上がりです。

% mrbc hello.rb
% cat main.bin.bz2.uboot hello.mrb > hello.img

hello.imgを上記の手順で焼けばOKです。

恒例のfib(32)を計ってみたのですが、447秒と極端に遅いです。メモリコントローラーの最適化などが必要なのかもしれません。

mrubyをupdateしたらfib(32)が114秒とおおよそ想定内のパフォーマンスが出ました。mruby以外はいじってないのですが、何が影響したのか謎です。

mrubyのコードも変わっているので単純な比較はできませんがgcc 4.2からgcc 4.9に変更してfib(32)を確認したら63秒でした。