ARMを採用するためのヒント

今やARMは組込みでもスタンダードとなりつつあります。しかし、実際に採用しようと思うと、まだまだ分からないことが多くて、悩んでおられる方も多いのではないでしょうか。

「CPUはどれを選べばいいの?」
   「開発環境には何が必要?」
   「乗り換えにかかるコストは?」

そんな疑問を持ちながらも今一歩を踏み出せない開発者のみなさんへ、SuperHとの比較を交えながら、ARMの素朴な疑問にお答えします。

どのARMからスタートするか

ARMコアのCPUはとても種類が多いため、どれを選択していいのか分からないというご意見をよくいただきます。CPUクロックや搭載されている周辺デバイス、チップのサイズ、価格などを考慮して決める必要がありますし、開発環境も忘れてはならない要素の一つです。

また、同じARMコアを使っていてもCPUベンダが異なると、ハードウェアの設計のしやすさが全く違うものになりますので、留意しておく必要があります。

実は私たちも、ARMを使った製品開発を行っているユーザの立場にいます。
今までに私たちが使用したARM CPUを以下の表にまとめました。

CPUベンダ CPU名 コア 用途
NXPセミコンダクターズ
(フリースケール・セミコンダクタ)
i.MX1
i.MX31
i.MX53
ARM920T
ARM1136
Cortex-A8
開発支援ツール
STマイクロエレクトロニクス STR911FA
STM32F0
STM32F2
STM32F4
ARM966
Cortex-M0
Cortex-M3
Cortex-M4
開発支援ツール
フラッシュプログラマ
組込み製品
テキサス・インスツルメンツ AM1808
AM3505
AM3352
ARM926EJ-S
Cortex-A8
Cortex-A8
開発支援ツール
評価ボード
組込み製品
IoTゲートウエイ
東芝 M362F10 Cortex-M3 評価ボード
ルネサス エレクトロニクス RZ/A1L
RZ/A1H
Cortex-A9
Cortex-A9
開発支援ツール
フラッシュプログラマ
組込み製品
評価ボード

このようにローエンドからハイエンドまで用途に応じて、各社のCPUを使用してきました。この経験をもとに、SuperHから移行するCPUを一つ選ぶとすれば、ルネサス エレクトロニクスのRZ/A1シリーズをおすすめします。

RZ/A1は扱いやすくARMのエントリチップとして最適です。実際、私たちも6つの製品にRZ/A1を使用しています。では、なぜRZ/A1をおすすめするのか、その理由をご説明しましょう。

まず、第一のポイントとしては、CPUのマニュアルがすべて日本語で用意されているということです。海外製のチップを使うとき、英語のドキュメントしかなく、苦労された経験をお持ちの方も多いのではないでしょうか。特に新しい概念が説明されている場合は、日本語で書かれている方が早く理解することができますし、製品の品質に差が出てくることもあります。

次に、周辺デバイスがルネサスの思想を受け継いだ設計になっていることが挙げられます。各ベンダのCPUを比較すると、周辺デバイスにベンダの特色が現れてきます。例えば、RZ/A1ではレジスタマップがSH726xを継承していますので、SH726xシリーズを使われていた場合は馴染みやすい仕様になっています。

内蔵RAMが大きいのも特長と言えます。最も少ないRZ/A1LCでも2MB、RZ/A1Hでは10MBものRAMが搭載されています。通常の組込み用コントローラに使用するなら十分なスペックですし、これだけあればµITRONも余裕で動作します。ハードウェアの設計上、外部RAMの搭載を考える必要がなくなりなり、部品の点数を抑えることができます。

そのほかのハードウェア設計上の利点としては、電源が2系統で済み、入切のシーケンスにも制約が無いため、電源周りの回路がシンプルに設計できます。また、パッケージとしてQFPが用意されていますので、基板面積は若干大きくなりますが、多層基板を使わずにコストダウンを図ることができます。ROMにはシリアルフラッシュメモリが使えますので、配線を少なくし、基板面積を小さくすることができます。

SH-2Aの置き換えとした場合、CPUクロックの周波数も200MHzから400MHzへとアップしています。整数演算性能を示すDMIPS値で比較すると、SH-2Aが480 DMIPS、RZ/A1が1000 DMIPSとなっていますので、周波数に比例して、およそ2倍の性能が得られます。

