LPC1114でSRAM上の命令を実行する

.09 2012 ARM-setup comment(0) trackback(0)
今年も不思議な植物(リトープス)が開花しましたー!
今年の春の脱皮で2つから4つに増えたので花も豪華。手間いらずで花も綺麗なのでとても気に入っています。

今回の記事もARMいじりの続き。
SRAM上の命令を実行できるといろいろ面白いことができそうだなーとなんとなく考えていた。
例えばUARTでバイナリ送ってそのまま実行させるとかすればいちいちフラッシュを書き換えずにいろいろ試せる。
そんなことしなくてもフラッシュは10万回くらい書き換えられるらしいのでフラッシュの寿命とかはあまり気にすることはないのかもしれない。

でもARM内蔵のフラッシュにOS書いておいて、SDカードからプログラムを読みだして実行するとかできたら面白いかも。

# AVRは命令メモリ空間(Flash)とデータメモリ空間(SRAM)が完全に分離されているのでこういうことはできないはず。



最終的にやりたいのはUARTでSRAM上にバイナリを流しこんで実行すること。
とりあえず今回は「SRAM上で実行」ってのが可能なのか簡単に調べるところまでやることにした。

実行の流れはこんな感じ。
  1. 電源投入後前述の初期化ルーチンが走る。
    具体的には
    1. SP(Stack pointer)の初期化(これはコードを書かなくても勝手にやってくれる)
    2. クロックの初期化
    3. SRAMの初期化
    くらいやればよかったはず。
  2. 初期化後、SRAM上に命令をコピー
    絶対アドレス指定のジャンプが含まれていなければそのままコピーして大丈夫。
    大抵のジャンプ命令は相対ジャンプなので今回は気にしないことにする。
  3. コピーした領域にジャンプ
    具体的にはコピー先のアドレスを覚えておいて、関数の間接呼び出しの要領でジャンプすればよさそう。
「main関数をSRAM上にコピーしてそこで実行する」ってのを簡単に書くとこんな感じ。
int init() {
	long *sram_main = (long *)(コピー先のSRAMのアドレス);
	int (*f)(void);
	// コピー
	for (i = (mainの先頭アドレス); i < (mainの末尾アドレス); i++)
		*(sram_main + i) = *i;
	f = sram_main;
	f();
}

mainの先頭アドレスと末尾アドレスはリンカスクリプトにちょっと手を加えれば簡単に取得できる。

普通にコンパイルするとmain関数は使われていないので綺麗サッパリ消えてしまう。
それを防ぐコンパイルオプションも存在するようだが(--no_remove)うまくいかなかった。
さらにattributeでusedを指定する方法でも勝手に消されてしまう。
仕方ないのでここではfのあとにmainを呼ぶように書いた。

(コンパイル時の解析ではfの中身がわからないのでmainは消されずに残るが、
実際にはmain関数の中身は無限ループなのでfで呼び出したあとは戻ってこないのでフラッシュ上のmainは実行されない。)

実行結果
フラッシュ上でそのまま実行するよりも点滅の周期が少し短くなった。
劇的に速くなったわけではないところから察するに前方に分岐するときの(分岐ミスの)コストが下がったということだろう。
# もしフラッシュの「3clk」ってのが非パイプラインで3clkだとしたらSRAM上にコピーして実行すれば3倍速になるはず。

フラッシュ(@50MHz)ではフェッチがパイプライン3段なので、分岐ミスが生じるとその3clk後にようやく有効な命令が到着する。
SRAMではそのコストが小さい分だけ速いということだろうと推測している。

次はUARTを使ってみる。
関連記事

  • comment
  • secret
  • 管理者にだけ表示を許可する

trackbackURL:http://yuranos.blog11.fc2.com/tb.php/226-54cdb6c0