「ソフトウェアー・エンジニアリング」カテゴリーアーカイブ

ソフトウェアー関係の話題など・・

最近は、github にプッシュしてます~

RX71MにLCDを接続してみる。

かなり昔に、Aitendo でセールしてたTFT-LCDをRX71Mに接続してみた。

2.2インチ、320x240、64Kカラー、コントローラーは、R61505

LCDコントローラーのバスをデータラインに直接接続するのがゴールだったが、動作しない・・

RXマイコンのバス設定は面倒なので、最初、I/Oポート制御でコントローラーの初期化を実験してみた。
しかし、何をやっても、思ったように動作しない・・
※初期化コードは Arduino 用をネットで拾ってきて、適当に改造して実験していた。

相当、色々やったが、不安定で動作しない・・・
※数回に1回、初期化だけ動作する事はあるが、ピクセルの描画では、おかしな動作をしてまともに動作しない。

それで、しばらく放置してた、2019年になって、そーいえば8ビットバスで試して無かったなぁーってふと思い、「IM」ピンをGNDからVCCに変更(8ビットバス)して、16ビットバス用から8ビットバス用に修正して試してみたら、普通に動作する。

何で?
8ビットバス接続では、DB8~DB15 を使う、16ビットバスでは、DB0~DB15 を使う。
以前に、DB0~DB15 の結線に誤りがあるのかと思い、調べたが問題無い・・

再度、DB0~DB7 の結線を調べたら、DB6 の結線が間違っていた・・
※PD6(145) に繋ぐべきところが、PG0(146) に接続していた・・・
最近、視力が落ちて、細かい配線が見難い(車の運転には支障無い視力)事が大きな要因と思うが、「メガネ」を作りにいくのが億劫で、先延ばしにしていた。

Start TFT sample
ID: C505
Probe TFT OK to start...
Render time: 630ms (1)

ところで、RX71M の 240MHz 動作では、320x240のレイトレースベンチマークは上記のように0.63秒だった。
ただ、ピクセルの描画コマンドをコントローラーに送る際のオーバーヘッドが大きいので、実際には、もっと速いと思うが、素晴らしいパフォーマンスだ!
RXマイコンは、本当に優秀なマイコンだと思う、使わない理由が無い、逆に何故ARMやPICを使うのか問いたいくらいだ。

https://github.com/hirakuni45/RX/tree/master/rx71m_LCD
※一応、テストで使っているプロジェクト

RXマイコン関係ソースコードを整理する

RX66T関係を追加する過程で、RX関連ソースコードを整理した。

今まで、RX24Tに始まり、RX64M、RX71M、RX65Nとサポートデバイスを広げてきたが、RX66Tを加えるにあたり、重複しているファイルや、醜い部分を整理した。
基本はRX64Mとなっている。
・RX64M、RX71Mはほぼ同じ
・RX651/RX65Nは、微妙に異なる
・RX24Tは別品種(割り込み関係が異なる)
・RX66Tは、RX24TとRX64M系の中間的
こんな感じだが、デバイスのレジスター関係は、全てにおいて共通部分が多く、共有出来る。
異なるのは、デバイス(自分のフレームワークでは、ペリフェラルと呼んでいる)の有無。
RX64M、RX71Mは非常に沢山のペリフェラルを持っているが、RX24Tなどは、それに比べて少ないが、中でも「割り込み」ベクターが大きく異なる。
これをどのように表現するかが大きな課題となっていたが、とりあえず、テンプレートにしておけば何とでもなる事が判った。
また、今までは、似ているけど少し異なるような場合は、「#if」などで、分けて凌いでいたが、基本的に各デバイスで、ファイルを分けて対応する事にした。
懸念事項としては、同じコードを全てのデバイス専用ファイルにコピーする必要があり、この場合、一つの修正が、他のデバイスのソースも同じように修正する必要が出てくる・・
一つのファイルで共有して「#if」で細かく分ける書き方だと、かなりトリッキーで読みにくくなる、どちらが良いかは、ケースバイケースで何とも言えないが、保守より読みやすさを優先した。

ペリフェラル:
各デバイスには、「peripheral.hpp」があり、このファイル内で、「enum class」により、「peripheral」型で、利用できるペリフェラルが列挙してある。
この「列挙型」の違いにより、ポートの設定、消費電力設定、割り込み制御など、大まかな動作を分けている、ペリフェラルを直接叩くドライバーは、たとえば割り込みベクター型の違いを考慮する必要が無いように実装してある。

