GR-MANGOでOpenJTalkを動かしてみた
オープンソースの音声合成ソフトのOpenJTalkを、mbedが使えて16MByteの外部RAMが付いているGR-MANGOで動かしてみました。
ビルドは直ぐに通ったのですが、動くまでにはいろいろ調整が必要だったので、その内容をメモしておきます。
コードは、Githubにあります。
下記の説明で、ソースコードの場所としてGithubへのリンクを付けてありますが、随筆時点のURLで、これが読まれているころには変更されているかもしれません。
OctaRAM 16MByte使えない問題
2021/7/13追記 下記コメントにある通り、テクニカルアップデートで報告されているように、RZ/A2MはOctaRAMは8MByteまでしか使えないようです。確認が甘かったことをお詫びします。
@yagisawa さん、連絡本当にありがとうございます。
↓↓↓ここから次の線まで、無効です。
GR-MANGOの製品版では16MByteのデバイスが載っていますが、プロトタイプが8MByteだったためか、mbed環境では8MByteまでしか使えない状態です。
OctaRAMの容量は、「デバイスイズレジスタ_1(DSR1
)」の「デバイス1容量設定(DV1SZ
)」に設定するようで、RZ/A2Mユーザーズマニュアルハードウェア編の「22. Octa メモリコントローラ」に書いてあります。
「デバイス1容量設定(DV1SZ
)」を設定しているコードを探すと、octaram_init.c(244)にありました。
OCTA.DSR1.BIT.DV1SZ = OCTARAM_SIZE; // RAM size
OCTARAM_SIZE
の定義を変更するためmbed_drv_cfg.h(51)にある定義を
# define OCTARAM_SIZE (0x00800000UL) /**< Available Memory */
から
# define OCTARAM_SIZE (0x01000000UL) /**< Available Memory */
に変更しましたが、効果がありません。
mapファイルを見ると、この処理はどこからも呼び出されず、実行ファイルからは省かれています。
しかし、main
関数に来るときには値が設定されているので、どこで設定しているのか探したところ、ブートローダーで設定しているようです。
mbed_sf_boot.c(131)にある下記の箇所
0x02, 0x15, 0xa0, 0xe3, 0x28, 0xe0, 0xa0, 0xe3, 0x39, 0x33, 0xd2, 0xe5, 0x1f, 0x31, 0xc2, 0xe7,
を
0x01, 0x14, 0xa0, 0xe3, 0x28, 0xe0, 0xa0, 0xe3, 0x39, 0x33, 0xd2, 0xe5, 0x1f, 0x31, 0xc2, 0xe7,
に変えると16MByte使えるようになりました! 残念ながら8MByteまでしか使えません。
で?何を変えたのかは、ディスアセンブルして説明します。
ディスアセンブルしたブートローダーの該当箇所周辺は下記の通り。
50000414: e92d4007 push {r0, r1, r2, lr}
50000418: e3a00001 mov r0, #1
5000041c: e59f21cc ldr r2, [pc, #460] ; 500005f0 <boot_loader+0x5f0>
50000420: e3a01401 mov r1, #16777216 ; 0x1000000
50000424: e3a0e028 mov lr, #40 ; 0x28
50000428: e5d23339 ldrb r3, [r2, #825] ; 0x339
5000042c: e7c2311f bfc r3, #2, #1
50000430: e5c23339 strb r3, [r2, #825] ; 0x339
50000434: e5d23339 ldrb r3, [r2, #825] ; 0x339
50000438: e6ef3073 uxtb r3, r3
5000043c: e5cd3007 strb r3, [sp, #7]
50000440: e5dd3007 ldrb r3, [sp, #7]
50000444: e59f31a8 ldr r3, [pc, #424] ; 500005f4 <boot_loader+0x5f4>
50000448: e5932010 ldr r2, [r3, #16]
5000044c: e7df2f10 bfi r2, r0, #30, #2
50000450: e5832010 str r2, [r3, #16]
50000454: e5932010 ldr r2, [r3, #16]
50000458: e7dd2011 bfi r2, r1, #0, #30
5000045c: e5832010 str r2, [r3, #16]
:
500005f4: 1f401000 svcne 0x00401000
※「アドレス」: 「ARM命令」 「アセンブラ」の順に並んでいます。
変更した箇所は、アドレス「50000420
」で、r1
レジスタに入れる値を8MByteの「0x00800000
」から16MByteの「0x01000000
」に変更しています。
これは、ARM命令で「e3a01502
」から「e3a01401
」に変更したということで、ブートローダーのバイト配列部分の数値も同じように変えています。
ただし、リトルエンディアンなので、ARM命令とバイト配列では4Byteづつ順番が逆になっています。
以降の処理の流れは以下のようになっています。
-
「デバイスイズレジスタ_1(
DSR1
)」のアドレスは「1F401010
」で、アドレス「500005f4
」にベースアドレスが格納されています。
これをアドレス「50000444
」でr3
レジスタに格納します。 -
アドレス「
50000454
」で「デバイスイズレジスタ_1(DSR1
)」の値をr2
レジスタに格納します。 -
アドレス「
50000458
」で、r2
レジスタの「デバイス1容量設定(DV1SZ
)」部分のビットをr1
レジスタで置き換えます。 -
アドレス「
5000045c
」で、r2
レジスタを「デバイスイズレジスタ_1(DSR1
)」に書いています。
これで、下のコードと同等な処理が、ブートローダーで行われるように変更できました。
OCTA.DSR1.BIT.DV1SZ = 0x1000000;
OpenJTalkを動かすには、16MByteはないと難しいので、これで何とかなりそうです。 8MByteでもキャッシュで、だましだまし動いていたようです。
↑↑↑ここまで、無効です。
SDカードアクセスのOctaRAM-DMA問題
read
関数で、ファイルをOctaRAMの領域に読み込もうとすると、正常に読み込めないようです。
調べてみると、SDカードのアクセスにはDMAを使用していて、read
関数で指定したバッファがOctaRAMだと、キャッシュの関係なのかファイル内容が正しくバッファに書かれないようです。
そこで、SDカードアクセスにDMAを使わないように変更できないか、調べたところ、r_sd_cfg.hに該当するものがありました。
/* ------------------------------------------------------
Set the method of data transfer
--------------------------------------------------------*/
# define SD_CFG_TRNS_DMA
/* #define SD_CFG_TRNS_SW */
を
/* ------------------------------------------------------
Set the method of data transfer
--------------------------------------------------------*/
/* #define SD_CFG_TRNS_DMA */
# define SD_CFG_TRNS_SW
に変更して試したのですが、なぜか改善されませんでした。
仕方がないので、一度内蔵RAMに読み込んでからOctaRAMにmemcpy
して使うようにしました。
解決した人が居ましたら、コメントください。
Mecabでmmapが使えない問題
OpenJTalkでは、漢字混じりの入力文字列を発音用文字列に変えるために、Mecabが使われています。
そのMecabは、辞書ファイルをmmapでメモリとして見えるようにして高速化を図っているのですが、mbed環境ではmmapは使用できません。(たぶん…)
Mecabのconfig.h
にあるHAVE_MMAP
が定義されない場合、ファイル全体を読み込むコードが使われるようになっているのですが、辞書ファイルの一つsys.dic
は100MByte近くあるので、16MByteのGR-MANGOではファイル全体を読み出すことは出来ません。
仕方がないので、このファイルの値が欲しい時には、メモリアドレスではなくファイルの位置を指定して随時読出すように変更しました。
これで、Mecabの誇る高速性は失われ、動作が遅くなったと思いますが、GR-MANGOで動いたので良しとしています。
あとがき
GR-MANGOで音声合成が動いたので、Webから拾ってきた天気予報を読ませるなどにも使用できないかなと思っています。
現状だと発声までの処理時間が数秒あるので、高速化を図りたいところです。