V850付録基板でOgg Vorbisプレイヤー(12)
Posted by arms22 on 2007年07月12日

Ogg Vorbisプレイヤーの再生処理を割り込み使った1バイトずつの転送方式からDMAを使った自動転送に変更しました。DMAを使うことで割り込み処理のオーバーヘッドをなくすことができます。
※相変わらず22KHz-16bit-stereoデータのデコードは再生に間に合っていませんが。。
DMAを使った再生処理は次のようになっています。
1.FIFO空き確認
2.OggVorbisファイルをデコード
3.デコードしたデータをFIFO(内蔵RAM)にコピー
4.FIFO(内蔵RAM)のアドレスをDMAの転送元アドレスに設定
5.DMA転送カウント数を設定
6.DMAが止まっていたらDMA転送開始
7.TQ0カウント開始
8.DMA転送完了割り込みでFIFOのリードポインタを更新
9.'1'から'8'をOggVorbisファイルの終わりまで繰り返します
FIFOのサイズは片チャンネルあたり4096バイトです。1回のDMA転送で1024バイト転送します。1024バイトの転送毎にDMA転送完了割り込みが発生します。DMA転送完了割り込みでFIFOのリードポインタを更新し、転送済みのデータを破棄。FIFOに空きを作ります。
DMAの設定は次のようになっています。
チャンネル | 転送元 | 転送先 | 起動要因 | |
DMA0 | Lch | 内蔵RAM | DA0CS0 | INTTQ0CC0 |
DMA1 | Rch | 内蔵RAM | DA0CS1 | INTTQ0CC1 |
LチャンネルにはDMA0を使用、RチャンネルにはDMA1を使用します。
DMA0の転送先にはDACの出力レジスタDA0CS0を設定、DMA1にはDA0CS1を設定します。
DMA0の起動要因はINTTQ0CC0を設定、DMA1にはINTTQ0CC1を設定します。
TQ0はインターバルタイマに設定します。
TQ0CCR0に再生サンプリング周波数で一致するように値を設定します。
TQ0CCR1にはTQ0CCR0からDMAの読み書きサイクル分11cycleを引いた値を設定します。
現在のOggVorbisPlayerのソースコード http://xfind.hp.infoseek.co.jp/electric/oggplayer/OggVorbisPlayer_7_12.zip
V850付録基板でOgg Vorbisプレイヤー(11)
Posted by arms22 on 2007年07月01日

Tremorを最適化して22KHz-16bit-monoデータを再生させることができた。22KHz-16bit-stereoはまだデコードが追いつかない。
今回行った最適化はov_read8関数の作成と幾つかの処理をインラインアセンブラにした。
ov_read8関数はov_readの改造版で符号なし8bitデータを返す関数だ。バッファに値をコピーする前に符号あり16bitデータを8bitデータに変換してコピーする。バッファ容量の節約と処理時間の節約に繋がる。
インラインアセンブラはasm_arm.hを参考にMULT32/MULT31_SHIFT15/CLIP_TO_15/CLIP_TO_8の4つの関数をインラインアセンブラ化した。※CLIP_TO_15関数はov_read8関数では使用していない。変わりにCLIP_TO_8関数を使用している。CLIP_TO_8関数はCLIP_TO_15関数に加算とシフトを追加した関数。
今回のインラインアセンブラで書いてて一番面白かったのはCLIP_TO_15関数だ。
CLIP_TO_15関数は入力値を符号あり16bitの最大値、最小値に飽和処理してくれる。C言語で書くと次のようになる。
if(x>32767)x=32767; if(x<-32768)x=-32768; return(x);Tremorはもう少し賢くて次のようなコードで書いている。
int ret=x; ret-= ((x<=32767)-1)&(x-32767); ret-= ((x>=-32768)-1)&(x+32768); return(ret);しかし上の二つのコードはV850でコンパイルするとどちらも条件分岐を含んだコードを生成する。 V850の飽和加算、飽和減算を使えば上記のコードは条件分岐なしで書く事ができる。
int tmp; asm volatile("mov 0x7fff8000, %1?n?t" "satadd %1, %0?n?t" "sub %1, %0?n?t" "satsub %1, %0?n?t" "add %1, %0?n?t" : "+r"(x), "=r"(tmp) : : "cc"); return x;satadd/satsub命令は飽和処理付きの加算/減算命令で、演算結果が正の最大値0x7fffffffを越えたときは0x7fffffffを、負の最大値0x80000000を越えたときは0x80000000をレジスタに格納する。
正側の飽和処理はsatadd命令で入力値xと0x7fff8000を加算し、その結果から0x7fff8000を引く。するとxが0x7fffを超えていた時は0x7fff、xが0x7fff以下ならxがそのまま返る。
負側の飽和処理は正側の逆で、satsub命令で入力値xから0x7fff8000を減算し、その結果に0x7fff8000を足す。するとxが0x8000を超えていた場合は0x8000、xが0x8000以下ならxがそのまま返る。
今回の成果物OggVorbisPlayerのソースコード
http://xfind.hp.infoseek.co.jp/electric/oggplayer/OggVorbisPlayer_7_1.zip
V850付録基板でOgg Vorbisプレイヤー(10)
Posted by arms22 on 2007年06月24日

