なぜDSPを使うのか――DSP入門【Part 4】リアルタイムIOのプログラミングにおいて考慮すべき事柄

本シリーズでは、Part 1~Part 3にわたり、DSP(Digital Signal Processor)に関する以下のような話題について説明してきました。

  • Part 1(vol. 31-1)】DSPのアーキテクチャ、従来のアナログ 回路に対するDSPの優位性
  • Part 2(vol. 31-2)】デジタル・フィルタによる処理の概念、 DSPによるフィルタ処理のアルゴリズム
  • Part 3(vol. 31-3)】FIRフィルタのアルゴリズムの実装、 デモ用ハードウェア・プラットフォーム「ADSP-2181 EZ-Kit Litesupsup」の概要

今回(Part 4)は、DSPをベースとするリアルタイム・システムに特有のプログラミング上の課題について詳しく解説します。なかでも、様々なIOインターフェースを備えるDSPシステム向けのアルゴリズムを開発する方法に焦点を絞ることにします。

「リアルタイム」とは何を意味するのか? アナログ・システムでは、連続した信号と連続した処理によってあらゆるタスクがリアルタイムに実行されます。それに対し、DSPシステムでは、一連のサンプル、つまり時間軸上に離散的に存在する値(データ)によって信号が表現されます。DSPシステムで特定の数のサンプルを処理する場合、それにかかる時間はサンプリング・レートの値によって決まります。そのため、DSPシステムの場合、リアルタイムという言葉は、サンプリング・レートに依存した任意の値であると解釈できます。もちろん、任意とはいえ、どのような値でも構わないということではありません。本シリーズのPart 1では、サンプリングの概念とナイキスト基準について解説しました。ナイキスト基準によれば、リアルタイム・アプリケーションにおけるサンプリング周波数は、対象とするアナログ信号の最高周波数成分の2倍以上の値(ナイキスト・レート)でなければなりません(ちなみに、サンプルとサンプルの間の時間は、サンプリング間隔と呼ばれます)。システムがリアルタイムで動作していると見なすには、特定のデータ・セット(アルゴリズムに依存する1つ以上のサンプル)に対するすべての処理が、新しいデータが到着する前に完了していなければなりません。このように考えれば、DSPシステムにおけるリアルタイムという言葉の定義はおのずと見えてくるでしょう。

特定のクロック・レートで動作するプロセッサにおいて、データ・ストリームに対して遅れることなくデータに適用できる処理の量は、入力データの速度と量によって決まります。処理に費やせる時間が限られているというのは、アナログ・システムとは親和性が低い概念だと言えます。そのため、アナログ設計者にとっては理解しにくいものかもしれません。ここで、図1をご覧ください。アナログ・システムの場合、信号は連続的に処理されます。システムが低速である場合に問題になるのは、周波数応答が制限されることだけです。それに対し、デジタル・システムでは信号の一部分を切り出し、その部分だけを対象として処理を行います。その処理が完了したら、次の一部分を対象として処理を行うということを繰り返します。このような方法でも、十分に正確な近似が行われます。ただ、図1のように特定の時間を1つのブロックとして扱い、連続する各ブロックを対象として一連の処理が行われるという点で、アナログ・システムとは異なります。リアルタイム動作に対応しなければならないDSPの場合、アルゴリズムの割り当て時間内に対応できるデータの量や処理の種類に応じて制限が加わることがあります。音声信号を例にとると、48kHzでサンプリングされたデータを扱うDSPでは、8kHzでサンプリングされたデータを扱うDSPと比べてデータの処理に費やせる時間は短くなります。その時間には、必要な全タスクの処理が含まれます。

本シリーズの以前の記事では、入力サンプリング・レートが8kHzのフィルタを例にとりました。その場合、DSPがリアルタイムのデータに遅れをとらないようにするためには、すべての処理を125マイクロ秒(1秒8kHz)の割り当て時間内で実行する必要があります。33MHzの動作に対応するDSP(1サイクルは30ナノ秒)の場合、必要なすべての処理やタスクを完了するために使用できるのは、4166命令サイクル(125マイクロ秒/30ナノ秒)となります。

