EWARM で開発する Cortex-M プログラミングガイド April 2015 IAR Systems K.K. FAE Team
本ドキュメントについて 目的 Cortex-M プロセッサファミリの特徴を理解し ソフトウェア開発をする上での注意事項を学ぶ ARM 用統合開発環境 ARM 用 IAR Embedded Workbench(EWARM) を用いて 実際の開発を理解する 内容 ARM Cortex-Mの概要 Cortex-Mの命令セット Cortex-Mのレジスタ Cortex-Mの割込みハンドリング スタートアップ処理 リンカ設定でメモリ配置コントロール Cortex-Mへの移行時の注意点 解析ツールを使用して 品質向上 本ドキュメントは 2015 年 4 月現在のIARシステムズWebサイト ST 社 Webサイト およびEWARMバージョン7.40.2を元に作成しています 2
IAR SYSTEMS A LEADING GLOBAL VENDOR 168 Employees with HQ in Uppsala, Sweden Listed in Stockholm/Nasdaq R&D investment 32% of revenue 32 years in the industry 24 hour technical support in 13 languages Uppsala Munich Sao Paulo Tokyo Seoul Shanghai London Paris San Francisco Dallas Boston Los Angeles +Distributor representation in 43 countries Stability and growth Licenses # (000 s) 20 15 10 5 2010-2013 Operating Margin % 20 15 10 5 0 2010 2011 2012 2013 License # Operating Margin 0
ARM Cortex-M の概要
Cortex-Mベースのマイコンとは 英ARM Limitedの設計 開発するARM プロセッサを採用したマイコン ARMはCPUコア(など)のIP(設計図)を販売 ARM チップベンダが周辺機能(ペリフェラル)や メモリを追加実装 周辺機能 周辺機能 周辺機能 周辺機能 メモリ メモリ メモリ メモリ ARM コア ARM コア ARM コア ARM コア A社 B社 C社 D社 5
クラシック ARM と ARM Cortex プロセッサファミリ ARM マイコンの進化の過程 Cortex-Ax Application Processors ARM11 Cortex-Rx Real Time Processors ARM7 ARM9 Cortex-Mx Microcontrollers Cortex- M0 V4 V5 V6 V7 基本的に x が大きいほど処理能力大 クラシック ARM ARM Cortex 6
クラシックARMとARM Cortex-M ARM7(v4)とCortex-M3/4(v7-M)の主要な違い ARM7 Cortex-M3/M4 アーキテクチャ ARM v4 ARM v7-m 命令セット ARM / Thumb Thumb2 DMIPS / MHz 0.93 1.25 割込みコントローラ 外部 ネスト型ベクタ割込みコントローラ(NVIC) ベクタテーブル方式 命令方式 アドレス方式 アセンブラ要不要 要 不要 タイマー 外付け Systickタイマー内蔵 モード User FIQ IRQ SuperViser Monitor Abort Undef System User(Thread) System (Handler) メモリマップ 未規定 定義済み Flash 基本外付け 基本内蔵 割り切り 簡単 高性能 7
STM32シリーズラインナップ 各種コアとシリーズの対応 M3 M4 M0 M3 M4 M0+ M3 M4 http://www.st.com/web/en/catalog/mmc /FM141/SC1169 M7 2015年4月1日 ST社Webサイトより抜粋 8
Cortex-M のラインナップ比較 システム機能概要 M0 M0+ M3/M4 M7 スリープモード Yes Yes Yes Yes WIC Yes Yes Yes Yes SVC PendSV Yes Yes Yes Yes MPU( オプション ) - 0 or 8 0 or 8 0 or 8 or 16 フォールト / 例外ハンドラ HardFault HardFault HardFault +3 設定 HardFault +3 設定 フォールトステータスレジスタ - - Yes Yes ビットバンド - - オプションオプション L1 キャッシュ - - - 最大 64KB(I&D) TCM( 密結合メモリ ) - - - 最大 1MB(I&D) http://www.arm.com/ja/products/processors/cortex-m/cortex-m0.php http://www.arm.com/ja/products/processors/cortex-m/cortex-m0plus.php http://www.arm.com/ja/products/processors/cortex-m/cortex-m3.php http://www.arm.com/ja/products/processors/cortex-m/cortex-m4-processor.php http://www.arm.com/ja/products/processors/cortex-m/cortex-m7-processor.php 2015 年 4 月 1 日 ARM 社 Web サイトより抜粋 9
Cortex-M のラインナップ比較 最大パフォーマンス比較 1MHz あたりの処理量 (CoreMark 値 ) http://www.eembc.org/coremark/index.php 2015 年 4 月 1 日 EEMBC Web サイトより抜粋 10
Cortex-M の命令セット
命令セット:Cortex-Mの命令セットThumb-2 Cortex-MではThumb-2命令をサポート ARM命令セット Thumb命令セット Thumb-2命令セット 16ビット 32ビット命令混在 32ビット命令 16ビット命令 ARM7からの伝統的な命令セット よく使う命令は16 bit 複雑な命令は32bitで高効率を実現 Cortex-Mのラインナップごとにサポート命令セットは異なる 12
命令セット:Cortex-Mの命令セットThumb-2 同じコードをコンパイルして結果を比較 int gcd(int a, int b) { while (a!= b) { if (a > b) a = a - b; else b = b - a; } return a; } 最大公約数を求める関数 ARM Thumb Thumb2でビルド 13
命令セット:Cortex-Mの命令セットThumb-2 同じコード(最大公約数を求める関数)をコンパイルして結果を比較 Thumb命令セット ARM命令セット gcd: gcd:??gcd_0: 0x148: 0x14c: 0x150: 0x154: 0xe1500001 0x0a000003 0xe1510000 0xa0411000 CMP BEQ CMP SUBGE R0, R1??gcd_1 R1, R0 R1, R1, 0x158: 0xb0400001 SUBLT R0, R0, 0x15c: 0xeafffff9 B gcd 0x160: 0xe12fff1e BX LR R1 Thumb-2命令セット 0x4288 0xd004 0x4281 0xbfac 0x1a09 CMP BEQ.N CMP ITE SUBGE R0, R1??gcd_1 R1, R0 GE R1, R1, 0x92: 0x1a40 SUBLT R0, R0, 0x94: 0xe7f8 B.N??gcd_0 0x96: 0x4770 BX LR??gcd_0 0x14a: 0x1a40 SUBS R0, R0, 0x14c: 0x14e: 0x150: 0x152: 0x154: CMP BEQ.N CMP BLT.N SUBS R0, R1??gcd_2 R1, R0??gcd_1 R1, R1, 0x156: 0xe7f9 B.N??gcd_0 0x158: 0x4770 0x15a: 0x0000 BX MOVS LR R0, R0 0x4288 0xd003 0x4281 0xdbfa 0x1a09 R0??gcd_2: gcd:??gcd_0: 0x88: 0x8a: 0x8c: 0x8e: 0x90: B.N R1??gcd_0: R0??gcd_1: 0x148: 0xe000??gcd_1: ARM gcd(バイト) Thumb 28 Thumb-2 20 16 R0 R1 コード効率が高い??gcd_1: 14
Cortex-Mプロセッサファミリごとのサポート命令 Cortex-Mに続く数字が大きくなるほど 多くの命令をサポート 浮動小数点演算 DSP(SIMD,高速加算乗算) アドバンストデータ処理 ビットフィールド処理 基本データ処理 I/Oハンドリング M0/M0+ M3 M4 M7 M4 FPU M7 FPU 15
Cortex-M プロセッサファミリの命令比較 命令セット概要 M0/M0+ M3 M4 M7 シングルサイクル乗算 Yes Yes Yes Yes ハードウェア除算 - Yes Yes Yes アドバンストメモリアクセス - Yes Yes Yes アドバンスト分岐サポート - Yes Yes Yes 排他アクセス - Yes Yes Yes SIMD DSP - - Yes Yes サチュレーションサポート - 一部 フル フル 浮動小数点演算 ( オプション ) - - 単精度 単精度 / 倍精度 16
Cortex-Mプロセッサファミリの命令比較 浮動小数点除算の例 元のCコード M4F 8命令 M3 44命令 M0 171命令 コンパイラが自動的にコア搭載する命令を有効活用 17
Cortex-M のレジスタ
Cortex-Mのレジスタ構成 CPUレジスタ MSP PSP R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13(SP) R14(LR) R15(PC) xpsr 汎用的に使えるレジスタ R0 R12 さらに R0 R7までを下位レジスタ R8 R12までを上位レジスタと呼ぶ R0 R7までが16ビット命令で 自由に使えるレジスタ R13はスタックポインタとして使用 2つあり 切り替えて使用することができる R14は関数の返りアドレスを保存するリンクレジスタとして使用 R15はプログラムの実行アドレスを示すPCに使用 xpsrは実行状態を示す 演算結果フラグ 割り込み番号 使用す る命令セット Thumb系か否か) NMIとフォルトハンドラ以外の割り込みの許可 禁止を制御する PRIMASK CONTROL FAULTMASK BASEPRI M3/M4/M7のみ 使用するスタックの指定 特権/ユーザ レベル NMI以外の割り込みの許可 禁止を制御する 指定優先度以下の割り込みの許可 禁止を制御する 19
Cortex-Mのレジスタ構成 ステータスレジスタ 31 30 29 28 27 26 25 24 23 xpsr N Z C V Q APSR N Z C V Q ICI/IT T 16 15 10 9 8 7 ICI/IT IPSR EPSR 0 Esception Number Exception Number ICI/IT T ICI/IT アプリケーションプログラムステータスレジスタ APSR 演算結果のフラグ 割り込みプログラムステータスレジスタ IPSR 割り込み番号 実行プログラムステータスレジスタEPSR 実行状態bit 各レジスタは個別にも まとめても(xPSR)アクセス可能 20
Cortex-M のレジスタ構成 コントロールレジスタ 31 2 1 0 CONTROL Reserved bit [0] でスレッドモード時の特権を設定 0: スレッドモードは特権あり 1: スレッドモードは特権なし. bit [1] はスタックを選択 0: メインスタックポインタを使用 1: スレッドモードのときは プロセススタックポインタを使用 MRS 命令で READ MSR 命令で WRITE 特権がないとアクセスできない 21
Cortex-Mの関数コールとレジスタ 関数のジャンプ時には レジスタ操作が行われる 引数は R0,R1,R2,R3を利用 5つ目からスタックに 返り値はR0を利用 ジャンプするときには BL命令 戻る時はいろいろPC(R15)を操作する命令 たとえば BX命令 POP命令 R0 R1 R2 R3 R4 R5 int f1(int a ) { return a+1; } int main (void) { t = f1(10); Cコード f1: ADDS #1 BX R6 R0, R0, LR R7 R8 R9 main: BL R10 R11 f1 コンパイル結果 R12 R13 (SP) R14 (LR) R15 (PC) CPSR 22
Cortex-Mの関数コールとレジスタ 実際に動作を見てみる PC=0x200_01FA0 LR= 0x200_01F91 戻りアドレス=0x20001FA4 BLでジャンプ BL命令は LR(R14)レジスタに戻りアドレスを 保存し PCを関数にセットする PC=0x20001F98 LR= 0x20001FA5 23
Cortex-Mの関数コールとレジスタ 実際に動きを見てみる:関数からの戻り PC=0x20001F9A LR= 0x20001FA5 BXでジャンプ BX命令は LR(R14)レジスタで指 定されるアドレスにジャンプ PC=0x20001FA4 LR= 0x20001FA5 *Thumb命令では ジャンプ時にLRのアドレスのLSBが常に1にセットされる 24
ARM のレジスタと ST のレジスタ ARM マイコンは ARM CPU レジスタと周辺レジスタがある ARM ST 25
Cortex-M の割込みハンドリング
NVIC 内蔵割込みコントローラ Cortex-Mプロセッサは割込みコントローラを内蔵している レジスタの退避 割込み優先度ハンドリング ネスト割込みのハンドリング Cortex-M NMI NVIC 各種割込み Core システム例外 SysTick タイマーペリフェラル 27
ベクタテーブルの記述 4バイト単位で 割込みハンドラのアドレスを記述 *先頭だけは スタックのアドレスを指定 スタックアドレス Vector No. Vector Offset 例外& 割り込み 値 例 00 0x00 Stack Top sfe (CSTACK) 01 0x04 Reset iar_program_start 02 0x08 NMI Default Handler 03 0x0C Hard Fault Default Handler 04 0x10 Memory Management 05 0x14 Bus Fault Default Handler 06 0x18 Usage Fault Default Handler 07~10 0x1C~0x28 Reserved 0 11 0x2C SVCall Default Handler 12 0x30 Debug Monitor Default Handler 13 0x34 Reserved 0 14 0x38 PendSV Default Handler 15 0x3C SysTick Default Handler 16 ~ 255 0x40~0x3FC External Interrupts Interrupt Handlers RESETハンドラアドレス Default Handler 外部割込みのハンドラアドレス 28
ベクタテーブルの比較 クラシックARMおよびCortex-R/Aとの比較 ARM9 ( + Cortex-R/A) vector: ARM LDR LDR LDR SWI/SVC) LDR LDR DCD LDR LDR PC,Reset_Addr PC,Undefined_Addr PC,SWI_Addr ; Reset ; Undefined instructions ; Software interrupt PC,Prefetch_Addr PC,Abort_Addr 0 PC,IRQ_Addr PC,FIQ_Addr ; ; ; ; ; Cortex-M #pragma location = ".intvec" const intvec_elem vector_table[] = { {. ptr = sfe( "CSTACK" ) }, iar_program_start, Prefetch abort Data abort RESERVED IRQ FIQ NMI_Handler, HardFault_Handler, MemManage_Handler, BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, SVC_Handler, DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler, DATA Reset_Addr: Undefined_Addr: SWI_Addr: Prefetch_Addr: Abort_Addr: IRQ_Addr: FIQ_Addr: DCD DCD DCD DCD DCD DCD DCD iar_program_start Undefined_Handler SWI_Handler Prefetch_Handler Abort_Handler IRQ_Handler FIQ_Handler //Device specified interrupt handler InterruptHandler0 InterruptHandler1 アセンブラ命令 (割込みハンドラにジャンプ) InterruptHandler240 }; C言語(ハンドラのアドレス指定) 29
SysTickタイマーで割込み動作確認 SysTickを有効にして確認 static int tick = 0; void SysTick_Handler(void) { 割り込みハンドラはCの関数 tick++; ハンドラと関数に差はない } int main( void ) { SysTick->LOAD = 0x0000FFFF; // リロードレジスタの値をセット SysTick->VAL = 0; // 初期値をセット SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk SysTick_CTRL_TICKINT_Msk SysTick_CTRL_ENABLE_Msk; // コアクロックで割込み有効でスタート enable_interrupt(); while (1) { //SysTick_Handler(); delay(); } } 30
SysTickタイマーで割込み動作確認 EWARMのデバッガ機能で確認 割り込みハンドラの先頭 LRは割込みを意味する 0xFFFF_FFxx PCはハンドラへ 31
SysTickタイマーで割込み動作確認 自動的にスタックにレジスタが退避される 0x0000_0000側 R0: R1: R2: R3: R12: LR: PC: 割込み前の LRの値 割り込み発生の アドレス CPSR 使用スタック 使用スタック 0xFFFF_FFFF側 表示-スタック-スタック1 スタックの表示可能 32
SysTickタイマーで割込み動作確認 割込みハンドラを通常の関数として呼んでみる コメントを外して メイクしてデバッグ 33
SysTickタイマーで割込み動作確認 割込みハンドラを通常の関数として呼んでみる 結果 関数から呼んだ場合 LR = 0x080001C1 SysTick_Handler: 0x800019e: 0x4815 [0x80001f4] tick 0x80001a0: 0x6801 0x80001a2: 0x1c49 0x80001a4: 0x6001 } 0x80001a6: 0x4770 int main( void ) { main: - - - SysTick_Handler(); 0x80001bc: 0xf7ff 0xffef 0x800019e 割込みから呼ばれた場合 LR = 0xFFFFFFE9 LDR.N R0, [PC, #0x54] LDR ADDS STR R1, [R0] R1, R1, #1 R1, [R0] BX LR BL SysTick_Handler ; ; 34
SysTickタイマーで割込み動作確認 割込みハンドラでのLRの値(EXC_RETURN) 割込みを抜ける際のスタックポインタ 多重割込み FPUなどにより異なる MSP メイン PSP プロセス 割り込み時 使用 その他 使用可能 使用可能 CONTROLレジスタの設定で使用スタックの変更が可能 LRの値 0xFFFF_FFF1 多重割込み 0xFFFF_FFF9 通常状態への復帰時にMSPを使用 0xFFFF_FFFD 通常状態への復帰時にPSPを使用 0xFFFF_FFE1 多重割込み(FPU状態) 0xFFFF_FFE9 通常状態への復帰時にMSPを使用(FPU状態) 0xFFFF_FFED 通常状態への復帰時にPSPを使用(FPU状態) 35
スタートアップ処理
Cortex-Mのリセット動作 Cortex-Mでは起動時にベクタテーブルの内容を元に初期化される ベクタ先頭 0x0000_0000 スタックの先頭アドレス リセットハンドラアドレス NMIハンドラアドレス リセットされるとCPUは メモリから2ワード読み出す 最初のデータでスタックポインタをセット 2つ目のデータにPCをセット リセット スタック 37
EWARM Cortex-Mの起動動作 参照 ジャンプ Cortex-M0/M3/M4 Vector Table: IAR DLIB ランライム ライブラリの コード ただし 上書き可能 thumb vector_table_m.s or thumb cstartup_m.c デフォルトプログラムエントリ: thumb cstartup_m.s or thumb cstartup_m.c main()前の初期化: thumb cmain.s ユーザコード User s Application: 例外& 割り込み 値 0x00 Stack Top sfe (CSTACK) 01 0x04 Reset iar_program_start 02 0x08 NMI Default Handler 03 0x0C Hard Fault Default Handler 04 0x10 Memory Management Default Handler 05 0x14 Bus Fault Default Handler 06 0x18 Usage Fault Default Handler 07~10 0x1C~0x28 Reserved 0 11 0x2C SVCall Default Handler 12 0x30 Debug Monitor Default Handler 13 0x34 Reserved 0 14 0x38 PendSV Default Handler 15 16 ~ 255 0x3C 0x40~0x3FC SysTick External Interrupts Default Handler Interrupt Handlers Vector No. Vector Offset 00 iar_program_start: bl iar_init_core bl iar_init_vfp bl cmain ; optional ; optional, enable VFP, thumb fpinit_m.s cmain: bl low_level_init bl iar_data_init3 bl main ; low_level_init.c ; initialize data sections, init data_init3.c int main (void) { } void xxx_interrupthandler (void) { } 38
初期化処理のデバッグ デバッグのオプションを変更することで main以前のデバッグ可能 チェックを外す 39
初期化処理のソースコード EWARMインストールフォルダに初期化処理のソースコードがる EWARMのインストールフォルダ #pragma location = ".intvec" const intvec_elem vector_table[] = { {. ptr = sfe( "CSTACK" ) }, iar_program_start, NMI_Handler, HardFault_Handler, }; void cmain( void ); weak void iar_init_core( void ); weak void iar_init_vfp( void ); Reset #pragma required= vector_table void iar_program_start( void ) { iar_init_core(); iar_init_vfp(); cmain(); } cmain: bl cmp beq bl?l1: MOVS BL line BL BL low_level_init r0,#0?l1 iar_data_init3 変数の初期化 r0,#0 iar_argc_argv ; No parameters ; Maybe setup command main exit main 40
リンカ設定でメモリ配置コントロール
ILINKの基本概念 キーワード EWARMのリンカはILINK 詳細使用方法はC/C++開発ガイドを参照 42
ILINKの基本概念 キーワード GUIの設定項目 プロジェクトオプション > リンカ > 設定 ベクタテーブル ROM/RAM領域 スタックサイズの変更が可能 43
ILINKの基本概念 キーワード リンカの働き 領域(Region)を指定して そこにセクションやブロックを配置 ROMの特定の アドレスに配置 Memory領域 Region 0x00000000-0x0000FFFF IARの標準セクション.intvec.text place in ROM に配置.data_init.rodata.data Region 0x20000000-0x2000FFFF place in ブロック(スタック) RAM に配置 ブロック(ヒープ) 割り込みベクター コード 変数の初期化データ constデータ 変数 44
ILINKの基本概念 キーワード リンカ設定ファイルのキーワード紹介 region メモリ上の領域を指定します block サイズを指定します section プログラムのセクション initialize 初期化方法の指定 do no initialize 初期化しない領域の指定 place regionにblockやsectionを配置 rw(readwrite), ro(readonly)の属性を指定
ILINKの基本概念 キーワード EWARMの基本セクション.bss 0初期化する変数.data 0以外で初期化される変数.data_init.intvec ベクタ.noinit 初期化しない変数.rodata readonlyの変数.text プログラム.textrw ramfuncを使った時のコード.textrw_init.textrwの初期化データ :.dataセクションの初期化データ 詳細はIAR C/C++ 開発ガイドのセクションリファレンスを参照
実際に動作をみてみる 関数を配置 ファイルfunc.cのLED絡みの関数を特定領域に配置 0x00000000 IROM 0x00050000 ROMLED 0x00FFFFF void set_led_port(void) void LED_Handler(void) 0x20000000 RAM 0x2000FFFF 47
実際に動作をみてみる ( 関数を配置 ) C 言語ソースコードでの指定 pragma でセクション定義 EWARM ではセクション定義は #pragma location= XXXX EWARM ではセクションを参照するときに #pragma section= XXXX とななる 48
実際に動作をみてみる 関数を配置 実際にセクションを指定 #pragma location="led" void set_led_port(void ) { printf("initialize LED port n"); } #pragma location="led" void LED_Handler(void) { Port0!= Port0; } set_led_portがセクション LED に割当 LED_Handlerがセクション LED に割当 49
実際に動作をみてみる 関数を配置 リンカ設定ファイル 抜粋 0x00000000から0x0004FFFFをIROM_region define region IROM_region = mem:[from ICFEDIT_region_IROM1_start to 0x0004FFFF ]; define region ROMLED_region= mem:[from 0x00050000 to ICFEDIT_region_ROM_end ]; 0x00050000から0x000FFFFFをROMLED_region place in IROM_region { readonly }except { section LED }; セクションLEDを除く readonlyをrom_regionに配置 place in ROMLED_region { readonly section LED }; セクションLEDのreadonlyをROMLED_regionに配置 50
実際に動作をみてみる 関数を配置 ビルド結果のmapファイル プロジェクトオプション > リンカ > リスト > リンカマップファイルの表示 Outputフォルダに生成されるマップファイルをダブルクリックで表示 Codeは関数を示す 関数が奇数アドレスは Thumb命令 Cortex-MはすべてThumb命令 51
実際に動作をみてみる 変数を配置 RAM2に図のように4つの変数を配置 0x00000000 ROM 0x000FFFFF 0x20000000 0x20004000 0x2000FFFF RAM int int int int mydat1[10]; mydat2[10]; mydat3[5]; mydat4[5]; RAM2 52
実際に動作をみてみる 変数を配置 実際にセクションを定義 ひとつひとつ変数にセクションを割り当て #pragma location="mydat" root int mydat1[10]; #pragma location="mydat" root int mydat2[10]; #pragma default_variable_attributes = root int mydat3[5]; int mydat4[5]; #pragma default_variable_attributes = @ "MYDAT" まとめて変数に属性をつける このとき rootもpragma側でつけること 53
実際に動作をみてみる 変数を配置 リンカ設定ファイル define region RAM_region define region RAM2_region place in RAM_region = mem:[from ICFEDIT_region_RAM_start = mem:[from 0x20004000 { readwrite, to 0x20003FFF]; to ICFEDIT_region_RAM_end ]; block CSTACK, }; place in RAM2_region { readwrite section MYDAT}; RAM2_regionにセクションMYDATの readwriteデータを配置 54
実際に動作をみてみる ( 変数を配置 ) MAP ファイルを確認 #pragma location="mydat" root int mydat1[10]; #pragma location="mydat" root int mydat2[10]; #pragma default_variable_attributes = root @ "MYDAT" int mydat3[5]; int mydat4[5]; #pragma default_variable_attributes = define region RAM2_region = mem:[from 0x20004000 to _ICFEDIT_region_IRAM1_end ]; place in RAM2_region { readwrite section MYDAT}; mydat1 0x20004000 0x28 Data Gb main.o [1] mydat2 0x20004028 0x28 Data Gb main.o [1] mydat3 0x20004050 0x14 Data Gb main.o [1] mydat4 0x20004064 0x14 Data Gb main.o [1] 55
実際に動作をみてみる 変数を手動初期化 初期化つき変数にセクション定義 初期化つき変数にセクションを割り当て #pragma location="mydat" root int mydat1[10] = {0,1,2,3,4,5,6,7,8,9,}; #pragma location="mydat" root int mydat2[10] = {10,11,12,13,14,16,17,18,19}; 初期化つき変数にセクションを割り当て 56
実際に動作をみてみる 変数を手動初期化 初期化ルーチンの作成 プログラム中でセクション名を参照する場合には #pragma sectionで明示する #pragma section="mydat" #pragma section="mydat_init" void init_mydat( ) { int i; unsigned char *psrc, *pdest; 変数はMYDATというセクションになる 初期化データはMYDAT_initというセクションにる pdest = section_begin("mydat"); psrc = section_begin("mydat_init"); for (i=0; i < section_size("mydat"); i++ ) { *pdest = *psrc; pdest++; psrc++; } セクションMYDATの先頭アドレスを section_beginで取得 セクションMYDAT_initの先頭アドレスを section_beginで取得 } セクションMYDATのサイズを section_sizeで取得 57
実際に動作をみてみる 変数を手動初期化 リンカ設定ファイルでの指定 この領域の初期化は自動に行う initialize by copy { readwrite } except {section MYDAT}; initialize manually with packing=none { readwrite section MYDAT}; place in RAM2_region { readwrite section MYDAT}; この領域の初期化は手動で行う 初期化データの圧縮はしない 58
実際に動作をみてみる 変数を手動初期化 マップファイルでの確認 initialize manually { readwrite section MYDAT};の場合 ************************************ *** INIT TABLE *** initialize by copy { readwrite } の場合 ****************************************** *** INIT TABLE *** Address Size ------- ---Zero ( iar_zero_init3) 1 destination range, total size 0x54: 0x20000030 0x54 Address Size ------- ---Zero ( iar_zero_init3) 1 destination range, total size 0x54: 0x20000030 0x54 Copy ( iar_copy_init3) 1 source range, total size 0x50: 0x000019dc 0x50 1 destination range, total size 0x50: 0x20004000 0x50 Copy ( iar_copy_init3) 1 source range, total size 0x30: 0x00001be4 0x30 1 destination range, total size 0x30: 0x20000000 0x30 ない mydat1 0x20004000 0x28 Data Gb main.o [1] mydat2 0x20004028 0x28 Data Gb main.o [1] ある Copy ( iar_copy_init3) 1 source range, total size 0x30: 0x00001bf8 0x30 1 destination range, total size 0x30: 0x20000000 0x30 59
実際に動作をみてみる 変数を手動初期化 初期化関数前後の変数の値を比較 コード for ( i=0; i< 10; i++){ printf("before No%d - %d n", i, mydat1[i]); } init_mydat( ); 初期化関数を実行 for ( i=0; i< 10; i++){ printf("after No%d - %d n", i, mydat1[i]); } printf出力結果 表示 ターミナルI/O initialize LED port BEFORE No0 - -842150451 BEFORE No1 - -842150451 BEFORE No2 - -842150451 BEFORE No3 - -842150451 BEFORE No4 - -842150451 BEFORE No5 - -842150451 BEFORE No6 - -842150451 BEFORE No7 - -842150451 BEFORE No8 - -842150451 BEFORE No9 - -842150451 AFTER No0-0 AFTER No1-1 AFTER No2-2 AFTER No3-3 AFTER No4-4 AFTER No5-5 AFTER No6-6 AFTER No7-7 AFTER No8-8 AFTER No9-9 60
Cortex-M への移行時の注意点
Cortex-Mへの移行時の注意点 アーキテクチャにより 仕様が異なる項目に注意する スタックサイズ intデータのサイズ 構造体のパッキング 62
スタックサイズ 8bit 16bitマイコンと比較するとスタック使用量は多い 全レジスタ汎用レジスタ+PC+STACK+CPSRを退避した際の例 Cortex-M 16bitマイコンの例 68?バイト 14?バイト EWARMでは2種類のスタック解析機能で使用量を解析可能 静的なスタック解析(メイク時) 動的なスタック解析(デバッグ時) 63
スタックサイズ 静的なスタック解析(メイク時) オプション設定 プロジェクトを選択し をダブルクリック [リンカ]-[アドバンスド]-[スタックの使用量解析を有効にする] 1 2 4 3 5 64
スタックサイズ 静的なスタック解析(メイク時) マップファイルに表示 メイクでマップファイルに解析結果表示 Call Graph Root Category -----------------------interrupt Program entry Max Use ------0 232 Total Use --------0 232 Program entry " iar_program_start": 0x0000058d Maximum call chain " iar_program_start" " cmain" "main" "dhry_main" "Proc_1" "Proc_6" "Func_3" 232 bytes 0 0 8 192 16 16 0 Mainの実行されるパスでの スタック使用量 interrupt " default_handler" in vector_table_m.o [4]: 0x00000473 Maximum call chain " default_handler" in vector_table_m.o [4] 0 bytes 0 ハンドラの実行されるパスでの スタック使用量 65
スタックサイズ 静的なスタック解析(メイク時) 解析サポート機能 関数ポインタなどを使うと 自動では追えない #pragma calls=xx,yy を使って手動指定 void Fun1(), Fun2(); void Caller(void (*fp)(void)) { #pragma calls = Fun1, Fun2 (*fp)(); この関数はFun1,Func2が呼ばれる } 解析のスタート関数を個別指定したい #pragma call_graph_root void delay_time(int number){ } 66
スタックサイズ 動的なスタック解析(デバッグ時) ツールオプション設定 [ツール]-[オプション] [スタック]-[グラフィカルスタック表示 ]を有効 1 2 4 3 67
スタックサイズ 動的なスタック解析(デバッグ時) 表示 [表示]-[スタック]-[スタック1] 1 2 3 68
スタックサイズ 動的なスタック解析(デバッグ時) 実行結果表示 灰色は使用した量 緑のバーは現状値 マウスをあてると 詳細が表示 69
intデータ型のサイズ intデータ型のサイズは マイコン コンパイラにより異なる EWARM 16ビットマイコン signed char 8 8 unsigned char 8 8 signed short 16 16 unsigned short 16 16 signed int 32 16 unsigned int 32 16 signed long 32 32 unsigned long 32 32 signed long long 64 32 unsigned long long 64 32 70
intデータ型のサイズ マイコンにより 結果が異なる例 unsigned short c=0xffff; int gg(unsigned short a, unsigned short b) { if ( c == ( a ~b) ) { return 0; } else { return 1; } } int main( void ) { int t; x =0で yが0, c=0xffffの時のprintfの出力 16 bit result=0 32 bit result=1 t= gg(0, 0 ); printf("result=%d n", t); return 0; } 71
構造体のパッキング 構造体のパッキングによる差 struct S1 { char s1; int s2; char s3; int s4; char s5; } A[10]; struct S2 { char s1; char s3; char s5; int s2; int s4; } B[10]; Entry Address Size(hex) Size(Dec) Type Object A 0x20000000 0xc8 200 Data Gb main.o [1] B 0x200000c8 0x78 120 40% Data Gb main.o [1] struct S1での状況 struct S2での状況 s1 s2 5 s5 s3 s4 s3 s2 s4 s1 3 s5 4バイト 4バイト 72
解析ツールを使用して品質向上
解析ツールを使用して品質向上 IARシステムズの提供する解析アドオン製品を使用して品質向上 静的解析アドオン機能 C-STAT MISRA-C 2004/2008C++/2012のチェック CWE / CERTルールに準拠した脆弱性のチェック 動的解析アドオン機能 C-RUN 実行時の数値演算エラーの検出 不適切なメモリ利用の検出 C-RUN/ C-STATはEWARM製品版に対するアドオン製品となります 74
静的解析アドオン機能C-STAT 実動作不要の静的解析で 潜在的なコードエラーを検出 ソースコードレベルにおいて潜在的なエラーやバグを EWARM上で発見 例えばメモリリーク アクセス違反 算術演算の エラー 配列や文字列のオーバーランといった 潜在的なコードエラーを発見でき アプリケーションの誤動作を未然に防止 主な機能 C, C++ 言語に対応 MISRA C:2004, MISRA C++:2008, MISRA C:2012に対応 CWE や CERT C/C++といった基準に基づく100以上のルールに沿ったチェック 直感的で簡単に使える設定 任意でコーディング規約単位または個別のルール単位でチェック IAR Embedded Workbenchとシームレスに統合 短時間で解析 ARM用IAR Embedded Workbenchバージョン7.40より利用可能 75
簡単に使える C-STAT EWARM のオプションで解析したいチェックルールを選択するだけ [ 静的解析 ] を選択 [C-STAT check] をクリック プロジェクトを右クリックし [ オプション ] を選択 CWE/CERT をベースに動作時不良リスクをチェック MISRA C コーディングガイド準拠チェック チェック項目選択画面 76
C-STAT の実行 EWARM のオプションメニューで選択するだけ プロジェクトを右クリック [C-STAT] 静的解析 プロジェクトを解析 数分で C-STAT メッセージが表示 77
動的解析アドオン機能C-RUN デバッグ実行時に 実際に発生した潜在エラーを検出 事前に設定したチェック項目を対象にデバッグ実行中に リアルタイムにチェックを行い 違反した挙動を 検出し EWARM上で通知 実装フェーズから問題を検出することで 後段での テスト 修正工数を削減し 品質向上 納期短縮を実現 主な機能 C, C++言語に対応 直感的で簡単に使える設定 包括的かつ詳細な実行時のエラー情報 エラーが見つかった場所のコールスタック情報 エディタ上でのコード位置を表示やグラフィカルな表示 自由度の高いエラー情報の管理 配列や他のオブジェクトが境界内に正しくアクセスしていることを保証する境界チェック バッファオーバーフローの検出 データのキャスト時の 値の変化を検出 算術計算時の値のチェック シフト演算のビット損失を検出 ヒープやメモリリークに関するチェック ARM用IAR Embedded Workbenchバージョン7.20以降で利用可能 78
動的解析アドオン機能C-RUN デバッグ実行時に 実際に発生した潜在エラーを検出 コンパイラ開発者による高効率 IDE tools Build tools IAR C-SPY Debugger Editor IAR C/C++ Compiler Simulator driver Project manager Assembler Hardware system drivers Library builder Linker Librarian 完全に統合された 動的解析 Power debugging RTOS plug-ins 詳細かつ柔軟な 動的エラー情報 79
簡単に使える C-RUN EWARMのオプションで解析したいチェックルールを選択するだけ ビルド時にチェックコードが実行コードに埋め込まれる [ランタイム解析]を選択 [有効化]をチェック チェックしたい項目をチェック 80
C-SRUN の実行 EWARMのデバッグ実行中に自動的に検出される 発生したソース位置 発生した違反 発生したPC位置 発生したコア マルチコア対応 データ値の表示 値0x000001f4を0xf4にした コールスタックで mainから convを呼んだ中で発 生したことを示す 81
まとめ
まとめ Cortex-M の特徴を理解することで レジスタや命令セットの読み方を習得できる 割込みハンドラやベクタテーブルは 従来の ARM コアから変更となっている 初期化処理やリンカ設定は EWARM 独自の記述となり 既存のものを参考にカスタマイズしていく マイコンアーキテクチャごとに動作の異なるコーディングに注意する 静的解析ツール 動的解析ツールを使用することで 不具合の早期発見 品質向上につながる 83
本資料について
本資料取り扱い上の注意 本資料は 2015 年 4 月 1 日時点の情報を基に作成されており 将来変更の可能性のあるものです あわせてご紹介する設定や機能に関連して 動作保証をお約束するものではございませんので ご了承ください 本資料で提供している情報は ご利用されている方のご判断 責任においてご使用ください 提供した情報に関連して ご利用される方が不利益等を被る事態が生じたとしても 弊社及び執筆者は一切の責任を負いかねますので ご了承ください 本資料の内容に関する弊社または各社へのお問合せはご遠慮ください 本資料及びデータの再配布 無断転用 転載等はご遠慮ください 85
商標について IAR Systems, IAR Embedded Workbench, C-SPY, C-RUN, C-STAT, visualstate, Focus on Your Code, IAR KickStart Kit, I-jet, I-scope, IAR, お よび IAR Systems のロゴタイプはIAR Systems ABが所有する商標または登録 商標です ARMおよびCortexは ARM Limited またはその子会社 のEUまたはその他の 国における登録商標です ARMおよびThumbは ARM Limited またはその子 会社 のEUまたはその他の国における商標です CoreSightは ARM Limited またはその子会社 のEUまたはその他の国における商標です All rights reserved. STM32は STマイクロエレクトロニクスの登録商標です その他 本資料中の製品名やサービス名は全てそれぞれの所有者に属する商標ま たは登録商標です 86