「RX」カテゴリーアーカイブ

RXマイコン、選択型割り込みを使う

最近、ようやくUSB関係を始めた。

フレームワークは、ルネサス純正の物を試用して実験している。
USBのフレームワークでは、イベント関係で割り込みを使っている。

RX65Nでは、選択型割り込みを使っている。

自分のフレームワークと、ルネサス社のフレームワークを合わせるのは、工夫が必要で、そのままではリンクが難しい。

少し調べると、割り込み関係は、「basic/src/hw/r_usb_rx_mcu.c」で設定されており、これを改修すれば良さそうだ。

それで、色々かち合う部分に手を入れ、USBマスストレージ関係をコンパイル、リンクする事が出来るようになった。
早速動作検証するのだが、動かない・・・
どうやら、割り込みがかからないようだった・・・

よくよく調べると、選択型割り込みクラスにバグがあり、それが原因だった。
※以前に実装していたが、選択型割り込みを扱うデバイスが無かったので、動作検証されていなかった・・

RXマイコンでは割り込み要因は256個まで設定できるが、非常に沢山あるペリフェラルで発生する割り込みをアサインするのは無理があり、物理的に足りない。

そこで、RXマイコンには、割り込みをシェアしたり(グループ割り込み)、使いたい割り込みをアサインしてプログラマブルに使う(選択型割り込み)などの仕組みが用意されている。

ただ、同じようなペリフェラルでも、デバイスによって異なるグループだったり、要因が異なったり、複雑で、ドライバーを作る場合に個々に対応しなければならない。

自分の C++ フレームワークでは、この問題を隠蔽して、簡単に扱えるように、色々な仕組みを実装してある。
※この仕組みこそが C++ を使う最大のメリットの一つと思う。
※特殊なツールを使ってコード生成する必要が無い。

例えば、CMT(コンペアマッチタイマー)を使いたい場合、「cmt_io」クラスを使う。

#include "common/cmt_io.hpp"

typedef device::cmt_io<device::CMT0> CMT;
CMT     cmt_;


    {
        uint8_t intr = 3;  // 割り込みレベル
        uint32_t freq = 1000;  // 1000Hz の周期 
        cmt_.start(freq, intr);
    }


    while(1) {

        cmt_.sync();  // 同期

    }

上記の例では、リソースとして「CMT0」を使い、1000Hz の周期を持ったタイマーを割り込みレベル「3」で起動している。

CMTには、チャネルが4つあり、CMT0~CMT3まで使える。

RX65N の場合 CMT0、CMT1 は通常の割り込みだが、CMT2、CMT3 は選択型割り込みを使う必要があるが、それらは隠蔽されていて、特別な設定を行う必要が無い。

#if defined(SIG_RX24T) || defined(SIG_RX66T) || defined(SIG_RX72T)
    typedef cmt_t<0x00088002, peripheral::CMT0, ICU::VECTOR, ICU::VECTOR::CMI0> CMT0;
    typedef cmt_t<0x00088008, peripheral::CMT1, ICU::VECTOR, ICU::VECTOR::CMI1> CMT1;
    typedef cmt_t<0x00088012, peripheral::CMT2, ICU::VECTOR, ICU::VECTOR::CMI2> CMT2;
    typedef cmt_t<0x00088018, peripheral::CMT3, ICU::VECTOR, ICU::VECTOR::CMI3> CMT3;
#elif defined(SIG_RX64M) || defined(SIG_RX71M) || defined(SIG_RX65N) || defined(SIG_RX72M)
    typedef cmt_t<0x00088002, peripheral::CMT0, ICU::VECTOR, ICU::VECTOR::CMI0> CMT0;
    typedef cmt_t<0x00088008, peripheral::CMT1, ICU::VECTOR, ICU::VECTOR::CMI1> CMT1;
    typedef cmt_t<0x00088012, peripheral::CMT2, ICU::VECTOR_SELB, ICU::VECTOR_SELB::CMI2> CMT2;
    typedef cmt_t<0x00088018, peripheral::CMT3, ICU::VECTOR_SELB, ICU::VECTOR_SELB::CMI3> CMT3;
#endif

CMT0~CMT3の定義は、上記のようになっており、割り込み要因をデバイス毎に定義してある。

auto vec = CMT::get_ivec();
if(level_ > 0) {
    if(task != nullptr) {
        icu_mgr::set_interrupt(vec, task, level_);
    } else {
        icu_mgr::set_interrupt(vec, i_task_, level_);
    }
    CMT::CMCR = CMT::CMCR.CKS.b(cks) | CMT::CMCR.CMIE.b();
} else {
    icu_mgr::set_interrupt(vec, nullptr, 0);
    CMT::CMCR = CMT::CMCR.CKS.b(cks);
}

上記は「cmt_io」の割り込み関係を設定する部分で、「auto」を使って、割り込み型「ICU::VECTOR」、「ICU::VECTOR_SELB」を自動で再定義している。

「set_interrupt」APIは、割り込み型の違いを定義してあり、それぞれ、割り込みの管理方法が異なる。