以上のようにRZ/A1を採用するメリットを説明しましたが、コスト面で見ても同クラスのCPUより安く(当社調べ)入手できますので、興味のある方は一度調べてみてはいかがでしょうか。

SuperHとの違いは

ARMコアはSuperHとアーキティクチャが全く異なる32/64ビットのRISCプロセッサです。最初は戸惑われるかもしれませんが、「ここが違う」というツボを押さえておけばプロセッサとしての原理は同じです。なお、ARMには64ビット版もありますが、ここではARMの主流である32ビット版について説明します。

ARMコアとは

ARMというCPUはARM社のコアライセンスを受けたチップベンダが、独自の周辺機能を付加してチップ化したものです。ですからARMのコアは同じでもチップベンダによって様々な特色が出てきます。

ARMコアとはARM7、ARM9、ARM11、Cortex-A、Cortex-R、Cortex-Mのことで、例えばルネサスのRZ/A1であれば、コアはCortex-A9という言い方をします。現在、主流のCortexシリーズにはアプリケーション用のCortex-A、リアルタイム制御用のCortex-R、マイクロコントローラ用のCortex-Mの3タイプがあり、さらにA9、R4、M3のように番号が付加されて、グレードや機能の有無が区別されます。

ARMとThumb

ARMにはSuperHにはない、ARM命令とThumb(サム)命令という命令セットの概念があります。ARM命令は32ビットでパフォーマンス重視、Thumb命令は16ビットでメモリサイズを重視した命令となっています。ARM命令は一つ一つが非常に高機能ですが、Thumb命令はシンプルに作られています。SuperHの命令を知っている方なら、16ビット命令のThumbの方がしっくりくるかもしれません。

ARMとThumbはBXという特殊な分岐命令を実行することで、ARM命令の領域とThumb命令の領域を自由に切り替えて実行させることができます。使い方としては、例えば、メインプログラムをThumb命令で動作させてメモリを節約し、パフォーマンスを上げたいサブルーチンだけARM命令で実行するというようなことが考えられます。

なお、リセットや割り込みなどの例外が発生した場合は、自動的にARM命令に切り替わりますので、例外処理はARM命令で書くのが普通です。

ARMかThumb、どちらの命令コードを出力するかは、コンパイラのオプションやソースコード内の#pragmaで指定することができます。各社のARM用コンパイラにはこのような機能が用意されています。

なお、Cortex-M系のコアはThumb命令のみ動作可能でARM命令は存在しません。また、Thumb命令にはThumbを拡張したThumb-2や、ThumbEEなどのバリエーションがあり、現在はThumb-2が主流となっています。

レジスタ

ARMにはSuperHと同様、R0~R15の汎用レジスタが16個あります。ただし、R13はスタックポインタ、R14はリンクレジスタ、R15はプログラムカウンタと役割が決まっていますので、実際に汎用レジスタとして使えるのはR0~R12です。

R14のリンクレジスタはBLなどのサブルーチンコール命令が実行されたときに、その次の実行番地が自動的に記憶されるレジスタです。関数の先頭でR14を保存しておき、関数の最後で保存したアドレスへジャンプすることでリターンを行います。ちなみに、ARMにはサブルーチンからリターンする専用の命令はありません。

その他、プログラムのステータスを表すレジスタとしてPSRがあります。PSRのビットはフラグとなっており、例えば、演算結果を示すゼロフラグやキャリーフラグなどがあります。

R13とR14はバンクレジスタになっており、例外発生時に自動的に切り替わるようになっています。また、高速割り込みという例外の場合は、R8~R12がバンクレジスタとして切り替わるようになっています。

ARM命令セットの特徴

ARMの命令は非常に高機能で、高級言語を意識した作りになっています。通常はコンパイラがうまくやってくれますので、命令を意識することはないかもしれません。しかし、ブートコードやデバイスドライバを書く場合は、まだまだアセンブラを使うことがありますので、知っておいて損はありません。以下にARM命令の特徴を説明します。

ほとんどの命令は条件判断付き

ほとんどのARM命令は条件判断が付加されていて、条件によってその命令を実行するかしないかを1命令で決定できるようになっています。例えば直前の演算結果が0ならレジスタへの代入を行う、というようなことができ、条件分岐命令を使わなくてもかなりのことができてしまいます。

  subs	r4, r4, #1
  moveq	r6, #2

