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

Figure 1. JTAG demo setup.
図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状態に遷移します。
このように、クロック信号に合わせてTAPコントローラを任意の状態に変えていくことが可能です。下の状態遷移図(図2)について、注目すべき点が2つあります:
  • 5回連続「1」の遷移(TMSをハイにした状態で完全に5サイクルの間TCKにクロックを供給すること)によって、最初の状態が何であっても、常にステートマシンをTest-Logic-Resetに戻すことができます。すなわち、TAPコントローラの現在の状態が不明の場合や、JTAGマスタ/スレーブ間の通信が何らかの形で中断された場合でも、5クロック分の「1」遷移を行うことによって、常にTAPコントローラを既知の状態に戻すことが可能です。
  • たとえTCKクロックが作動し続けていても、TAPコントローラの状態に影響を与えることなくJTAG通信を一時停止して、Run-Test-IdlePause-DR、またはPause-IRのいずれかの状態を無期限に保つことが可能です。
Figure 2. Test Access Port (TAP) state machine.
図2. Test Access Port (TAP)のステートマシン

TAPコントローラのステートマシンでは2つの制御レジスタへのアクセスが提供されており、さらにそれらのレジスタを通して、ブートローダ、デバッグインタフェース、およびその他の機能へのインタフェースが提供されています。

  • IR (Instruction Register:命令レジスタ)は、常に3ビット幅です。このレジスタはインデックスレジスタとして機能し、それがさらにDRの機能を制御します(後述)。
  • DR (Data Register:データレジスタ)は、TAPコントローラ内に複数存在するレジスタの1つに対するアクセスポイントになります。DRに対するビットの入出力に当たって実際にアクセスされるレジスタは、IRの現在の値によって決まります。
Figure 3. Register access in the TAP controller.
図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を通して出力されます。このレジスタは、ブートローダモードとインサーキットデバッグモードの両方でデータの転送に使用されます。
上で検討した3つのモードの内、バイパスモードは「無動作」モードであり、我々の関心の対象であるブートローダ機能へのアクセスは提供されません。システムプログラミングモードは、その名前から予想されるものとは異なり、実際にはブートローダとの通信には使用されず、単にブートローダへのアクセスを有効化するだけです。ひとたびブートローダが作動して通信を開始すると、このTAPモードはもう無用になります。10ビットの入出力レジスタへのアクセスが可能になるデバッグモードが、ブートローダとのすべての通信に使用されるモードです。いったんブートローダが有効化されると、ローダセッションが完了するまでの間は、TAPコントローラのこのモードだけが使用されます。

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
ポート端子の初期化後、clock0clock1の各ルーチンを使用して静的な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レジスタのロード/アンロードは次のような動作になります。
  • 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ブートローダモードに移行させるためには、以下の手順が必要になります。

  1. TAPコントローラを初期化し、Test-Logic-Reset状態にリセットします。
  2. 命令レジスタ(IR)に100bをセットして、システムプログラミングモードを有効化します。
  3. データレジスタ(DR)に001bをセットします。これによって. SPE (システムプログラミングイネーブル)ビットに1がセットされてブートローダが有効化され、PSS[1:0] (プログラミングソース選択)ビットに00bがセットされてJTAGインタフェースが選択されます。
  4. nRESETをローに保持してMAXQ2000をリセットします。
  5. nRESETを解放します。これによってMAXQ2000はユーティリティROM内の標準リセットポイント(8000h)にジャンプします。続いてユーティリティROMのコードがSPEおよびPSSビットの値を調べ、それにしたがってJTAGブートローダを起動します。この時点でブートローダの実行が開始され、JTAGコマンドの受入れが可能になります。
  6. 命令レジスタ(IR)に010bをセットして、デバッグモードを有効化します。これは、JTAGブートローダまたはデバッグエンジンのいずれかとの通信に使用されるモードです。この場合は、ブートローダとの通信を行うためにこのモードを使用します。
  7. 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個のステータスビットによって実現されます。