static ICU::VECTOR set_interrupt(ICU::VECTOR vec, utils::TASK task, uint8_t lvl) noexcept {
    set_task(vec, task);
    set_level(vec, lvl);
    return vec;
}

static ICU::VECTOR set_interrupt(ICU::VECTOR_SELB vec, utils::TASK task, uint8_t lvl) noexcept
{
    for(uint16_t i = 144; i <= 207; ++i) {
        if(lvl > 0) {
            if(ICU::SLIXR[i] == 0) {
                ICU::IER.enable(i, 0);
                set_task(static_cast<ICU::VECTOR>(i), task);
                ICU::IPR[i] = lvl;
                ICU::SLIXR[i] = static_cast<uint8_t>(vec);
                ICU::IR[i] = 0;
                ICU::IER.enable(i, 1);
                return static_cast<ICU::VECTOR>(i);
            }
        } else if(ICU::SLIXR[i] == static_cast<uint8_t>(vec)) {
            ICU::IER.enable(i, 0);
            set_task(static_cast<ICU::VECTOR>(i), nullptr);
            ICU::SLIXR[i] = 0;
            ICU::IR[i] = 0;
            return static_cast<ICU::VECTOR>(i);
        }
    }
    return ICU::VECTOR::NONE;
}

※選択型割り込みでは、SLIXR レジスタに、割り込み要因番号を設定する事で行われ、割り込み番号144~207まで定義できる。
※選択型割り込みのバグは、「ICU::SLIXR」レジスタアドレスがタイポしていたものだった・・・

RXマイコン用、libpng の構築

アルファ値を含んだ画像を扱う必要があるので、libpng をインポートした。
PNG では、インデックスカラーの場合でも、カラーパレットにアルファ値を含める事が出来る。
ただ、zlib が必要なのと、記憶割り当てを使う事から、BMP やJPEG を使っていたが、インデックスカラーでアルファ付画像を扱う必要性があり、やはりインポートをする事になってしまった・・
書き込みを使う事は「稀」と考えて、デコーダーのみではあるが・・・
Windows のフレームワーク「glfw_app」では以前からサポートしてあるので、そのコードを再利用したので簡単ではあったけど、意外とライブラリビルド時の「configure」の使い方で google 先生の助けを借りたw

libpng をビルドするには、zlib が必要なので、事前に zlib をビルドしておいた。
※zlib のビルドは普通に出来ると思う。

libpng のビルドでは、ツール関係(コマンドライン実行ファイル)のビルドで失敗するものの(RX マイコンでは、動かす環境のハードルが高い)、ライブラリの構築には成功しているので、それで「ヨシ」とした。
※github には「libpng.a」とヘッダーなど必要な物を上げてあるので、それを利用するぶんには、自分でビルドをする必要は無い。

 ./configure --includedir="/d/Git/RX/zlib" --host=rx-elf --disable-shared

Makefile 編集

make
cp .libs/libpng16.a /d/Git/RX/libpng/libpng.a
cp png.h /d/Git/RX/libpng/.
cp pnglibconf.h /d/Git/RX/libpng/.
cp pngconf.h /d/Git/RX/libpng/.

上記のように、「ホスト」を指定し、zlib のパスを追加してある。
しかしこれだけでは不十分で、make すると、zlib.h が無いとか言ってエラーになる・・・
正しいやり方が判らなかったので、手っ取り早く、configure で生成された Makefile を直接編集した。
・DEFAULT_INCLUDES = -I. -I/d/Git/RX/zlib
※zlib のパスを追加
・CFLAGS = -mcpu=rx600 -O2 -I/d/Git/RX/zlib
※最適化「-O2」を追加
・シェアードライブラリは必要無いので、省いてある。
・ビルドすると、「.libs」ディレクトリにライブラリが出来ている。
・必要なヘッダーをコピーする。

png ファイルのロードは、以前に実装したので、それをほぼそのまま使っている。(glfw3_app/common/img_io/png_io.hpp)

※組み込みでは、png ファイルを出力する事は「稀」と思うので、ロードのみサポートしている。

PNG 画像のアルファ値は、元の画素と合成されて描画する。
※アルファ値が「0」の場合、そのピクセルは描画されない。

#include "graphics/img_in.hpp"

namespace {

     typedef img::scaling<RENDER> PLOT;
     PLOT        plot_(render_);
     typedef img::img_in<PLOT> IMG_IN;
     IMG_IN      imgs_(plot_);
}


     imgs_.load(filename);・

・img_in クラスは、BMP、JPEG、PNG を自動で判別してロードするクラス。
・scaling クラスはワークメモリを最小限にして、スケールしながら描画するもので、それなりにエイリアシングも除去してくれる。