上の例はR4をデクリメントした結果が0ならば、R6に2を代入します。mov命令の後にeq(equal zero)が付加されて、条件判断を行っています。この条件には16のパターンがあり、eqのほかne、ge、gt、le、lt、alなどがあります。al(always)は常に実行される条件で、通常はmovalをmovのようにalを省略して記述します。

算術系オペランドは3つ

算術系ARM命令のオペランドは3つあります。これにより、C言語の2項演算式の代入文を表すことができます。例えば、「r1 = r2 + 4;」は、「add    r1, r2, #4」となりC言語の式をそのまま表すことができます。

オペランドでシフト処理

ARM命令にはオペランドにシフト処理が指定できます。専用のシフト命令が無くても、算術演算や代入命令一つでシフト処理まで完結します。

  mov		r4, r4, lsl #1
  add		r4, r4, r3, lsr #3

上の例はR4を左へ1ビットシフトします。下の例はR3を右に3ビットシフトした結果とR4を加算してR4に代入します。この例では符号ビットを考慮しないロジカルシフトですが、ほかにも算術シフトやローテイトなどがあり、シフト演算に必要なものはそろっています。

インクリメント、デクリメントもOK

ロード、ストア命令のオペランドにはC言語のポインタを意識したインクリメント、デクリメントの機能がついています。例えばC言語での「*p++ = 0;」は以下のように展開されます。

  mov		r3, #0
  strb	r3, [r0], #1

このようにポインタへの代入とインクリメントがstr命令一つで実現されていることが分かります。

レジスタプッシュとポップ

C言語では関数の入口で保護したいレジスタをプッシュし、出口でポップする命令が実行されるようになっています。通常なら保護したいレジスタの数だけPUSH命令を連ねる必要があるのですが、ARMでは1命令で複数のレジスタのプッシュとポップができるようになっています。

  stmdb	r13!, { r4-r7, r9, r14 }
  ldmia	r13!, { r4-r7, r9, r14 }

stmdbの例はR13の示すアドレスよりR4、R5、R6、R7、R9、R14をプッシュし、プッシュしたアドレス分R13を更新します。ldmiaの例はプッシュしたレジスタをポップし、R13を更新します。R13はスタックポインタの役割をします。

例外処理(Cortex-M以外)

例外処理にはリセット例外、通常の割り込み、ソフトウェア割り込み、未定義命令例外、アボートなどがあります。例外ベクタにはSH-2Aのようにジャンプ先アドレスではなく、ジャンプ命令そのものを順番に書いておく方式が採用されています。順番は下記の表のようになっており、リセット例外が0番目と決まっています。

例外名 番地(オフセット) コメント
リセット 0x00
未定義命令 0x04
ソフトウェア割り込み 0x08 SWI命令を実行したとき
プリフェッチアボート 0x0C
データアボート 0x10
予約 0x14 通常NOP命令としておく
IRQ 0x18 通常の割り込み
FIQ 0x1C 高速割り込み

リセット信号が入力されると0番地へ一旦飛び、そこに書かれてあるジャンプ命令が実行される仕組みになっています。実際には以下のようにブランチ命令ではなく、PCへのロード命令で行うことが多いようです。

  00000000	ldr	pc, =ResetHandler
  00000004	ldr	pc, =UndefinedHandler
  00000008	ldr	pc, =SWIHandler
  0000000C	ldr	pc, =PrefetchHandler
  00000010	ldr	pc, =AbortHandler
  00000014	nop
  00000018	ldr	pc, =IRQHandler
  0000001C	ldr	pc, =FIQHandler

ARMではPCへ値を代入する命令があり、代入した値の番地にジャンプします。これはブランチ命令が-32MB~+32MB空間までしかジャンプできないため、メモリ配置によっては目的のプログラムに届かないことがあるためです。

なお、ベクタのアドレスは0番地とFFFF0000番地が選べますが、多くの場合ブートモードによって決まっています。0番地の設定をローベクタ、FFFF0000番地の設定をハイベクタと呼んでいます。また、任意のベクタアドレスに設定するにはVBARというコプロセッサのレジスタに値を書き込みます。これはSuperHのVBRに相当します。