任意のアルゴリズムを実行するために割り当てることが可能な時間は有限です。時間の管理は、DSPシステムのソフトウェアを設計する際の中核的な要素になります。時間の管理に関する戦略に応じ、プロセッサがイベントの通知を受け取る方法が決まります。また、その戦略はデータの処理にも影響を及ぼします。言い換えれば、そうした戦略を構築することによって、DSPによる通信を具体化することが可能になります。

図1. アナログ・システム(a)とデジタル・システム(b)の違い。アナログ・システムにおいて、応答の値はすべての瞬間で各データ(信号)の値に対応しています。一方、デジタル・システムでは、サンプリングが行われるごとにデータを転送して処理を実行します。各処理(制御)が終了したことは、イベントによって示されます。各サイクルにおける処理が実施された後に、他のタスクを行うための余分の時間が必要になることがあります。
図1. アナログ・システム(a)とデジタル・システム(b)の違い。アナログ・システムにおいて、応答の値はすべての瞬間で各データ(信号)の値に対応しています。一方、デジタル・システムでは、サンプリングが行われるごとにデータを転送して処理を実行します。各処理(制御)が終了したことは、イベントによって示されます。各サイクルにおける処理が実施された後に、他のタスクを行うための余分の時間が必要になることがあります。

イベントの通知、割り込みの処理: DSPを使用する場合、イベント(データの到着)を扱うためのいくつかの戦略のうち1つを使用することによって、データ処理の内容をプログラミングします。例えば、ステータス・ビットまたはフラグ・ピンの値を定期的に読み取ることで、新しいデータを利用できるか否かを判断することは可能です。ただ、ポーリングはプロセッサ(DSPにおいて処理/制御を担うプロセッサ回路)のサイクルを浪費します。最後のポーリングの直後にデータが到着するかもしれませんが、次のポーリングまでその存在を通知することはできません。そのため、リアルタイム・システムを開発するのは難易度の高い作業になります。

もう1つの戦略は、データが到着した際にプロセッサに割り込みをかけるというものです。プロセッサに対しては割り込みを使って通知を行うのが効率的です。但し、そのためのプログラミングは容易だとは言えません。また、割り込みを待っている間はクロック・サイクルが浪費される可能性があります。とはいえ、現実の世界の信号を迅速に処理する方法としては、イベント駆動型の割り込みのプログラミングが適しています。そのため、ほとんどのDSPは、そうしたプログラムを効率的に処理できるように設計されています。実際、多くのDSPは割り込みに対して非常に迅速に応答します。例えば、「ADSP-2181」の場合、割り込みに対する応答時間は約3プロセッサ・サイクルです。つまり、DSPが実行していた処理を75ナノ秒以内に停止し、割り込みのイベント(ベクトル)を処理することができます。

DSPベースの多くのシステムでは、割り込みのレートは入力データのサンプリング・レートに基づきます。多くの場合、DSPのクロック・レートとは全く関係がありません。本シリーズの以前の記事では、FIR(Finite Impulse Response)フィルタを紹介しました。その例では、プロセッサに125マイクロ秒の間隔で割り込みがかかり、新しいデータを取得していました。

割り込み処理と割り込みベクトル: DSPシステムにおいて、割り込み処理は非常に重要な要素です。そのため、ほとんどのDSPは、割り込みを効率的に処理するためのハードウェア・ベースの機構を備えています。その種の機構は、ソフトウェア単独で効率を高めようとするよりも効果的に機能します。DSPの割り込みサービス・ルーチン(ISR:Interrupt Service Routine)は、以下のすべての要求を満たさなければならないケースがあるからです。

  • コンテキストの高速なスイッチング:あるコンテキスト(何らかのタスクとそれに使用されるデータ)から別のコンテキストへ切り替える際、レジスタの内容とチップのステータス情報を保存するためのプログラミングに伴う時間の損失や複雑さを回避することが求められます。
  • ネスト型の割り込み処理:様々な優先度の複数の割り込みを同時に処理することが求められます。DSP は、一度に 1 つの割り込みを処理します。ただ、優先度が高い割り込みは、優先度が低い割り込み処理よりも優先されることがあります。
  • データの受け入れ/ステータスの記録の継続:DSP が割り込みを処理している間も、現実の世界ではイベントが発生し続け、データが継続的に到着します。それに遅れないようにするために、DSP はそれらのイベントを記録してデータを受け入れます。割り込み処理が完了したら、それらを処理しなければなりません。