# cd res
# image ff.png
libpng warning: iCCP: known incorrect sRGB profile
# dir
      6727 Jul  9 2016 08:21  ff.png
      3407 Jul  9 2016 08:21  forte.png
     23148 Jul  9 2016 08:21  NoImage.png
      6371 Jul  9 2016 08:21  pause.png
      3419 Jul  9 2016 08:21  piano.png
      6856 Jul  9 2016 08:21  play.png
    373882 Jul  9 2016 08:21  Player.icns
    117306 Jul  9 2016 08:21  player.ico
     17632 Jul  9 2016 08:21  PlayerICON.png
      6361 Jul  9 2016 08:21  plus.png
      6748 Jul  9 2016 08:21  rew.png
      6607 Jul  9 2016 08:21  right.png
      3934 Jul  9 2016 08:21  seek_handle.png
      6364 Jul  9 2016 08:21  seg12.ttf
      4680 May  6 2019 23:08  select.bmp
      6780 Jul  9 2016 08:21  select.png
      4028 Jul  9 2016 08:21  slider_handle.png
      6343 Jul  9 2016 08:21  stop.png
      6605 Jul  9 2016 08:21  up.png
Total 19 files
# image NoImage.png
libpng warning: iCCP: known incorrect sRGB profile
# image ff.png
libpng warning: iCCP: known incorrect sRGB profile
# image PlayerICON.png
libpng warning: iCCP: known incorrect sRGB profile
#

実験は、RX65N Envision Kit で行った。

RXマイコンSDHIインターフェースその2(完了)

相変わらず、SDHCなどの高容量タイプで、ACMD41が失敗する問題に悩んで1週間くらい?

ロジックアナライザを繋いで制御ピンの状態を確認するとか、ありとあらゆる方策を試していたが・・・

全く成果無し状態でいた。

電源の状態が悪いのかとかも確認したが、電源のリップルはそれ程多くは無く、許容範囲だった。
RTK5 RX65N Envision Kit では、SD カード電源制御と、電源電圧の確認用に専用のICを使っているが、それは実装されていない為、とりあえず、P チャネルの MOSFET を取り付けて、電源制御を加えてみたりもしたが、全く効果は無い。
※秋月電子で入手出来る「DMG3415U」を使った。

LA2016 の SDIO 解析画面

LA2016 には、色々な解析モードが用意されており、それを使う事で、CMDピンで、ホストとスレーブ間でやりとりするデータ列を具体的に確認する事が出来る。
凄く便利で、素晴らしい機能だーー
※今まで、こんなに便利な機能なのに、あまり積極的に使っていなかった、他にも色々と解析が出来るので、これからは重宝すると思う。

これで、確認した限りでは、問題無さそうで、2GのSDカードと8GのSDカードの違いは無さそうだった。
※ただ、SDHCカードでは、BUSYのまま・・・

SD カード関係の正規資料なども、色々読んで、何か間違いが無いかを確認していた。

そんな時、アルテラ社のFPGA向けライブラリでSDIOの説明を見つけ、ACMD41関係の部分を読んでいたら、
> SD_SEND_OP_COND(ACMD41)コマンドを送信します。
> ビット [23:0] = サポートされている電圧範囲

ん?

サポートされている電圧範囲?

現状では、0x000000 を送っている・・・

SPI だと、0x000000 を送っていて、SDHCカードを使えている。

それで、ルネサスの r_sdhi ソースを確認してみると、2.7V から 3.6V の場合、0xFF8000 を設定する事が判った・・・

で、修正してみると、今度は成功する・・・
今までの苦労は何だったのか・・・

まぁ、良くある話ではあるのだが、思い込みで、正規の資料を読んでも、重要な事をスルーしてしまう・・・

まぁ動いたから「ヨシ」とする・・

SDモードでの初期化の手順をまとめると・・・

・SDモードによる初期化手順
(1)CMD0、0x00000000
※複数打った方が良いかもしれない(SDカードは応答を返さない)
(2)CMD8、0x000001AA
※0x100 は電圧範囲(2.7V ~ 3.6V)
※0x0AA は、マッチパターン
(3)CMD8 のステータスで、0x1AA が返れば、SDV2 カード
それ以外の場合、エラー
※CMD8のレスポンスが無い場合、そのカードはCMD8をサポートしておらず、別の初期化シーケンスに切り替える。
これは多分、容量が少ない昔のカードの場合など(自分のドライバーでは、現状、サポートしていない)
(4)ACMD41、0x40FF8000
レスポンスのB31が「1」になるまで投げ続ける。
※1回投げて、1ms 待つ、1000回繰り返しても「1」に成らなければエラーとする。
※レスポンスで、B30が「1」なら、そのカードは、ブロックアクセスを行う。
これは、32 ビットだと 0 ~ 4G までしかアクセス出来ないので、それに対応する方法、このビットが有効なら、read/write はブロックアクセスとなる。
(5)CMD2、0 で CID を取得
(6)CMD3、0 で RCA を取得(B31~B16)
(7)CMD7、RCA でカード選択
(8)CMD16,512 でセクターサイズ設定
(9)ACMD6、0x00000002 で、バス幅を4ビットにする。
※1ビットの場合、0x00000000
(10)SDHI のバス幅を切り替えて、クロック速度をブーストする。

まだ、エラー検査とかがズブズブで、割り込みやDMAに対応していないが、とりあえず、動くようになった・・・
速度はかなり高速で、以下のような感じ~
※GitHub のマスターにマージ済み

