GR-MANGOでOpenJTalkを動かしてみた

# GR-MANGOでOpenJTalkを動かしてみた

オープンソースの音声合成ソフトの[OpenJTalk](http://open-jtalk.sp.nitech.ac.jp/)を、[mbed](https://os.mbed.com/)が使えて16MByteの外部RAMが付いている[GR-MANGO](https://www.renesas.com/jp/ja/products/gadget-renesas/boards/gr-mango)で動かしてみました。

ビルドは直ぐに通ったのですが、動くまでにはいろいろ調整が必要だったので、その内容をメモしておきます。

コードは、[Github](https://github.com/h7ga40/HTS_Engine_GR-MANGO)にあります。

下記の説明で、ソースコードの場所としてGithubへのリンクを付けてありますが、随筆時点のURLで、これが読まれているころには変更されているかもしれません。

## OctaRAM 16MByte使えない問題

**2021/7/13追記** 下記コメントにある通り、**[テクニカルアップデート](https://www.renesas.com/jp/ja/document/tcu/note-size-octaram-memory-octa-memory-controller-rza2m-group-products?language=ja&r=1054511)**で報告されているように、RZ/A2MはOctaRAMは**8MByteまでしか使えない**ようです。確認が甘かったことをお詫びします。
@yagisawa さん、連絡本当にありがとうございます。

↓↓↓ここから次の線まで、無効です。
---

GR-MANGOの製品版では16MByteのデバイスが載っていますが、プロトタイプが8MByteだったためか、mbed環境では8MByteまでしか使えない状態です。

OctaRAMの容量は、「デバイスイズレジスタ_1（`DSR1`）」の「デバイス1容量設定（`DV1SZ`）」に設定するようで、[RZ/A2Mユーザーズマニュアルハードウェア編](https://www.renesas.com/jp/ja/document/man/rza2m-group-users-manual-hardware?language=ja)の「22. Octa メモリコントローラ」に書いてあります。

「デバイス1容量設定（`DV1SZ`）」を設定しているコードを探すと、[octaram_init.c](https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_RENESAS/TARGET_RZ_A2XX/TARGET_GR_MANGO/device/octaram_init.c)(244)にありました。

```c
OCTA.DSR1.BIT.DV1SZ = OCTARAM_SIZE; // RAM size
```

`OCTARAM_SIZE`の定義を変更するため[mbed_drv_cfg.h](https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_RENESAS/TARGET_RZ_A2XX/TARGET_GR_MANGO/mbed_drv_cfg.h)(51)にある定義を

```c
#define OCTARAM_SIZE (0x00800000UL) /**&lt; Available Memory */
```

から

```c
#define OCTARAM_SIZE (0x01000000UL) /**&lt; Available Memory */
```

に変更しましたが、効果がありません。
mapファイルを見ると、この処理はどこからも呼び出されず、実行ファイルからは省かれています。

しかし、`main`関数に来るときには値が設定されているので、どこで設定しているのか探したところ、ブートローダーで設定しているようです。

[mbed_sf_boot.c](https://github.com/ARMmbed/mbed-os/blob/master/targets/TARGET_RENESAS/TARGET_RZ_A2XX/TARGET_GR_MANGO/device/mbed_sf_boot.c)(131)にある下記の箇所

```c
    0x02, 0x15, 0xa0, 0xe3, 0x28, 0xe0, 0xa0, 0xe3, 0x39, 0x33, 0xd2, 0xe5, 0x1f, 0x31, 0xc2, 0xe7,
```

を

```c
    0x01, 0x14, 0xa0, 0xe3, 0x28, 0xe0, 0xa0, 0xe3, 0x39, 0x33, 0xd2, 0xe5, 0x1f, 0x31, 0xc2, 0xe7,
```

に変えると**~~16MByte使えるようになりました！~~　残念ながら8MByteまでしか使えません。**

で？何を変えたのかは、ディスアセンブルして説明します。

ディスアセンブルしたブートローダーの該当箇所周辺は下記の通り。

```arm
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. 「デバイスイズレジスタ_1（`DSR1`）」のアドレスは「`1F401010`」で、アドレス「`500005f4`」にベースアドレスが格納されています。
   これをアドレス「`50000444`」で`r3`レジスタに格納します。

2. アドレス「`50000454`」で「デバイスイズレジスタ_1（`DSR1`）」の値を`r2`レジスタに格納します。

3. アドレス「`50000458`」で、`r2`レジスタの「デバイス1容量設定（`DV1SZ`）」部分のビットを`r1`レジスタで置き換えます。

4. アドレス「`5000045c`」で、`r2`レジスタを「デバイスイズレジスタ_1（`DSR1`）」に書いています。

これで、下のコードと同等な処理が、ブートローダーで行われるように変更できました。

```c
OCTA.DSR1.BIT.DV1SZ = 0x1000000;
```

OpenJTalkを動かすには、16MByteはないと難しいので、**~~これで何とかなりそうです。~~ 8MByteでもキャッシュで、だましだまし動いていたようです。**

↑↑↑ここまで、無効です。
---

## SDカードアクセスのOctaRAM-DMA問題

`read`関数で、ファイルをOctaRAMの領域に読み込もうとすると、正常に読み込めないようです。

調べてみると、SDカードのアクセスにはDMAを使用していて、`read`関数で指定したバッファがOctaRAMだと、キャッシュの関係なのかファイル内容が正しくバッファに書かれないようです。

そこで、SDカードアクセスにDMAを使わないように変更できないか、調べたところ、[r_sd_cfg.h](https://github.com/renesas-rz/mbed-gr-libs/blob/master/SDBlockDevice_GRBoard/TARGET_RZ_A2XX/sdhi-driver/r_sdhi_simplified/inc/r_sd_cfg.h)に該当するものがありました。

```c
/* ------------------------------------------------------
  Set the method of data transfer
--------------------------------------------------------*/
#define SD_CFG_TRNS_DMA
/* #define SD_CFG_TRNS_SW */
```

を

```c
/* ------------------------------------------------------
  Set the method of data transfer
--------------------------------------------------------*/
/* #define SD_CFG_TRNS_DMA */
#define SD_CFG_TRNS_SW
```

に変更して試したのですが、なぜか改善されませんでした。

仕方がないので、一度内蔵RAMに読み込んでからOctaRAMに`memcpy`して使うようにしました。

解決した人が居ましたら、コメントください。

## Mecabでmmapが使えない問題

OpenJTalkでは、漢字混じりの入力文字列を発音用文字列に変えるために、[Mecab](https://taku910.github.io/mecab/)が使われています。

そのMecabは、辞書ファイルをmmapでメモリとして見えるようにして高速化を図っているのですが、mbed環境ではmmapは使用できません。（たぶん…）

Mecabの`config.h`にある`HAVE_MMAP`が定義されない場合、ファイル全体を読み込むコードが使われるようになっているのですが、辞書ファイルの一つ`sys.dic`は100MByte近くあるので、16MByteのGR-MANGOではファイル全体を読み出すことは出来ません。

仕方がないので、このファイルの値が欲しい時には、メモリアドレスではなくファイルの位置を指定して随時読出すように変更しました。

これで、Mecabの誇る高速性は失われ、動作が遅くなったと思いますが、GR-MANGOで動いたので良しとしています。

## あとがき

GR-MANGOで音声合成が動いたので、Webから拾ってきた天気予報を読ませるなどにも使用できないかなと思っています。

現状だと発声までの処理時間が数秒あるので、高速化を図りたいところです。