Figure 4. Shifting Data and Status Bits Through DR.
図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ビット値には、有効なデータが格納されています。
ブートローダのコマンドシーケンスを実行する場合、shiftDRshiftDR_nextの2つのルーチンが、この同期処理を自動的に行います。シーケンス中の最初のバイトについては、shiftDRルーチンを呼び出します。その後のバイトについては、shiftDR_nextを呼び出します。shiftDR_nextルーチンは、2つのステータスビットを確認し、必要に応じて前述のように以前のDRバイトを再転送するという点を除いては、shiftDRと同じように動作します。この処理をすべてまとめたのがsendCommandルーチンであり、shiftDRshiftDR_nextを呼び出してコマンドシーケンスを転送し、コマンドが完了するまで必要に応じて遅延とバイトの再送を行います。
;==============================================================================
;=
;=  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コマンドですべてのフラッシュメモリを一度に消去する代わりに、このコマンドを使用して、フラッシュプログラムメモリの各ページを個別に消去することができます。
MAXQマイクロコントローラ上のTAPコントローラを使用して、そのマイクロコントローラをJTAGローダモードに移行させたら(この過程はすべてのMAXQマイクロコントローラで同一です)、その後の最初のステップとして、ローダから応答があるかどうかを判定します。これを最も迅速に行う方法は、ブートローダコマンドコード00h (No Operation)を繰り返し転送することです。各ブートローダコマンドの完了後、ブートローダは応答としてプロンプト(「>」、すなわち3Eh)バイトを出力します。00hを転送したときに応答として3Ehが返ってくれば、ブートローダが動作中でありコマンドの受け入れ準備ができていることを示します。

表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値がプログラムされます。
デモは、ブートローダコマンド10h (Load Code、Variable Length)の呼び出しを3回使用してアプリケーションコードをロードします。このコマンドのパラメータには、ロードするバイト数(コードメモリをロードする場合は、ワードアラインメントを保証するため偶数にする必要があります)、開始アドレス、そしてもちろんデータ自体が含まれます。MAXQのメモリはリトルエンディアンであり、したがってアプリケーションは各プログラムワードの下位バイトを先に送信する必要があります。
   ;;;;
   ;;;;  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_failStatus
JTAGデモアプリケーションは、これらのコードバイトを読んだ後、16進形式でシリアルポートに出力します。

ブートローダの終了

単一バイトのファミリ0コマンド01h (Exit Loader)によって、ブートローダは動作を完了し、次のようにして終了します。

  • ブートローダは内部的なリセットを開始します。それによってSPEおよびPSSの各ビットがゼロクリアされ、マイクロコントローラがリセットされます。
  • マイクロコントローラはリセットを終了し、8000hのユーティリティROMから実行を開始します。
  • SPEがゼロクリアされたため、ユーティリティROMコードによって実行がアドレス0000hのユーザアプリケーションコード先頭にジャンプします。
Exit LoaderコマンドがSPEおよびPSSの各ビットを自動的にクリアし、その後のリセットによってTAPコントローラがクリアされてバイパスモードに戻るため、JTAGマスタがこれらの動作を行う必要はありません。
   ;;;;
   ;;;;  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インチピッチのピンヘッダ