QIDIAN MLC 32GB (SDHC) Class10
# write test.bin
disk_ioctl: 00
Open:  0 [ms]
Write: 440393 Bytes/Sec
Write: 430 KBytes/Sec
Close: 5 [ms]
# read test.bin
SD Read test...
Open:  0 [ms]
Read: 1048576 Bytes/Sec
Read: 1024 KBytes/Sec
Close: 0 [ms]

Lexar 633x 8GB (SDHC) Class10
# write test.bin
disk_ioctl: 00
Open:  170 [ms]
Write: 215048 Bytes/Sec
Write: 210 KBytes/Sec
Close: 12 [ms]
# read test.bin
SD Read test...
Open:  2 [ms]
Read: 1302578 Bytes/Sec
Read: 1272 KBytes/Sec
Close: 0 [ms]

SanDisk Industrial 8GB (SDHC) Class10
# write test.bin
disk_ioctl: 00
Open:  3 [ms]
Write: 338359 Bytes/Sec
Write: 330 KBytes/Sec
Close: 98 [ms]
# read test.bin
SD Read test...
Open:  1 [ms]
Read: 1747626 Bytes/Sec
Read: 1706 KBytes/Sec
Close: 0 [ms]

SanDisk Industrial 16GB (SDHC) Class10
# write test.bin
disk_ioctl: 00
Open:  6 [ms]
Write: 397941 Bytes/Sec
Write: 388 KBytes/Sec
Close: 5 [ms]
# read test.bin
SD Read test...
Open:  2 [ms]
Read: 1227840 Bytes/Sec
Read: 1199 KBytes/Sec
Close: 0 [ms]

TOSHIBA 40MB/s Taiwan 32GB (SDHC) Class10
# write test.bin
disk_ioctl: 00
Open:  1 [ms]
Write: 204920 Bytes/Sec
Write: 200 KBytes/Sec
Close: 46 [ms]
# read test.bin
SD Read test...
Open:  1 [ms]
Read: 1091130 Bytes/Sec
Read: 1065 KBytes/Sec
Close: 0 [ms]

SanDisk Industrial 16GB (SDHC) Class10 for Soft-SPI
# write test3.bin
Open:  0 [ms]
Write: 181634 Bytes/Sec
Write: 177 KBytes/Sec
Close: 17 [ms]
# read test3.bin
SD Read test...
Open:  2 [ms]
Read: 232758 Bytes/Sec
Read: 227 KBytes/Sec
Close: 0 [ms]

以下のようにして、SDHIインターフェースを使う場合とSPI(ソフトSPI)を使う場合を切り替えできる。

    // カード電源制御は使わない場合、「device::NULL_PORT」を指定する。
//  typedef device::NULL_PORT SDC_POWER;
    typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;

#ifdef SDHI_IF
    // RX65N Envision Kit の SDHI ポートは、候補3になっている
    typedef fatfs::sdhi_io<device::SDHI, SDC_POWER, device::port_map::option::THIRD> SDHI;
    SDHI    sdh_;
#else
    // Soft SDC 用 SPI 定義(SPI)
    typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
    typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
    typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

    typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  ///< Soft SPI 定義

    SPI     spi_;

    typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
    typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

    typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

    MMC     sdh_(spi_, 35000000);
#endif

RXマイコンSDHIインターフェースその1

RX65N Envision Kit の、SD カードインターフェースは、SDHI インターフェースを想定した設計になっている。

しかし、RX マイコンの SDHI インターフェースでは、ハードウェアーマニュアルの情報だけでは、不明な事が多く、ソフトウェアを実装出来ない状態だった。
※SDHI のハードウェアー操作は、SD カードの制御シーケンスと密接に関連している為、これは仕方無いかもしれない・・
※何回かトライしたが初期化の段階で、思ったように動かないので、断念していた・・
※とりあえず、実績のある ChaN 氏のソフトウェアー SPI で動かしていた。

最近、ルネサスは、SD カードの操作が含まれるマネージャー関連(SD カードの初期化などが含まれる r_sdc_sdmem_rx)を公開するようになったので、具体的にどのように SDHI にアクセスするのか不明な部分が明らかになってきた。
※以前、RX64M、RX71M は、SDHI インターフェースがオプションとなっており、自分の持っているデバイスは、「SDHI なし」なので、試せないでいた・・

また、SD カードを SPI でアクセスする方法は、かなり情報があるのだが、4ビット(SD モード)でアクセスする方法は、情報が少なく、どのような初期化をするのか、イマイチ判らなかった・・・
最近ネットで、kingston SD カードの詳細な 解説を見つけ、この情報をたよりにする事で、SD モードでの推移方法がかなり詳しく判った。

それらの情報を元に、初期化プロセスを実装してみたが、正常に動作しない・・・
(1) 1GB、2GB の HC ではない SD カードだと、初期化に成功する事を発見したが、8GB、16GB、32GB(SDHC)のカードでは、初期化に失敗する。
※ACMD41コマンドで失敗しているが原因が判らない。
ACMD41 コマンドは、ステートに、BUSYがREADYに変わるまで呼び続ける仕様だが、SDHC カードの場合、いつまでたっても READY にならない・・・