FLASH上においたOgg Vorbisファイルをオープンして、デコード、再生するところまでできた。
用意したデータはmacosxのシステムサウンド「Submarine」をiTunesで44KHz-16bit-stereoから16KHz-16bit-monoデータに変換し、Ogg Drop Xエンコーダで.oggファイルに変換したものを使った。Ogg Drop Xエンコーダで変換する時、ビットレートは最高のVBR Quality 10を指定した。
ov_readで読み込んだデータは符号ありの16bitデータなので符号なしの8bitデータに変換する必要がある。読み込んだ値に32768を足して8bit右シフトしてDA0CSxに書き込む。
16KHz-16bit-monoだとデコードは再生に間に合っている。けど22KHz-16bit-monoだと少しデコードが再生に間に合わない。今はTMQ0の割り込みを使って再生しているので割り込みの負荷が大きい(62.5usに1回割り込みが発生)。これはDMAを使って再生を行えば再生の負荷は減らせる。それにTremorの最適化の余地もまだ残してあるので、22KHz-16bit-stereoぐらいまでは再生できるようにしたい。
現在のOggVorbisPlayerのソースコード
http://xfind.hp.infoseek.co.jp/electric/oggplayer/OggVorbisPlayer.zip
V850付録基板でOgg Vorbisプレイヤー(9)
Posted by arms22 on 2007年06月20日

TMQ0の一致割り込みでDMA0,1を起動させようと思っていたのだが、1つの割り込み信号で複数のDMAの起動してはいけないという注意書きがあった。
またDMAのリード→ライトには11cycle必要なのでDA0にライトしてからDA1へライトするまでに最大550ns(20MHz動作時)の遅延が発生する(無視しても問題ないかもしれない)。
そこでDAxのリアルタイム出力機能を使ってこの遅延をなくす方法を検討してみる。
リアルタイム出力機能はDA0CSxに書き込んだ値がINTTP2CC0信号(DA0),INTTP3CC0信号(DA1)で出力される。
しかし1命令ずつしか命令を実行できないマイコンでTMP2,TMP3を同時に起動することはできない。
INTTP2CC0信号とINTTP3CC0信号を同時に発生させるには少しプログラムを工夫しないといけない。
TMP2を起動してからTMP3を起動するまでのカウント数をコンペアレジスタから予め引いておけばINTTP2CC0信号とINTTP3CC0信号を同時に発生させることができるかもしれない。
こんな感じでどうだろう、、
static USHORT RealTP3CCR0; void TMP2AndTMP3SyncedStart(void) { register USHORT tp3ctl0,tp2ctl0; RealTP3CCR0 = TP3CCR0; /*此処は要検討 カウントクロックをfxxとした場合、 単純に命令実行クロック分の遅延になる?*/ TP3CCR0 = RealTP3CCR0 - 1; tp3ctl0 = TP3CTL0 | 0x80; tp2ctl0 = TP2CTL0 | 0x80; ClrIORBit(TP3CCIC0,0x40); __DI(); TP2CTL0 = tp2ctl0; TP3CTL0 = tp3ctl0; __EI(); } void MD_INTTP3CC0( void ) { /* 1回目の割り込みで本当のTP3CCR0レジスタの値を設定する */ TP3CCR0 = RealTP3CCR0; /* Mask interrupt */ SetIORBit(TP3CCIC0,0x40); /* Clear interrupt request flag */ ClrIORBit(TP3CCIC0,0x80); }とここまで書いて、本当にそんなことする必要があるのだろうかと思えてきた。。DMAの起動はINTTQ0CC0とINTTQ0CC1で良いんじゃないかと。
V850付録基板でOgg Vorbisプレイヤー(8)
Posted by arms22 on 2007年06月15日