ちなみに、JTAGエミュレータのデバッグ機能としてベクタキャッチブレークというのがあります。これを使えばメモリアボートなどの例外発生時にブレークをかけることができますので、プログラムが暴走した時のデバッグに役立ちます。

ブートシーケンス

ARMではリセット時のブートシーケンスにも一癖あるので注意が必要です。SuperHではリセットがかかると、リセットベクタから命令通りに動作しますが、ARMでは一旦、チップに内蔵されている固定のROMコードを経由して、ブートのデバイスを選択する処理が動作します。

多くのARM CPUはROM以外にもブートのデバイスを選ぶことができ、SDカードやLAN、シリアルポートなどからブートすることができます。例えばSDカードからブートする場合は、以下のようなシーケンスとなります。

  1. SDデバイスを初期化
  2. SDカード上の固定ファイルをロードしRAMに展開
  3. RAMに展開されたコードを実行

実際にはSDカードから固定ファイルを読み出すために、1次ブートローダをRAMに展開して動作させるCPUもあります。このようにCPUをブートさせるために複雑なシーケンスと、かなりの量のプログラムコードが実行されることになります。

なお、JTAGエミュレータでデバッグを行う際に、チップベンダの仕様によってはリセット番地できっちり止めることができず、内蔵ROMのコードが少しだけ実行されてしまうことがあります。

このようになると初期化が中途半端な状態になり、デバッグのときにメモリへアクセスできなかったり、RUNさせるとすぐに暴走するなどの現象を引き起こす場合があります(この現象を業界用語で「チョロ走り」といいます)。RZ/A1ではリセット番地できっちり止めることができますので、こういう面でも安心できます。

キャッシュ

多くのARMコアにはキャッシュが搭載されています。しかも1次キャッシュと2次キャッシュがあり、1次キャッシュはプログラム領域とデータ領域の2系統を別々にキャッシュすることができます。Cortex-Aクラスのコアでは当たり前のように大容量(128KB以上)の2次キャッシュが搭載されています。

エンディアン

ARMは基本的にリトルエンディアンのCPUです。ビッグエンディアンで動作させることも可能ですが、ARMのシステムとしては主流ではありません。
ちなみに、ARMにはbig.LITTLEという技術がありますが、これは省電力を目的としたマルチコアの制御のことで、エンディアンとは関係ありません。

トレース機能(ETB/ETF)

一部のSuperHでも搭載されていますが、ARMにはETB(Embedded Trace Buffer)または、ETF(Embedded Trace FIFO)と呼ばれる内蔵メモリをトレースバッファとして使うデバッグ機能が搭載されています。JTAGエミュレータがETB/ETFをサポートしていれば、数Kサイクル程度の命令実行の履歴を残すことができます。これを使えば、不正な例外処理や、エラー発生時の原因調査ができますので非常に便利です。

ARM社が提唱しているデバッグ機能は他にも強力なものがありますが、これらを搭載するかどうかはARMチップベンダの判断となっています。ETB/ETFについては基本的なデバッグ機能として、多くのチップベンダが自社のCPUに搭載しています。

コンパイラは何を選べばいいか

ARMコアに対応している主なコンパイラを下記の表に示します。

パッケージ名 ベンダ 統合開発環境 コメント
DS-5(ARM C/C++) ARM DS-5(Eclipseベース) ARM純正コンパイラ、64ビット版もある。
MDK-ARM(ARM C/C++) ARM µVision Cortex-M/Rなどマイクロコントローラに特化した製品。
EW ARM(IAR C/C++) IARシステムズ Embedded Workbench サードパーティでは高い実績がある。当社の一部の製品でも使用。
Code Composer Studio(TI C/C++) テキサス・インスツルメンツ Code Composer Studio IDE(Eclipseベース) TI製のチップをサポート。
TrueSTUDIO(GNU C/C++) Atollic TrueSTUDIO(Eclipseベース) 有償版があり、サポートもOK。日本ではAICが窓口。
GNU Tools(GNU C/C++) GNU e2 studio(Eclipseベース) Cyber THOR Studiosがサポート。ルネサス向けのGNUコンパイラ。
GNU ARM Embedded Toolchain(GNU C/C++) GNU なし Cortex-M/Rなどマイクロコントローラに特化したGNUコンパイラ。
Linaro(GNU C/C++) GNU なし ARM LinuxがメインだがWindows用GNUコンパイラも利用可能。