ソフトウェア セットアップの手引き
  1. まだMAX-IDEがインストールされていない場合、MAXQ2000のEVキットに含まれているドキュメントにしたがってダウンロードとインストールを行ってください。
  2. まだMTKがインストールされていない場合、MAXQ2000のEVキットに含まれているドキュメントにしたがってダウンロードとインストールを行ってください。
  3. 前出の図1に示すように、LCDドータボードをスレーブ側MAXQ2000キットのヘッダJ3に接続してください。LCDドータボードは、MAXQ2000キットボードの上端からはみ出す形になります。
  4. 8.00MHzの水晶を両方のMAXQ2000のEVキットボードに装着してください(Y1の位置)。
  5. 2 x 5 JTAGヘッダを、マスタ側MAXQ2000キットの試作用エリアに装着してください。表1の一覧にしたがって、各ピンをマスタ側MAXQ2000の端子に接続してください。
  6. 表2の一覧にしたがって、シリアル/JTAGボードおよび両方のMAXQ2000のEVキットボード上のジャンパおよびDIPスイッチの設定を行ってください。
  7. PCのCOM1ポートに接続されたDB9シリアルケーブルを、シリアル/JTAGボードのJ1に接続してください。
  8. 第1の5V電源を、シリアル/JTAGボードのJ2に接続してください。
  9. 第2の5V電源を、スレーブ側MAXQ2000のEVキットボードのJ1に接続してください。
  10. シリアル/JTAGボードのP2に接続された第1のJTAGケーブルを、マスタ側MAXQ2000キットのJ4に接続してください。どちらのJTAGヘッダでも、赤い線がピン1に接続されるはずです。
  11. マスタ側MAXQ2000キットの試作用エリアのJTAGヘッダに接続された第2のJTAGケーブルを、スレーブ側MAXQ2000キットのJ4に接続してください。どちらのJTAGヘッダでも、赤い線がピン1に接続されるはずです。
JTAGデモのコンパイルとロード
  1. JTAGデモのソフトウェアパッケージをダウンロードして、作業ディレクトリに解凍してください。
  2. MAX-IDEを起動してください。
  3. 両方の5Vアダプタの電源をオンにしてください。
  4. メニューからProject Arrow Open Projectを選択し、プロジェクトファイルmaxqjtag.prjを選択してオープンしてください。
  5. メニューからDebug Arrow Makeを選択してください。「Build Successful」というメッセージが表示されるはずです。
  6. メニューからDebug Arrow Runを選択してください。一連の「Loading」メッセージが表示され、最後に「Done」が表示されるはずです。
  7. メニューからDebug Arrow Stopを選択してください。
  8. MAX-IDEを終了してください。
  9. 電源をオフにしてください。
JTAGデモの実行
  1. 電源をオフにした状態で、シリアル/JTAGボードからDB9シリアルケーブルを取り外してください。
  2. DB9ケーブルをマスタ側MAXQ2000キットボードのJ5に接続してください。
  3. MTKを起動してください。起動時のダイアログボックスで、「Dumb Terminal」を選択してください。
  4. メニューからOptions Arrow Configure Serial Portを選択してください。ダイアログボックス内で、PortをCOM1に、Speedを9600 baudに設定してください。
  5. メニューからTarget Arrow Open COM1 at 9600 baudを選択してください。
  6. 電源をオンにしてください。
アプリケーションが正しくロードされ、すべての接続が正しく行われていれば、次のテキスト(図5)がシリアルポートに出力されます。

Figure 5. Demo application, serial-port prompt.
図5. デモアプリケーション、シリアルポートのプロンプト

ここで、4桁の10進数を入力してください。最後にENTERを押下する必要はありません。デモアプリケーションは残りの作業(マスタ消去、コードのロード、およびコードのダンプ)を完了して、図6に示すように、その結果およびプログラムメモリからダンプしたバイトデータの16進値を出力します。

Figure 6. Demo application, serial-port output.
図6. デモアプリケーション、シリアルポートの出力

結論

MAXQマイクロコントローラが提供するJTAGブートローダは、標準化された一組のコマンドを使って、外部のJTAGマスタが任意のMAXQマイクロコントローラを容易に識別およびプログラムすることができるようにします。このアプリケーションノートに含まれているコードを元に、標準化されたブートローダ用コマンドセットをサポートする任意のMAXQマイクロコントローラのコードおよびデータメモリ内容の識別、初期化、ロード、およびベリファイを行うことができる、フル機能のJTAGブートローダマスタアプリケーションを構築することが可能です。