GCCで任意の関数(ramfunc)をRAM実行するには???

GCCでRAM実行

IARでは任意の関数をRAMから実行するのに、魔法のキーワードを付けて関数を定義するだけで、簡単にRAMに配置、フラッシュから自動的にコピーしてくれました。

では、MCUXpresso IDE、いわゆるGCC環境ではどのようにすれば良いのでしょうか?
今回は、そんなGCC環境で特定の関数をRAMから実行するには、どのようにするのか紹介します。

GCC環境で任意の関数をRAM実行させるのは、IAR社 EWARM(以下、EWARM)と同様に非常に簡単です。

基本的には、EWARMと同様にあるキーワードを付加するとその関数や変数がRAMに配置されます。

参考 IAR EWARMなら関数のRAM実行が超簡単!魔法の呪文1つだけ。

はい、では、行ってみましょう。

要点

  1. __attribute__キーワードが必要
  2. .dataセクションに配置する
  3. FlashROMの.dataセクションからRAMセクションにコピーするのは、自動的にやってくれる

__attribute__キーワード

__attribute__キーワードは、GCCのオンラインドキュメンテーションでは、以下のように説明されています。

In GNU C, you can use function attributes to declare certain things about functions called in your program which help the compiler optimize calls and check your code more carefully. Using the GNU Compiler Collection (GCC)
You can also use attributes to control memory placement, code generation options or call/return conventions within the function being annotated. Many of these attributes are target-specific.Using the GNU Compiler Collection (GCC)

特に、関数の属性を宣言するために使用されるもので、コンパイラが関数の呼び出しを最適化したり、コードを慎重にチェックするのに役立つとあります。

また、コードの配置、コード生成のオプション、更にはアノテーション付きの関数でのコール・戻り値を制御できるとあります。アノテーションって、C言語では使わないですよね。

例えば、割り込みハンドラの関数だと明示して上げると、コンパイラが割り込みハンドラに最適な関数のエントリやエグジットを生成してくれるそうです。

この__attribute__キーワードは、変数や構造体などにも使用でき、関数や変数の配置も制御できる訳です。

属性

longcall:関数アトリビュートとしての属性で、指定した関数が実行場所が異なることを明示します。

section :特定のセクションに変数や関数などを配置します。

これら、属性を指定することで、指定した関数を指定したセクションに配置できます。

変数をメモリ配置する場合には、longcallは必要ないです。

記述例:__attribute__ ((longcall, section(“メモリセクション”)))

__attribute__属性指定には、(( ))括弧をダブルで囲む必要があります。

リンカースクリプト

変数や関数の配置場所は、リンカースクリプト内に記述されているRAM領域を指定ます。

では、MCUXpresso SDKのリンカースクリプトを見て行きましょう。


.data_RAM2 : ALIGN(4)
    {
        FILL(0xff)
        PROVIDE(__start_data_RAM2 = .) ;
        *(.ramfunc.$RAM2)
        *(.ramfunc.$SRAM_LOWER)
        *(.data.$RAM2*)
        *(.data.$SRAM_LOWER*)
        . = ALIGN(4) ;
        PROVIDE(__end_data_RAM2 = .) ;
     } > SRAM_LOWER AT>PROGRAM_FLASH

    /* DATA section for FLEX_RAM */
    .data_RAM3 : ALIGN(4)
    {
        FILL(0xff)
        PROVIDE(__start_data_RAM3 = .) ;
        *(.ramfunc.$RAM3)
        *(.ramfunc.$FLEX_RAM)
        *(.data.$RAM3*)
        *(.data.$FLEX_RAM*)
        . = ALIGN(4) ;
        PROVIDE(__end_data_RAM3 = .) ;
     } > FLEX_RAM AT>PROGRAM_FLASH

.data : ALIGN(4)
    {
       FILL(0xff)
       _data = . ;
       *(vtable)
       *(.ramfunc*)
       *(.data*)
       . = ALIGN(4) ;
       _edata = . ;
    } > SRAM_UPPER AT>PROGRAM_FLASH