アナログ・デバイセズが提供するDSP製品では、2組のデータ用レジスタを使用してコンテキストの高速スイッチングを実現しています。2組が同時にアクティブになることはなく、各コンテキストには処理中のすべてのデータが含まれます。割り込み処理の際、プロセッサはデータをメモリに一時的に保存する必要はなく、アクティブな状態のレジスタからもう一方のレジスタへと切り替えを行うことができます。それにより、タスク間の切り替えを迅速かつ円滑に実施できます。

また、アナログ・デバイセズのDSP製品は、複数の割り込みを処理するためにそれぞれの状態を記録します。プロセッサの状態に関する情報は、DSPのProgram Sequencerに配置されたステータスのセットであるスタックに保持されます。スタックは、ハードウェア・ベースの1組のレジスタで構成されています。現在のステータス情報は、イベントの発生時にスタックにプッシュされます。また、このスタックの仕組みによって割り込みをネストすることも可能です。つまり、優先度の高い方が優先度の低い方に割り込むことができます。

アナログ・デバイセズのDSP製品は、割り込みラッチと自動化IOという2つのハードウェア機能を備えています。それらによって、割り込み処理の最中でも現実世界に遅れないようにすることができます。割り込みラッチは、DSPが割り込み処理の最中に重要なイベントを見落とさないようにするためのものです。一方、自動化IO(シリアル・ポート、DMA、オートバッファリングなどを含む)の機能を利用することで、外部デバイスはDSPが介入しなくてもDSPのメモリにデータを送出できるようになります。そのため、DSPがビジーの状態であっても、データは一切欠落することはありません。

外部ソースまたは内部リソースから割り込みの要求が発生すると、DSPは自動的に現在の動作状態を保存して割り込みルーチンの実行に備えます。割り込みルーチンは、割り込みベクトルのテーブルからディスパッチされます。割り込みベクトルのテーブルは、プログラム・メモリ内の一領域として存在します。同テーブルは、DSPの特定の割り込み機能に割り当てられた命令アドレスを備えています。そのテーブルの例を以下に示しました。この例の場合、ADSP-2181のシリアル・ポート1(SPORT1)における送信(Tx)割り込みによって、プログラム・メモリ(PM)の0x0020という位置で次の命令が実行されます。それに続いて、0x0023までの3つの位置の内容(割り込みルーチン)が実行されます。このテーブルに記述された12個の項目を見るとわかるように、ADSP-2181は、11の位置(外部ハードウェア、DMAポート、シリアル・ポート)とプロセッサのリセットからの割り込みを処理することができます。なお、このテーブルはFIRフィルタ用のものです。これは、0x0000から0x002Fのメモリ位置にある各割り込みベクトルのソースに割り当てられたプログラム済みの命令を表しています。

Jump start; nop; nop; nop; /* PM(0x0000-03): Reset vector */
rti; nop; nop; nop;        /* PM(0x0004-07): IRQ2 vector */
rti; nop; nop; nop;        /* PM(0x0008-0B): IRQL1 vector */
rti; nop; nop; nop;        /* PM(0x000C-0F): IRQL0 vector */
ar = dm(stat_flag); ar = pass ar; if eq_rti; jump next_cmd;
                       /* PM(0x0010-13): SPORT0 Tx vector */
jump input_samples; nop; nop; nop;
                       /* PM(0x0014-17): SPORT0 Rx vector */