RX24Tのペリフェラルでは、割り込みベクターは、通常ベクターに全て割り当てられているが、RX64Mなどペリフェラルが多いデバイスでは、通常ベクターの数が足りずに、グループベクター、選択型ベクターを新規に設け、それらに割り当てるように工夫されている為、そのままでは同じように扱う事が出来ない。
この違いを吸収して、デバイスドライバーを共通化する為、考えた結果、以下のような実装を行う事でかなりスマートに解決出来た。

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
        @brief  CMT I/O クラス
        @param[in]  CMT チャネルクラス
        @param[in]  TASK    タイマー動作クラス
*/    //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class CMT, class TASK = utils::null_task>
class cmt_io {

...

};

上記はCMTの制御クラスの型だが、「CMT」は、CMTペリフェラルクラスで、CMTの制御レジスターを定義したものになっている、通常CMTは0~3まで4チャネルある。
RX64M、RX71M、RX65Nでは、CMT0、CMT1の割り込みは「通常」ベクターだが、CMT2、CMT3は「選択型」ベクターとなっていて、割り込みの設定手順が異なる。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  CMT 定義基底クラス
    @param[in]  base    ベース・アドレス
    @param[in]  per     ペリフェラル
    @param[in]  INT     割り込みベクター型
    @param[in]  ivec    割り込みベクター
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t base, peripheral per, typename INT, INT ivec>
struct cmt_t {

...

};
#if defined(SIG_RX24T) || defined(SIG_RX66T)
    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)
    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

上記のように、テンプレートパラメータで「INT」(割り込みベクター型)を設ける事で、cmt_t クラスは、どの型のベクターなのかを意識しなくて済むようになっていて、「typedef」だけで対応できる。

cmt_io クラスでは、ベクターの型を元に、割り込み設定の方法を分ける事ができ、面倒な「#if文」から開放される。
また、C++ では、ベクター型の違いによる分岐は、コンパイル時に決定できるので、最適化により、余分の分岐一切が無くなるので、速度の面もリソースの面も好都合となる。
このような仕組みは、C言語だと、スマートに実現する事は難しい、C++ が組み込みマイコンのプログラムに向いた言語である事の一例であると思う。
※C言語プログラマは、#define で実現可能と思うかもしれないが、#define では、正確で厳密な「型」チェックは行われないし、構造的や論理的に誤ったコードをエラー無くコンパイルしてしまう可能性があり、テンプレートとは雲泥の差がある。

C++ のテンプレートでは、構造的や論理的に誤った構造は、「ほぼ」エラーになるので、最初の設計が必要十分な要素を詰め込んであれば、自ずと正しい方向に誘導してくれる感じがある。
大雑把に言うと、コンパイルエラーが無くなったら、プログラムも正しく動く事が多いとも言える。

自分が実装したソースでは、「#define」でマクロ的な定義を行う事は一切していない。

RXv3コア使用、RX66Tが発表された

RXv3 コアを内臓した初めての製品が発表された。
予想ではRX71Mの上位製品が最初だと思っていたが、モーター制御系の低価格版が最初のようだー。
RX66Tは、RXv3以外にも面白い特徴がある。
その一つは、高分解能PWMだろう、マスタークロックも160MHz動作が可能な上に、位相制御で、「最小195ps(ピコセカンド)でタイミング調整が可能」となっている点が新しい、位相制御は以前には、RX62Gで採用されたが、インバーターでは、必要な機能だろう、デジタルSWレギュレーターなどを実現するのに欲しい機能だ。

デバイスは、主に、RAMが128KBと64KBの製品があり(コードフラッシュは256K~1M)、多分、最初に出てくるのはRAMが64KBの製品と思う、また、USBも無しタイプが最初だろうと思う。
※コードフラッシュは256Kで十分だが、512K版との価格差は少ないものと思う。

現段階では、デバイスの販売はされていないものの、ハードウェアーマニュアルはダウンロード可能になっているので、早速、ダウンロードして、フレームワークに必要な実装を始めた。

構成は、RX63Tの後継らしいが、レジスターの構成は、RX64MやRX65Nに近いと思う。
RXマイコンのソフトウェアー資産は、今となっては沢山あるので、自分にとっては非常にメリットがある。
また、シリアルポートからのコードフラッシュプログラムも、プロトコルはRX24Tとほとんど同じ仕様と思うので、少しの追加でサポートできるだろう。

