AVR Libc Home Page AVRs AVR Libc Development Pages
Main Page User Manual FAQ Library Reference Additional Documentation Example Projects

メモリセクション

Original page is here.

覚え書き:
avrで利用可能なすべてのセクションをリストする必要があります。
Weak Bindings
FIXME: .weak 指令について議論する必要あり

以下は利用できるいろいろな "Section" について解説しています。

The .text Section

.text セクションはプログラムを構成する実際の機械語命令を含んでいます。このセクションは .initN と .finiN のセクションに分けられています。

Note:
UNIX風土から来たavr-size プログラム (binutilsの一部)は、.textに追加される.dat4a 初期化領域について考慮していません。そのため、最終的にフラッシュメモリの占有す量を知るには、.text と .data(.bssは不要) 両方を加え合わせなければなりません。また、あらかじめ確保されたSRAMは.data と.bssの合計になります。

The .data Section

このセクションはあなたの書いたコードで宣言された静的データ(※初期値が指定されたglobal変数・static宣言変数)を表します。下記のようなものは最終的に .data 内に納められます。

char err_str[] = "Your program has died a horrible death!";

struct point pt = { 1, 1 };

リンカに.data セクション開始SRAMアドレスを指示することが可能です。
これはavr-gccのリンクコマンドのオプションに-Wl,-Tdata,addr を加えることで可能です。
addr は0x800000を実際のSRAMアドレスに加える必要があります。これによってリンカはこれがSRAM内のメモリスペースであることを知ることができます。.data セクションが0x1100から始まるよう指定したければ、0x801100をリンカへのアドレス値として渡してやります。(offsetについてはここで説明しています )
※アドレスは23bitで、最上位bitはバンク番号とでも考えればいい?

Note:malloc() をアプリケーション内で使用している場合は(ライブラリ内で使用されている場合も含む)、さらに追加の調整 が必要です。

The .bss Section

初期化されていない、global またはstatic 変数は、.bss セクションに納められます。

The .eeprom Section

ここはeeprom内変数が保存される場所です。

The .noinit Section

このセクションは .bss セクションの一部です。下記のように定義される変数が .noinit に納められます。

 int foo __attribute__ ((section (".noinit")));

この定義による変数は、通常の .bss データと異なり、スタートアップ時 0 で初期化されません。
初期化されない変数だけが .noinit セクションに置かれます。下記のようなコードはエラーをおこします。

 int bar __attribute__ ((section (".noinit"))) = 0xaa;

リンカに明示的に .noinit セクションが置かれるかをavr-gccコマンドラインのリンクコマンドのオプションに、 -Wl,--section-start=.noinit=addr を加えることで指定できます。以下の例では、.noinit セクションを SRAMの0x2000に置くことを指定しています。

        $ avr-gcc ... -Wl,--section-start=.noinit=0x802000 ...

Note: AVRデバイスはハーバードアーキテクチャを採用しているので、リンカにセクションの開始アドレスを伝えるには 0x800000のオフセットを加えなければなりません。さもないとリンカはそのアドレスを .data/.bssではなく .text セクションのものと考え、「.textセクションに .noinit を置こうとしている」としてエラーを吐きます。

代わりに、この辺を自動化するために、あなた自身のリンカスクリプトを書くこともできます。 [FIXME: リンカスクリプトを書くための例題かリファレンスを募集中.]

The .initN Sections

このセクションはリセットから main() が始まるまでの間のスタートアップコードを表します。これらは.text sectionの一部です。

これらのセクションの目的はプログラム内でのコードの置き場所をより特定できるようにするためです。

Note: .initN と.finiN を関数として考えると時にわかりやすいです。しかし現実には、これらはリンカに対し、どこにそのコード塊を差し込むかを知らせるシンボリック名であるにすぎず、これは関数とは言えません。asmC で解説している.initN section コードは関数としてコールしたり、そこにジャンプしたりすることはできません。

.initN セクションは0〜9の順番で実行されます。

.init0:
弱く __init() と結びつけられている。ユーザーが __init() を定義すれば、リセット後すぐに実行されます。
.init1:
未使用。ユーザー定義可能。
.init2:
Cプログラムでは、スタック初期化に弱く結合されています。 __zero_reg__ (r1)をクリアしています。
.init3:
未使用。ユーザー定義可能。
.init4:
ROMが64KBytesを越えるデバイスでは、.init4.data の内容をフラッシュからSRAMへコピーするコードを納めています。
その他のデバイスにおいては、 .bss セクションをゼロクリアするコードを libgcc,a からロードします。
.init5:
未使用。ユーザー定義可能。
.init6:
Cプログラムでは未使用だが、C++プログラムのコンストラクタで使用される。
.init7:
未使用。ユーザー定義可能。
.init8:
未使用。ユーザー定義可能。
.init9:
main()へのジャンプ

The .finiN Sections

これらのセクションは main() からの return、または exit() 関数コール後に実行されるものです。これらすべては .text sectionの一部です。

.finiN セクションは9番から0版にかけて実行されます。

.finit9:
未使用。ユーザー定義可能。これは_exit() が始まるとすぐ実行されます。
.fini8:
未使用。ユーザー定義可能。
.fini7:
未使用。ユーザー定義可能。
.fini6:
Cプログラムでは未使用だが、C++プログラムのデストラクタで使用される。
.fini5:
未使用。ユーザー定義可能。
.fini4:
未使用。ユーザー定義可能。
.fini3:
未使用。ユーザー定義可能。
.fini2:
未使用。ユーザー定義可能。
.fini1:
未使用。ユーザー定義可能。
.fini0:
プログラム終了や_exit()コード(.fini9->.fini1セクション)完了後に無限ループに入る。

アセンブラコードでセクションを扱う

Example:

#include <avr/io.h>

        .section .init1,"ax",@progbits
        ldi       r0, 0xff
        out       _SFR_IO_ADDR(PORTB), r0
        out       _SFR_IO_ADDR(DDRB), r0
Note:
,"ax",@progbits はアセンブラにセクションが allocatable("a")で、実行可能("x")で、データを含む("@progbits")であることを伝えます。
.section使用法の詳しい情報については gas user manualをご覧ください。

Cコードでセクションを扱う

Example:

#include <avr/io.h>

void my_init_portb (void) __attribute__ ((naked)) \
    __attribute__ ((section (".init3")));

void
my_init_portb (void)
{
        PORTB = 0xff;
        DDRB = 0xff;
}
Note:
セクション .init3 はこの例では__zero_reg__ はすでに (.init2で) 初期化済みだと保証されます。コンパイラで生成されるコードは __zero_reg__ が 0 であると盲目的に依存してもかまいません。
※このコードで__zero_reg__ を使う理由がよく分からない・・・以下のようなコードになるし。
0000001e <my_init_portb>:
  1e:   8f ef           ldi     r24, 0xFF       ; 255
  20:   88 bb           out     0x18, r24       ; 24
  22:   87 bb           out     0x17, r24       ; 23
※例題は、PORTB=0x00; DDRB=0x00;の間違いかと思われます。こうすると以下のように __zero_reg__ を使ってくれます。
0000001e <my_init_portb>:
  1e:   18 ba           out     0x18, r1        ; 24
  20:   17 ba           out     0x17, r1        ; 23