とりあえず、すぐに開発を始めるならGNU Cをおすすめします。ルネサスのRZを使用する場合はEclipseをベースにカスタムされたe2 studioという統合開発環境がありますので、強力なコンパイラと統合開発環境を無償でそろえることができます。

GNU CにはGPL(GNU Public License)があり、「ソースコードを公開しなければならない」と思っておられる方も多いと思いますが、GPLのライブラリと一緒にリンクしなければこの問題は避けられます。組込み用ではNewlibというフリーソフトウェアの標準Cライブラリがあり、これを使えばソースコードの公開を回避できます。

GNU Cは最終的に責任をもってサポートしてくれるところがないのですが、使い方やバグ情報はネット上にアップされており、参考にできる資料がたくさんあります。私たちの製品開発にもGNU Cを使用していますが、コンパイラのバグや使い方で問題となったことはほとんどありません。

製品開発時にきっちりとしたサポートを望まれる場合は、ARM純正またはIARシステムズのコンパイラを選択されるとよいでしょう。

OSの選択はどうするか

ARM対応しているOSにはµITRON系、Linux、TOPPERS、RTXなど各種あります。ARMで使える主なOSを以下の表にまとめました。

製品名 ベンダ コメント
NORTi ミスポ µITRON系RTOSとして国内では人気のあるOS。
µC3 イーフォース µITRON系RTOS。
TOPPERS/ASP TOPPERSプロジェクト オープンソースのµITRON系RTOS。
ライセンス既定に従えば無償での使用が可能。
TOPPERS-Pro/ASP ユビキタスAIコーポレーション TOPPERS/ASPをベースにパッケージ化された製品。
組込みLinux --- 様々な組織やチップベンダから提供されている。
RTX ARM ロイヤリティフリーの軽量RTOS。

SuperHを使われてきた方であれば、ミスポのNORTiをご存知の方も多いのではないかと思います。NORTiはARM用も充実しており、各社のARMコアに対応しています。ルネサスのRZ/A1用もあり、当社のRZ/A1のボード用にもシリアルポートとLANがすぐ動くサンプルが用意されています。

特にOSにこだわらなければRTXというロイヤリティフリーのRTOSもあります。これはもともとCortex-M用に開発されたものですが、ルネサスのホームページからRZ/A1用に移植したものをダウンロードすることができます。システムコールの呼び出しはµITRONと異なりますが、RTOSの概念としては共通するものがありますので、選択肢として検討の価値はあります。

Cortex-Aクラスのコアを搭載したチップには、ほぼ組込みLinuxが搭載されており、当社でもLinuxを使用した製品をいくつかリリースしています。オープンソースの強力なミドルウェアやアプリケーションが無償で使用できるというメリットは大きいですが、反面、カーネルやドライバを自社ターゲットで動作させるためのハードルは高いです。

ドライバやミドルウェアをそろえるには

ARMの各チップベンダからは、ほとんどと言っていいほど、周辺I/O用のドライバサンプルが提供されていますので、ひと昔前に比べると開発工数がかなり削減できます。また、主なミドルウェアもオープンソースのものが充実してきていますので、サポートを気にしなければ安価にシステムを構築できるようになっています。

RZもルネサスのホームページからドライバがダウンロードできるようになっています。自社のターゲットで動かすにはカスタマイズが必要ですが、ここでもドキュメントが日本語で用意されているので、比較的移植は簡単に行えます。

なお、ミスポのNORTiにはカーネルのほかにTCP/IPプロトコルスタックが標準でついていますので、NORTiを採用される場合はTCP/IPのミドルウェアを改めて導入する必要がありません。

開発ボードとJTAGエミュレータ

自社でボードを開発される場合でも、お手本となる開発用のリファレンスボードは必要です。ボードがなくても開発は進められますが、あった方がはるかに開発効率は上がります。市販されているボードには、周辺デバイスを制御するドライバも用意されていることが多いので、ハードもソフトもそれに習って開発すれば、工数がかなり短縮できます。

ARMのリファレンスボードも少し前までは高価でしたが、今では数千円から数万円で購入できますので、開発のための初期投資が楽になりました。ちなみに、当社でもRZ/A1Lのボードを7,700円(税込)で販売しています。

