ENGLISH 简体中文 日本語 한국어  



   
 
キーワードまたは型番を入力    




アプリケーションノート 3770

MAXQアーキテクチャのテーブル処理

要約:MAXQマイクロコントローラはハーバードマシンです。ただし、MAXQマイクロコントローラは、コード空間に格納された定数へのアクセスを禁止する多くのハーバードマシンで共通した制約を受けると想定する必要はありません。むしろ、あらゆるMAXQデバイスに内蔵されたツールを使用すると、このようなテーブルルックアップが簡単になります。このアプリケーションノートでは、MAXQマイクロコントローラで効率的なテーブル処理を実行する方法について説明します。

はじめに

MAXQアーキテクチャでは、典型的なハーバードマシンに基づいた、強力なシングルサイクルRISCマイクロコントローラについて説明しています。ハーバードマシンは、重要な設計要素において、一般的なフォンノイマンマシンとは異なります。すなわち、ハーバードマシンの命令とデータは別々のバスで処理されます。単一データバスのための競合が全くないので、MAXQの命令はシングルサイクルのみで実行することができます。従来のノイマン型アーキテクチャで同じ演算を実施すると、複数サイクルを必要とします。

ただし、ハーバードマシンでデータとコードを厳格に分離すると、一連の独自の問題が生じます。コード空間にデータテーブルを格納することは、ノイマンマシンでは一般的な手法ですが、典型的なハーバードマシンでは問題になります。所定のバス上で1つのサイクルで1つの処理しか行えないので、CPUコアは、同じサイクルで、コードメモリバス上の命令をフェッチし、さらにコード空間のデータテーブルからメモリオペランドをフェッチすることはできません。

ハーバードマシンであるため、MAXQマイクロコントローラもコード空間にデータ要素を格納することが禁止されていると思われているかもしれません。しかし、あらゆるMAXQのデバイスにはROMツールが内蔵されているため、実際にはこのテーブルルックアップは簡単です。

コード空間でのテーブルルックアップ

コード空間のMAXQテーブルから値を読み取ることは簡単そうに見えます。しかし、MAXQアーキテクチャの経験のないプログラマが初めて値を読み取ろうとすると、失敗する可能性があります。
IncorrectTableLookup:
movedp[0], #w:StartOfTable
moveacc, @dp[0]
.
.
.
ret
.
.
.
StartOfTable:
dc1601234h
dc1605678h
dc16098abh
dc160cdefh
上記のこのコードは問題なくアセンブルされますが、2番目の命令の後、アキュムレータにはほぼ間違いなく0x1234がありません。理由は単純です。単一のメモリ空間しかなく、命令が完了するのに必要なサイクル数だけ使えるノイマンマシンとは異なり、MAXQのmove <reg>, @dp[0]命令は暗黙のうちにデータ空間にアクセスし、コード空間からの命令のフェッチとデータ空間からの読取りを1つのサイクルで完了することになるからです。アキュムレータにロードされる値は、コード空間のStartOfTableと同じオフセットでデータ空間にある値になります。

最初、この問題は解決困難と思われます。結局のところ、コード空間にアクセスするには、一定の時間が必要になります。アーキテクチャが許可したとしても、CPUコアは1つのクロックサイクルに2回のメモリアクセスを割り込ませることができないからです。ただし、MAXQアーキテクチャでは、マイクロコントローラがさまざまなメモリ空間に物理メモリブロックをマップする方法、およびユーティリティROMの少数のルーチンについて細部を記述すれば、この問題は解決されます。

第一に、MAXQのアーキテクチャでは、コード空間とデータ空間への物理メモリブロックのマッピングは固定されているのではなく、どの物理メモリブロックがアクセスされるかによって変化します。一般に、ほとんどのMAXQマイクロコントローラのフラッシュメモリ空間で動作するコードを書くプログラマは、ソフトウェアをコード空間の0の位置で開始するようリンクします。プログラマは、RAMがデータ空間の0位置から始まると想定し、それはその通り機能します。