jump irqe; nop; nop; nop; /* PM(0x0018-1B): IRQE vector */
rti; nop; nop; nop;        /* PM(0x001C-1F): BDMA vector */
rti; nop; nop; nop;        /* PM(0x0020-23): SPORT1 Tx vector */
rti; nop; nop; nop;        /* PM(0x0024-27): SPORT1 Rx vector */
rti; nop; nop; nop;        /* PM(0x0028-2B): Timer vector */
rti; nop; nop; nop;        /* PM(0x002C-2F): Powerdown vector */

各割り込みベクトルには4つの命令位置があります。通常、それらの命令は、Reset(0x0000)、SPORT0 Rx(0x0014)、IRQE(0x0018)の割り込みベクトルに示されているように、データを処理するためにプロセッサの処理をメモリの別の領域にジャンプさせます。また、値の読み取り、ステータスのチェック、メモリのロードなど、使用可能な4つの命令位置の範囲内で実行可能なわずか数ステップの処理しか必要ないケースがあります。そうした場合、SPORT0 Txのベクトル(0x0010~13)と同様に直接プログラムを記述することができます。未使用の割り込みベクトルは、いずれも割り込みからの復帰(rti)命令を呼び出した上で3つのnop(何も行わない)命令を処理します。

nop命令は、プレース・ホルダとして機能します。これは、ハードウェアで指定された割り込みベクトルを正しい割り込み動作に適合させるために使用される命令スペースです。未使用の各ベクトル位置の先頭にあるrti命令は、プレース・ホルダと“安全弁”の両方の働きをします。未使用の割り込みが誤ってマスクされていない状態になったり、意図せずトリガされてしまったりした場合でも、rti命令によって正常な動作に復帰させることができます。

データの入出力

通常、DSPシステムにおいては、データが到着するか、または新たな出力データを提供することが要求されると割り込みが発生します。その際、割り込みはサンプルごとに発生するか、またはデータのフレームが収集された後に発生することになります。このような違いは、DSPのアルゴリズムにおけるデータの取り扱い方に大きな影響を及ぼします。

サンプルごとに動作するアルゴリズムを使用する場合、DSPのソフトウェアとしては受信したデータの値と送信するデータの値ごとに処理を行わなければならない可能性があります。DSPの各シリアル・ポートには、データの入出力(IO)に使用するレジスタとして、受信(Rx)用のレジスタと送信(Tx)用のレジスタが組み込まれています。通常、シリアル・ワードを受信したら、ポートは受信(Receive)割り込みを発生させます。それを受けて、プロセッサは実行中の処理を停止し、割り込みベクトルの位置でコードの実行を開始します。次に、受信用のレジスタのデータをプロセッサのデータ用レジスタに読み込み、それに対する演算を行うか、バックグラウンドのタスクに戻ります。上記のテーブルの場合、コンピュータはinput_samplesというプログラムのセグメントにジャンプします。続いて、そのセグメントでプログラムされている命令をすべて実行し、直接または割り込みベクトルに戻ることによって割り込みから復帰します。

データを送信する際には、シリアル・ポートは送信(Transmit)割り込みを発生させます。それにより、SPORT Txのレジスタに新しいデータを書き込めることを示します。次に、DSPはSPORT Txの割り込みベクトルによってコードの実行を開始します。その際には、通常、データ用レジスタからSPORT Txのレジスタへデータを転送することができます。データの入出力が同じサンプル・クロックで制御される場合、必要な割り込みは1つだけです。例えば、プログラムのセグメントが受信割り込みのタイミングで開始されるとします。その場合、割り込みルーチンの中に新しいデータが読み込まれます。その後、以前に計算した結果(レジスタに保持されています)が送信されるか、または 割り込みルーチンの最後のステップとして新しい結果が算出されて即座に送信されます。

このような仕組みによって、DSPはアナログ・システムが自然に行っていること(リアルタイムで信号を連続的に処理する)を模擬することができます。アナログ・システムに近い能力を発揮するだけでなく、デジタルならではの正確さと柔軟性も得られます。加えて、効率的にプログラムされたデジタル・システムであれば、データ・セットの処理を行う間に、残りのプロセッサ・サイクルによって他のタスクを処理することも可能です。

