バッファの渡し方

TOPPERS/JSP for Blackfin上にTalkthroughを作りました。タスクのひとつにAD1836Aの初期化と、SPORT0のダブルバッファDMAの管理を行わせています。受信バッファはデータキューを使って他のタスクに送られ、同じくデータキューを使って返されます。ダブルバッファDMAは管理タスクが裁きますのでユーザーは意識せずにデータキューを使って送られるデータだけを処理すればいいのです。管理タスクとDMAの同期には割り込みハンドラからのセマフォへのシグナルを使います。
オーディオ入出力をひとつのコーデックでまかなっているため、データキューの使い方には二通りあります。

送受バッファ一括転送

割り込みハンドラからバッファ転送終了を知らせるシグナルを受け取ったら、受信バッファと送信バッファをまとめてデータキューで送り出す方式です。これは送りと受け合わせて2回のシステムコールで済む為、オーバーヘッドが小さくなります。
両バッファを受け取ったタスクは受信バッファからデータを取り出して処理し、送信バッファに書き込みます。書き込んだバッファは特に送信などせず、タスクは次のバッファが送られてくるのを待ちます。管理タスクは次に送信される送信バッファをデータキュー経由で渡しているため、何もしなくても時期が来れば勝手にDMAが送信を始めます。
この方式はオーバーヘッドが軽いものの、一点同期なので宙ぶらりんな気がして気持ち悪いのが欠点です。

送受バッファ個別転送

管理タスクはSPORT0の割り込みハンドラから転送終了のシグナルを受け取ると、受信バッファを外部のタスクに向けてデータキューで送り出します。次に外部のタスクからデータキューで送信バッファが送られてくるのを待ちます。送信バッファを受け取ったら、ダブルバッファのうちのしかるべきほうにコピーします。
この方法はサービスコールの呼び出し回数が両タスクで計4回になり、オーバーヘッドが大きな気分になるのが欠点です。実際にはオーバーヘッドは無視してかまいません。また、タスク切り替えが余計に発生する気がしますが、実際にはデータキューの深さを1以上にしておけば、タスク切り替え回数は先の場合と同じです。
この方式はには、(そんな場合があるかどうかは別として)複数のタスクでパイプラインを組むのが簡単であるという長所があります。

今回のお試し

今回は後者の「送受バッファ一括転送」を行いました。600MHzで8サンプル一括DMA処理の場合、全体の97.2%が割り込み待ちでした。つまり17MIPSでRTOS上のTalkthroughを実行していることになります。処理プログラムはこんな感じです。

while( 1 )
{
    int sample, slot;
	
    rcv_dtq( CODEC_RX_DTQ, (VP_INT*)& rx_buf );          // 受信データ受け取り
	
    tx_buf.count = rx_buf->count;                        // サンプル数コピー
    for ( sample = 0; sample < rx_buf->count; sample++ ) // オーディデータのコピー
        for ( slot =0; slot < 8; slot ++ )
            tx_buf.data[sample][slot] = rx_buf->data[sample][slot];

    snd_dtq( CODEC_TX_DTQ, (VP_INT) & tx_buf );          // 送信データ引渡し
}

ついでながら管理タスクのメインループはこんな感じです。

while( 1 ){
    int sample, slot, buf;
    struct CODEC_BUFFER * BufToBeTransmit; 
	
    wai_sem( SPORT0_SEM );                          // SPORT0 RX 割り込み待ち
		
    buf = ( &tDescA == *pDMA2_NEXT_DESC_PTR) ? 0 : 1;           // どちらのバッファを使うか判定

    snd_dtq( CODEC_RX_DTQ, (VP_INT) &RxBuffer[buf] );    // 受信データ渡し
    rcv_dtq( CODEC_TX_DTQ, (VP_INT *)&BufToBeTransmit ); // 送信データ待ち

                                                             // 受け取った送信データを送信バッファへ
    for ( sample = 0; sample < RxBuffer[0].count; sample++ )
        for ( slot =0; slot < 8; slot ++ )
            TxBuffer[buf].data[sample][slot] = BufToBeTransmit->data[sample][slot];
}