ただし、MAXQマイクロコントローラは別の物理メモリ、すなわちユーティリティROMも備えています。ユーティリティROMは、すべてのMAXQマイクロコントローラのコード空間の0x8000の位置にあります。ユーザコードは、ユーティリティROMの0x8000ページのルーチンを呼び出し、特定の機能を実行することができます。さらに、ユーティリティROMで実行される限り、ユーザコードメモリはデータ空間の新しい位置に再マップされます。

ユーティリティROM以外で実行すると、データRAMはデータ空間の位置0x8000に引き続きアクセス可能ですが、コードメモリはデータ空間の位置0x8000に再マップされます。コードフラッシュはデータ空間で行われるようになるため、ユーティリティROMから動作するコードは、データであるかのようにユーザコードに格納された情報にアクセスすることができます。ポインタレジスタを介して間接的に値を読んで返すだけのユーティリティROM関数が用意されています。

したがって、上記のルーチンは、次のように幾分変化します。
BetterTableLookup:
movedp[0], #w:StartOfTable + 08000h
callUtilityROMGetDP0
.
.
.
ret
.
.
.
StartOfTable:
dc1601234h
dc1605678h
dc16098abh
dc160cdefh
この例では、読取り対象のアドレスは、ユーティリティROMの実行中にフラッシュがマップされる位置を反映するよう調整されてからDP[0]にロードされます。直接、データを読み取ろうとするのではなく、ユーティリティROMルーチンへのコールが行われます。データを直接読み取るには1サイクル必要としますが、この処理は当然、1サイクルではなく、ロングコールに2サイクル、読取りの実行に1サイクル、復帰処理に1サイクル、計4サイクルを要します。

このコード例に伴うさらに大きな問題は、アセンブルされないということです! ラベルUtilityROMGetDP0は定義されていませんが、それにはもっともな理由があります。ユーティリティルーチンが、それぞれのMAXQマイクロコントローラによって異なる場所にあるからです。実際、ユーティリティルーチンは、特定のMAXQデバイスでバージョンが変更されたときに同じ場所にあることが保証されていません!

この問題を解決するため、あらゆるMAXQマイクロコントローラのユーティリティROMには、ユーティリティ関数のアドレステーブルと周知の位置:0x800Dのテーブルへのポインタが含まれています。具体的には、ユーティリティROMには、以下のコードが含まれています。
org0800Dh
dwUtilityFunctionTable
.
.
.
UtilityFunctionTable:
.
.
.
dwGetDP0
dwGetDP0Inc
dwGetDP0Dec
dwGetDP1
dwGetDP1Inc
dwGetDP1Dec
dwGetBP
dwGetBPInc
dwGetBPDec
第1に、ユーティリティ関数テーブルは、命令ではなくアドレスで構成されていることがわかります。したがって、アプリケーションプログラマは、単純にテーブルにジャンプするのではなく、アドレスを検索し、それをコールする必要があります。第2に、最初のメモリ参照関数がテーブルの最初のエントリである必要はないということに注意して下さい。それぞれのMAXQマイクロコントローラには、さまざまなタイプと量のメモリ、およびさまざまな周辺回路を搭載することができるため、それぞれのデバイスではテーブル内の異なる相対オフセットで異なる関数リストを含むことが可能です。

3つの各ポインタレジスタは、それに関連する3つの関数を持っており、合計で9つのテーブルルックアップ関数があります。各ポインタレジスタの最初の関数は所定のアドレスにある値を検索するだけですが、後の2つはそれぞれ、間接ロードのポストインクリメント形式とポストデクリメント形式を使用します。いずれの場合にも、検索されたデータはGRレジスタにロードされます。

これで、今回のコードは、次のようになります。
CorrectTableLookup:
movedp[0], #0800Dh; Point to pointer to function table
moveacc, @dp[0]; acc now has pointer to ftable
add#3; For 2000, GetDP0
movedp[0], acc; Load ptr + offset to dp0
movea[1], @dp[0]; Get address of GetDP0 into A1
movedp[0], #StartOfTable + 08000h
calla[1]; This will call GetDP0, finally!
.
.
.
ret
.
.
.
StartOfTable:
dc1601234h
dc1605678h
dc16098abh
dc160cdefh
GetDP0ルーチンのアドレスが見つかれば、そのアドレスを記憶しておき、再使用することができます。上記の最初の5つの命令は1回しか実行する必要がありません。また、その後の各テーブルのフェッチには、コール、リード(ユーティリティROMから実行)、およびリターン(これもユーティリティROMから実行)の3サイクルしかかかりません。