とりあえず、手持ちの中で、動作する2枚のカードをテストした。
2GB のカードより、1GB のカードの方が性能が高い為、1GB のカードのみ評価した。
・1ビットバスと4ビットバスの速度比較
・クロック速度による違い

KINGMAX 1GB MicroSD CARD:
Clock: 15MHz
1 bit bus:
Open:  0 [ms]
Read: 825650 Bytes/Sec
Read: 806 KBytes/Sec
Close: 0 [ms]

4 bits bus:
Open:  0 [ms]
Read: 1233618 Bytes/Sec
Read: 1204 KBytes/Sec
Close: 0 [ms]

Clock: 30MHz:
Open:  0 [ms]
Read: 1347784 Bytes/Sec
Read: 1316 KBytes/Sec
Close: 0 [ms]

上記のように、大体1.5倍くらいの違いがある。
駆動クロックを倍にすると、10% 弱速くなるようだが、最終的に扱う場合には、ノイズ耐性、インピーダンスのマッチング(ダンピング抵抗、プルアップ、プルダウン抵抗)など色々考える事が多く、微妙だろうと思う。

流石、4ビットモードは、昔の 1GB の SD カードでも 1.3M バイト毎秒以上の速度が出るので、十分利便性が高い。(早急にSDHCカードが動かない原因を突き止めないと・・・)

最近の高速、高容量の SD カードは、高速動作時に、消費電力を下げる為、より低い電圧(1.8V など)で動作するような仕組みがあるようだ。

しかしながら、インターフェースの I/O 電圧が、低い電圧に対応していないとならない為、たとえ、電源電圧を制御する事が出来ても、対応出来ない。
RX マイコンの SDHI は、1.8V などの I/O 電圧に対応していないので、レベルシフターを間に入れるなどの対応をしないと、電圧を下げて使う事が出来ない。

又、バスのサンプリングポイントを調整するコマンドもあるようだ。

今回はここまで、SDHC における、ACMD41 が失敗する原因を色々探って、色々な実験をしたが、成果は無かった・・・
ハードウェアーで足りない部分があるのかとも思ったが、それも違うようだ・・・
ルネサスのソースコードも読んで、同じようなシーケンスを組んでいる筈だが、動かない・・・

ChaN さんの SPI 仕様では、ACMD41 は正常終了して、SDHC は動くのだが、それと何が違うのか、判らないでいる・・・

RX65N Envision Kit ファミコンエミュレーター再び

RX65N Envision Kit で実現する、ファミコンエミュレーターだけど、内臓メモリの空きエリアなどの問題で、動作させる事が出来るカートリッジファイルに大きな制限があった。

それもあって、実用性が乏しいので、機能を追加する事に消極的だった。
なので、本来持っている機能(ステートのセーブ、ロードなど)実装していなかった。

以前のバージョンでは、最大でも1MビットROM1個分(128Kバイト)までしか動作出来なかった。
※1Mビット1個でもメモリ不足になるカートリッジファイルもあった・・
最近、nes ファイルのロードと、メモリアロケーション関係を整理(nes_rom.c)、修正したら、実は2Mビット(256K)まで動作可能な事が判った。
※何で、こんな「無駄」な事をしていたのか理解に苦しむが、元のソースコードは、ESP32用だったので、ESP32 用になっていた物を Windows で動かす実験をした際に、適当な実装になっていたのがそのまま残っていた・・・

2Mビットまでとなると、過去に自分がプログラムを担当した「暴れん坊天狗」が動く!(RX65NでNESEMUを走らせるゴールのようなもの)
※かれこれ30年前のファミコン向けゲーム(NES版は、「Zombie Nation」)
※1Mビット(PRG)+1Mビット(CHR)、MMC3バンク切り替えという仕様
※生産数が少ない為、今では貴重なカートリッジで、中古価格は意外と高い、確か、まだ実家に未使用品が1個あったハズだが、捨てられているかもしれない・・
※国内版と海外版でタイトルが異なるが、本来、海外版向けとして開発していたが、国内で売る場合に、京都のN社の「倫理規定」が通らず、社長の一存で、国内向けにタイトルを変更したものだった。
今でも覚えているが、社長が、新聞の番組欄で「暴れん坊将軍」を見て、それと、その当時N社が販売していた「花札の天狗」をかけて出来たタイトルで、自機のキャラクターも、落ち武者の顔から天狗になった・・・
まぁ、B級とか、糞ゲーとか揶揄されるが、結構真面目に丁寧に設計され作っている。
感覚と理論的なバックボーン、バランスで出来ている。
※サウンドドライバーや、スコアのオーサリングツールも自分で実装していて、ドット絵ツール以外は全て一人でコーディングした。
このゲームは、音楽も高く評価されている。
源平のN氏が最後に作曲をしたファミコンの作品だと思う。
※楽曲は、N氏とO氏の共同作業だった。
※ドラムやスネアなどで使っている、デルタモジュレータのビットストリームは、当時交流があったU氏が、FMタウンズでサンプリングして作成したデータが元になっていて、音楽や企画を担当したO氏が、それのスタートアドレスを微妙にずらしてバリエーションを作り色々工夫して使っている。
「NOAのテスターにNESでは最高の音楽」と言ってもらった。
※国内版(海外版は修正されている)には、バグがあり、割り込み内で、プログラムバンクを切り替えていて、元に戻し忘れているので、微妙な確率で、暴走して、奇妙な動作をする事がある。
このバグは処理が重い状態じゃないと発生しないので、デバッグでは見つける事が出来なかった・・・
驚く事に、ネットで流通している nes ファイルの中には、このバグにパッチを当てて修正しているものもある。
※エミュレーターでは未定義動作で停止してしまうのだろう~

