アライメントの話
stellarisのようなARMのCPUでは、メモリアクセスの際のアドレス制約があるので、コンパイラは構造体をアライメントに従って配置してくれます。しかし、ローンチパッドのようにRAMが少なかったり、構造体をバッファからシリアライズ・デシリアライズしたい場合なんかは、アライメントが邪魔になるので、pragmaで目的の構造体のアライメントを調整したりします。stellarisの環境ではどうするのか調べました。
検索してみると、まずこのページが見つかります。
http://e2e.ti.com/support/microcontrollers/stellaris_arm/f/471/p/56616/201142.aspx
2010年の話ですが、gcc拡張の __attribute__ *1 は使えるけれども、グローバルにアライメント設定する機能は無いと頑なに言っています。CCSのコンパイラのプロパティをみると、ARM Compiler->Advanced Options->Runtime Model Optionsに --align_structs オプションが存在しますので、このオプションを1にしてコンパイルリンクしてみます。
#include <stdint.h> struct stTest { uint8_t a; uint16_t b; uint8_t c; uint32_t d; }; struct stTest stt; void main(void) { int i = sizeof(stt); stt.a = 1; stt.b = 2; stt.c = 3; stt.d = 4; }
0x2000114 stt 0x2000114 00020001 00000003 00000004
結果、stTestのサイズは12で、1バイトアライメントは効きませんでした。続いて、stTestに __attribute__ *2 を指定してみると、結果はstTestのサイズが8バイトになりました。こちらは効いてます。
0x2000114 stt 0x2000114 03000201 00000004
次に、__attribute__ *3 の書き方をいくつか試してみました。
#include <stdint.h> struct stA1 { uint8_t a; uint16_t b; uint8_t c; uint32_t d; }; struct stA2 { uint8_t a; uint16_t b; uint8_t c; uint32_t d; } __attribute__ ((packed)); struct stB1 { uint8_t e; uint8_t f[5]; uint32_t g; struct stA1 h; } __attribute__ ((packed)); struct stB2 { uint8_t e; uint8_t f[5]; uint32_t g; struct stA1 h; }; typedef struct { uint8_t e __attribute__ ((packed)); uint8_t f[5] __attribute__ ((packed)); uint32_t g __attribute__ ((packed)); struct stA1 h __attribute__ ((packed)); } stB3_t; struct stB4 { uint8_t e; uint8_t f[5]; uint32_t g; struct stA2 h; } __attribute__ ((packed)); struct stB1 stb1; struct __attribute__ ((packed)) stB2 stb21; struct stB2 stb22; stB3_t stb3; struct stB4 stb4; void main(void) { int i; i = sizeof(stb1); i = sizeof(stb21); i = sizeof(stb22); i = sizeof(stb3); i = sizeof(stb4); }
サイズは、stb1から順に、22、24、24、22、18となりました。__attribute__*4 は構造体の定義で記述しないと効かなかったり、構造体の構造体とかは、両方の構造体で__attribute__ *5しておかないと効かなかったりしますね。packedを使うのは、サイズがシビアな環境だと思うので、きちんと効果がでるように書きたいです。しかしながら、アライメントをまたぐと、メモリアクセス回数が単純に増えたり、キャッシュの読み直しが起きたりでパフォーマンスには不利らしいです。
ちなみに、stellarisのコンパイラTMS470のVer.5.1のマニュアルには#pragma PACK(n)についての記載もありました。pragma が使えると、ちまちま__attribute__ *6を書かなくてすみますね。