フラッシュからRAMへのテーブルのコピー

テーブル全体をフラッシュからRAMに移動する方法をテーブル読取り関数から構築することができるようになります。たとえば、宛先アドレスがBPで与えられている場合、1つの方法を以下に示します。
SlowTableMove:
movedp[0], #0800Dh; Point to pointer to function table
moveacc, @dp[0]; acc now has pointer to ftable
add#4; For 2000, GetDP0Inc
movedp[0], acc; Load ptr + offset to dp0
movea[1], @dp[0]; Get address of GetDP0 into A1
movedp[0], #StartOfTable + 08000h
movebp, #RAMDest; Set this label to desired dest
moveoffs, #0ffh; Pre-decremented offset
movelc[0], #4; Move four words
TableMoveLoop:
movedp[0], dp[0]; Set source pointer
calla[1]; This will call GetDP0inc
move@bp[++offs], gr; Store retrieved word to dest
djnzlc[0], TableMoveLoop
.
.
.
ret
.
.
.
StartOfTable:
dc1601234h
dc1605678h
dc16098abh
dc160cdefh
前述のように、最初の5つの命令は1回しか実行する必要がありません。その後、テーブルの移動を必要な回数だけ実施することが可能で、GetDP0incサブルーチンのアドレスはA1に留まります。テーブルの移動には、1回実行するたびに6サイクルを要し、さらにセットアップのためのオーバヘッドが必要です。

move dp[0], dp[0]命令は、MAXQアーキテクチャの特異な特性のために必要となります。データ空間に関連するアドレスバスは1つしかないため、データ空間に対して実行されるいずれの読取りよりも少なくとも1サイクル先にセットアップする必要があります。テーブル移動ループでは、DP[0]によって与えられたアドレスで読取りが実行された後、書込みアドレスがバス上に設定されます。しかし、move dp[0], dp[0]命令がなければ、テーブル内の次の位置を読み取るときに、書込みアドレスはまだバス上にあります。この明らかにnullの命令を挿入することによって、次の読取りイベントを見越してソースオペランドのアドレスバスがリフレッシュされます。

ただし、このタスクを実現するさらに優れた方法があります。ユーティリティROMには、copyBufferというルーチンがあり、上記と同じ機能をより少ないサイクルで実行します。copyBufferルーチンは、ユーティリティROMのテーブルルックアップルーチンのすぐ下に置かれています。
FasterTableMove:
movedp[0], #0800Dh; Point to pointer to function table
moveacc, @dp[0]; acc now has pointer to ftable
add#12; For 2000, copyBuffer
movedp[0], acc; Load ptr + offset to dp0
movea[1], @dp[0]; Get address of GetDP0 into A1
movedp[0], #StartOfTable + 08000h
movebp, #RAMDest; Set this label to desired dest
moveoffs, #0; No need to pre-decrement offset
movelc[0], #4; Move four words
calla[1]; This will call copyBuffer
.
.
.
ret
.
.
.
StartOfTable:
dc1601234h
dc1605678h
dc16098abh
dc160cdefh
このcopyBufferルーチンでは、1回の実行当りのサイクル数が3に減少しているため、前述の方法に比べて時間が約1/2に短縮されます。copyBufferルーチンが復帰するとき、LC[0]は0になり、OFFSレジスタは、最後に書き込まれた宛先位置の次の位置を示します。OFFSは8ビットレジスタであるため、最大256ワードのテーブルをこの方法でコピーすることができます。

例:文字列の出力

多くのマイクロコントローラベースのプロジェクトに共通するタスクとして、いくつかのあらかじめ用意されたメッセージの中から1つをコンソールに出力するというタスクがあります。多くの場合、各メッセージには番号が与えられており、共通ルーチンによってその番号をメッセージ文に変換する必要があります。