プログラミングについて考慮すべき事柄

リアルタイム・システムでは、処理速度が非常に重要です。SPORTのオートバッファリングを使用することで、データの入出力による時間のロスはなくなります。その代わり、データの管理については、選択されたアドレスが確実に新しいデータを指し示すようにすることが重要になります。

FIRフィルタの例(アナログ・ダイアログ 31-3、p.15)では、入力のオートバッファが満杯になると、SPORTの受信割り込みが生成されます。これは、DSPが3つのデータ・ワード(ステータス、左チャンネルのデータ、右チャンネルのデータ)を受信したということを意味します。この簡単なアプリケーションでは、シングルチャンネルのデータを使用しています。そのため、rx_buf+1の位置にあるデータだけがアルゴリズムで使用されます。

実際のフィルタ・アプリケーションでは、この例のアルゴリズムを拡張する必要があり、おそらくはデータの処理がもっと複雑になるでしょう。とはいえ、この例のFIRフィルタを2チャンネルの実装に拡張するといった場合、中核となるアルゴリズムのコードを変更する必要はありません。ただ、データの処理に関連するコードは、もう1つのデータ・ストリームともう1つの係数のセットに対応できるよう修正する必要があります。

追加されるデータ・ストリームと係数のセットの両方を処理するためには、フィルタのコードにおいてメモリ内の2つの新しいバッファを使用する必要があります。ここで、コア・フィルタ(中核となるフィルタ)のループは、呼び出しが可能な個別の関数として分離することができます。この手法を使うことにより、入力されたデータの値にかかわらず、同じコードを再利用することが可能になります。このプログラミング・スタイルを採用すれば、可読性の高いコードを実現できる、アルゴリズムを再利用できる、コードのサイズを削減できるといったメリットが得られます。このようなモジュール方式のコードを採用しない場合には、DSPのメモリ空間を更に使用して、フィルタのループを繰り返さなければなりません。

SPORTの受信割り込みルーチンは、ポインタの設定とフィルタの呼び出しで構成されます。修正後のフィルタのルーチンは、以下のようになります。

Filter: cntr = taps - 1;
mr = 0, mx0 = dm(i2,m1), my0 = pm(i5,m5);
                    /* clear accumulator, get first data
                       and coefficient value */
do filt_loop until ce;     /* set-up zero-overhead loop */
filt_loop: mr = mr + mx0*my0(ss), mx0 = dm(i2,m1),
my0 = pm(i5,m5);     /* MAC and two data fetches */
mr = mr + mx0 * my0 (rnd);   /* final multiply, round to 16-bit
                       result */
if mv sat mr;             / * check for overflow*/
rts;                     /* return */

注目すべき重要なポイントは、コア・フィルタのループの修正個所は次の2つだけだということです。1つは、ルーチンの先頭に「Filter」というラベルを追加したことです。もう1つは、末尾にrts(return from subroutine)という命令を追加したことです。これだけで、フィルタのコードを独立型のルーチンからサブルーチンへと変更することができます。その結果、このコードを他のルーチンから呼び出すことが可能になります。言い換えれば、単一の目的を果たすルーチンではなく、呼び出し/再利用が可能なサブルーチンになったということです。

ここまでで、コア・フィルタのコードを、呼び出しが可能なサブルーチンに変更することができました。それにより、2つのチャンネルのデータを処理するという要件に対応できるようになりました。なお、プログラミング上のいくつかの問題をシンプルに示すために、この例では、左右のチャンネルでは同じフィルタ係数を使用するということを前提としています。

本シリーズのPart 3では、フィルタ・アプリケーション向けのアセンブリ・コードの全体を示しました。そのコードの先頭部分には、必要なメモリ・バッファがすべて宣言されていました。ここでは、そのコードを拡張して2チャンネルのデータを処理できるようにする方法を考えます。そのためには、新たに必要になる変数とバッファを宣言しなければなりません。受信データについては、以下のようにバッファを宣言していました。