MCUXpresso SDKでは、デフォルトで上記data、data_RAM2、data_RAM3セクションに指定された変数や関数をRAMに配置することが可能です。

変数とコードの配置セクション

このリンカースクリプトでは、全てのオブジェクトファイルに対して、.ramfanc.$RAM2と.ramfanc.$RAM3のセクションに指定されている変数や関数を、それぞれdata_RAM2とdata_RAM3セクションに配置すると記述されています。

.dataセクションは、全てのオブジェクトファイルに対して、.ramfanc*とあるので、上記以外の.ramfancが付くセクションは、全て.dataセクションに配置されるようになっています。

実際に格納される場所と実行される場所

>RAM領域 AT>PROGRAM_FLASH

そして、実際に格納されるのが、PROGRAM_FLASHになり、実行される場所がRAM領域になります。

MCUXPresso SDKでは、実行される場所の”RAM領域”として、KinetisのSRAMには上位アドレスバンク(SRAM_UPPER)と下位アドレスバンク(SRAM_LOWER)があります。また、 フレックスメモリ対応製品ではFLEX_RAM領域があります。

もし、SRAM_LOWERのRAM領域に配置させたい場合には、__attribute__属性セクション指定で、.ramfanc.$SRAM_LOWER(.ramfanc.$RAM2も可)とセクション指定すると、.data_RAM2セクション(下位アドレスRAM)に配置されるようになります。

特に、気にする必要がなければ、.ramfancセクションに指定すると、.dataセクション(SRAM_UPPER領域)に配置されます。

FlashROMからRAMへのコピーは自動でやってくれて楽チン

リンカースクリプトでRAMに配置しても、実際にコードが格納されるのはフラッシュメモリ(PROGRAM_FLASH)です。コード実行される時は、指定したRAM領域から実行されます。

ですが、RAMは電源が落ちるとコードは消えて無くなります。

そのため起動時にFLASHからRAMに配置指定した変数や関数をコピーする必要があります。

MCUXpresso SDKでは、スタートアップコード内でこれらRAM領域の変数や関数を自動でコピーしてくれるので、__attribute__属性でセクション指定(.data、.data_RAM2、.data_RAM3)すると、RAM内のこれらセクションを自動的にコピーしてくれるので楽チンです。

実験

テストコード


volatile uint32_t cnt;
__attribute__ ((longcall, section(".data.$SRAM_LOWER"))) void test(){
	cnt++;
}

int main(void)
{
    char ch;

    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    PRINTF("hello world.\r\n");

    while (1)
    {
    	test();
        ch = GETCHAR();
        PUTCHAR(ch);
    }
}

コンパイル結果(mapファイル)



.data_RAM2      0x000000001fff0000       0x1c load address 0x0000000000003600
 FILL mask 0xff
                [!provide]                        PROVIDE (__start_data_RAM2, .)
 *(.ramfunc.$RAM2)
 *(.ramfunc.$SRAM_LOWER)
 *(.data.$RAM2*)
 *(.data.$SRAM_LOWER*)
 .data.$SRAM_LOWER
                0x000000001fff0000       0x1c ./source/hello_world.o
                0x000000001fff0000                test
                0x000000001fff001c                . = ALIGN (0x4)
                [!provide]                        PROVIDE (__end_data_RAM2, .)

.rel.dyn        0x000000001fff001c        0x0 load address 0x000000000000361c
.rel.iplt      0x000000001fff001c        0x0 ./utilities/fsl_assert.o

ちゃんとSRAMの下位アドレスバンク0x1FFF0000(SRAM_LOWER)に配置されていますね。

まとめ

IAR社のEWARMでは、__ramfuncキーワードを付けるだけでしたが、GCC環境でもこれと同等のキーワードとして、__attribute__キーワードがあります。
この属性でRAM領域を示すセクションに指定してあげることで、自動的に変数や関数をRAMに配置させ、関数ならRAM実行が可能になります。

是非、RAMを有効活用する際に、参考にしてください。