RX65N のパフォーマンスは素晴らしい、ファミコンのハードウェアを完全にエミュレーションできる。
音も完全に近い状態で再生される。
そして、ステートのセーブとロード機能も実装しておいたので、バッテリーバックアップ機能が無いカートリッジでも、途中の状態をまるまるセーブして保存出来る。
※ファミコンの電源を切らない状態の再現

カートリッジファイルは拡張子が「nes」になっており、PC などのエミュレーターなどで一般的な形式を使っている。
※このファイル形式は、カートリッジのハードウェアー設定なども含まれていて、自動で、キャラジェネのH、Vリンク、マッパーのエミュレーションを切り替える。

ESP32 版と大きく異なるのは、2Mビットまでだが、カートリッジファイルを選んでエミュレーションする事が出来る(この違いは大きい)点
オーディオの再生品質がほぼ完全な事。
※ESP32 では、nes ファイルを ROM データとして、プログラムと同梱しなければならず、又、オーディオの品質もかなり低いと思われる。
エミュレータの品質が高いのは、nesemu コードが優れており、ルネサス RX65N マイコンの高機能によるところが大きいものとなっている。

RX65N Envision Kit の改造方法は、以下のリンクを参照して頂きたい。
NESEMU_sample for GitHub

改造は、比較的ハードルが低く、プログラムをコンパイルする環境も、 gcc で、ソースコードを取得して、手順を進めるだけなので意外と簡単だと思う。
※ルネサス純正コンパイラでは、試していないが、コンパイルが出来ないかもしれない。
※RX65N にプログラムを書き込む場合、ルネサス社のツール(Renesas Flash Programmer)を利用する必要があるが、無料版をダウンロードして使う事が出来る。

ゲームの操作は、タッチパネルでは操作が難しいので、外部にファミコンと同等なゲームパッドを接続する必要がある。
※ファミコンパッドには8ビットのシフトレジスタ(CMOS 4021B)が載っており、シリアルクロック、ロード/シフト、データの3本で情報を取得する。

RTK5RX65N Start for NES Emulator
Start GLCDC
# help
    dir [xxx]       list current directory
    pwd             current directory path
    cd [xxx]        change current directory
    nes filename
    pause
    reset
    save [slot-no]
    load [slot-no]
    info
    call-151
# nes GALAXIAN.NES
ROM header dirty, possible problem
ROM loaded: GALAXIAN.NES [0] 32k/8k Hcreated memory mapper: None
setting up mapper 0
reset memory mapper
# call-151
$fff8.ffff
FFF8- FF FF 0C E2 20 E0 20 E0
$e020l
E020- 78       SEI
E021- D8       CLD
E022- A2 4D    LDX  #$4D
E024- 9A       TXS
E025- A9 10    LDA  #$10
E027- 8D 00 20 STA  $2000
E02A- AD 02 20 LDA  $2002
E02D- 10 FB    BPL  $E02A
E02F- A2 00    LDX  #$00
E031- 8A       TXA
E032- 95 00    STA  $00,X
E034- 9D 00 02 STA  $0200,X
E037- 9D 00 06 STA  $0600,X
E03A- 9D 00 07 STA  $0700,X
E03D- E8       INX
E03E- D0 F2    BNE  $E032
E040- A2 00    LDX  #$00
E042- BD 05 01 LDA  $0105,X
E045- DD 10 E0 CMP  $E010,X
E048- D0 0B    BNE  $E055
$0.ff
0000- 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
0010- 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
0020- 00 00 00 40 00 00 00 24  00 00 01 00 00 14 01 00
0030- 00 00 00 00 00 00 03 20  F2 E1 F3 E9 00 01 01 00
0040- 00 00 00 01 02 00 00 00  00 00 00 00 04 00 00 00
0050- 00 00 00 00 00 00 00 00  00 00 00 21 2B 41 4A 50
0060- 59 5C 07 0F 10 00 0C 00  32 00 00 00 00 00 00 0B
0070- 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
0080- 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
0090- 00 00 00 00 00 00 00 00  00 00 01 00 00 F0 FF 00
00A0- 00 01 00 09 08 07 06 05  04 03 02 01 00 09 08 07
00B0- 06 05 04 03 02 01 00 09  08 07 06 05 04 03 02 01
00C0- 00 00 FB 07 0F 1F 3F 1F  18 3E 1C 07 00 07 0F 1F
00D0- 3F 1F 1F 3F 1F 0F 07 00  2F 00 86 20 33 00 3D 09
00E0- 23 00 77 06 F8 F9 00 00  CF E9 B0 E9 0B 0B 00 00
00F0- 00 00 0B 00 00 00 00 FF  00 00 06 90 00 00 04 02
$0.
0000- 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
$0:1 2 3 4
$0.
0000- 01 02 03 04 00 00 00 00  00 00 00 00 00 00 00 00
$400
0400- FD
$