.var/dm/circ_filt_data[taps]; /* input data buffer */

2つのバッファに対応するために、上記の宣言を以下のように書き換えます。

.var/dm/circ_filt1_data[taps]; /* left channel input data buffer */
.var/dm/circ_filt2_data[taps]; /* right channel input data buffer */

先述したように、両チャンネルには同じフィルタ係数を適用します。そのため、データ・バッファの長さは等しくなります。

フィルタのループに対応するサブルーチンでは、特定のアドレスに対応するレジスタを使用して特定のデータと係数の値にアクセスすることを想定しています。そのため、アドレスのレジスタI2は最も古いサンプルを指し示している必要があります。また、アドレスI5はフィルタのルーチンの呼び出しに先立って適切な係数値を指し示していなければなりません。

この例では、両チャンネルのフィルタが同じメモリ・ポインタを共有します。そのため、2つのデータ・ストリームを区別するための仕組みを用意しなければなりません。つまり、データ・ポインタI2には、2つの新たな変数filter1_ptr、filter2_ptrを定義する必要があります。

メモリ内のこれらの位置は、各データ・ストリームに適切なアドレスの値を格納するために使用されます。ADSP-2181の循環バッファ機能を使用すれば、フィルタ処理が実行されるたびにデータ・ポインタが常にバッファ内で適切な位置にあるようにすることができます。このサブルーチンは、2つのバッファに対応しています。そのため、各チャンネルの処理が完了したら、各ポインタの位置を保存しなければなりません。

ポインタを設定するためには、データ・メモリ内の2つの変数を次のように宣言します。

.var/dm filter1_ptr; /* data pointer for left channel data */
.var/dm filter2_ptr; /* data pointer for right channel data */

これらの変数は、各データ・バッファの開始アドレスで初期化する必要があります(以下参照)。

.init filter1_ptr: ^filt1_data; /* initialize starting point,
                       left channel */
.init filter2_ptr: ^filt2_data; /* initialize starting point,
                       right channel */

DSP用のアセンブラ・ソフトウェアは、「^」という記号を「~のアドレス」という意味で認識します。それに応じて、DSPのリンカ・ソフトウェアが適切なアドレスの値を書き込みます。このようにすることで、実行可能なプログラムのポインタ変数が、適切なメモリ・バッファの開始アドレスで初期化されます。

以下のリストは、FIRフィルタの割り込みルーチンがこれらの新たなメモリ関連の要素をどのように使用するのかを示しています。本シリーズのPart 3では、Filterというサブルーチンを示しました。そのサブルーチンを修正することで、2つのチャンネルに対して個別にフィルタ処理を適用できるようになりました。このルーチンは、フィルタの計算を直接開始するわけではありません。まずは、適切なデータ・ポインタをロードする必要があります。次に、フィルタのルーチンが呼び出されます。その結果は、送信を実行するために適切な位置に配置されます。

/*--------------------FIR Filter--------------------*/
input_samples:
   ena sec_reg;        /* use shadow register bank */

/* set up for filter 1 */
i2 = dm(filter1_ptr); /* set data pointer for filter 1 */
ax0 = dm(rx_buf + 1); /* read left channel data */
dm(i2,m1) = ax0; /* write new data into delay line,
             pointer now pointing to oldest data */

call filter;        /* perform the first filter for left
               channel data */

dm(tx_buf+1) = mr1;     /* write left-channel output data */
dm(filter1_ptr) = i2; /* save updated filter1 data pointer */

/* set up for filter 2 */
i2 = dm(filter2_ptr); /* set data pointer for filter 2 */
ax0 = dm(rx_buf + 2); /* read right channel data */
dm(i2,m1) = ax0;        /* write new data into delay line,
                  pointer now pointing to oldest data */

call filter;        /* perform the filter again for the
               right channel data */

dm(tx_buf+2) = mr1; /* write right channel output data */
dm(filter2_ptr) = i2; /* save updated filter2 data pointer */

