LPC4370は、282kBのRAMを内蔵するかわりFlashを内蔵しておらず、RAM上で実行することを前提としている。204MHzという高速な動作クロックのため、内蔵Flash上の動作では読み出しアクセスがボトルネックになるためである。(回路の特性上、SRAMはFlashより高速動作できる。)
しかし、LPCXpresso + LPC Open の開発環境は、どうもLPC4370のRAM上実行に関してサポートがおざなりな感じである。サンプルコードや新規作成プロジェクトをどう修正すべきか、以下にまとめる。
準備
- 開発環境:LPCXpresso v8.1.4_606
- ライブラリ:LPCOpen Software for LPC43XX v3.01
- デバッガアダプタ:LPC-Link2 (1枚目)
- ターゲットボード:LPC-Link2 (2枚目)
LPC-Link2を2個用意し、片方をデバッガアダプタ、もう片方をLPC4370のターゲットボードとする。これはジャンパで設定できる。デバッガアダプタのJ7とターゲットボードのJ2をケーブルで接続する。
ジャンパ | デバッガ | ターゲット |
---|---|---|
JP1 | 開放 (デバッガとして使用) | 短絡 (ターゲットとして使用) |
JP2 | 短絡 (ターゲットへの給電) | 開放 (給電しない) |
やりたいこと
- コードは外部SPI Flashに保存する
- スタートアップルーチン(リセットハンドラ)はSPI Flash上で動作 ※
- スタートアップルーチン内で、コードをSPI FlashからSRAMにロード
- main関数以降のコードはSRAM上で動作
※ LPC4370はSPI Flashがメモリマップ上にマッピングされており、SPI Flash上のコードを実行できる。ただし、当然ながら実行速度は桁違いに遅い。
ところが…
LPC4370の仕様を考えれば、とうぜん上記のようにすべきである。しかし、サンプルコードも新規作成プロジェクトもそのようにはなっていない。
解決法
Flashドライバやメモリマップの設定 (プロジェクト新規作成の場合)
サンプルコードと同様の設定をする。
- 「Default flash driver」に LPC18_43_SPIFI_16MB_64kB.cfx を指定する。
- プロジェクトのProperty > C/C++ Build > MCU settings > Memory details(LPC4370) のメモリマップにFlashの領域を追加する。
Type | Name | Alias | Location | Size |
---|---|---|---|---|
Flash | SPIFI | Flash | 0x14000000 | 0x400000 |
リンカスクリプトの修正
デフォルトではリンカスクリプトは自動生成される。プロジェクトのProperty > C/C++ Build > Settings > MCU Linker > Managed Linker Script で、Manage linker scriptのチェックを外し、Linker scriptでリンカスクリプトを指定する。自動生成のリンカスクリプトをベースに下記のように修正する。
SPI FlashからSRAMにコピーするコードのアドレスとサイズをSPI Flashに保持する指定。
/* MAIN TEXT SECTION */ .text : ALIGN(4) { FILL(0xff) __vectors_start__ = ABSOLUTE(.) ; KEEP(*(.isr_vector)) /* Global Section Table */ . = ALIGN(4) ; __section_table_start = .; __data_section_table = .; LONG(LOADADDR(.text_RAM)); /* ←追加 */ LONG( ADDR(.text_RAM)); /* ←追加 */ LONG( SIZEOF(.text_RAM)); /* ←追加 */
実行アドレス(VMA)と格納アドレス(LMA)が異なるセクションの指定
.text_RAM : ALIGN(4) { *(.text*) *(.rodata .rodata.* .constdata .constdata.*) . = ALIGN(4); } > RamLoc128 AT>SPIFI /* ← VMAはSRAM、LMAはSPI Flash */
スタートアップルーチンのセクション指定
cr_startup_lpc43xx.c の ResetISR()関数がスタートアップルーチンである。この関数定義の前にセクション指定の記述をする。
__attribute__ ((section(".after_vectors"))) void ResetISR(void) {
スタートアップルーチンの修正
スタートアップルーチン内の下記の処理で、FlashからSRAMにコードがコピーされる。
// Copy the data sections from flash to SRAM. while (SectionTableAddr < &__data_section_table_end) { LoadAddr = *SectionTableAddr++; ExeAddr = *SectionTableAddr++; SectionLen = *SectionTableAddr++; data_init(LoadAddr, ExeAddr, SectionLen); }
したがって、これより前の段階では、Flash上のコードしか実行できない。SystemInit関数はSRAM上に配置されているので、処理の順番を変えて、main関数呼び出しの直前に持ってくる。
SystemInit(); __main();
参考文献
- CQ出版 インターフェース 2015年7月号 「初体験 オール・ソフトウェア無線」Appendix 7