上記のように、シリアル接続により、モニターぽぃ機能が使える。
※SCI9、8ビット、1ストップビット、115200 BPS

・「call-151」でモニターが起動(「exit」で戻る)
・6502逆アセンブラが使える。
・メモリダンプや、メモリの書き換え等が出来る。
※「call-151」の意味が判る人なら、使い方は判ると思うが、上記機能くらいしか実装していない。

LAN8720A モジュールの試用

FreeRTOS でネットワーク関係を実験したくて、中華製のLAN8720 モジュールを試してみた。

モジュールは、アマゾンで購入した。

本当は、RX65N Envition kit で未実装のPHYチップやトランスなどを実装して、実験するつもりで部品を買っていたが、QFNパッケージのハンダ付けが難しく(1個失敗した)、また、本体を壊してしまうのではと思い、工具も無いので、ストールしていた。

LAN8720A はマイクロチップの 10/100 イーサネット PHY で、安価で入手しやすく、比較的良く使われているデバイスだ。
※GR-KAEDE にも使われている。

QFNパッケージで、裏が全面 GND、DIY ではユニバーサル基板で製作しにくい。
※QFN のユニバーサル基板が割高で入手しにくいのも要因

モジュールは安く送料も無料だが、船便なので、注文してから10日程かかる、基板が届いて早速開封、ネットで回路などを探す。
「LAN8720 ETH Board」

このモジュールは、50MHz の OSC が乗っていて、一般的な25MHz クリスタルとは異なっている。

LAN8720Aは、リセット時のピン状態を読み込んで、基本的な設定を自動で行う「Configuration Straps」と呼ばれる機能がある。

50MHz 外部クロックを使う場合、「LED2/nINTSEL(2)」をオープンかプルアップしておく必要がある。
※ボードはLEDが接続されており「オープン状態なので」50MHz外部クロックモードになっていると思われる。
同時に LED のドライブはアクティブ LOW となる。(吸い込み)

最初に試すソフトは、以前に実験した、ルネサスの T4 ライブラリなので、GR-KAEDE などと同等にしておく必要がある。

RX マイコンの仕様なのかもしれないが、PHY デバイスとの通信で使う「MDC、MDIO」は参考回路ではプルアップしてあるので、同じようにプルアップしておく。
※以前に、プルアップが無い場合にPHY通信が失敗した事があった。
※回路図を見ると、MDIOはプルアップしてあるので、MDCのみ適当な抵抗(3.9K)でプルアップしておいた。

このボードで、問題なのは、RX_ER 端子がプルアップされて、外部コネクタに出ていない。
また、この端子は、「 Configuration Straps 」における、PHYアドレスを設定するピンでもある、チップ抵抗を外して、直接ラインを接続した。
※ボードは、写真のように、ピンヘッダーを外して、直接ボードに取りつけてある。
※このような組み方を良くする、やってみると簡単で確実。

RX_ER 端子を直接接続

今回実験に使ったRXマイコンは、RX71Mで、169ピンタイプ。

イーサーネットは、チャネル0を使った。
各ピンの接続は以下のようになっている。
RX71M: P83/RMMI0_CRS_DV(74) ---> CRS_DV(7)
RX71M: P82/RMMI0_TXD1(79) ---> TXD1(14)
RX71M: P81/RMMI0_TXD0(80) ---> TXD0(11)
RX71M: P80/RMMI0_TXD_EN(81) ---> TXD_EN(12)
RX71M: P77/RMMI0_RX_ER(84) ---> LAN8720 (10) 直接続
RX71M: P76/REF50CK0(85) ---> RX_CLK(8)
RX71M: P75/RMMI0_RXD0(87) ---> RXD0(10)
RX71M: P71/RMMI0_RXD1(88) ---> RXD1(9)
RX71M: P72/ET0_MDC(101) ---> MDC(5)
RX71M: P71/ET0_MDIO(102) ---> MDIO(6)
RX71M: Vcc(3.3V) ---> VCC(1, 2)
RX71M: Vss(GND) ---> GND(3, 4)

接続の様子:

Start RX71M http sample
Link no proccess (0)
Link no proccess (1)
Link no proccess (2)
Link no proccess (3)
Link no proccess (4)
Get DHCP: 192.168.0.4

RXマイコン、FreeRTOS、FatFs でオーディオ再生

前回、マルチスレッドとは言え、シンプルな物で、動作検証を行った。

そこで、今回はもう少し複雑となる、MP3、WAV、のコーデックを動かして、実用的な実験を行った。

詳細は「RXマイコン、FreeRTOS、FatFs、で MP3、WAV の再生」に投稿した。

