JTAGコネクタでdebugWIRE接続
AVR32ボードをEAGLEで設計し、OLIMEXで製造してもらおうとしたが、基板面積に余裕があり、ついでにATTiny13Aの勉強用ボードを作る予定。AVR32も初めてだが、AVRも初めてである。
KEE AVR JTAGICE mkII(以下JTAGICE)をAVR32だけでなくATTiny13Aにも使用する。
JTAGコネクタからdebugWireへどう変換すればよいのか悩んでしまった(解決した)。
純正AVR JTAGICE mkIIはATTiny13AをdebugWIREでサポートしているし、JTAGICEはdebugWIREフル・コンパチブルだというが、コネクタのピン数から違うので何らかの変換や信号の共用が必要なはず。
とりあえずdebugWIREで使用するISPコネクタのピン配置は以下の通り。
ピン番号 | 信号名 |
---|---|
1 | MISO |
2 | VCC |
3 | SCK |
4 | MOSI |
5 | RESET |
6 | GND |
ピン番号 | 信号名 | 備考 |
---|---|---|
1 | TCK | |
2 | GND | |
3 | TDO | |
4 | VTref | |
5 | TMS | |
6 | nSRST | |
7 | N.C. | |
8 | N.C.(nTRST) | 一部のAVR32で利用 |
9 | TDI | |
10 | GND |
いろいろAtmelのWEBサイトを駆け回ったところ、「Connecting to a target board with the AVR JTAGICE mkII」というドキュメントを見つけた。
なかなか見つけられなかったのは、WEBのタイトルが「AVR JTAGICE mkII Quick Start Guide」となっているから、大したこと書いてないだろうと無視してしまったからだ。うかつ。
このドキュメントの「Table 1. Connections required for ISP and debugWIRE」を見れば一目瞭然である。またこのドキュメントを探すのが嫌なので、掲載しておく。
JTAGICE mkII probe | ISP6PIN header | ISP | debugWIRE |
---|---|---|---|
TCK(1pin) | SCK(3pin) | ○ | |
GND(2pin) | GND(6pin) | ○ | ○ |
TDO(3pin) | MISO(1pin) | ○ | |
VTref(4pin) | Vcc(2pin) | ○ | ○ |
nSRST(6pin) | RESET(5pin) | ○ | ○ |
TDI(9pin) | MOSI(4pin) | ○ |
○の付いたピン(あるいは信号)が必要なもの。ISPは掲載したすべてのピンが必要、debugWIREは3つのピンが必要なことを表す。
JTAGの10pinのうち3本だけ接続すればよい。
AVR32の割込って...遅い?
最近徐々にマニュアルの周辺モジュールを読み始めているのだが、やはりAVR32って割込が遅いようだ。割込や例外が発生して、ハンドラ先頭命令が実行されるのは早いのだが、ハンドラ本体処理に至までが長いのだ。ソフトウェアにより割込グループと割込ラインを判定し、2段階構成で割込要因を確定するからであると、これまで書いたがこれはINTCモジュールに限った話でまだ先がある。
割込ラインは各モジュール毎に来るので、そのモジュール内でどのような割込要因が発生したかは判らない。モジュール内のステータス・レジスタを評価する処理が必要となり、トータルすると3段階の手間が掛かる訳だ。
具体的には、USARTならUSART0〜2毎に割込ラインがあるが、各USARTの受信バッファ・フルなのか送信バッファが空なのか、はたまた通信エラーなのかは別途ステータス・レジスタ(CSR)を見て判断しなければ判らない。しかもこれがまたビットフラグだったりする。
ビットフラグなら割込ラインを調べるときのように、clz(Count Leading Zeros)命令を使用すればよいと考えるかもしれないが、この命令を使うには問題がある。割込ラインを調べるときも同様に問題だが、優先度の高い順にビットフラグが並んでいるとは限らないのに、clz命令でmsb側からチェックするため、システム仕様から見たとき優先度が低いものを選択してしまうことがある。
USARTの場合、途切れなくデータを送受信するためにCSR(USART Channel Status Register)のTXBUFEビット(bit11)やRXBUFF(bit12)、その他をチェックする必要があるが、それよりmsb側にCTSビットがある。CTSはCTS信号ラインをモニタするためのもので、セットされている可能性が高い。では、上位ビットをマスクすれば良いかというと、下位ビットにあるエラー関係のビットを優先的にチェックする必要がある。従って一般的には
- エラー関係以外のビットをマスク&評価して、0でないなら、各ビットからエラー原因を特定し、エラー処理に入る。
- 上記で0なら、送受信関係のビット以外のビットをマスク&評価して、各ビットから受信or送信を判定し処理する。
- いずれでもなければ、何も処理せずreteする。
ということになる。ちなみにマスク&評価するとき、and(cond4付き)命令はステータス・フラグが変化しないので、and(shift付き)かandh/andl命令を使用すべきである。
割込要因毎にハンドラ・ベクタ・テーブルを持つ一般的な組み込みMCUなら割込処理本体直前までを省けるが、AVR32ではソフトウェアによる逐次処理がたくさん必要となり、結果的に割込処理応答が遅くなるだろう。
ハンドラ・ベクタ・テーブルを持つMCUだと、割込優先度が固定化されているため、ソフト側で優先度を変更する余地は無いが、一方で割込要因と優先度の判定が不要で高速化できる。
AVR32では、ソフトで割込要因と優先度を判定するため、速度が遅い反面、柔軟であるとも言える。ハードが簡潔になりコストが安くもなる。ただ、割り込み応答が少しでも早くなるような配慮が無いところが残念だ。USARTの話に戻れば、ステータス・レジスタを外部信号モニタと割込に分割するとか、一般的な割込優先度に応じてmsbからフラグを割り当てるとかやりようはあるはずだ。clz命令の結果がそのままインデックスとして使える。
各モジュールのステータス・レジスタを眺めると、下位ビットを利用しているようだが、イミディエイトによるコンペア命令を意識しているのだろうか?それならclz命令ではなく、ctz(Count Tailing Zeros)命令が欲しかった。
USARTを例にして割込処理が遅くなる話をしたが、USARTやSPI,TWI等の転送系モジュールについては心配する必要はない。AT32UC3B0256系ではPDCA(Peripheral DMA Controller)が7つもあり、CPUに対する割込を低減できるからである。USARTなら割込要因のチェックがエラー系統のみに絞り込める。USBにはPDCAとは別のDMAが付いているようだ。
一方、割込応答が遅いということは、TC(Timer/Counter)やPWMにおいて周期を短く取れないということである。出力面ではそれほどネックにならないが、インプット・キャプチャになると、要求仕様に対応できるかよく検討する必要がある。
またTCからPDCAを起動できないのも注意が必要だ。
2009/11/29 追記
私がPDCAについて勘違いをしているようだ。
PDCA(Controllerのこと)は1つであり、実際に転送作業するPDCが各モジュールUSART1, USART0/2, SPI, SSC, TWI, PWM, ADCにそれぞれ付いているようだ。
PDCAが7つあるといってもそれぞれ対等に動作するのではなく、担当のモジュールが決まっているようだ。
まだまだマニュアルをよく読む必要がある。
EAGLEにおけるAT32UC3Bのパッケージ for OLIMEX
私の使っているEAGLE(4.15)の話(最新バージョンは持っていないので知らない)。
CADツールEAGLEの製品付属ライブラリには当然AVR32が含まれていない。こういうときは自分でライブラリ(シンボル、パッケージ、デバイス)を作る必要がある。シンボルが回路図用、パッケージが基板レイアウト用、デバイスがシンボル&パッケージの対応といったところだ。パッケージはパッドの寸法・配置を持つので、自分でマニュアルのパッケージ情報を参考に作成するのだが、手半田しようとすると少し大きめにする必要があり結構面倒だ。
EAGLEの付属ライブラリにあるパッケージを流用すると楽が出来る。今回使用するパッケージはTQFP48なのでライブラリのref-packages.lbrからTQFP48.pacを利用すればよい。
しかし基板製造の安さで有名なOLIMEXで製造しようとすると、8mil.druでデザイン・ルール・チェックに引っかかってしまう。OLIMEXに出そうとするとsmdパッドを細くするしかない。
製品パッケージ情報では幅が0.27mm(Max)だが、TQFP48.pacでは0.30mmになっている。試したところ、寸法幅上限の0.27mmまで細くすれば問題が無くなる。
自分のライブラリへTQFP48.pacをコピーし、ライブラリ編集でこれを開き、gridをmm単位に切り替えたら、以下のコマンドを実行する(どちらか一方)。
横長SMDの変更: change smd 1.5 0.27
縦長SMDの変更: change smd 0.27 1.5
変更モードに移ったら、変更するSMDをクリックしていく。1.5というのは元々のSMD長編の長さである。
AT32UC3B164, 1128, 1256の端子寸法はどれも同じなので、上記の話は共通である。
AT32UC3B064, 0128, 0256も端子寸法は共通だが、パッケージがTQFP64である。
AVR32 Studioがアップデート(2.3)していた
AVR32 Studio機能でプロジェクトを新規作成すると、ソースファイルを生成してくれるが、どうもこのファイルが古いということに気が付いた。
ソースファイルの元は、AVR32 UC3 Software Frameworkのはずだが、最新版Ver 1.5.0ではなく1.4.0のものが利用されている。
ふと思いついてAVR32 Studioのページを見てみると、「2.3」が出ている。私がダウンロードして1週間程後にバージョンが上がっていたらしい。
つまり最新版を使っているつもりで、最新版ではなかったと言うこと。
私のミスだけど、Update実行しても教えてくれない開発環境にも問題があるよなあ。
旧版をアンインストールして、AVR32 Studio 2.3.0をインストールして、新しくプロジェクトを作成したら、プロジェクトに含まれるドライバの先頭行が「AVR32-SoftwareFramework-AT32UC3B-1.5.0」になった。
ついでにグチを。
AVR32 UC3 Software Frameworkのソースコードの改行はかなりバラバラだ。WindowsだけでなくLinuxもサポートするので良くありがちな話でもあるが、1つのファイルの中でも改行コードの「\n」と「\r\n」が混じっていたりする。
見つけたところでは、先頭行の「/* This source file is part 〜」が常に「\r\n」でその他の行(\nのこともあれば\r\nもある)と食い違っている。たぶんこの先頭行はプログラムで自動挿入したんだろう。
コンパイルでエラーが出る訳でもないし、AVR32 Studio(というかEclipse)のエディタが文句を言う訳でもないが、以前書いた「割込処理の高速化(案)」を実現するツールを作ってみて、元ソースとdiffしたらいろいろ食い違いが発生するから何だろうと気に掛かった次第。
Windowsならともかく、Linuxならsed等のフィルタ・プログラムを使えば間単位統一できるだろうに。
割込処理の高速化(案)
割込処理(exception.xの_int0〜_int3とintc.cの_get_interrupt_handler)を見てスピードの点で不満を持ち、もっと高速化できないものか考えてみた。実際に問題があって高速化するのではないが、「割込ルーチンは出来るだけ短く(早く)」が基本なので。
例としていつものAT32UC3B0256を取り上げるが、レジスタのアドレスが変わるくらいでAVR32なら共通しているはずだ。
まずは現状から。INTCの割込ハンドラ部分_int0。Releaseでビルドした結果を以下に示す。_int1〜_int3もあるが、マクロで展開しているだけなので省略。
00000104 <_int0>: // CPU upon interrupt entry. No other register is saved by hardware. #elif __AVR32_AP__ // PC and SR are automatically saved in respectively RAR_INTx and RSR_INTx by // the CPU upon interrupt entry. No other register is saved by hardware. pushm r8-r12, lr #endif 104: 30 0c mov r12,0 106: f0 1f 00 12 mcall 14c10a: 58 0c cp.w r12,0 10c: f8 0f 17 10 movne pc,r12 110: d6 03 rete
_int1〜_int3のコードは、オフセット104の第2オペランドがINTに応じて1〜3へ変化するだけで、それ以外は共通である。使用される命令はすべて1cycleで実行され、オフセット10cで目的の割込要因本体の割込ハンドラへジャンプするため、4サイクルを消費する。割込ハンドラのアドレスが0(未登録)のときオフセット110へ到達してreteする(登録されているとき、割込ハンドラ本体からreteする)。
次はオフセット106で呼び出している_get_interrupt_handlerのコードである(Releaseモードで最適化した場合)。
<<C言語ソース>> __int_handler _get_interrupt_handler(unsigned int int_lev) { unsigned int int_grp = AVR32_INTC.icr[AVR32_INTC_INT3 - int_lev]; unsigned int int_req = AVR32_INTC.irr[int_grp]; return (int_req) ? _int_handler_table[int_grp]._int_line_handler_table[32 - clz(int_req) - 1] : NULL; } <<オブジェクトファイルの逆アセンブル・リスト>> 00000000 <_get_interrupt_handler>: 0: e0 68 00 83 mov r8,131 4: fe 79 08 00 mov r9,-63488 8: f0 0c 01 0c sub r12,r8,r12 c: f2 0c 03 2a ld.w r10,r9[r12<<0x2] 10: f4 c8 ff c0 sub r8,r10,-64 14: f2 08 03 2c ld.w r12,r9[r8<<0x2] 18: 58 0c cp.w r12,0 1a: 5e 0c reteq r12 1c: f8 08 12 00 clz r8,r12 20: 48 09 lddpc r9,20 <_get_interrupt_handler+0x20> 20: R_AVR32_9W_CP .text._get_interrupt_handler+0x34 22: f0 08 11 1f rsub r8,r8,31 26: f2 0a 00 39 add r9,r9,r10<<0x3 2a: 72 1a ld.w r10,r9[0x4] 2c: f4 08 03 2c ld.w r12,r10[r8<<0x2] 30: 5e fc retal r12
割込要因が見つからなければオフセット1aで返るが、通常処理は割込ハンドラが呼ばれるオフセット30であるから15サイクル。
合わせて19cycle掛かっている。
まず_get_interrupt_handlerの先頭で
intc.c, 113: unsigned int int_grp = AVR32_INTC.icr[AVR32_INTC_INT3 - int_lev];
とやっているが、「AVR32_INTC_INT3 - int_lev」などと引き算するのが回りくどい。この行は該当レジスタICRnの値を取得したいだけだ。なら引数で割込優先度(int_lev)でなく、レジスタICRnのポインタあるいはレジスタ値を渡せばよい。そうすればオフセット0, オフセット8の2cycle、8byteが省ける。
intc.c, 162: return (int_req) ? _int_handler_table[int_grp]._int_line_handler_table[32 - clz(int_req) - 1] : NULL;
clz命令は、msb側からビットをチェックして、0がいくつ連続して存在するか調べる。int_irqの元になるIRRnがビットフラグだからこのようになるが、問題は値がインデックス番号と逆になるため、32-1(=31)から引く必要がある。この手間を省くためには_int_line_handler_table配列の並び順を逆にする方法で対処する。ただし、いずれの配列(_int_line_handler_table[]に代入されている_int_line_handler_table_??のこと)も長さが32も無いので、予め_int_handler_table[int_grp]のポインタをマイナス方向へずらしておく。これで問題ないはずだ(他にもINTC_init_interruptsとINTC_register_interruptの対応が必要)。これで省けるのはオフセット22の僅か1cycle, 4byteだ。
1つ手前で、clz命令を使ったインデックス・アクセスで「予め_int_handler_table[int_grp]のポインタをずらす」と書いたが、更にこの配列を1つ余計に確保してしまうという方法もある。
この方法の利点は、int_req==0の対処が要らなくなることだ。int_req==0のときclz(int_req)は32となり、1つ余計に確保した末尾要素へアクセスするので、ここに何もせずreteする無効ハンドラを登録しておけばよい。
オフセット18, 1a、更にはオフセット10aが不要になり、3cycle, 6byte省略できる。
他には_get_interrupt_handlerの関数コールを止めて、コード展開する方法がある(C言語のinline展開に相当)。割込レベル毎に完全展開してしまえば、mcallとretalの2cycle, 6byteを省略できる(当然展開した分、使用メモリは増える)。
以上、すべて合計して19cycle→11cycle、57.9%の低減(高速化)である。
ICRnレジスタのアドレスかオフセットが必要なため、特定の製品で修正するのは難しくないが、汎用的に修正可能とするとちょっと厄介だ。何かツールを作るか、あるいはAtmelが対処してくれるまで待つか...対処する訳無いか。
しかし、高速化しても割込本体処理開始までに11cycleは掛かり過ぎ。
根本的にソフトウェアで割込要因を調べて該当ハンドラへ飛ぶだけなのだが、割込要因と無関係な割込レベルのレジスタを読み必要があること(多重割込サポート上仕方がない?)、その上割込グループと割込ラインの2段階で割込要因を特定するという2つの手間が掛かる点が問題だ。
割込グループ番号を、それより小さい番号の割込グループの割込ライン総計にすれば、割込ハンドラ・テーブルを1次元配列にでき、要素番号のアクセスが割込グループ番号+割込ラインで計算できて少し早くなるのだがどうだろう。
AVR32関連のまとめ
AVR32 Studioへリンカ・スクリプトを追加
AVR32 Studioから既存プロジェクトに新規作成したリンカ・スクリプトを追加する。OSが存在する場合、まずリンカ・スクリプトでリンカを操作することはないので仕方がないのかもしれないが、組み込みだとリンカ・スクリプト修正が必要となることが多い。
ファイルを新規作成し、プロジェクトに登録する。
- メニュー「File」→「New」→「File」を選択。
- 追加対象プロジェクトの任意フォルダを選択。
- 「File name」にファイル名を入力(拡張子は任意だが、とりあえず.ldsとする)。
- Finishボタンを押す。
これで空のリンカ・スクリプト・ファイルが作成された。
リンカに対して、新規作成したリンカ・スクリプトを読み込むよう指定する。
- プロジェクトのアイコンを選択して、右クリックからPropertiesを選択。
- 「C/C++ Build」→「Settings」を選択。
- 「Configuration」を「[ All Configurations ]」に切り替える。
- タブ「Tool Settings」を選択。
- 「AVR32/GNU C Linker」→「Miscellaneos」を選択。
- 「Other options」のAdd(アイコン)ボタンをクリック。
- 表示されたダイアログで以下のように入力し、OKボタンを押す。
- OKボタンを押して、プロパティ設定ダイアログを閉じる。
ビルドして試してみる。以下はTC(Timer Counter)の例題3にリンカ・スクリプトlink-script.ldsを指定した。中身はデフォルト・リンカ・スクリプトそのままである。
avr32-gcc -nostartfiles -L../src/SOFTWARE_FRAMEWORK/UTILS/LIBS/NEWLIB_ADDONS -march=ucr1 -Wl,--gc-sections -Wl,-e,_trampoline -Xlinker -TC:\dev\avr32\tc3\src\link-script.lds -mpart=uc3b0256 -Wl,--gc-sections --rodata-writable --direct-data -otc3_DEBUG.elf src\tc_example3.o src\intc.o src\exception.o src\SOFTWARE_FRAMEWORK\UTILS\STARTUP_FILES\GCC\crt0.o src\SOFTWARE_FRAMEWORK\DRIVERS\USART\usart.o src\SOFTWARE_FRAMEWORK\DRIVERS\TC\tc.o src\SOFTWARE_FRAMEWORK\DRIVERS\PM\pm_conf_clocks.o src\SOFTWARE_FRAMEWORK\DRIVERS\PM\pm.o src\SOFTWARE_FRAMEWORK\DRIVERS\GPIO\gpio.o src\SOFTWARE_FRAMEWORK\BOARDS\EVK1101\led.o src\SOFTWARE_FRAMEWORK\ASM\trampoline.o -lnewlib_addons-at32ucr1-speed_opt
拡張子が何でもよいのは、ベースとなるCDT(EclipseのC言語開発用プラグイン)がリンカ・スクリプトを認識しないからだ。
いくらリンカ・スクリプトをプロジェクトに追加しても、AVR32 Studio内で編集できない(外部エディタが起動される)し、書き換えを検出できない(ビルドしてもリンカが実行されない)。