RX66TにはUSB内臓タイプがあるが、クロックレジスタの構成を考えると、USBを使う前提だと、最大周波数の160MHzでは動かせないと思う。
※USBで基準になる、24MHzの倍数で動かす必要があると思うので、156MHz動作になると思う(24MHz×6.5倍)。
RXv3コアは、RXv2コアに比べて、命令の効率が高いので、周波数が同じでもRXv2コアに比べてパフォーマンスが高いと思われる、どのくらい高いのか、早くデバイスを入手して試してみたい!
※多分、レイトレーサーを動かす事になると思う。

デバイスは、果たして、幾らで買えるのか?
RX24Tと同じく、10個購入程度で、それなりに安く買えるとありがたい・・
※上記ボードは、ルネサス純正ボードで、13200円(自分は高いのでスルー)

CPが最高に高いRX24T

100円マイコンのR8Cは別格としても、安くても十分機能があり、それでいてパフォーマンス(馬力)があるマイコンが必要な場合がある。
ルネサス社が発表しているマイコンで、どれが、コスト、パフォーマンス、機能が優れているか、検討してみた。
※実際は、かなり前に検討したものだが、未だにCPの高さトップに君臨している。

ちょっとした物を作る場合でも、昔には戻れない・・
※ファミコンやゲームボーイの時代はROMが高かったので、より多くのリソースを積み込む為、データ圧縮や、手動による最適化を行ったものだ、ハードウェアーがアップグレードして、記憶媒体がROMから、RAMになってCD-ROMなどの光学媒体になってから、もう容量の事で悩むような事は少なくなり、開発環境もアセンブラから高級言語に移っていった。
※PS2、EEのようにアセンブラでチューニングしなければ性能が出なかった例外的なハードもあるのだけど・・

今は、時代が違う、それでも、組み込みマイコンの場合、容量は有限だし、価格の安いデバイスはRAMも少ない、それゆえ、小さな工夫はしないとならない。
「数」を沢山作るような製品とは異なり、DIYでは機能を実現できれば、コストは度外視しても良いが、開発がやりやすいとか、リソースの再利用とか、便利で短時間で完成させる事が出来る方が良い。
C++ のテンプレートライブラリを多様したペリフェラルフレームワークは、Arduino のスケッチを書くより簡潔で判りやすく、最適化されたコードを作る事が出来る。

8/16ビットマイコンは、CPが高いと思うかもしれないが、実際はそうでは無い(ルネサス社のラインナップでは・・)、また、個人で数個単位で扱う場合と、大手が数百個、数千個、数万などスケールが異なる場合は、全く異なる価格帯になると思われるが、実際、数個のオーダーで個人が買う場合には、状況が異なる。
また、8/16ビットマイコンは、通常ポインターが16ビットで、大きな容量にアクセスする場合は、その都度、「方法」を選択してトリッキーなコードを実装しなければならない。
意外な盲点として、RXのような32ビットマイコンに比べて、同じプログラムでも、コードサイズが大きくなってしまうケースは良くある、又、内部レジスターは16ビットなので32ビットの演算では、さらに遅くなるので、場合により汎用のクラスは改修する必要もある。
32ビットマイコンは、プログラミングにおける基本的な制限はほぼ無いので、開発のしやすさや、リソースの再利用性から、少々コストが高くなっても選択したい理由となる、それでも@1000くらいだと、少し考えてしまう・・

マイコンの選択は、単純に、フラッシュの容量と内臓RAMサイズ、動作周波数などだが、マイコンの販売価格を見るに、非常にCPの高い製品がある事に気がつく。
※ARMやPIC32の価格には到底太刀打ち出来ないのだが、自分は、RXマイコンのソフトウェアー資産が豊富にあるので、ARMやPIC32よりも、RXマイコンを選択する事は十分メリットがある。
※まぁ、AVR、ARM、など各社マイコンは使った事もあるにはあるが、ルネサス社のマイコンだけにフォーカスしている人は少ないかもしれない。

