公開日:2025/11/08
title.png

36th Season


#36-10 マンデルブロ集合の探索

 前回作成したマンデルブロ集合描画プログラムを使って、マンデルブロ集合の世界を旅してみたいと思います🚅。と、その前に、マンデルブロ集合描画プログラムの要点を解説しておきます。

Blockly for Moscoviumのデュアルコア実装

 探索の前に、マンデルブロ集合描画プログラム(mandelbrot.bxml)の要点を解説しておきます。

 以下の記事で、Blockly for Moscoviumにデュアルコア対応のカスタムブロックを追加してありました。

  「#32-2 Blocklyにデュアルコアを実装

  「#32-3 Blocklyにデュアルコアを実装(二芯)

 今回は、そのカスタムブロックを使った実装例となっています。

 グラフィカルなBlocklyのコードは、画像データにすると結構大きな画像となってしまうため、Blocklyコード自体は、各自Blockly for Moscovium開発環境を立ち上げてご確認ください。
 本ページでは、コンパクトに参照できるよう、JavaScriptへ変換後のソースリストでご覧に入れます。

サブCPU2のエントリポイント

 taskブロックの「CPU2」(下図)を配置すると、デュアルコアのサブCPU2だけが、この場所に分岐して来ます。

a36a_taskblk.png

 マンデルブロ集合の描画プログラムでは、以下の様なコードです(JavaScript変換後のソースリスト)。

  // CPU2 entry point
  function _task_CPU2 () {
    // variables
    cpu_id = (sreg_id_2>>sreg_b_id0);
    // interrupt setting: ICF clear, ICE enable
    mem.write_word (intcpu,(intc_icf|intc_ice));
    // mask CPU1 interrupts
    mem.write_word (intmskh,intc_mskh_1);
    mem.write_word (intmsk,intc_msk_1);
    // interrupt enable
    cpu.srie (sreg_ie_3);
    // loop
    loop {
      // wait interrupt from CPU1
      cpu.asm ('pause');
      // command
      if (cpu_cmnd == MDBR_CMND_START && (! cpu_sleep)) {
        // command: start calculation
        posr = mem.read_long (MDBR_PARA_ADR);
        posi = mem.read_long ((MDBR_PARA_ADR+4));
        sizr = mem.read_long ((MDBR_PARA_ADR+8));
        mandelbrot(posr, posi, sizr);
      } else if (cpu_cmnd == MDBR_CMND_COND) {
        // command: set condition
        mdbr_nitr = mem.read_long (MDBR_PARA_ADR);
        mdbr_ninf_sq = mem.read_long ((MDBR_PARA_ADR+4));
      } else if (cpu_cmnd == MDBR_CMND_SLEEP) {
        // command: sleep request
        cpu_sleep = (!! mem.read_word (MDBR_PARA_ADR));
      }
      cpu_cmnd = MDBR_CMND_IDLE;
    }
  }


  要点解説:

 デュアルコアに分散するプログラムも、たったこれだけの記述量で実現できてしまいます。意外と簡単でしょう?所謂OSと言う奴らは、何かと手続き(API)を複雑にしたがる傾向があるので、好かんです。

サブCPU2の割り込みサービスルーチン

 同じく、taskブロックの「IRQ2」が、サブCPU2の割り込みサービスルーチンです。CPU2が割り込みを受け付けると、この場所に制御が移ります。

a36a_taskblk.png

 マンデルブロ集合の描画プログラムでは、以下の様なコードです。

  // CPU2 interrupt service routine
  function _task_IRQ2 () {
    // command from CPU1
    cpu_cmnd = mem.read_word (MDBR_CMND_ADR);
    // clear ICF
    mem.write_word (intcpu,(intc_icf|intc_ice));
  }


  要点解説:

 割り込みが発生すると、CPU2プログラム(task CPU2ブロック)のpause命令が終了して次命令へ制御が移るので、受信したコマンドcpu_cmndはCPU2プログラムが解釈して実行します。

メインCPU1からのコマンド送信

 メインCPU1からサブCPU2へコマンドを送信する手順は、例えば以下の様なコードとなります。

  // kick CPU2
  mem.write_word (MDBR_CMND_ADR,MDBR_CMND_START);
  mem.write_word (MDBR_LINE_ADR,0);
  mem.write_long (MDBR_PARA_ADR,posr);
  mem.write_long ((MDBR_PARA_ADR+4),posi);
  mem.write_long ((MDBR_PARA_ADR+8),sizr);
  mem.write_word (intcpu,intc_icrq);


  要点解説:

 今回のプログラムでは、CPU1からCPU2へ一方的に命令するだけなので、CPU2が停止していたり、あるいはシングルコア版のマイコンだった場合でも処理の場合分けは不要です。
 もし、双方向で通信するようなインターフェイスにした場合は、デュアルコア版なのかシングルコア版なのかを認識して動作する必要があります。さもないと、存在しないCPU2からの応答を待ち続けてハングアップしてしまうからです。
 その場合、CPUの状態レジスタsrにアクセスしてIDフィールドを参照すると判定可能です。以下にCPU IDを取得するコード例を示します。