SRAMのバスの配線が終わって基板が完成した。
テストプログラムでSRAMのリードとライト動作を確認。
念入りにバイトアクセス、ハーフワードアクセス、ワードアクセスも確認した。
UART経由で結果(OKかNG)を送信する。
Interface6月号のADPCMのサンプルコードをgcc用に移植してオーディオ出力動作も確認した。「あったり~」「ざんね~ん」を繰り返し出力させた。思った以上に音が悪かったのは電源のせいかな?ヘッドフォンに出力しているから音がしゃりしゃりいうのかな。。
アナログ部の電源だけは別にする必要がありそうだ。
SRAMテストプログラム
http://xfind.hp.infoseek.co.jp/electric/oggplayer/sramtest.zip
ADPCMサンプルコード(移植)
http://xfind.hp.infoseek.co.jp/electric/oggplayer/audiosample.zip
参考書籍
Interface (インターフェース) 2007年 06月号 [雑誌]
V850付録基板でOgg Vorbisプレイヤー(7)
Posted by arms22 on 2007年06月08日

アプリレットを使ってV850の周辺機能のレジスタ設定ファイルを作成しました。
gccでコンパイルを通す為にいくつかの修正を加えます。
1.レジスタ定義ファイルの作成
まずgccにはV850のレジスタ定義がないのでレジスタ定義ファイルを作成します。PDFからレジスタ一覧をコピーし型を合わせてヘッダファイルにします。v850es_jg2.hとアセンブラ用にv850es_jg2.incを作成しました。
macrodriver.hを次のように書き換えv850es_jg2.hをインクルードします。
#pragma io → #include "v850es_jg2.h"crte.sとsystem.sでv850es_jg2.incをインクルードします。
.include "v850es_jg2.inc"
2.アセンブラファイルの修正(crte.s)
mov命令を書き換えます。
TOPPERS/JSPのコードを参考にlea(load effective address)といマクロを作成します。
.macro lea name, destination mov hilo(?name), ?destination .endmleaマクロでmov命令を次のように書き換えます。
mov #__ep, ep → lea __ep, ep省略表記を修正します。
st.w r0, [r13] → st.w r0, 0[r13]
mainの引数は要らないので次の行をコメントアウトします。
ld.w $__argc, r6 -- set argc movea $__argv, gp, r7 -- set argv
3.vectorテーブルの作成(inttab.s)
TOPPERS/JSPのコードを参考にjrとnopを挿入するマクロでvectorテーブルを作成します。
4.リンカスクリプトの作成(v850.ld)
TOPPERS/JSPのコードを参考にリンカスクリプトを作成します。vectorテーブルをROMの先頭に配置するようにします。
inttab.sで次のようにセクションを記述し、
.section ".vector", "ax" .align 4 InterruptHandler RESET, __start ReservedHandler NMI ...リンカスクリプトで次のように配置します。
MEMORY { IROM(raxi) : org = 0x00000000, len = 256k IRAM(wax) : org = 0x03FF9000, len = 24k }; SECTIONS { .vector : AT(0) { *(.vector) } >IROM ...gccでコンパイルが通るように修正したソースはこちら。
devdrv.zip レジスタ定義ヘッダファイルも含みます。
V850付録基板でOgg Vorbisプレイヤー(6)
Posted by arms22 on 2007年06月05日

OggVorbisプレイヤーに必要なV850の周辺機能をまとめてみました。
機能 | ブロック | 用途 |
BUS | CS0 | 外付けSRAM(Tremor用ワークRAM64K) |
DAC | DAC0 | オーディオ出力L(8bit) |
DAC1 | オーディオ出力R(8bit) | |
DMA | DMA0 | DACレジスタへのデータ転送(内蔵RAM→DAC0) |
DMA1 | DACレジスタへのデータ転送(内蔵RAM→DAC1) | |
TIMER | TMQ0 | サンプリング周波数(DMA0,DMA1の転送トリガ) |
TMM | 1msインターバルタイマ(速度計測用他) | |
WT | WT | 0.5s時計タイマ(LED点滅用) |
INT | INTDMA0 | DMA0転送完了割り込み |
INTDMA1 | DMA1転送完了割り込み | |
INTTM0EQ0 | 1ms割り込み | |
INTWT | 0.5s割り込み | |
INTUA0R | UART受信割り込み | |
INTUA0T | UART送信割り込み | |
UART | UART0 | デバッグ用出力 |
PORT | P10 | ANO0端子 |
P11 | ANO1端子 | |
P30 | UART0 TX端子 | |
P31 | UART0 RX端子 | |
P32-P35 | LCD D4-D7 | |
P36 | LCD RS | |
P38 | LCD RW | |
P39 | LCD E | |
P90-P915 | SRAMアドレスバス(A0-A15) | |
PCT0 | SRAMライトイネーブル(WR0) | |
PCT1 | SRAMライトイネーブル(WR1) | |
PCT4 | SRAMリードイネーブル(RD) | |
PCT6 | LED | |
PDH0-PDH5 | SRAMアドレスバス(A16-A21) | |
PDL0-PDL15 | SRAMデータバス(AD0-AD15) |
OggVorbisプレイヤーのシステムの概要は次のとおりです。
1.TremorでOggVorbisファイルをデコードする
2.TremorでデコードしたPCMデータを内蔵RAMにコピー
3.PCMデータのアドレスをDMAの転送元アドレスに設定
4.転送カウント数を設定しDMAを起動
5.DMAはTMQ0の割り込み信号により転送を開始
6.TMQ0の割り込み毎に1byteずつDAC出力レジスタにPCMデータを書き込む
7.DMAの転送完了割り込みで次のPCMデータのアドレスをDMAの転送元アドレスに設定
8.4に戻る
DMA転送中に次のPCMデータをデコードする。
これが間に合うかどうかだな。。
V850付録基板でOgg Vorbisプレイヤー(5)
Posted by arms22 on 2007年06月03日

少し間が空いてしまったけど、今回は回路図を作成した。
V850の内蔵RAMは24Kしかなく、Ogg VorbisをデコードするにはRAMが足りないので外付けのSRAMを追加した。手持ちのIS61LV256を2個使って、32K*2=64Kに。
IS61LV256のデータ幅は8ビットなので1つをD0-D7に接続して、もう1つをD8-D15に接続する。
V850にはCS信号の出力機能が無いのでA20,A21からCS信号を生成する。アドレスのデコードには定番の74VHC138を使った。A20=H,A21=Lの時にSRAMが選択されるようにする。
アナログ回路周辺はInterface6月号のADPCMを再生する記事を参考にした。
アンプはLM386を2個用意する。
またV850基板上のレギュレータではSRAM2個を駆動するには出力が足りないので、SRAM用の3.3v電源にTA48033レギュレータを追加した。
さらにデバッグ用にキャラクタLCDをP32~P39に接続する。入力は5VトレラントになっているのでLCDの5V出力をそのまま入力できる。出力もLCD側の入力しきい値が2.2vとなっているのでV850側の3.3v出力で十分'1'判定ができる。これもInterface 6月号を参考にした。
部品はすべて購入して8割ほど半田付けがすんでいる。後はSRAMのバスを接続するだけなのだが、バスの一筆配線が難しくて思うように作業がはかどらない。UEW線を試してみたのだが、うまく被膜が溶けず半田がのらない。むむ。。
バス配線をらくちんに行えるアイテムがあればいいのだが。。
参考書籍
Interface (インターフェース) 2007年 06月号 [雑誌]
Interface (インターフェース) 2007年 07月号 [雑誌]
V850付録基板でOgg Vorbisプレイヤー(3)
Posted by arms22 on 2007年05月13日

Tremorは浮動小数点演算を使わず64bit整数演算のみでデコードを行います。其の為、Tremorのコンパイルにはコンパイラが64bit演算に対応している必要があります。
残念なことにNECのCコンパイラCA850は64bit演算に対応していません。そこでgccでV850のクロス開発環境を構築することにしました。
利用するパッケージはbinutils-2.17とgcc-3.4.6。gccのクロスコンパイルの手順はこちらを参考にした。
ただ今のところMacOSX(10.3)ではgccのコンパイルが通っていない。gcc/config/v850/lib1funcs.asmのコンパイルで次のようなエラーが発生している。
gcc-3.4.6/build-v850/gcc/xgcc -Bgcc-3.4.6/build-v850/gcc/ -nostdinc -Bgcc-3.4.6/build-v850/v850-nec-elf/newlib/ -isystem gcc-3.4.6/build-v850/v850-nec-elf/newlib/targ-include -isystem gcc-3.4.6/newlib/libc/include -B/usr/local/cross/v850-nec-elf/bin/ -B/usr/local/cross/v850-nec-elf/lib/ -isystem /usr/local/cross/v850-nec-elf/include -isystem /usr/local/cross/v850-nec-elf/sys-include -O2 -DIN_GCC -DCROSS_COMPILE -W -Wall -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition -isystem ./include -mno-app-regs -msmall-sld -Wa,-mwarn-signed-overflow -Wa,-mwarn-unsigned-overflow -g -DIN_LIBGCC2 -D__GCC_FLOAT_NOT_NEEDED -Dinhibit_libc -I. -I. -I../../gcc -I../../gcc/. -I../../gcc/../include -I../intl -mv850e -DL_callt_save_20 -xassembler-with-cpp -c ../../gcc/config/v850/lib1funcs.asm -o libgcc/v850e/_callt_save_20.o ../../gcc/config/v850/lib1funcs.asm: Assembler messages: ../../gcc/config/v850/lib1funcs.asm:1613: Error: prepare {r20-r29},0: operand out of range (0 is not between 0 and -1) ../../gcc/config/v850/lib1funcs.asm:1613: Error: prepare {r20-r29},0: operand out of range (0 is not between 0 and -1) ../../gcc/config/v850/lib1funcs.asm:1613: Error: prepare {r20-r29},0: operand out of range (0 is not between 0 and -1) ../../gcc/config/v850/lib1funcs.asm:1613: Error: prepare {r20-r29},0: operand out of range (0 is not between 0 and -1) ../../gcc/config/v850/lib1funcs.asm:1613: Error: prepare {r20-r29},0: operand out of range (0 is not between 0 and -1) ../../gcc/config/v850/lib1funcs.asm:1613: Error: dispose 0,{r20-r29},r31: operand out of range (0 is not between 0 and -1) make[2]: *** [libgcc/v850e/_callt_save_20.o] Error 1 make[1]: *** [libgcc.a] Error 2 make: *** [all-gcc] Error 2ソースコード自体はすでに実績があるので問題ないとして、アセンブラv850-elf-asのコンパイルがうまくいっていないのか?
エラーが発生した該当箇所のコードを抜き出してv850-elf-asに食わせてやると同じエラーが発生している。
追記:binutils-2.14では上記エラーは警告扱いになるのでgccのコンパイルが通るかも。。
V850付録基板でOgg Vorbisプレイヤー(2)
Posted by arms22 on 2007年05月13日

前回、TremorをMacOSX上でコンパイル+実行しメモリの使用量を調べるところまでやりました。結果は100KBを超えるメモリを使用し、さらにメモリリークまで起こしていました。
メモリリークしているライブラリは使えませんので、さっそくリークしている箇所を探してみました。Tremorのコードは大きくないのでリークしている箇所を特定するのは難しくはありませんでした。
ogg_stream_destroyと_ogg_buffer_destroyでの解放もれがありました。
これらはOggファイルをクローズする時に実行される処理なので、ファイルを開いて閉じてを繰り返して行くとどんどんリークしてゆきます。
ogg_stream_destroyと_ogg_buffer_destroyを修正して再度メモリの使用量を計測してみました。あいかわらず100KBを超えていましたけど、、メモリリークはなくなったみたいです。
修正後のソースの差分は「続きを読む」。