RX24Tは、ブラシレスモーターのベクトル制御に特化した製品のようで、多分、エアコンや冷蔵庫などに使う目的でデザインされラインナップされた物と思う、イーサーネットやUSBは内臓していないが、他のペリフェラルが豊富で高機能なマイコンだ、そしてそれなりに安い。
※コード256K、RAM16K、データフラッシュ8K、LQFP100(10個購入時@594)
chip1stop「R5F524TAADFP#30」
※10個購入時だけど、DIY用でも10個くらいは使うだろって事で勘弁してほしい~
※RL78の256K版(@400)と比較しても、馬力を考えるとお得感がある。
※32MHzでFPU無しのRX220より安い。
※RXマイコンは、意外と命令効率が高いので、128KBの製品でも十分かもしれないが、価格差が僅かなので、256Kの製品リンクを張った。
※最近、Bバージョンが発売され、CANが使えて、RAM容量が2倍の32Kになった製品もある。(自動車向けか?)
#コード256K、RAM32K、データフラッシュ8K、LQFP100(10個購入時@657)
chip1stop「R5F524TBADFP#31」

  • RXv2コア
  • 80MHz動作(80MHz動作では、プログラムフラッシュにはウェイトが入る)
  • DSP命令(有効に使うには、アセンブラで実装する必要がある・・)
  • FPU(各種32ビットの浮動小数点命令)
  • ベクトルの正規化に威力を発揮するFSQRT(平方根)命令
  • 2.7V ~ 5.5V の単一電源動作
  • 普通に低消費電力
  • 12ビットA/D(最大1uS、3ユニット)
  • RSPI(1チャネル)※SDカードもかなり高速にアクセス可能
  • SCI(3チャネル、簡易SPI、I2Cが使える)
  • I2C(1チャネル)
  • DTC(データトランスファーコントローラー)
  • 高速で、豊富なタイマー郡(最大80MHz動作)
  • コンパレーター(4チャネル)
  • 必要十分なI/Oポート数
  • BバージョンのみCANインターフェースを備える
  • コードフラッシュの書き換え回数(1000回と少し少ない・・)

これだけの特異な特徴があり、他のRXマイコンと基本的にペリフェラルは共通。
ちょっとした用途ならこれで十分と思う、また、フラッシュROMの書き込みはシリアルで行うが(J-TAGはサポートしないが、エミュレーター専用端子FINEを持っているので、対応したE1でも当然書き込めるしデバッグもできる)、何故か、RX64Mなどと異なり、かなり高速に書き込めるので、シリアル書き込みでも開発ではあまり不自由しない。
※自分の開発スタイルでは、コードのブレークは必要としない。
※Apple、Linux、Windowsで開発出来る。
この価格帯にしては、浮動小数点がかなり強力なので、ジャイロや加速度センサーを使った姿勢制御(ロボットなど)にも、かなりマッチすると思う。
タイマーも非常に多く使えるので、RCサーボを沢山動かすのにも向いている。
多少残念なのは、コードフラッシュの書き換え回数が1000回と少し少ない。
それでも、ちょっとしたDIYボードに最適と思う。

おなじみ、レイトレーサーを走らせてみたが、約1.2秒くらいでレンダリングする。
これは、160MHz動作のESP32の10倍以上高速となっている、FPU内臓は伊達では無い。
※ピクセルの描画コストを5マイクロ秒としている。
※ESP32でのレイトレーサー速度は、ネットでの記事を参考にしており、RXマイコンに比べてあまりに遅い、ESP32はFPUを内臓しているので、ベンチマークの方法に問題があるかもしれず、単純に比較するのはフェアではないかもしれない事を付け加えておく。
200MHz動作のARMでは0.6秒との事なので、妥当なスピードと思う。

デバイスは以前に10個ほど買った(Aバージョン)ので、自分用にでも汎用ボードを作ろうと思っている。
又、折角のDSP命令も使わないのは勿体無いので、gcc 向けアセンブラ・ライブラリを実装しようと思っている、これはRXマイコン全般で使えると思う。

GitHub の「主要言語」

GitHub のホームを開くと、各プロジェクトに主要言語が表示されるー

自分の場合、以前は「C」、「C++」などで、どのようなロジックで表示されてるのか疑問だった。
調べると、コミットしてあるファイルの拡張子などから、集計を取り、一番多い「言語」を表示している事が判った。
「主要言語」が「C++」でも、「C」のライブラリなどがプロジェクトにコミットされていて、単純にCのソース数がC++ソース数を上回ると、「C」と表示されてしまう・・
※自分のプロジェクトでは、C++はヘッダーのみの場合がほとんどで、ソースは必要無いので、ソースの数は1/2で、評価が意図と異なる。
githubの検索などでは「言語」をキーに検索する事もできるので、これは、意図と違う結果になってしまう。
そこで、調べると、「.gitattribute」ファイルをプロジェクトのルートに置いて、このファイルで、集計を除外する事で、「主要言語」の評価を変えられる事が判った。

