MAXQ2000マイクロコントローラ用JTAGブートローダマスタの実装
要約
MAXQ®マイクロコントローラが提供するJTAGブートローダは、標準化されたコマンドセットを使って、外部のJTAGマスタが任意のMAXQマイクロコントローラを容易に識別およびプログラムすることができるようにします。このアプリケーションノートに含まれているコードを基に、フル機能のJTAGブートローダマスタアプリケーションを構築することが可能です。マスタアプリケーションは、標準化されたブートローダ用コマンドセットをサポートする任意のMAXQマイクロコントローラに対してコードおよびデータメモリ内容を識別、初期化、ロード、およびベリファイを行うことができます。
概要
書き換え可能なプログラムメモリを内蔵したMAXQマイクロコントローラは、一般的にマイクロコントローラのJTAG互換デバッグポートを使用したプログラムメモリのロードを可能にするROMベースのブートローダを備えています。JTAGブートローダが提供する正確な機能はデバイスによって異なりますが、一般的に、プログラム/データメモリの読取り、書込み、ベリファイ、および消去を可能にするコマンドが含まれています。デバイスによっては、ブートローダ用に他のインタフェース(シリアルポートやSPI™インタフェースなど)を提供している場合もありますが、2つの理由から、JTAGインタフェースが最も広く使用されています。第1に、JTAGインタフェースはインサーキットデバッグ機能をサポートするために最初から用意されています。第2に、JTAGインタフェースは(シリアルポートと違って)普通はエンドユーザアプリケーションによって使用されることはありません。いったんプログラムコードがロードされた後は、必要に応じてパスワード機構を使用することにより、ブートローダやインサーキットデバッグ機能へのアクセスを制限することができます。各MAXQデバイスがサポートしている機能の詳細については、データシートおよびユーザーズガイドを含む、製品固有の情報をご覧ください。
このアプリケーションノートでは、MAXQ2000用JTAGブートローダマスタの実装に当たって必要になる基本的な手順について説明します。この手順には、JTAGポートへのインタフェース、Test Access Port (TAP)コントローラとの通信、ブートローダモードの有効化、およびROMベースのブートローダへのコマンドの送信が含まれます。JTAGポートの動作は一般的にすべてのMAXQデバイスで同一であり、MAXQブートローダは共通のコマンドセットを使って動作するため、このアプリケーションノートで取り上げる話題のほとんどは(そしてサンプルコードの大部分も)、どのMAXQマイクロコントローラ用にJTAGブートローダマスタを実装する場合にも該当します。
この実装には、シリアルポートを除いてMAXQ2000の特別な機能を何も使用していません。すなわち、ここで示すサンプルコードは、十分なプログラムメモリを備えた任意のMAXQ20デバイス上で動作するように容易にターゲットを変更することができます。コードはMAXQのアセンブリ言語で書かれており、MAX-IDE開発環境を使用してコンパイルを行いました。コードはダウンロードしてご利用いただけます。
ハードウェアの構成
このアプリケーションノートのサンプルコードは、2組のMAXQ2000の評価(EV)キットを使用して開発されました。ここで説明するソフトウェアの実行には、2組のMAXQ2000のEVキットが必要になります。1組目のMAXQ2000 (JTAGマスタ)がサンプルコードを実行し、2組目のMAXQ2000はJTAGスレーブとして動作し、マスタによって再プログラムされます。どちらのMAXQ2000マイクロコントローラにも、標準的な8.00MHzの水晶を使用しました。
マスタ側のMAXQ2000のEVキットには、試作用エリアに2 x 5ピンのヘッダを装着するという変更を加えました。これによって、JTAGケーブル接続のマスタ側を提供しています。ヘッダ上のピンは標準的なMAXQのJTAGヘッダレイアウトにしたがっており、表1に示した接続になっています。
表1. MAXQ2000のJTAG接続
JTAG Header Pin | JTAG Cable Function | MAXQ2000 JTAG Master Connection |
MAXQ2000 JTAG Slave Connection |
1 | TCK (Test Clock) | P0.0 (Output) | P4.0 (Input) |
2 | GND | GND | GND |
3 | TDO (Test Data Out) | P0.1 (Input) | P4.3 (Output) |
4 | VREF | – | – |
5 | TMS (Test Mode Select) | P0.2 (Output) | P4.2 (Input) |
6 | nRESET | P0.4 (Open Drain) | nRESET (Input) |
7 | Keyed pin | – | – |
8 | +5V | – | – |
9 | TDI (Test Data In) | P0.3 (Output) | P4.1 (Input) |
10 | GND | GND | GND |
スレーブ側のMAXQ2000のEVキットには何も変更を加える必要がありません。マスタ側のMAXQ2000のEVキットは、試作用エリアに2 x 5のJTAGヘッダを装着して前述の通りに配線します。そして、2組のEVキットを接続します。マスタ側EVキット上の試作用エリアに装着した2 x 5 JTAGヘッダと、スレーブ側のEVキット上の標準JTAGヘッダ(J4)の間を、標準の2 x 5 JTAGケーブル(一般的にシリアル/JTAG変換ボードとMAXQのEVキットとの接続に使用されるタイプのJTAGケーブル)で接続します。このJTAG 2 x 5コネクタは、MAXQ2000のEVキットに同梱されています。
話を簡単にするため、JTAGケーブルを通してマスタ側EVキットからスレーブ側EVキットに電源または基準電圧を接続することはしませんでした。その代わりに、両方のEVキットが同じVDDIO電圧(約3.6V)で動作するように構成してあります。この構成によって、マスタとスレーブのMAXQ2000が共通のI/Oレイルレベルで確実に動作させることが出来ます。
スレーブ側のEVキットには、ヘッダJ3にLCDドータボード(MAXQ2000-K01)も装着します。両方のボード(およびシリアル/JTAGボード)のジャンパおよびDIPスイッチの設定を表2に示します。注:記載されていないジャンパはすべて開放にしてください。図1に、最終的な構成を示します。
表2. 各ボードのスイッチおよびジャンパの設定
Board | Switch or Jumper | Setting | Notes |
Serial-to-JTAG Board | JH1 | Connected | |
JH2 | Connected | ||
JH3 | Connected | Supplies 5V power over JTAG cable | |
Master MAXQ2000 EV Kit | JU1 | Pins 1 and 2 connected | Powers VDD from 2.5V supply |
JU2 | Pins 1 and 2 connected | Powers VDDIO from 3.6V supply | |
JU3 | Pins 1 and 2 connected | Powers VLCD from 3.6V supply | |
JU11 | Connected | Powers kit board from JTAG 5V supply | |
DIP SW1 | Switches #4 and #8 ON; all other switches OFF | Enables serial port 0 output to J5 | |
DIP SW3 | All switches OFF | ||
DIP SW6 | All switches OFF | ||
Slave MAXQ2000 EV Kit | JU1 | Pins 1 and 2 connected | Powers VDD from 2.5V supply |
JU2 | Pins 1 and 2 connected | Powers VDDIO from 3.6V supply | |
JU3 | Pins 1 and 2 connected | Powers VLCD from 3.6V supply | |
DIP SW1 | All switches OFF | ||
DIP SW3 | All switches OFF | ||
DIP SW6 | All switches OFF |
図1. JTAGデモ用の構成
MAXQのJTAGインタフェース
MAXQマイクロコントローラのJTAGインタフェースは、Test Access Port (TAP)コントローラに対する情報のシフトイン/シフトアウトに使用する4本の信号線で構成されています。そしてこのTAPコントローラが、MAXQのブートローダおよびインサーキットデバッグ機能へのアクセスを提供します(デバッグマスタの実装は、ブートローダマスタの実装と似ていますが、このアプリケーションノートの範囲外のことです)。4本のJTAG信号線を表3に示します。
表3. JTAGインタフェースの信号
JTAG Signal | Signal Name | Direction (Master) | Direction (Slave) | Signal Description |
TMS | Test Mode Select | Output | Input | この信号線は、TCKとともに、TAPコントローラを1つの動作状態から次の動作状態に移行させるために使用されます。 |
TCK | Test Clock | Output | Input | この信号はJTAGインタフェースのクロックを提供します。JTAGのクロックは、最高でもスレーブのクロック周波数の8分の1に制限されています。たとえば、スレーブがクロック周波数8MHzで動作する場合、TCKのJTAGクロックを1MHzより高い速度で動作させることはできません。 |
TDI | Test Data In | Output | Input | この信号はマスタからスレーブに送信されるデータを搬送します。 |
TDO | Test Data Out | Input | Output | この信号はスレーブからマスタに返信されるデータを搬送します。 |
厳密に言うと、nRESETピンはJTAGインタフェースの一部ではありません。JTAGマスタによるスレーブマイクロコントローラのリセットを可能にするために、JTAGケーブルに含められています。スレーブマイクロコントローラのリセットはブートローダモードに入るために必要な手順であり、またJTAG通信に想定外の中断が発生した場合にも役立ちます。
JTAGインタフェースは全二重であるため、TDI信号線でマスタからスレーブにデータがシフトインされるのと同時に、TDO信号線でスレーブからマスタにデータがシフトアウトされます。スレーブはTCKの立上りエッジで(TDIとTMSの両方について)入力データのサンプリングを行い、TCKの立下りエッジにおいてマスタへの出力データをTDO上に駆動します。入力、出力データともに、値の転送は最下位ビットから順に行われます。
このアプリケーションノートでは、JTAGインタフェースとTAPコントローラについて、サンプルコードの動作を説明する目的で簡単に概要だけを示します。これらの機能に関するさらに詳細な解説については、当社のWebサイト上に用意されている「MAXQファミリのユーザガイド」の、「テストアクセスポート(TAP)」、「インサーキットデバッグモード」、および「インシステムプログラミング」の各項を参照してください。
TAPコントローラとの通信
下の図2に示すように、TAPコントローラはステートマシンを中心に構成されています。TAPのステートマシンには、16通りの独立した状態が含まれています。1つの状態から次の状態への遷移は、TCKの立上りエッジごとに、TMS信号の値に基づいて行われます。たとえば、TAPコントローラがSelect-DR-Scan状態のとき、TCKに立上りエッジが発生したとすると:
- TMS = 1の場合、TAPコントローラはSelect-IR-Scan状態に遷移します。
- TMS = 0の場合、TAPコントローラはCapture-DR状態に遷移します。
- 5回連続「1」の遷移(TMSをハイにした状態で完全に5サイクルの間TCKにクロックを供給すること)によって、最初の状態が何であっても、常にステートマシンをTest-Logic-Resetに戻すことができます。すなわち、TAPコントローラの現在の状態が不明の場合や、JTAGマスタ/スレーブ間の通信が何らかの形で中断された場合でも、5クロック分の「1」遷移を行うことによって、常にTAPコントローラを既知の状態に戻すことが可能です。
- たとえTCKクロックが作動し続けていても、TAPコントローラの状態に影響を与えることなくJTAG通信を一時停止して、Run-Test-Idle、Pause-DR、またはPause-IRのいずれかの状態を無期限に保つことが可能です。
図2. Test Access Port (TAP)のステートマシン
TAPコントローラのステートマシンでは2つの制御レジスタへのアクセスが提供されており、さらにそれらのレジスタを通して、ブートローダ、デバッグインタフェース、およびその他の機能へのインタフェースが提供されています。
- IR (Instruction Register:命令レジスタ)は、常に3ビット幅です。このレジスタはインデックスレジスタとして機能し、それがさらにDRの機能を制御します(後述)。
- DR (Data Register:データレジスタ)は、TAPコントローラ内に複数存在するレジスタの1つに対するアクセスポイントになります。DRに対するビットの入出力に当たって実際にアクセスされるレジスタは、IRの現在の値によって決まります。
図3. TAPコントローラのレジスタアクセス
図3に示すように、DRはIRの値に応じて3個の内部レジスタの1つを指します。
- IR = 011bのとき、TAPコントローラはバイパスモードです。このモード(=TAPコントローラのデフォルトのモード)では、(TDIを通して)DRにシフトインされたデータが、単純にTDOを通して再びシフトアウトされます。TAPコントローラを通ってシフトされるデータによって変更される内部レジスタはありません。
- IR = 100bにセットすると、TAPコントローラはシステムプログラミングモードになります。このモードでは、DRにシフトインされるデータが3ビットのシステムプログラミングレジスタにシフトインされます。このレジスタ(MAXQマイクロコントローラからはICDFレジスタのビット[3:1]という形でアクセス可能)は、リセット後にMAXQが通常のプログラム実行モードに入るかブートローダモードに入るかを制御します。ブートローダモードを有効化する場合、ブートローダがどのインタフェース(JTAG、シリアル、またはSPI)を使用するかの制御も行います。
- IR = 010bにセットすると、TAPコントローラはデバッグモードになります。このモードでは、DRにシフトインされたデータが、ブートローダから読出し可能な内部の10ビットデバッグレジスタにシフトインされます。ブートローダから出力されるデータも(2ビットのステータスビットと一緒に)このレジスタを通してシフトアウトされ、そこからTDOを通して出力されます。このレジスタは、ブートローダモードとインサーキットデバッグモードの両方でデータの転送に使用されます。
JTAGポートおよびリセット信号線の制御
スレーブ側MAXQ2000上のJTAG/TAPポートの4本の信号線(TMS、TCK、TDO、およびTDI)とnRESET信号線は、それぞれマスタ側MAXQ2000上の1つのポート端子に接続されています。JTAGインタフェースを制御するための最初のステップは、これらの信号線を適切に設定することです。
#define TCK PO0.0 ; Test Clock - Master output #define TDO PI0.1 ; Test Data Out - Slave output, master input #define TMS PO0.2 ; Test Mode Sel - Master output #define TDI PO0.3 ; Test Data In - Master output #define RST PD0.4 ; Reset - Master open-drain output (on 1)4本のJTAG信号線は、標準的な駆動モードで操作します。マスタの側から見ると、TMS、TCK、およびTDIは常に出力として駆動され、一方(スレーブによって駆動される) TDOは常に入力になります。JTAG信号線の方向は固定であり、双方向ではありません。nRESET信号線だけは特別で、マスタ側からのオープンドレイン出力になるように構成します。通常、スレーブが自分のnRESET信号線をハイにプルアップしておき、マスタは信号線をプルダウンするか(スレーブをリセットするとき)または完全に解放しておくか(その他すべての場合)どちらかで良いようにします。マスタが明示的にスレーブ側のnRESET信号線をハイに駆動すべき理由はありません。
;============================================================================== ;= ;= initializeJTAG ;= ;= Sets up the port pins for the JTAG interface. ;= ;= Inputs : None ;= Outputs : None ;= Destroys : None ;= initializeJTAG: move PD0.0, #1 ; TCK - master output move PO0.0, #1 ; Drive high move PD0.1, #0 ; TDO - master input move PO0.1, #1 ; Weak pullup on move PD0.2, #1 ; TMS - master output move PO0.2, #1 ; Drive low move PD0.3, #1 ; TDI - master output move PO0.3, #1 ; Drive high move PD0.4, #0 ; RST - open drain when 1, tristate when 0 move PO0.4, #0 ; Weak pullup off retポート端子の初期化後、clock0とclock1の各ルーチンを使用して静的な0および1をTMS信号線にクロックインすることによって、TAPコントローラを1つの状態から次の状態に進ませます。JTAGのクロック速度がスレーブ側マイクロコントローラのシステムクロック速度の8分の1という最大値以下に保たれている限り、JTAGクロックは任意の周波数で駆動することが可能です。このとき、マスタのシステムクロック速度について検討する必要はありません。マスタのシステムクロック速度を、何らかの形でスレーブのシステムクロック速度に整合させる必要はありません。マスタの動作がスレーブより速くても遅くても、JTAGの通信に問題が生じることはありません。
この例では、スレーブ側MAXQ2000に8MHzクロックの水晶が装着されているため、マスタは最高1MHzまで問題なくJTAGクロックを駆動することができます。この例の場合、JTAGのクロック速度は100kHzなので十分です。
#define JCLOCK 40 ; 100kHz : (((10us / (1/8MHz)) / 2) ;============================================================================== ;= ;= clock0 ;= ;= Clocks a zero TMS bit into the JTAG interface. ;= ;= Inputs : None ;= Outputs : None ;= Destroys : LC[0] clock0: move TMS, #0 ; Drive TMS low move LC[0], #JCLOCK djnz LC[0], $ move TCK, #1 ; Clock rising edge move LC[0], #JCLOCK djnz LC[0], $ move TCK, #0 ; Clock falling edge move LC[0], #JCLOCK djnz LC[0], $ ret ;============================================================================== ;= ;= clock1 ;= ;= Clocks a one TMS bit into the JTAG interface. ;= ;= Inputs : None ;= Outputs : None ;= Destroys : LC[0] clock1: move TMS, #1 ; Drive TMS high move LC[0], #JCLOCK djnz LC[0], $ move TCK, #1 ; Clock rising edge move LC[0], #JCLOCK djnz LC[0], $ move TCK, #0 ; Clock falling edge move LC[0], #JCLOCK djnz LC[0], $ retこの2つのルーチンがあれば、強制的にTest-Logic-Reset状態に戻すことによってTAPコントローラを初期化するルーチンを追加することができます。Test-Logic-Reset状態は、その名が示す通りTAPロジックの完全なリセットを実行するものであり、リセットの対象にはブートローダモードを有効化するかどうか、およびブートローダがどのインタフェースを使用するかを決定する各ビット(SPEおよびPSS[1:0])も含まれます。したがって、いったんブートローダモードに入った後でTAPコントローラをTest-Logic-Resetに設定すると、デバイスがリセットされ、ブートローダモードが終了します。このデモアプリケーションで使用するすべてのJTAGルーチン(testLogicReset自体は除く)では、ルーチンの開始時にTAPコントローラがRun-Test-Idle状態であると想定しています。各JTAG通信ルーチンの実行中、TAPコントローラは様々な状態を遷移します。そしてルーチンの終了時に、TAPコントローラは常にRun-Test-Idleに戻されます。
;============================================================================== ;= ;= testLogicReset ;= clock0, clock1 ;= ;= Resets the JTAG/TAP controller to its starting state. ;= ;= Inputs : None ;= Outputs : None ;= Destroys : LC[0] ;= testLogicReset: call clock1 call clock1 call clock1 call clock1 call clock1 call clock1 call clock1 call clock0 ; Brings us to Run-Test-Idle ret
TAP命令レジスタへの書込み
TAPコントローラのIRに対する値のロードは、TAPステートマシンをShift-IR状態に遷移させた上で、新しい3ビットの値をクロックインすることによって行います。この値が実際にシフトレジスタから命令レジスタにコピーされるのは、Update-IR状態に入ってからです。
すべてのJTAGのシフトレジスタ操作と同様に、新しい値がシフトインされる際には、レジスタの現在の内容が(TDOによって)シフトアウトされます。しかし、シフトアウトされる値は常に固定しています(001b)。これは、JTAGインタフェースの機能をテストする際に使用するようJTAG規格によって要求されている動作です。
ビットをシフトイン/シフトアウトするための手順は、どちらのシフトレジスタ(IRまたはDR)でも同じです。TAPステートマシンが、それぞれShift-IRまたはShift-DR状態になっている必要があります。入力ビットは(TDIにおいて)各TCKサイクルの立上りエッジでTAPコントローラによってサンプリングされ、出力ビットは(TDOにおいて) TCKサイクルの立下りエッジで駆動されます。
;============================================================================== ;= ;= shift ;= ;= In a shift register state, clocks in a TDI bit and clocks out a TDO bit. ;= ;= Inputs : C - Bit to shift in to TDI. ;= Outputs : C - Bit shifted out from TDO. ;= Destroys : PSW, LC[0] ;= shift: jump C, shift_bit1 shift_bit0: move TDI, #0 ; Shift in zero bit jump shift_bitEnd shift_bit1: move TDI, #1 ; Shift in one bit jump shift_bitEnd shift_bitEnd: move LC[0], #JCLOCK djnz LC[0], $ move TCK, #1 ; Rising edge, TDI is sampled move LC[0], #JCLOCK djnz LC[0], $ move TCK, #0 ; Falling edge, TDO is driven out move LC[0], #JCLOCK djnz LC[0], $ move C, TDO ; Latch TDO value ret最後のビットを除いて、転送する各ビットについてTMSをローに保つ必要があります。これは、各ビットのシフトイン/シフトアウト時にステートマシンがShift-IRまたはShift-DR状態に保たれるようにするために重要となります。最終ビットサイクルにはTMSをハイに駆動して、最終ビットのクロックイン/クロックアウト時にTAPコントローラがExit1-DRまたはExit1-IR状態に進めるようにする必要があります。
一例として、表4ではJTAGマスタによって100b (システムプログラミングモード)という値がIRレジスタにシフトインされる様子を示しています。IRレジスタは起動時にバイパス値011bにプログラムされ、TAPコントローラはRun-Test-Idle状態からスタートします。下の図4に示すように、各ビットは最初に最下位ビット、最後に最上位ビットという順番でTAPコントローラにシフトイン(およびシフトアウト)されます。したがって、最初のシフトサイクルでは新しい値のビット0がシフトインされ、元の値のビット0がシフトアウトされます。
表4. 命令レジスタのシフトの例
TCK | TMS | TDI | TDO | TAP State | Shift Register | Instruction Register | ||||
bit 2 | bit 1 | bit 0 | bit 2 | bit 1 | bit 0 | |||||
0 | 1 | x | x | Run-Test-Idle | x | x | x | 0 | 1 | 1 |
1 | 1 | x | x | Select-DR-Scan | x | x | x | 0 | 1 | 1 |
0 | 1 | x | x | Select-DR-Scan | x | x | x | 0 | 1 | 1 |
1 | 1 | x | x | Select-IR-Scan | x | x | x | 0 | 1 | 1 |
0 | 0 | x | x | Select-IR-Scan | x | x | x | 0 | 1 | 1 |
1 | 0 | x | x | Capture-IR | 0 | 0 | 1 | 0 | 1 | 1 |
0 | 0 | x | x | Capture-IR | 0 | 0 | 1 | 0 | 1 | 1 |
1 | 0 | x | x | Shift-IR | 0 | 0 | 1 | 0 | 1 | 1 |
0 | 0 | 0 | x | Shift-IR | 0 | 0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | x | Shift-IR | 0 | 0 | 1 | 0 | 1 | 1 |
0 | 0 | 0 | 1 | Shift-IR | 0 | 0 | 0 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | Shift-IR | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 1 | 1 | 0 | Shift-IR | 0 | 0 | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 0 | Exit1-IR | 0 | 0 | 0 | 0 | 1 | 1 |
0 | 1 | x | 0 | Exit1-IR | 1 | 0 | 0 | 0 | 1 | 1 |
1 | 1 | x | 0 | Update-IR | 1 | 0 | 0 | 1 | 0 | 0 |
0 | 0 | x | x | Update-IR | 1 | 0 | 0 | 1 | 0 | 0 |
1 | 0 | x | x | Run-Test-Idle | x | x | x | 1 | 0 | 0 |
次に示すのが、この操作を行うために使用したルーチンshiftIR3です。
;============================================================================== ;= ;= shiftIR3 ;= clock0, clock1, shift ;= ;= Shifts a 3-bit value into the IR register. ;= ;= Inputs : A[0] - Low three bits contain value to shift into IR ;= Outputs : None ;= Destroys : AP, APC, A[0], PSW, LC[0] ;= shiftIR3: move APC, #80h ; Acc => A[0], turn off auto inc/dec call clock1 ; (Select DR Scan) call clock1 ; (Select IR Scan) call clock0 ; (Capture IR - loads 001b to shift register) call clock0 ; (Shift IR) move TMS, #0 ; Drive TMS low move C, TDO ; xxxxx210 c = s rrc ; sxxxxx21 c = 0 call shift ; Shift in IR bit 0 rrc ; ssxxxxx2 c = 1 call shift ; Shift in IR bit 1 rrc ; sssxxxxx c = 2 move TMS, #1 ; Drive TMS high for last bit call shift ; Shift in IR bit 2 (Exit1 IR) call clock1 ; (Update IR) call clock0 ; (Run Test Idle) ret
TAPデータレジスタへの書込み
TAPコントローラのDRに対する値のシフトイン/シフトアウトは、IRのロード/アンロードと良く似た方法で行います。通常この操作は、100b (TAPコントローラをシステムプログラミングモードに移行)または010b (TAPコントローラをデバッグモードに移行)という2つの値のいずれかをIRにセットした場合にのみ行います。
システムプログラミングモードが有効化されている場合、DRレジスタのロード/アンロードは次のような動作になります。
- DRレジスタのシフトイン/シフトアウトは3ビット幅です。3つすべてのビットが有効なデータを表します。
- DRレジスタに転送された値は、状態がUpdate-DRに到達した時点でスレーブ側マイクロコントローラの内蔵システムプログラミングレジスタにコピーされます。この3つのビットは次のように使用されます。
- ビット0は、スレーブ側マイクロコントローラからレジスタビットICDF.1 (SPE)としてアクセス(読み/書き)可能であり、システムプログラムイネーブルビットとも呼ばれます。このビットは、ブートローダモード(SPE = 1)または通常のプログラム実行モード(SPE = 0)のどちらに入るかを決定するために、リセット後にユーティリティROMによって調べられます。
- ビット1および2は、スレーブ側マイクロコントローラからレジスタビットICDF.2-3 (PSS0-PSS1)としてアクセス(読み/書き)可能であり、プログラミングソース選択ビットとも呼ばれます。MAXQ2000などのような、ブートローダへのインタフェースを2種類以上サポートするマイクロコントローラでは、SPE = 1の場合に、どのブートローダインタフェースが有効化されるかを選択するためにこれらのビットが使用されます。SPE = 0 (通常のプログラム実行モード)の場合、これらのビットの設定には何の効果もありません。
- DRレジスタから転送される値は、システムプログラミングレジスタの元の値です(Capture-DR状態に入った時点でシフトレジスタにラッチされます)。
- DRレジスタのシフトイン/シフトアウトは10ビット幅です。シフトアウトされるデータの場合、10個すべてのビットが有効なデータを表します(8個のデータビットと2個のステータスビット)。シフトインされるデータの場合、8個のデータビットの値だけが使用され、2個のステータスビットは使用されません。
- DRレジスタにシフトインされた値の上位8ビットが、さらに(ユーティリティROMから実行される)ブートローダによって、ブートローダコマンドの一部としてアンロードされ、読まれます。
- DRレジスタからシフトアウトされる10ビットの値は、ブートローダによって(コマンド出力の一部として)ロードされた8ビットの値と、TAPコントローラによってセットされた2ビットのステータス情報で構成されます。
;============================================================================== ;= ;= shiftDR3 ;= ;= Shifts a 3-bit value into the DR register. This operation should only be ;= performed when IR =100b (System Programming Mode). ;= ;= Inputs : A[0] - Low 3 bits contain value to shift into SPB (PSS1:PSS0:SPE) ;= Outputs : None ;= Destroys : AP, APC, A[0], PSW, LC[0] shiftDR3: move APC, #80h ; Acc => A[0], turn off auto inc/dec call clock1 ; (Select DR Scan) call clock0 ; (Capture DR) call clock0 ; (Shift DR) move TMS, #0 ; Drive TMS low move C, TDO ; xxxxx210 c = s rrc ; sxxxxx21 c = 0 call shift ; Shift in DR bit 0 rrc ; ssxxxxx2 c = 1 call shift ; Shift in DR bit 1 rrc ; sssxxxxx c = 2 move TMS, #1 ; Drive TMS high for last bit call shift ; Shift in DR bit 2 (Exit1 DR) call clock1 ; (Update DR) call clock0 ; (Run Test Idle) ret ;============================================================================== ;= ;= shiftDR ;= clock0, clock1, shift ;= ;= Shifts a 10-bit value into and out of the DR register. This operation ;= should only be performed when IR = 010b (Debug/Loader Mode). ;= ;= Inputs : A[0] - Byte value (input) to shift into DR ;= Outputs : A[0] - Byte value (output) shifted out of DR ;= A[1] - Low two bits are status bits 1:0 shifted out of DR ;= A[15] - Byte value shifted in, cached for use by shiftDR_next ;= Destroys : AP, APC, PSW, LC[0] shiftDR: move APC, #80h ; Acc => A[0], turn off auto inc/dec move A[15], A[0] ; Cache input byte value for use by shiftDR_next sla2 ; Add two empty bits (for status) call clock1 ; (Select DR Scan) call clock0 ; (Capture DR) call clock0 ; (Shift DR) move TMS, #0 ; Drive TMS low move C, TDO ; xxxxxxxx76543210 c = s rrc call shift ; Shift in DR bit 0 rrc call shift ; Shift in DR bit 1 rrc call shift ; Shift in DR bit 2 rrc call shift ; Shift in DR bit 3 rrc call shift ; Shift in DR bit 4 rrc call shift ; Shift in DR bit 5 rrc call shift ; Shift in DR bit 6 rrc call shift ; Shift in DR bit 7 rrc call shift ; Shift in DR bit 8 rrc move TMS, #1 ; Drive TMS high for last bit call shift ; Shift in DR bit 9 (Exit1 DR) call clock1 ; (Update DR) call clock0 ; (Run Test Idle) push Acc ; sddd dddd 10xx xxxx sra4 ; ssss sddd dddd 10xx sra2 ; ssss sssd dddd dd10 and #0003h ; ---- ---- ---- --10 move A[1], Acc ; Return status bits only in A[1] pop Acc and #0FF00h xch ; Return data bits only in A[0] ret
JTAGブートローダモードへの移行
MAXQ2000をJTAGブートローダモードに移行させるためには、以下の手順が必要になります。
- TAPコントローラを初期化し、Test-Logic-Reset状態にリセットします。
- 命令レジスタ(IR)に100bをセットして、システムプログラミングモードを有効化します。
- データレジスタ(DR)に001bをセットします。これによって. SPE (システムプログラミングイネーブル)ビットに1がセットされてブートローダが有効化され、PSS[1:0] (プログラミングソース選択)ビットに00bがセットされてJTAGインタフェースが選択されます。
- nRESETをローに保持してMAXQ2000をリセットします。
- nRESETを解放します。これによってMAXQ2000はユーティリティROM内の標準リセットポイント(8000h)にジャンプします。続いてユーティリティROMのコードがSPEおよびPSSビットの値を調べ、それにしたがってJTAGブートローダを起動します。この時点でブートローダの実行が開始され、JTAGコマンドの受入れが可能になります。
- 命令レジスタ(IR)に010bをセットして、デバッグモードを有効化します。これは、JTAGブートローダまたはデバッグエンジンのいずれかとの通信に使用されるモードです。この場合は、ブートローダとの通信を行うためにこのモードを使用します。
- DRを通して10ビットの値をシフトインすることによって、JTAGブートローダへのコマンド送信を開始します。
#define IR_DEBUG 010b ; Debug Mode #define IR_BYPASS 011b ; Bypass Mode (default) #define IR_SYSTEM_PROG 100b ; System Programming Mode (activate loader) ; System Programming Register settings #define SP_EXECUTE 000b ; Bootloader disabled #define SP_LOAD_JTAG 001b ; Activate JTAG bootloader #define SP_LOAD_UART 011b ; Activate UART bootloader #define SP_LOAD_SPI 101b ; Activate SPI bootloader (invalid on 2000) #define SP_RESERVED 111b ; Reserved value ... call initializeJTAG ; Set up port pins for JTAG call testLogicReset ; Reset JTAG port (ending state: Run-Test-Idle) move Acc, #IR_SYSTEM_PROG call shiftIR3 ; Load the System Programming instruction into IR move Acc, #SP_LOAD_JTAG call shiftDR3 ; Enable the bootloader in JTAG interface mode move RST, #1 ; Drive nRESET low move Acc, #100 ; Delay for 100ms call delayMS call clock0 ; Remain in Run-Test-Idle move RST, #0 ; Release nRESET move Acc, #100 ; Delay for 100ms call delayMS move Acc, #IR_DEBUG call shiftIR3 ; Enable access to the 10-bit debug shift register ;;;; Bootloader commands may now be shifted through the DR register call waitForPrompt ; Verify that the bootloader is responding ...
ブートローダとの通信
ブートローダの実行が開始されると、ユーティリティROM内のブートローダのコードは、DRシフトレジスタからロードされる内部レジスタから、コマンドコードの読取りを行います。またユーティリティROMは、JTAGマスタによるDRレジスタ経由のシフトアウトが可能な別の内部レジスタに対して、結果データの書込みを行います。こうして、ブートローダのコードとJTAGマスタの間で情報交換が可能になります。
しかし、ブートローダのコードとJTAGマスタは同期化されていません。JTAGは同期インタフェースであるため、JTAGクロック速度がMAXQのシステムクロックの8分の1という最高値より低い値に維持されている限り、通信という目的に関しては2つのクロックの正確な値は重要ではありません。しかし、MAXQのシステムクロック速度と、受け取ったブートローダコマンドの性格によって、ブートローダがコマンドを受け取ってから応答を返すまでにどの程度の遅延が発生するかが決まります。たとえば、たとえ両方のマイクロコントローラが100kHzのJTAGクロックを使って通信することができる場合でも、1MHzで動作しているMAXQ2000が与えられたブートローダコマンドに応答するには、10MHzで動作しているMAXQ2000よりも長い時間を必要とします。また、ROMバナーIDを返すコマンドやプログラムメモリを読み出すコマンドなど、単に情報の読出しと返却を行うコマンドは、フラッシュメモリのプログラミングなどの操作を含むコマンドよりも短い時間で完了します。
JTAGインタフェース経由で転送されるデータは、バッファリングされません。以前のコマンドが読み出される前に別の10ビットコマンドが送信されると、前のコマンドの結果は失われてしまいます。ROMコードとTAPコントローラは、以前のデータがJTAGマスタによってアンロードされるまで、それ以上のデータがブートローダによって送出されないことを保証していますが、逆方向の同期も必要です。JTAGマスタにとって、前回DRにシフトインしたバイトがブートローダによって読まれたかどうかを判断する手段が必要です。その手段があれば、JTAGマスタは次のバイトを送出するタイミングを判断することができます。これを知るための方法は、ブートローダの各8ビット/バイト出力とともにTAPコントローラによって送出される、追加の2個のステータスビットによって実現されます。
図4. DR経由によるデータおよびステータスビットのシフト
上の図4に示すように、TAPのデバッグモードでDRに対してシフトイン/シフトアウトされるデータは、8ビットのデータビット(ビット2~9)と2ビットのステータスビット(ビット0および1)で構成されます。JTAGマスタがデータをシフトインするときには、8個のデータビットだけが使用されます。2個のステータスビット(その場合でもシフトインしなければなりません)はTAPコントローラによって使用されず、ゼロまたは他の任意の値をセットして構いません。
TAPコントローラからシフトアウトされる10ビットの値の中で2ビットを占めているステータスビットは、ブートローダまたはデバッグエンジンの状態に関する情報を提供します。ブートローダの実行中に遭遇するのは、通常は後の2つのステータス(Debug BusyまたはDebug Valid)だけです。他の2つのステータス値は、ブートローダモードでは発生しないためです(図4のStatus/Conditionを参照)。
- ステータスビットの値としてDebug Busy (10b)がセットされていた場合、ブートローダはJTAGマスタが前回DRにシフトインした値をまだ読んでいません。これはまた、ブートローダがまだコマンドを完了していないため、シフトアウトされた8ビットのデータ値が無意味であることも示しています。この値を受け取った場合、JTAGマスタは前回転送したバイト値をDRにリロードして、ブートローダがその値にアクセスすることができるようにしなければなりません。この操作を正しく実行するためのステートシーケンスは、次の通りです。
- Shift-DR状態で最後のビットをDRにシフトインしてExit1-DRに遷移した後、JTAGマスタは2つのステータスビットを確認します。
- Debug Busyの値を受け取った場合、Pause-DR状態に遷移し、そこからExit2-DRに遷移し、さらにShift-DRに戻ります。以前のDRの値(今回シフトインされた値ではない)をリロードした後、Exit1-DRを経てUpdate-DRに遷移します。この2回目の過程では、ステータスビットの値は無視します。
- 短時間の遅延(実行するコマンドに依存します)の後、バイト転送を再試行します。
- ステータスビットの値としてDebug Valid (11b)がセットされていた場合、ブートローダは前回シフトインされたバイトを読んでおり、応答のバイトをロードしています。シフトアウトされた8ビット値には、有効なデータが格納されています。
;============================================================================== ;= ;= sendCommand ;= ;= Transmits a loader command by shifting bytes through DR. ;= ;= Inputs : DP[0] - Points to area of RAM which stores input bytes ;= for command and which will be filled with output. ;= LC[1] - Number of bytes to transmit/receive ;= Outputs : C - Set on JTAG communication error ;= Destroys : AP, APC, A[0], A[1], A[2], A[15], PSW, LC[0] ;= sendCommand: move APC, #80h ; Acc => A[0], turn off auto inc/dec push LC[1] call waitForPrompt pop LC[1] jump C, sendCommand_fail move Acc, @DP[0] ; Read first byte to transmit call shiftDR push Acc move Acc, A[1] cmp #3 ; Should be valid status since we had a prompt pop Acc jump NE, sendCommand_fail move @DP[0], Acc ; Store first received byte move NUL, @DP[0]++ ; Increment data pointer djnz LC[1], sendCommand_loop jump sendCommand_pass sendCommand_loop: move A[2], #10 ; Number of retries allowed sendCommand_retry: move Acc, @DP[0] ; Get next byte to transmit call shiftDR_next push Acc move Acc, A[1] cmp #3 pop Acc jump NE, sendCommand_stall move @DP[0], Acc ; Store received byte move NUL, @DP[0]++ ; Increment data pointer djnz LC[1], sendCommand_loop jump sendCommand_pass sendCommand_stall: move LC[0], #8000 ; About a millisecond djnz LC[0], $ move Acc, A[2] sub #1 jump NZ, sendCommand_retry jump sendCommand_fail
ブートローダが提供する機能
MAXQマイクロコントローラのブートローダ機能は、通常は共通のパターンにしたがっており、コマンドおよびステータスコードの多くはデバイス間にわたって同一になっています。デバイスによっては、内部プログラムメモリの構造やその他の要件に応じて、ブートローダコマンドセットの異なるサブセットが実装されている場合もあります。具体的な詳細については、ご使用になるMAXQマイクロコントローラのユーザガイド補足資料をご覧ください。ここでは、「MAXQ2000用補足資料」の「インシステムプログラミング」という項に記載されているブートローダコマンドを参照することになります。
他のMAXQブートローダと同様、MAXQ2000のブートローダが提供するコマンドは、0~15のコマンドファミリに別れています。各コマンドはコマンドバイトから始まりますが、これは表5に示すように、コマンドファミリ(上位4ビット)と、各コマンド固有の番号(下位4ビット)の組み合わせになっています。原則として、ファミリ0コマンド(情報系の性格を持つもの)はすべてのデバイスで実装されています。その他のコマンドファミリはオプショナルです。特定のMAXQデバイスが実装しているコマンドファミリを判断するには、そのデバイスのドキュメントを参照してください。ファミリ0コマンド05h (Get Supported Commands)で、他にどのコマンドファミリをそのブートローダがサポートしているかを示すビットマスクが返されます。
MAXQ2000がサポートしているブートローダコマンドファミリは、次の通りです。
- ファミリ0—情報および状態。このファミリのコマンドを使用して、ROM/ブートローダのIDとバージョン、一番最後に行したコマンドの結果(ステータスコード)、およびプログラム/データメモリのサイズを含む、MAXQデバイスについての基本的な情報を取得することができます。デバイス上の全プログラム/データメモリを消去するMaster Eraseコマンドも、このファミリに含まれています。
- ファミリ1—可変長ロード。このファミリのコマンドを使用して、プログラム(フラッシュ)またはデータ(RAM)メモリをロードすることができます。
- ファミリ2—可変長ダンプ。このファミリのコマンドを使用して、プログラムまたはデータメモリの内容を読むことができます。
- ファミリ3—可変長CRC。このファミリのコマンドを使用して、指定した範囲のプログラムまたはデータメモリについて計算されたCRC-16の値を取得することができます。
- ファミリ4—可変長ベリファイ。このファミリのコマンドを使用して、指定した範囲のプログラムまたはデータメモリがJTAGマスタによって提供されたデータと一致するかどうか検証することができます。
- ファミリ5—可変長ロード/ベリファイ。このファミリのコマンドは、ロードコマンドとベリファイコマンドの機能を1つのコマンドに組み合わせたものです。
- ファミリ6—可変長消去。MAXQ2000では、このコマンドを使用して、指定した領域のデータRAMをゼロクリアすることができます。
- ファミリ7—固定長消去。MAXQ2000では、Master Eraseコマンドですべてのフラッシュメモリを一度に消去する代わりに、このコマンドを使用して、フラッシュプログラムメモリの各ページを個別に消去することができます。
表5. MAXQの全般的なブートローダコマンドファミリ
Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 | Code | Family/Command |
0 | 0 | 0 | 0 | x | x | x | x | 0 x h | Family 0—Informational Commands |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00h | No Operation |
0 | 0 | 0 | 1 | 01h | Exit Loader | ||||
0 | 0 | 1 | 0 | 02h | Master Erase | ||||
0 | 0 | 1 | 1 | 03h | Password Match | ||||
0 | 1 | 0 | 0 | 04h | Get Status | ||||
0 | 1 | 0 | 1 | 05h | Get Supported Commands | ||||
0 | 1 | 1 | 0 | 06h | Get Code Memory Size | ||||
0 | 1 | 1 | 1 | 07h | Get Data Memory Size | ||||
1 | 0 | 0 | 0 | 08h | Get Loader Version | ||||
1 | 0 | 0 | 1 | 09h | Get Utility ROM Version | ||||
1 | 0 | 1 | 0 | 0Ah | Set Word/Byte Access Mode | ||||
1 | 1 | 0 | 1 | 0Dh | Get ID Information | ||||
0 | 0 | 0 | 1 | x | x | x | x | 1 x h | Family 1—Variable-Length Load |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 10h | Load Code Variable Length |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 11h | Load Data Variable Length |
0 | 0 | 1 | 0 | x | x | x | x | 2 x h | Family 2—Variable-Length Dump |
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 20h | Dump Code Variable Length |
0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 21h | Dump Data Variable Length |
0 | 0 | 1 | 1 | x | x | x | x | 3 x h | Family 3—Variable-Length CRC |
0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 30h | CRC Code Variable Length |
0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 31h | CRC Data Variable Length |
0 | 1 | 0 | 0 | x | x | x | x | 4 x h | Family 4—Variable-Length Verify |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 40h | Verify Code Variable Length |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 41h | Verify Data Variable Length |
0 | 1 | 0 | 1 | x | x | x | x | 5 x h | Family 5—Variable-Length Load and Verify |
0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 50h | Load/Verify Code Variable Length |
0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 51h | Load/Verify Data Variable Length |
0 | 1 | 1 | 0 | x | x | x | x | 6 x h | Family 6—Variable-Length Erase |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 60h | Erase Code Variable Length |
0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 61h | Erase Data Variable Length |
0 | 1 | 1 | 1 | x | x | x | x | 7 x h | Family 7—Reserved (for expansion) |
1 | 0 | 0 | 0 | x | x | x | x | 8 x h | Family 8—Reserved (for expansion) |
1 | 0 | 0 | 1 | x | x | x | x | 9 x h | Family 9—Fixed-Length Load |
1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 90h | Load Code Fixed Length |
1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 91h | Load Data Fixed Length |
1 | 0 | 1 | 0 | x | x | x | x | A x h | Family A —Fixed-Length Dump |
1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | A0h | Dump Code Fixed Length |
1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | A1h | Dump Data Fixed Length |
1 | 0 | 1 | 1 | x | x | x | x | B x h | Family B—Fixed-Length CRC |
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | B0h | CRC Code Fixed Length |
1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | B1h | CRC Data Fixed Length |
1 | 1 | 0 | 0 | x | x | x | x | C x h | Family C—Fixed-Length Verify |
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | C0h | Verify Code Fixed Length |
1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | C1h | Verify Data Fixed Length |
1 | 1 | 0 | 1 | x | x | x | x | D x h | Family D—Fixed-Length Load and Verify |
1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | D0h | Load/Verify Code Fixed Length |
1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | D1h | Load/Verify Data Fixed Length |
1 | 1 | 1 | 0 | x | x | x | x | E x h | Family E —Fixed-Length Erase |
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | E0h | Erase Code Fixed Length |
1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | E1h | Erase Data Fixed Length |
1 | 1 | 1 | 1 | x | x | x | x | F x h | Family F—Reserved (device specific) |
JTAGスレーブ側マイクロコントローラの識別
ブートローダが動作を開始したら、次のステップはスレーブ側MAXQマイクロコントローラの識別です。ファミリ0コマンド0Dh (Get ID Information)は、デバイスとユーティリティROMのバージョンを識別する可変長ASCII文字列(ゼロ終端)を返します。
ここで紹介しているサンプルプログラムでは、この情報を取得した上で、デモの一環としてそれをシリアルポートに出力しています。これを行うために使用しているルーチンがgetBannerです。
;============================================================================== ;= ;= getBanner ;= waitForPrompt ;= shiftDR ;= clock0, clock1, shift ;= ;= Executes command 0Dh to retrieve the ROM (device ID) banner and prints ;= the banner text out over the serial port. ;= ;= Inputs : None ;= Outputs : C - Set on JTAG communication error ;= Destroys : AP, APC, Acc, PSW, LC[0] ;= getBanner: call waitForPrompt jump C, getBanner_fail move Acc, #CMD_GET_ROM_BANNER call shiftDR move Acc, #00h call shiftDR getBanner_loop: move Acc, #00h call shiftDR cmp #0FFh ; The banner is ASCII, so receiving this character ; most likely indicates that the JTAG lines are ; floating high and that no device is connected jump E, getBanner_fail jump Z, getBanner_done call txChar jump getBanner_loop getBanner_done: call txNewline jump getBanner_pass getBanner_fail: move C, #1 ret getBanner_pass: move C, #0 ret
プログラムメモリの消去
MAXQ2000に内蔵されているフラッシュプログラムメモリは、プログラミングの前に消去を行う(0FFFFhをセットする)必要があります。JTAGデモアプリケーションでは、ブートローダコマンド02h (Master Erase)を送信し、すべてのプログラム/データメモリを消去するようブートローダに命令することによって、フラッシュメモリの消去を行っています。このコマンドではパスワードロックビットもクリアされ、それによって、サポートされている全コマンドファミリが有効になります。
MAXQデバイスによっては、この02h (Master Erase)コマンドの完了までに数秒かかる場合があります。これは単一バイトコマンドであるため、完了したかどうかを判断するための最も簡単な方法は、No Operation (00h)コマンドを送信した後で1ミリ秒の遅延を行うという操作を、コマンドの完了を示す3Ehがブートローダから返ってくるまで繰り返すことです。次のmasterEraseルーチンが、この方法を示しています。
;============================================================================== ;= ;= masterErase ;= ;= Executes command 02h (Master Erase) to clear all program and data memory. ;= ;= Inputs : None ;= Outputs : C - Set on JTAG communication error ;= Destroys : Acc, PSW, LC[0], LC[1] ;= masterErase: call waitForPrompt jump C, masterErase_fail move Acc, #CMD_MASTER_ERASE call shiftDR move Acc, #00h call shiftDR move LC[1], #5000 ; Number of retries before returning an error masterErase_loop: move Acc, #CMD_NOP call shiftDR cmp #3Eh jump E, masterErase_pass move LC[0], #8000 ; Delay for about a millisecond djnz LC[0], $ djnz LC[1], masterErase_loop masterErase_pass: move C, #0 ret masterErase_fail: move C, #1 ret
ステータス情報の取出し
MasterEraseが(または事実上その他のどのブートローダコマンドでも)完了した後で、04h (Get Status)コマンドを使用することによって、コマンドが成功裏に完了したかどうかを判断することができます。Get Statusコマンドは2バイトのデータを返します。1バイト目には(パスワードロックの設定/解除、ワード/バイトモードの有効化状態、その他の情報を示す)ステータスフラグが格納されており、2バイト目が単一バイトのステータスコードです。
最新のコマンドが成功裏に完了した場合、ステータスコードは常に00h (No Error)になります。ゼロ以外のステータスコードはエラーを示します。ステータスコードを網羅した一覧は「MAXQ2000用補足資料」に掲載されていますが、ここでも一般的なエラーコードをいくつか列挙します。
- 01h/02h—サポート外のファミリ/無効なコマンド。通常これらのエラーコードは、通信の瞬間的異常またはJTAGマスタとのアラインメント不整合を示します。JTAGマスタから送られたバイト数が、所定のコマンドコードに対してブートローダが予期する数よりも多い(または少ない)場合、ブートローダはそれらのデータバイトの1つを新しいコマンドの始まりと解釈します。
- 03h—パスワード不一致。通常このエラーコードは、JTAGマスタが先にパスワードロックを解除せずに、パスワードで保護されたコマンド(一般に、ファミリ0以外の全コマンドが含まれます)を使用しようとしたことを示します。このエラーは、ワードアドレス0010h~001Fhにすでにコードがロードされている部分にJTAGマスタがアクセスしたときに発生します。この場合、Master Eraseでその部分を消去するか、またはPassword Matchコマンド(03h)を使用してその部分のロックを解除しなければなりません。
- 05h—ベリファイ失敗。ロード/ベリファイまたはベリファイコマンドにおいて、検証ステップが失敗しました。このエラーは通信の瞬間的異常の後で発生する可能性があり、また、すでにプログラム済みのフラッシュメモリを、先に消去せずに再プログラムしようとした場合にも発生します。
プログラムメモリへのコードのロード
JTAGブートローダの機能を示すためには、デモアプリケーションがJTAG接続を使ってコードをスレーブ側MAXQ2000にロードする必要があります。この例でロードするコードは、LCDコントローラを起動してLCDディスプレイに4桁の数字を表示するという単純なルーチンです。
スレーブ側MAXQ2000にロードされるコードは、次に示す簡単なデモプロジェクトをベースにしています。
org 0 ljump main org 20h main: move LCRA, #03E0h ; xxx0001111100000 ; 00 - DUTY : Static ; 0111 - FRM : Frame freq ; 1 - LCCS : HFClk / 128 ; 1 - LRIG : Ground VADJ ; 00000 - LRA : RADJ = Min move LCFG, #0F3h ; 1111xx11 ; 1111 - PCF : All segments enabled ; 1 - OPM : Normal operation ; 1 - DPE : Display enabled move LCD0, #LCD_CHAR_0 move LCD1, #LCD_CHAR_0 move LCD2, #LCD_CHAR_0 move LCD3, #LCD_CHAR_2 move LCD4, #00h sjump $しかし、この例では(MTKやMAX-IDEでコードをロードする場合のような)ハードコードされたHEXファイルからではなく、デモアプリケーションからコードバイトをロードするため、デモアプリケーションはユーザの入力に基づいてロード対象のアプリケーションに変更を加えます。
コードをロードする前に、JTAG通信デモアプリケーションはまずユーザに4桁の10進数を入力するよう指示し、その後ロード中のアプリケーションに次の変更を加えます。
- LCDに表示する数字は、ユーザが入力した4桁の数字になります。
- (ワードアドレス010hから始まる)パスワード領域の先頭4バイトには、ユーザが入力した4文字のASCII値がプログラムされます。
;;;; ;;;; First load - LJUMP 0020h at start of program memory ;;;; move DP[0], #0 move @DP[0], #CMD_LOAD_CODE_VARIABLE move @++DP[0], #4 ; Length - 4 bytes move @++DP[0], #00h ; AddrL (byte address 0000h) move @++DP[0], #00h ; AddrH move @++DP[0], #000h ; 00 0B 20 0C - ljump 0020h move @++DP[0], #00Bh move @++DP[0], #020h move @++DP[0], #00Ch move @++DP[0], #000h ; Padding move @++DP[0], #000h ; Padding move @++DP[0], #55h move LC[1], DP[0] move DP[0], #0 nop call sendCommand nop jump C, main_failJTAG call getStatus jump C, main_failJTAG move Acc, A[3] ; Check that loader status is 00h (no error) jump NZ, main_failStatus残り2ブロックのコードメモリも、同様にロードを行います。
プログラムメモリ内のコードのベリファイ
コードのロードが終わったら、いくつかの方法でロードが正しく行われたことを検証することができます。
- Load Code Variable Lengthコマンド(10h)を使う代わりに、Load and Verify Code Variable Lengthコマンド(50h)を使用します。この後者のコマンドは、コードのロード操作とベリファイのステップを組み合わせたものです。
- あるいは、Verify Code Variable Lengthコマンド(40h)を呼び出すことによって、ベリファイを独立して行うこともできます。
- ロードしたコードのベリファイをブートローダに行わせる代わりに、ロードした値のベリファイをJTAGホストが直接行うことも可能です。Dump Code Variable Length (20h)を使って、ロードしたコードが送信データに一致することを確認します。
プログラムメモリからのコードのダンプ
すべてのコードをロードした後、JTAGデモの最後のステップとして、すべてのコードを単一ブロックで再び読み出します。この読出しは、ブートローダにDump Code Variable Length (20h)コマンドを送ることで行います。 このコマンドのパラメータは、ダンプする(読み出す)アドレスと、ダンプするバイト数です。すべてのJTAGブートローダコマンドと同様に、1バイト読み出すごとに1バイトを送信しなければならないことに注意してください。したがってこのコマンドでは、プログラムメモリから読み出す各バイトごとに1バイトという、膨大な数のゼロパッドバイトが必要になります。
;;;; ;;;; Dump program code ;;;; move DP[0], #str_dumpCodeVariable call txString move DP[0], #0 move @DP[0], #CMD_DUMP_CODE_VARIABLE move @++DP[0], #1 ; Indicates single byte length (<256 bytes) move @++DP[0], #00h ; AddrL (byte address 0000h) move @++DP[0], #00h ; AddrH move @++DP[0], #128 ; Length - 128 bytes move LC[1], #128 main_loop1: move @++DP[0], #00h djnz LC[1], main_loop1 move @++DP[0], #000h ; Padding move @++DP[0], #000h ; Padding move @++DP[0], #55h move LC[1], DP[0] move DP[0], #0 nop call sendCommand nop jump C, main_failJTAG call getStatus jump C, main_failJTAG move Acc, A[3] ; Check that loader status is 00h (no error) jump NZ, main_failStatusJTAGデモアプリケーションは、これらのコードバイトを読んだ後、16進形式でシリアルポートに出力します。
ブートローダの終了
単一バイトのファミリ0コマンド01h (Exit Loader)によって、ブートローダは動作を完了し、次のようにして終了します。
- ブートローダは内部的なリセットを開始します。それによってSPEおよびPSSの各ビットがゼロクリアされ、マイクロコントローラがリセットされます。
- マイクロコントローラはリセットを終了し、8000hのユーティリティROMから実行を開始します。
- SPEがゼロクリアされたため、ユーティリティROMコードによって実行がアドレス0000hのユーザアプリケーションコード先頭にジャンプします。
;;;; ;;;; Exit loader mode and allow program code to execute ;;;; call waitForPrompt move Acc, #CMD_EXIT_LOADER call shiftDR move Acc, #00h call shiftDR move Acc, #00h call shiftDR move Acc, #00h call shiftDR
デモの実行
JTAGブートローダのデモを実行するためには、以下のハードウェアおよびソフトウェアコンポーネントが必要になります。
ハードウェア
- MAXQ2000評価キットボード x 2セット(MAXQ2000-K00 REV B)。一方のEVキットはマスタ側MAXQ2000キットの役割を果たし、もう一方はスレーブ側MAXQ2000キットになります。
- MAXQ2000用LCDドータボード(MAXQ2000-K01 REV B)
- 2 x 5 JTAGインタフェースケーブル x 2本(MAXQ2000のEVキットに付属)
- シリアル/JTAGインタフェースボード(MAXQJTAG-001 REV B)
- DB9ストレートシリアルケーブル
- 5V安定化(±5%) ACアダプタ×2個、センタープラス、CUI Inc. DPR050030-P6または同等品
- HC49US 8.00MHz水晶×2個
- 2 x 5 0.100インチピッチのピンヘッダ
- JTAGデモソフトウェアパッケージ
- MAXQ用MAX-IDE開発環境
- MAXQ用マイクロコントローラツールキット(MTK)
- まだMAX-IDEがインストールされていない場合、MAXQ2000のEVキットに含まれているドキュメントにしたがってダウンロードとインストールを行ってください。
- まだMTKがインストールされていない場合、MAXQ2000のEVキットに含まれているドキュメントにしたがってダウンロードとインストールを行ってください。
- 前出の図1に示すように、LCDドータボードをスレーブ側MAXQ2000キットのヘッダJ3に接続してください。LCDドータボードは、MAXQ2000キットボードの上端からはみ出す形になります。
- 8.00MHzの水晶を両方のMAXQ2000のEVキットボードに装着してください(Y1の位置)。
- 2 x 5 JTAGヘッダを、マスタ側MAXQ2000キットの試作用エリアに装着してください。表1の一覧にしたがって、各ピンをマスタ側MAXQ2000の端子に接続してください。
- 表2の一覧にしたがって、シリアル/JTAGボードおよび両方のMAXQ2000のEVキットボード上のジャンパおよびDIPスイッチの設定を行ってください。
- PCのCOM1ポートに接続されたDB9シリアルケーブルを、シリアル/JTAGボードのJ1に接続してください。
- 第1の5V電源を、シリアル/JTAGボードのJ2に接続してください。
- 第2の5V電源を、スレーブ側MAXQ2000のEVキットボードのJ1に接続してください。
- シリアル/JTAGボードのP2に接続された第1のJTAGケーブルを、マスタ側MAXQ2000キットのJ4に接続してください。どちらのJTAGヘッダでも、赤い線がピン1に接続されるはずです。
- マスタ側MAXQ2000キットの試作用エリアのJTAGヘッダに接続された第2のJTAGケーブルを、スレーブ側MAXQ2000キットのJ4に接続してください。どちらのJTAGヘッダでも、赤い線がピン1に接続されるはずです。
- JTAGデモのソフトウェアパッケージをダウンロードして、作業ディレクトリに解凍してください。
- MAX-IDEを起動してください。
- 両方の5Vアダプタの電源をオンにしてください。
- メニューからProject Open Projectを選択し、プロジェクトファイルmaxqjtag.prjを選択してオープンしてください。
- メニューからDebug Makeを選択してください。「Build Successful」というメッセージが表示されるはずです。
- メニューからDebug Runを選択してください。一連の「Loading」メッセージが表示され、最後に「Done」が表示されるはずです。
- メニューからDebug Stopを選択してください。
- MAX-IDEを終了してください。
- 電源をオフにしてください。
- 電源をオフにした状態で、シリアル/JTAGボードからDB9シリアルケーブルを取り外してください。
- DB9ケーブルをマスタ側MAXQ2000キットボードのJ5に接続してください。
- MTKを起動してください。起動時のダイアログボックスで、「Dumb Terminal」を選択してください。
- メニューからOptions Configure Serial Portを選択してください。ダイアログボックス内で、PortをCOM1に、Speedを9600 baudに設定してください。
- メニューからTarget Open COM1 at 9600 baudを選択してください。
- 電源をオンにしてください。
図5. デモアプリケーション、シリアルポートのプロンプト
ここで、4桁の10進数を入力してください。最後にENTERを押下する必要はありません。デモアプリケーションは残りの作業(マスタ消去、コードのロード、およびコードのダンプ)を完了して、図6に示すように、その結果およびプログラムメモリからダンプしたバイトデータの16進値を出力します。
図6. デモアプリケーション、シリアルポートの出力
結論
MAXQマイクロコントローラが提供するJTAGブートローダは、標準化された一組のコマンドを使って、外部のJTAGマスタが任意のMAXQマイクロコントローラを容易に識別およびプログラムすることができるようにします。このアプリケーションノートに含まれているコードを元に、標準化されたブートローダ用コマンドセットをサポートする任意のMAXQマイクロコントローラのコードおよびデータメモリ内容の識別、初期化、ロード、およびベリファイを行うことができる、フル機能のJTAGブートローダマスタアプリケーションを構築することが可能です。