LPC4370のRAM上実行について

LPC4370は、282kBのRAMを内蔵するかわりFlashを内蔵しておらず、RAM上で実行することを前提としている。204MHzという高速な動作クロックのため、内蔵Flash上の動作では読み出しアクセスがボトルネックになるためである。(回路の特性上、SRAMFlashより高速動作できる。)

しかし、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の仕様を考えれば、とうぜん上記のようにすべきである。しかし、サンプルコードも新規作成プロジェクトもそのようにはなっていない。

  • LPC Openのサンプルコードでは、コードはSPI Flashに書き込まれ、SPI Flash上で実行される。そのため、実行速度が非常に遅い。
  • LPC Xpressoで新規作成したプロジェクトでは、コードはデバッガからSRAMに直接ダウンロードされ、SRAM上で実行される。そのため実行速度は速いが、デバッガなしには実行できない。

解決法

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();
ブレークポイントについて

デバッガで実行する場合、ブレークポイントが最初だけうまく動作しない。ResetISR関数でmain関数を呼び出す行にブレークポイントを貼っておくとうまく動作する。原因は不明。

    __main();

ちなみに

コードのサイズが大きくて全てをSRAMにロードできない場合、高速実行したい関数のみをSRAMに配置することもできる。その方法は、下記を参照。

参考文献

  • CQ出版 インターフェース 2015年7月号 「初体験 オール・ソフトウェア無線」Appendix 7