RTK5_NESEMU/emu/* linguist-vendored
ff12b/src/* linguist-vendored
zlib/* linguist-vendored
libmad/* linguist-vendored
jpeg-6b/* linguist-vendored
RX65x/drw_2d_ver1.02/* linguist-vendored

たとえば、「RX」プロジェクトは↑のように、ライブラリを除外してある。

これで、「主要言語」の評価を「C++」に出来た。

※上記の「バー」をクリックすると、ファイルと言語のプロパティが表示される。

さらに、「言語」をクリックすると、どのファイルがその言語と認識されているか詳細が表示できるので、「意図」と合わないファイルは、「.gitattribute」で、細かく指定できる。

RX65N Envision Kit オーディオプレイヤー(その2)

picojpeg デコーダーのポーティングが出来たので、ID3 タグ内のジャケット画像などを格納するタグ「APIC、PIC」内データ(JPEG)をデコードして画像表示は出来たのだが、画面サイズにフィットさせるのは後回しになっていた。

まず、仕組みを考える・・

画面デザインから、画面の右端に272×272ピクセル(液晶の縦ピクセル)で表示させるのが良さそうだ。
ただ、タグ内の画像サイズは様々なので、適切にスケーリングする必要がある。
スケーリングの方法も重要だ、RX65Nの内蔵メモリはあまり大きく無いので、画像のような大きなデータを扱う場合、出来れば、一時メモリを使用しない(利用できない)方が良い。

それらを踏まえて、画像のデコーダーテンプレートは、直接画像の操作を扱うAPIを叩かないで、中間に「スケーリング」を行うオブジェクト(クラス)を入れる事にした、こうしておけば、レンダリングを行う対象がどんな場合にも対応可能となる。

    typedef graphics::render<uint16_t, LCD_X, LCD_Y, AFONT, KFONT> RENDER;
    RENDER      render_(reinterpret_cast<uint16_t*>(0x00000000), kfont_);
    typedef img::scaling<RENDER> PLOT;
    PLOT        plot_(render_);
    typedef img::picojpeg_in<PLOT> JPEG_IN;
    JPEG_IN     jpeg_(plot_);

C++ では、「ファンクタ」と呼ばれる方法(通常「()」オペレーターを使う)で、クラスに対しての操作を一般化できる。

//-----------------------------------------------------------------//
/*!
    @brief  描画ファンクタ
    @param[in]  x   X 座標
    @param[in]  y   Y 座標
    @param[in]  r   R カラー
    @param[in]  g   G カラー
    @param[in]  b   B カラー
 */
//-----------------------------------------------------------------//
void operator() (int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) noexcept
{
・・・
}
まず、plot_ クラスに対して、スケールを設定する。
※スケールは、「整数比」で設定する。
※画像全体を収める為、縦か横で長い方を基準にする。

    auto n = std::max(ifo.width, ifo.height);
    plot_.set_scale(272, n);

jpeg_ クラス内から、plot_ クラスに対して、以下のように普通に描画すれば、設定された拡大、縮小比で描画される。
※ワークメモリを使わないのと引き換えに、描画する領域は、あらかじめ「0」クリアしておく必要がある。

    plot_(x, y, r, g, b);

また、描画時にオフセットを与えられるようにしてある。

    plot_.set_offset(vtx::spos(480 - 272, 0));

最初、縮小をさせる場合に、エイリアシングを考慮しない簡易的な実装を行ってみたのだが、当然のように描画品質はあまり良くない。
※ジャギジャギしてる・・

そこで、ピクセルの加重割合を考えずに平均化してみる事にした。
・フレームバッファはRGB565となっている。
・フレームバッファを読み出して、「0x0000」なら、初期に書き込むピクセルとして、そのまま書き込む。
・もし、新規に書き込むピクセルが「0x0000」なら、「0x0001」に直して書き込む。
・フレームバッファを読み出して、「0x0000」以外なら、既に書き込まれている値なので、新規に書き込む値との平均(1/2)を求めて書き込む。

この実装は、厳密な意味では正確ではないものの、「ジャギー」感が減り、かなりマシに見える。
とりあえず、これで良しとした、ピクセルサイズによる厳密な加重平均や、フィルタ的要素は、次の課題とする。

※現状では、「拡大」する場合は、簡易的なものになっていて「ジャギー」が目立つ。

RXマイコンの最適化に対する知見

RX65N Envision Kit が発売されてもうじき1年がたとうとしているが、アプリケーションを作っている人は非常に少ないように感じる・・
サーチエンジンで探しても、あまり有益な情報を探せない、現在、秋月では売り切れとなっており、それなりに購入している人はいると思うのだが、何か面白いアプリを作って発表し、ソースコードを公開している人はほとんどいないように感じる。
何がハードルになっているのか?・・・
それに比べて、M5Stack、ESP32、ARMなど海外のマイクロコントローラーは、非常に沢山あるようなのだが・・・
※何かあったら、リンクを教えて下さい。