組込みソフトウェアの開発にJTAGエミュレータは欠かせない存在です。JTAGエミュレータにはARM純正品や、海外製の安い製品、中にはJTAGエミュレータの回路を取り込んだリファレンスボードまであります。これらのほとんどが、Eclipseベースのデバッガと接続して使用できるようになっています。

デバッグの工数は開発工程の30%~50%を占めると言われています。デバッグツールに接している時間は思っている以上に長いため、使い慣れたツールを使うのが結局のところ工数短縮につながることになります。

私たちは、自社の組込み製品の開発にPALMiCE3 ARMを使っていますが、自分たちのツールを自ら使うことで、機能や使い勝手をツールにフィードバックしています。PALMiCE3 ARMは組込みに特化したデバッグツールとして、さまざまなリファレンスボードでの動作確認や最新のARM CPUへの対応を行っています。下記は最近、動作確認を行ったボードの一覧表です。

ボード名 CPU名 コア メーカ
LS1043ARDB-PB* LS1043 Cortex-A53 × 4 NXPセミコンダクターズ
Raspberry Pi3* BCM2837 Cortex-A53 × 4 Raspberry Pi Foundation
R-Car Starter Kit pro* R-Car M3 Cortex-A57 × 2
Cortex-A53 × 4
Cortex-R7
ルネサス エレクトロニクス
AM437x Starter Kit EVM AM4378 Cortex-A9 テキサス・インスツルメンツ
AM5728 Industrial
Development Kit EVM
AM5728 Cortex-A15 × 2
Cortex-M4 × 2
テキサス・インスツルメンツ
MCIMX6Q-SDB i.MX 6Quad Cortex-A9 × 4 NXPセミコンダクターズ
MCIMX6UL-EVK i.MX 6UltraLite Cortex-A7 NXPセミコンダクターズ
Armadillo-X1 i.MX 7Dual Cortex-A7 × 2
Cortex-M4
アットマークテクノ
TWR-LS1021A LS1021A Cortex-A7 × 2 NXPセミコンダクターズ
R-Car E2 System
Evaluation Board SILK
R-Car E2 Cortex-A7 × 2
SH-4A
ルネサス エレクトロニクス
R-Car H2 LAGER R-Car H2 Cortex-A15 × 4
Cortex-A7 × 4
ルネサス エレクトロニクス
RZ/G1E Starter Kit
Evaluation Board
RZ/G1E Cortex-A7 × 2 ルネサス エレクトロニクス
HRX1000 RZ/G1E Cortex-A7 × 2 萩原電気
Armadillo-EVA 1500 RZ/G1M Cortex-A15 × 2 アットマークテクノ
SK-S7G2 S7G2 Cortex-M4 ルネサス エレクトロニクス
*PALMiCE3 ARM64でのみ使用可能です。

RZを採用したときの環境のまとめ

RZ/A1を採用する場合に、おすすめできる開発環境の組み合わせを表にしました。上から組み合わせの価格が安い順に並んでいます。

コンパイラ 統合開発環境 RTOS JTAG-ICE コメント
GNU C/C++ e2 studio RTX J-Link パワーユーザ向け
GNU C/C++ e2 studio RTX PALMiCE3
GNU C/C++ e2 studio NORTi PALMiCE3 おすすめ環境
IAR C/C++ Embedded Workbench NORTi PALMiCE3 この組み合わせの実績多数
IAR C/C++ Embedded Workbench µC3 I-Jet
IAR C/C++ Embedded Workbench µC3 PALMiCE3  
ARM C/C++ DS-5 NORTi PALMiCE3
ARM C/C++ DS-5 µC3 PALMiCE3

GNU CとRTXとJ-Linkを組み合わせて使えば、最も安く開発環境をそろえることができます。ただし、ソフトウェアにはサポートがありませんので、何か問題が発生しても自力で解決していく力量が必要になります。また、J-Linkのハードウェアが故障した場合、海外のメーカへ直接修理を依頼する必要がありますので、タイトな開発プロジェクトでは避けた方が無難かもしれません。

SuperHでNORTiを使われてきた方であれば、GNU C+e2 studio+NORTi+PALMiCE3の組み合わせをおすすめします。使い慣れたRTOSと、強力な開発環境をリーズナブルな価格でそろえることができます。