このタスクのために一般に使用される手法は、各メッセージ文字列を0で終了し、メッセージ番号を各文字列が存在するアドレスに変換するテーブルを提供するというものです。この手法は信頼性が高く、迅速ですが、2つのデータ構造、すなわちアドレステーブルと文字列自体を構築することが必要となります。2つ目の手法は0で終了する文字列を1つの大きな連続メモリ空間に配置し、順次検索するだけです。この方法は経済的ですが、出力が始まる前に、対象文字列に至るまでのすべての文字を検索しなければならないため、実行時間が膨大なものになります。

実用的な妥協案は、0で区切るのではなく、長さで区切る方法です。この手法では、各文字列の長さが最初に与えられ、その後にメッセージの実際のバイトが続きます。この方法では、使用されないメッセージをすばやく飛ばすことが可能で、またテーブル自体は0で区切られた場合よりも長くなることはありません。この妥協案の唯一の制約は、テーブルの各文字列が255文字以下に制限されるということです。
;
; Output String
;
; Enter with ACC=an index value (one based) indicating which
; string to output.
;
; On exit, LC0=0, DPC=0, ACC, A1, A2, DP0 used.
;
output_string:
movelc[0], acc;Set LC0 to index of string
movedpc, #4;Set DP0 to word mode
movedp[0], #800dh;Point to table of pointers
moveacc, @dp[0];Get address of table
add#3;Offset to GETDP0 routine
movedp[0], acc;Load pointer to table
movea[1], @dp[0]++;Get GETDP0
movea[2], @dp[0];Get GETDP0INC
movedpc, #0;Set DP0 to byte mode
movedp[0], #string_table + 8000h
str_search_loop:
calla[1];Get a string length
djnzlc[0], next_str;If not this string, go to next
movelc[0], gr;Otherwise, put len in LC0
moveacc, @dp[0]++;...and point past length
out_loop:
calla[2];Get a char and bump pointer
callchar_out;Output the character
djnzlc[0], out_loop;If more characters, loop
ret;Otherwise, we're done.
next_str:
moveacc, gr;GR contains len of this string
adddp[0];Add current ptr to current len...
movedp[0], acc;...to create a new pointer
jumpstr_search_loop;Jump back and test index again
;
; Each entry in the string table begins with the string length
; followed by the string characters.
;
string_table:
dc8string1 - string_table
dc8"This is the first string."
string1:
dc8string2 - string1
dc8"This is a second example of a string"
string2:
dc8string3 - string2
dc8"A third string."
string3:
dc8string4 - string3
dc8"Finally, a fourth string in the array!!!"
string4:


関連製品  APP 3770: Sep 15, 2006
MAXQ2000 低電力LCDマイクロコントローラ フルデータシート
(PDF, 720kB)
無料
サンプル
MAXQ2010 LCDインタフェース付き、16ビットミックスドシグナルマイクロコントローラ フルデータシート
(PDF, 912kB)
MAXQ3210 電圧レギュレータ、圧電ホーンドライバ、およびコンパレータ内蔵、マイクロコントローラ フルデータシート
(PDF, 400kB)
MAXQ3212 アナログコンパレータおよびLEDドライバ付きマイクロコントローラ フルデータシート
(PDF, 324kB)
MAXQ7665 16ビットRISCマイクロコントローラベースのスマートデータ収集システム フルデータシート
(PDF, 480kB)

自動アップデート
お客様が関心のある分野でアプリケーションノートが新規に掲載された際に自動通知Eメールの受信を希望する場合は、EE-Mail™にご登録ください。


We Want Your Feedback!



フィードバックをお寄せください。
内容に満足されましたか、あるいは満足されていませんか?もっと良いページにできると思いますか?あるいは、単なるコメントでも結構です。フィードバックをお待ちしています。—マキシムはお客様からいただく訂正、提案を元に改善していきます。 このページを評価し、フィードバックを送信する。

 

ダウンロード、PDFフォーマットダウンロード、PDFフォーマット(27kB)
 AN3770, AN 3770, APP3770, Appnote3770, Appnote 3770

        •         •         •     プライバシーポリシー     •     法的お知らせ

    Copyright © 2009 by Maxim Integrated Products