さて、以前に「Arduino 用レイトレーサー」を実装している人がいて、コードが公開されていたので、RX65Nにポートしてみた。
その過程で、ルネサス公式のフォーラムにそのリンクを張った。
しばらくして、色々な指摘を受けた。
※自分のブログにも同じような指摘が返信されていたが、検証の材料が無く、ほぼスルー状態だった。
※自分の間違った知見もある・・

ルネサスのフォーラムでは、具体的な実験を行い、検証を行ってくれた。(感謝)
※単に思いつきで返信するだけじゃなく、このような、有益な内容のある濃い情報が重要だ、言うだけなら誰でもできる、何か発信するなら、せめて、自分で「手」を動かして、検証可能な情報を返して欲しいものだ。

そこで、指摘された事についてまとめておきたい。

  • gcc では RXv2 コアの最適化が不十分
  • double 定数を使っているのが余計
  • std::sqrt はdoubleの関数なので、sqrtf を使った方が良い
  • RXv2には、fsqrt 専用命令が用意されている
  • ESP32 などで公開されているベンチマークは、レイのサンプリングは「1」で行っているがRXでは、「4」で行っていて条件が異なる

gcc の RXv2 の最適化に関しては、自分の考え方が少しある。
まず、gcc のオフィシャルなソースコードを使う事を前提としているので、ルネサス社が、gcc に最適化のコードをコミットするまで待つしかない・・
現在ルネサス社は、独自にGNUツールを公開しているが、最新版は4.8系となっており、C++ の最新のコードをコンパイルする事が出来ない。
※ルネサス社が提供するGNUツールでコンパイルした場合、最適化「-O3」で、最大7~15%くらい良いコードが出る場合があるようだが、これは今後検証してみたい。
※オフィシャルな gcc では、「-mcpu=rx64m」など専用デバイスのオプションは無いが、標準で、RX600 系のコード出力となっている。

double の定数や、double 専用 API は、指摘を受ける前に既に、変更済みで、それで、ほんの少し高速になっていた。

標準の数学ライブラリでは、平方根を求める「sqrt、sqrtf」はニュートン法を使っており、RXv2 に備わっている、fsqrt 専用命令とは微妙に計算結果が異なっているものと思うので、標準では使わないのだと思っていた。
だが、この命令に切り替えた場合、倍の速度でレンダリングするようなので、この知見を素直に受け入れた。

#if defined(SIG_RX64M) || defined(SIG_RX71M) || defined(SIG_RX65N) || defined(SIG_RX24T)
static inline float sqrtf_(float x)
{
    __asm __volatile(
        "fsqrt %0, %0\n" \
        : "+r"(x) \
    );
    return x;
}
static inline int ceilf_(float x)
{
    int y;
    __asm __volatile(
        "pushc fpsw\n"
        "mvtc #0b10, fpsw\n"
        "round %0, %0\n"
        "popc fpsw\n"
        : "=r"(y) \
        : "r"(x) \
    );
    return y;
}
#else
static inline float sqrtf_(float x) { return sqrtf(x); }
static inline int ceilf_(float x) { return ceilf(x); }
#endif

レイのサンプリング数に関しては、全くノーマークだった、元ソースでは、標準「4」だったので、素直に「4」で行っていたが、ベンチマークを行っている Arduino のスケッチでは、「1」にして API を呼んでおり(卑怯なのではw)、大体4倍のレンダリング時間となっていた。
※「STM32F7 200MHz」では、0.62秒と非常に高速なので、この違いは、何なのか、疑問に思っていた・・・

以上の知見を受け、RX65N Envision Kit でレンダリングすると・・・

約0.83秒と、STM32F7 200MHz と比較しても、十分戦闘力のある値となったw
元は7.7秒とかだったので、素晴らしいパフォーマンスアップとなった。
※ESP32(160MHz)は、13秒みたいだが、これは、他にバリアとなる事象があるように思う
※このような有益な情報を提供してくれた「fujita nozomu」氏に感謝したい!

全ソースコードは以前から公開しているが、コンパイル環境を作り、フレームワークなどをチェックアウトする必要があるので、実行バイナリーも公開している。
・裏にあるスイッチを押す毎に、フルスクリーン(480×272)、レイのサンプリングを変更してのレンダリングを行う。

「raytracer.hpp」は、かなり柔軟性があり、ベンチマークには最適なので、PIC32や、他のマイコンでも十分実行できる、別のマイコンで試した人がいれば、是非情報を公開して欲しい~

PicoJPEG デコーダーを使う