rti;                /* return from interrupt */

これでコア・フィルタのアルゴリズムでは、データの入出力の処理は行われなくなりました。このサブルーチンでは、単にポインタ変数を追加し、より多くのバッファ空間を宣言しているだけです。同様の修正を行うことにより、(メモリが十分に存在する限り)フィルタ処理の対象とするチャンネル数を拡張することができます。また、係数用のバッファのポインタ情報を含む変数を設定すれば、2つのフィルタにおいて異なる係数を使用することも可能です。どちらについても、フィルタのアルゴリズム自体は変更する必要はありません。このようなモジュール方式のプログラミング・スタイルを採用することで、呼び出しが可能なDSP用の関数ライブラリを構築することができます。そのようにすれば、個々のシステム間の違いに対しては、新たなアルゴリズムを開発するのではなく、データ処理の問題に集約して対処できるようになります。もちろん、このようなプログラミング・スタイルを採用したからといって、アルゴリズムを使用するタスクをより高速に実行できるようになるとは限りません。それでも、システム内でデータをどのように伝送するのかということを、より柔軟に設定することができます。

リアルタイム・インターフェースの問題: ここまで、組み込みシステムにおけるリアルタイム対応のプログラミングが、迅速な割り込み応答、効率的なデータ処理、高速なプログラムの実行にどのように依存するのかということについて検討してきました。ただ、プロセッサに継続的に入出力されるデータの伝送方式も、リアルタイム対応の組み込み環境でシステムがいかにうまく機能するのかということに対して影響を及ぼします。

DSPに入出力されるデータ伝送方式としては、パラレルでもシリアルでも構いません。通常、パラレルのデータ伝送では、プロセッサのアーキテクチャのネイティブなデータ・ワードと同じ幅を使用します。その幅は、ADSP-2100ファミリでは16ビット、SHARC®ファミリでは32ビットです。パラレルのデータ伝送は、プロセッサの外部メモリ・バスまたは外部ホスト・インターフェース・バスを介して行われます。一方、シリアルのデータ伝送では、必要なインターコネクトの数をかなり少なく抑えられます。シリアルのデータ伝送は、ADコンバータやDAコンバータと通信を行う場合によく使用されます。

シリアル・インターフェース: ハードウェアのインターフェースが扱いやすいか否かは、DSPシステムを効率的に実装する上で重要な要素になります。「ADSP-2181 EZ-Kit Lite」では、シリアル対応のコーデック(Coder-Decoder)「AD1847」を使用しています。このようなコーデックを採用すれば、DSPのシリアル・ポート(SPORT)を介してデータ伝送を行うことができます。ここで言うシリアル・ポートは、PCなどで使われるRS-232のような非同期のシリアル・ポートとは異なるものです。5線式の同期式インターフェースであり、ビット・クロック、受信データ、送信データ、フレーム同期信号を伝送します。シリアル・インターフェースの主な長所としては、ピン数が少なく、ハードウェアの接続が容易であることが挙げられます。AD1847は、DSPとのインターフェースとして4つの信号しか使用しません。シリアル・クロック、受信データ、送信データ、受信フレーム同期信号の4つです。シリアルのデータ・ストリームには、TDM(Time Division Multiplexing:時分割多重化)が適用されています。つまり、同じ物理回線によって複数種の情報を順番に伝送するということです。EZ-Kit Liteが備えるAD1847のアプリケーションでは、シリアル・ラインを使用することで、コーデックの制御情報/ステータス情報と共に左右のチャンネルの音声情報を伝送します。

先述したように、プロセッサはそうしたデータを処理するための様々な方法を備えています。受信データと送信データのうちいずれか、あるいは単一ワードまたは1ブロックのワードに対しては、シリアル・ポートのハードウェアによって、SPORT割り込みが自動的に発生します(図2)。

図2. DSPとIOデバイスの間のシリアル・インターフェース
図2. DSPとIOデバイスの間のシリアル・インターフェース