プログラムは、
・自作 RX64M
・GR-KAEDE
・RX65N Envition kit
などで行った。

FreeRTOS は使える~

元は、シングルタスク用に作ったもので、それを別タスクで動かし、コーデックのデコードをやっている。

タスク間は、ファイル名の受け渡しを行っている。

音楽再生中に、SDカードのディレクトリーを取るなど、平行動作させても、音楽の再生は途切れずに鳴り続ける。

FreeRTOS で FatFs を使う

FatFs 0.13c には、スレッドセーフで動かす為の機能が用意されている。

そこで、FreeRTOS で異なるタスクから、ファイル操作を行う実験を行い、その設定などをまとめ Qiita に投稿した。

RXマイコンを使って、FatFs を FreeRTOS で運用する

GR-KAEDE(RX64M)、自作のRX64Mボード、RX65N Envition Kit などで動作を確認した。
※RX66T、RX71M でも動作するだろうが、SDカードのインターフェースを付けていないので確認できない・・

RX24Tではメモリが少なく、動作を確認出来なかった・・

RX24Tは、RAMが16Kしか無いので、タスク別にスタックを確保する必要があるので、どうしても無理があるのかもしれない。
多分、32Kあれば、動くと思える。

FreeRTOS で FatFs を使う場合、現在のバージョンでは、多少、FatFs のソースコードを修正する必要がある。

FatFs をバージョンアップ

FreeRTOS を本格的に運用する目処がたったので、まず、FatFs を最新版にした。

以前は、「ff12b」を使っていた。

今回「ff13c」に移行した。

意外と大きく変更になっている。

・ファイル名やパスが整理された。
・ヘッダー定義名やマクロ名などが、吟味され、より良くなった。
※マクロ名など、他のシステムと「当たらない」ように修正されたようだ。
・ファイルパスの文字コードがより洗練されて扱いやすくなった。
※ファイルのパスコードで、以前は、CP932 か、UTF-16 しか選べなかったが、UTF-8 も使えるようになった。
・排他制御用をやりやすいように変更があったようだ。
※lock、unlock などの関数コールが追加され、FreeRTOSと親和性が高い。
・一部、API が廃止になり、新しい API になった。
※日本だけなら、コードページ932だけで良いが、世界中で使っているので、その辺りを柔軟に改良したようだ。

などなど、細かく色々修正されている。

えるむ/ChaN さんのこのプロジェクトは世界中の人が使っている。
本当に素晴らしく、高機能なもので、今でも、少しづつ改良されているのには驚くばかりで、本当に頭が下がる。

FreeRTOS のような RTOS では、複数のタスクから、ファイル操作が出来ないとならないので、ドライバーの出来は、性能に直接影響するので、現在のソフト転送は改良する必要性がある。

ただ、難しい部分でもあり、性能を上げるのは簡単ではなさそうだ・・・

-----

とりあえず、github の FatFs を使っているアプリを全て修正し、master ブランチにマージしてある。

FreeRTOS、Rxv2 と、他デバイス対応

現在自分が扱う RX マイコンはどれも、RXv2 コアなので、GCC/RX600v2 のコードを使いたい。
しかしながら、gcc-6.4.0 は「RXv2」に対応していない。

rx-elf-as は、RXv2 に対応している。

% rx-elf-as -v --help
GNU assembler version 2.28 (rx-elf) using BFD version (GNU Binutils) 2.28
Usage: ./rx-elf-as [option...] [asmfile...]
Options:

.....
.....
.....

  --mcpu=<rx100|rx200|rx600|rx610|rxv2>
  --mno-allow-string-insns
Report bugs to <http://www.sourceware.org/bugzilla/>

そこで、RXv2 依存のアセンブリコードを使った関数を、アセンブラソースに分離して、対応する事にした。

多少の問題としては、「FreeRTOSConfig.h」の設定を使っている部分で(割り込みの優先順位)なのだが、まぁこれはあまり変更する事が無いと思うので、とりあえず、直接値を代入しておいた。

これで、リンクして、無事実行ファイルが出来、動作を確認したのだけど、そーいえば、コンパイラからアセンブラにオプションを渡せないのかな?

調べたら、あったー・・・

-Wa,option
option をアセンブラに対するオプションとして渡します。

なんだー、これだー、とゆー事で、Makefile を少し修正して、ソースコードはそのままで、「RXv2」に対応する事が出来たー
非常にスマートに対応出来た。

下記のようにコンパイラオプションを追加する事で、内部動作は、コンパイル後にアセンブラを起動する場合に、以下のオプションが追加される。

-Wa,-mcpu=rxv2

続いて、他のCPUについても、ICU 関係のクラスに「SWINT」関係を追加して、Makefile を作成して、実行ファイルを各マイコンに書き込んで試してみた。
とりあえず、問題なく動作するようだ。

これで、

RX24T
RX64M
RX71M
RX65N
RX66T

に対応する事が出来た、次は、よく使うドライバークラスをマルチタスク対応にして、ネットスタックの実験に進みたい。

ソースコードは、github の master ブランチにマージ済みとなっている。