以前に JPEG デコーダー「libjpeg」ライブラリーをポートしようと思ったものの、記憶割り当て不足で、実用的に使えなかった。
※libjpeg では、内部で画像サイズのRGBメモリを割り当てる為、当然のようにメモリ不足になってしまう。
ルネサスでは、オリジナルの JPEG(RXv2 DSP 命令で最適化した)ライブラリを公開しているようだが、肝心のコア部分ソースコードは未公開で、バイナリ化されており、gcc からは使う事が出来ないようだった。
そこで、libjepg を改修して、記憶割り当てを何とかしようと思ったが、既に、JPEGのデコードを少メモリで実現する「PicoJPEG」がある事に気が付いた。
なので、このライブラリを使わせてもらう事にした。

ポートは簡単で、「picojpeg.c」をコンパイルしてリンクするだけだ。
ただ、コンパイルエラーが出たので、多少修正した。
※ sequence-point のエラー
※このエラーは、式の評価順番に関係するもので、オリジナルの書き方は、C 言語の規約では「未定義」となるもののようだ。
https://www.jpcert.or.jp/sc-rules/c-exp30-c.htm

    *pDstR++ = addAndClamp(pDstR[0], crR);

以下のように書き換えた。

    *pDstR = addAndClamp(pDstR[0], crR);
    pDstR++;

サンプルを参考に C++ から簡潔に呼べるようにラッパーを実装した。
ラッパーは、テンプレートライブラリで、描画クラスを「参照」で与える仕様とした。
※BMP のデコーダーと同等の仕様。

ただ、PicoJPEG デコーダーは、「プログレッシブ」でセーブされたフォーマットをサポートしておらず「ベースライン」のみとなっている。
※「プログレッシブ」は、ブラウザなどで、最初荒く、後、詳細に表示するもので、通信速度が遅かった昔の名残で、最近のネット速度を考えると、ほぼ使う必要が無いモードと言える。
※プログレッシブ・エラーが出た場合には、フォームを確認して、「ベースライン」でセーブする必要がある。

480x272、ベースライン(最適)のJPEG表示サンプル
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  PicoJPEG デコーダー
    @param[in]  RENDER  描画ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class RENDER>
class picojpeg_in {
.....
};

※描画を行うクラスを「RENDER」で渡す。
※「RENDER」クラスでは、「plot」APIを通して描画を行う。
picojpeg_in.hpp

今回、RX65N Envision Kit (AUDIO プレイヤー)でデコードと描画のテストを行った。
https://github.com/hirakuni45/RX/tree/master/RTK5_AUDIO_sample
※ターミナルからの「jpeg」コマンドでJPEGファイルを描画できる。

また、ID3 に含まれるPIC、APIC タグで埋め込まれたJPEG画像をデコードして表示させるようにした、ただ、適切なスケーリングをしていない為適切な大きさで表示されない。
※これは、今後対応する事にする。
又、PNG 画像もサポートされない・・

組み込みマイコンでstd::functionを~

少し落ち着いたので(実際は、やるべき事が山済み状態だけど・・)気になっていた部分をクリーンナップしている。
現在、俺俺フレームワークは、リアルタイムOSの助けを借りない方法で実装を行っているので、一連の長い処理を書く場合などにはコールバックの仕組みを与えている。
C++11 以降、STL には、そのような場合便利で安全な方法が多数用意されている。
しかし、その場合、通常は「stdc++」をリンクする必要があり、その場合、許容出来ない程大量のワークエリアを消費し、実用的では無い場合があった。

さて、コールバック機能を実装する場合、どうしても、「std::function」を使いたい。
・このテンプレートは、非常に強力かつ高機能だが、詳しい使い方は、検索してほしい。
・このテンプレートにする最大の理由は、ラムダ式が使える事で、コールバックの中身を「直」で実装できる。

しかしながら、これを使うとなると、関連する例外処理など他の機能を利用している為、stdc++ ライブラリのリンクが必須となってしまう。
しかし、よく調べると、g++では、「supc++」ライブラリが分離されておりこれをリンクすれば、最低限の消費で済む事が判った。
※「supc++」は「stdc++」ライブラリのランタイム部分のみ。
※例外の外部関数を実装する必要がある。

namespace std {
    void __throw_bad_function_call()
    {
        abort();
    }
}

※コンパイラオプションで例外を禁止していても、これだけはリンクする必要があるようだ。
※これは、「common/stdapi.cpp」に分離してあるので、Makefile にこのソースを追加する必要がある。