パラレル・インターフェース: シリアル・インターフェースでは、ビット・クロックがDSPのプロセッサと同じ速度である場合、配線の簡便さと引き換えにデータ伝送速度が犠牲になります。つまり、DSPのプロセッサの速度の数分の一程度でデータ・ワードを転送することになります。したがって、システム性能を得るために、より高いデータ・レートが必要な場合には、パラレル・インターフェースを使用するとよいでしょう。パラレル・インターフェースの場合、DSPは外部データ・バスとアドレス・バスを使用し、ペリフェラル・デバイスに対してデータを読み書きします。ADSP-2181の場合、各バスは最大16ビットのパラレル・データに対応できます。

パラレル・インターフェースでは、シリアル・インターフェースよりも高いデータ伝送速度を実現することが可能です。DSPでは、プロセッサ・サイクルごとに外部にアクセスすることができますが、そのためには、高速なSRAMチップなど、DSPに追従できる非常に高速なパラレル対応のペリフェラルが必要になります。他のエンティティとの間のパラレル・データ伝送は、通常はプロセッサ・サイクルよりも短い時間で実現可能です。

シリアル・インターフェースを使用する場合とパラレル・インターフェースを使用する場合とでは、割り込み処理に違いがあります。DSPのプロセッサの外部データ・バスは、あらゆる種類のデータを扱える汎用のエンティティです。そのため、割り込みを発生させたり制御を行ったりするための専用の信号線は備えていませんが、DSPの他のリソースは利用可能です。例えば、ADSP-2181では、IOメモリの選択用のものなど、外部ハードウェア用のいくつかの割り込みラインを利用できるようになっています。それらを使えば、ADコンバータやコーデックなどの外部デバイスからトリガすることが可能です。パラレル・デバイスとADSP-2181のインターフェースの例を図3に示しました。

図3. DSPに対する入出力用のパラレル・インターフェース
図3. DSPに対する入出力用のパラレル・インターフェース

パラレル・インターフェースを使用する場合、割り込みに応答する際にはプロセッサが適切なソースを読み出します。そして通常は、以下に示すのと同様の命令を実行することによって、データをメモリに読み込みます。

irq2_svc: ax0 = IO(ad_converter); dm(i2,m1) = ax0; rti;
このコードにおいて、ad_converterはIO空間であらかじめ定義されたアドレスのことを意味します。

まとめ

本稿では、DSPをベースとするリアルタイム・システムで、データの入出力(IO)やその他のイベントを処理する際に直面するプログラミング上の懸念事項について詳しく説明しました。具体的には、リアルタイムのデータ(サンプルとフレーム)という言葉の意味、割り込みと割り込み処理、自動化されたIO、呼び出しが可能なサブルーチンを開発するためのルーチンの一般化などについて解説しました。各トピックについて、実際にはより多くの事柄について検討する必要があります。詳細については、稿末に示した参考資料をご覧ください。今後、本シリーズでは本稿で示したアプリケーションに基づいて解説を進める予定です。次回は、サンプル・プログラムに更に機能を追加します。また、ソフトウェアの検証方法(デバッグ)について説明します。

参考資料

ADSP-2100 Family Assembler Tools & Simulator Manual (ADSP-2100ファミリのアセンブラ・ツールとシミュレータのマニュアル。詳細については、アナログ・デバイセズの販売代理店にお問い合わせください)
ADSP-2100 Family User's Manual(ADSP-2100ファミリのユーザ・マニュアル。アナログ・デバイセズ。無料)

当社のウェブサイトでは、多くの有用な出版物を紹介しています。詳細については、製品ドキュメントの下に示した設計支援のページをご覧ください。

併せて、本シリーズのPart 1Part 2Part 3もご覧ください。

著者

Generic_Author_image

David Skolnick

Generic_Author_image

Noam Levine

Noam Levine氏は、The MathWorksのテクニカル・マーケティング部門に所属しています。2008年に入社しました。主に、組み込みプラットフォームを対象としたモデル・ベース設計のワークフローを担当しています。