a36a_cpuid.png

 Blockly for Moscoviumは「高機動アセンブラ」としての使い方を想定して、アセンブラとの連携を意識した設計を施してあるので、Moscovium特有な機能を使う上でも、まず、困ることはありません。

マンデルブロ集合計算ルーチンのデュアルコア対応

 デュアルコアに対応するため、共有メモリMDBR_LINE_ADRを使って、メインCPU1/サブCPU2間で互いに計算中の行番号を把握しながら処理を進めています(以下コード)。

  // check status
  get_semaphore();
  if (mem.read_word (MDBR_LINE_ADR) > mdbr_ii) {
    rel_semaphore();
    continue;
  }
  mem.write_word (MDBR_LINE_ADR,(1+ mdbr_ii));
  rel_semaphore();


  要点解説:

SPI-TFTへの描画

 今回のプログラムでは、SPI-TFTへの描画も、各々のCPUに実行させました。SPI-TFTも共有資源なので、セマフォを取得してから描画しなくてはなりません。

  // draw
  get_semaphore();
  stft.dot (mdbr_ir,(MDBR_SIZE-1) - mdbr_ii,mdbr_pix);
  rel_semaphore();


 前節で示した通り、セマフォ取得手続きget_semaphore()は、セマフォを取得できるまで待機するので、メインCPU1とサブCPU2が同時に描画しようとすると、少しでも遅かった一方が待たされることになります。これが、オーバヘッドとなっています。
 少なくとも、セマフォを取得している時間は最小化すべきなので、共有リソースを使う最小限の範囲に留めておきましょう。
 マルチコアCPUの処理性能を最大化するためには、如何にしてオーバヘッドを軽減するか?が大きな課題です。知恵の絞り所ですね。今回のプログラムでは、全く考慮していないので参考には、なりませんm(1__)m。

 なお、今回、2か所でセマフォによる排他制御を実施しています。

 それぞれ異なる共有リソースですが、今回のプログラムでは同じ一種類のセマフォで制御しました。少しでも競合によるオーバヘッドを減らしたいなら、各々異なる種類のセマフォを使って処理した方が有利な事を申し添えておきます。

 以上で、プログラムの解説は終了です。いよいよ、マンデルブロ集合の世界へ旅立ちましょう。

マンデルブロ集合探索

 マンデルブロ集合の世界を探索して見ましょう。
 インターネットで調べると、色々なマンデルブロ集合の画像が見つかり、座標が記されているページもあります。その様なページから座標値を拾って描画して見ました。

 【save】コマンドで吸い上げた、実際にMoscoviumマイコンで描画した画像と、その時の条件や範囲などを以下表にまとめておきます。

描画画像 描画コマンド
a36a_mdbr_0.png cond,120,100
mdbr,-700,0,3000
a36a_mdbr_1.png cond,360,100
mdbr,-1779880,0,500/1000000
a36a_mdbr_2.png cond,400,100
mdbr,-743,126,1
a36a_mdbr_3.png cond,600,100
mdbr,-743000,126000,8/1000000
a36a_mdbr_4.png cond,200,100
mdbr,-743567,131402,2288/1000000
a36a_mdbr_5.png cond,200,100
mdbr,-743641,131827,121/1000000
a36a_mdbr_6.png cond,500,100
mdbr,-743643,131826,14/1000000
a36a_mdbr_7.png cond,600,100
mdbr,-920,270,50/1000
a36a_mdbr_8.png cond,200,100
mdbr,-743650,131882,738/1000000
a36a_mdbr_9.png cond,1200,100
mdbr,-1145894,-266907,61/1000000

 画像が小さいのはアレですが……、@24MHzやそこらのマイコンで描画しているのですから、この程度でゴメンナサイです。もっと大きな画像で高精細に楽しみたい方は、マンデルブロ集合の座標は共通なので、PC上のソフトウェアなどで計算して見ても、同様な画像が得られます。


 色々な座標にズームアップして行くと、自己相似図形が観測出来たり、綺麗な模様が観測出来たりして飽きません。マンデルブロ集合の世界を旅して見てください。


btn_arduino.png

btn_prev.png btn_next.png

home