コールバック関数の登録で、重要となるのは、たとえば、クラス内からコールバックを設定するような場合だが、普通に書いた場合、「static」な関数しか登録できない。
※それでも別の逃げ方が無い訳では無いが、少し面倒・・
static だと、クラス内の public メンバーなどにアクセス出来ない。
そこで便利なテクニックとして、ラムダ式によるキャプチャー機能を利用する事で、以下のようにクラス内メソッドを呼び出す事が可能となる。

void play_loop_(const char* root, const char* start)
{
    static loop_t t;
    t.start = start;
    if(strlen(start) != 0) {
        t.enable = true;
    } else {
        t.enable = false;
    }
    sdc_.set_dir_list_limit(1);
    sdc_.start_dir_list(root,
        [&](const char* name, const FILINFO* fi, bool dir, void* option) { play_loop_func_(name, fi, dir, option); }, true, &t);
}

上記コードは、オーディオプレイヤーのコードで、ディレクトリーを巡って、オーディオファイルを再生する。
「sdc_.start_dir_list()」で、ファイル情報アクセス時に、設定されたコールバックが起動する。
「play_loop_func_」は、ラムダ式のキャプチャー「[&]」が あり、クラス内のリソースにアクセスする事が出来る。(これは本当に便利だ!)

まとめ:
「std::function」を使う事で、関数オブジェクトの利便性が強化された、これは、C++11以降の恩恵で、かなり大きなトピックと言えるが、組み込みマイコンのような制限されたリソースでもそれを活用できる事が判った事は大きい~

RX71Mを試す

かなり昔にデバイスは購入してあったのだが、中々試せないまま、RX65Nなどに寄り道していたのだが、240MHzの実力を評価したくて、一応試してみた。
RXv3
の発表があり、「RX71M」も最高性能のRXマイコンでは無くなってしまったが、それでも、240MHzは120MHz版に比べてどのくらいの性能があるのか試しておきたい。

169ピンのユニバーサル基板も手持ちが最後なので、次は基板を起こす方が良さそうだ・・(この基板、安くて小さくて便利なのだが、販売終了しているぽぃ)
以前にRX64Mの試作では、SDRAMの32ビットバスを試したが、あれは、ちょっとマズイ・・、32ビットのデータ幅を割り当てると、他に使うペリフェラルが極端に制限されてしまい、イーサーネットやSDHCに割り振る事が難しくなる、なので、今回は16ビットバスでSDRAMを載せようと思う。
※このデバイスは「もらった」もので、SDHC未対応なのだが・・・
RX65NのEnvisionキットでファミコンエミュレーターなどを試して、やはり外部メモリーは必須だ・・(内臓メモリが1Mバイトくらいあれば・・)
RX71Mには、RX65NのようなLCD制御機能は無いが、単純に接続するだけなら、EXDMA転送で代用できそうだし、SSI(シリアルサウンドインターフェース)など、デジタルオーディオ系にも強そうだ。

今回試作して判った事として、電源ラインの重要性だった、面倒なので、アナログの電源は接続しなかったのだが、それが原因で、ブートモード(SCIインターフェース)でデバイスのコネクションが正常に終了せず、接続できなかった、電源は全て接続してパスコンも付けていたのだが、原因がわからずにいたが、関係無いと思うけど的な感じで、アナログ電源を接続したら、あっけなく正常動作した。

240MHz動作は、既に実績があると(依然にRX71Mを使ったボードを設計して、納入した実績があり、その時は問題なかった・・)思っていたが、何か怪しい動作をする。
具体的には、240MHzで動かすと、SCI関係で文字化けを起こす・・
CPUのクロック以外は、120MHz版と変わらないので、原因が判らない・・
プログラムは暴走する訳ではなく、動いているようだ。
120MHzであれば問題無い・・
どうも、240MHz動作時は、最大で240mAの電流が必要らしいので、そのせいかと思ったが違うようだ・・
ただ、USBから供給する電源の品質はあんまし良くないのと、瞬間的に大きな電流を「引く」とかなりドロップする。

オシロで、SCIの信号を確認して、やっと解決、ベースクロックのPLL定数を求めるソフトのバグだった・・、240MHzと思っていたら180MHzだった。
※そりゃー文字化けするよなぁーwww
クロック設定クラスは、以前に収めた機器では、240MHz固定だったのだが、最近テンプレート化して、色々柔軟性を求める仕様に拡張した過程で、マズイ実装を行っており、120MHzだけは正確に機能していた・・・
ハードの問題だと思って色々対策して、結局ソフトのバグとゆーのは、まぁ良くある話なのだがー、最近視力が落ちて、配線の質が落ちており、ハードに自信が無いと思っているのもある・・
さらに、鉛フリーハンダは、温度に敏感で、少し低いと綺麗に付かない。