Pico三昧(54) ラズパイPico2:RP2350、MicroPythonでコア間通信

Joseph Halfmoon

MicroPythonメインの別シリーズがあるのに、こちらでやっているのはRP2350のハードを直接操作するような実験をしているため。何にしてもMicroPythonはお楽。ただしMicroPython処理系の予期せぬところを触ってヤバイことになることもあり。前回は2コア実行、今回はコア間の通信機能を使ってみます。

※Pico関係投稿一覧は こちら 『Pico三昧』は一覧の末尾付近にひっそりと。

※Pico2対応のMicroPython処理系(バイナリ、uf2形式)は以下のURLからダウンロード可能です。

https://micropython.org/download/RPI_PICO2/

※動作確認に使用しているMicroPython処理系は以下です。

MicroPython-1.24.0-riscv–with-newlib4.3.0

ラズパイPico2搭載のRP2350マイクロコントローラは2個のプロセッサを同時に走らせることが可能です。プロセッサの「座席」は2つ。コア0とコア1。どちらの座席にもArmとRISC-Vを選択可能。このところ使用させていただいているのは、両席ともRISC-VのMicroPython処理系です。

2個のコアの平行処理が可能ということは、当然コア間の通信機構も備えています。この仕組みは SIO(Single cycle I/O)というブロック内に収められています。SIOについては以下の過去回で調べてます。

Pico三昧(34) ラズパイPico2:RP2350、Pico:RP2040、SIO比較

ただしMicroPython処理系では、一部の割り込みしかユーザが使えるようになっておらず、コア間の割り込みをユーザも使えるのか否かは今のところ不明です。しかし、コア間に存在するMailbox(FIFO)は多分使えそうな感じ(もしかすると何か良からぬことが起こるかも知れんので、あくまで実験っす。)

セキュアとノンセキュア

さて、前回もSIOブロック内に存在するCPU_IDなどという「私はだあれ」レジスタを読み出したりしてました。CPU_IDは特に隠す必要もなく、またリードオンリなので問題も起こりようがない?こいつにはセキュアもノンセキュアもないみたい。

しかし今回使おうとするのはコア間に張られた通信FIFOです。32ビットx4段構成のFIFOが2本。コア0からコア1への1本と、コア1からコア0への1本です。メッセージを送信可能なので Mailbox などとも呼ばれています。

さてこのMailbox構造は、ノンセキュアなコードがセキュアなコードに何かよからぬメッセージなど送り付けてしまえるとマズイためか、セキュアなコードとノンセキュアなコード用に2重化されてます。ここで調べて以下に気づきました。

MicroPython上で走っているスクリプトはノンセキュア

の側で走っているようです。つまり2重化されているFIFO構造にアクセスする場合、ノンセキュアな方のFIFOにアクセスしないとなりません。セキュアな方を使おうとすると何も起こりませぬ。。。無反応?

コア間FIFO通信の実験コード

前回使用した2コアのそれぞれにTaskAとTaskBを走らせる実験コードにFIFO通信部分を追加しました。上記の理由からFIFOはノンセキュアな方を使わないとならないので、SIO_NONSEC_BASEという定数も追加してます。

FIFOは2本ありますが、コア0から書き込みできるFIFOはコア1の読み出し、コア1から書き込みできるFIFOはコア0の読み出しという関係なので、それぞれのコアには所定の番地にライトFIFOとリードFIFOがあるように見えてます。そしてFIFO_STというステータスレジスタあり、その中にFIFOがバリッド(読み出し可能なデータあり)、FIFOがレディ(FIFOに書き込み可能な隙間あり)かを示すステータスビットが存在します。

今回はコア1で実行されているTaskAから、1001、1002。。。というメセージを毎秒送り出し、コア0で実行されているTaskBでそのメッセージを受け取って標準出力にその旨表示しています。TaskAからゼロを送出し、TaskBがゼロを受け取るとこの関係は終了します。

実験コードが以下に。

import time, machine, _thread
from micropython import const

SIO_BASE = const(0xd0000000)
SIO_NONSEC_BASE = const(0xd0020000)
FIFO_ST = const(0x050)
FIFO_WR = const(0x054)
FIFO_RD = const(0x058)

def taskA(n, delay):
    cnt = 0
    while cnt < n:
        while (machine.mem32[SIO_NONSEC_BASE + FIFO_ST] & 2) == 0:
            time.sleep_ms(100)
        machine.mem32[SIO_NONSEC_BASE + FIFO_WR] = cnt + 1000
        cnt += 1
        time.sleep(delay)
    machine.mem32[SIO_NONSEC_BASE + FIFO_WR] = 0

def taskB():
    while True:
        while (machine.mem32[SIO_NONSEC_BASE + FIFO_ST] & 1) == 0:
            time.sleep_ms(100)    
        rdat = machine.mem32[SIO_NONSEC_BASE + FIFO_RD]
        if rdat != 0:
            print("  task B Received: {0}".format(rdat))
        else:
            break

def main():
    print("FIFO test: TaskA --FIFO--> TaskB")
    _thread.start_new_thread(taskA, (10, 1))
    taskB()
 
if __name__ == "__main__":
    main()
動作確認

2コアが協力して動作しているところが以下に。FifoTrial

ただし、MicroPython処理系も、Thonny IDEも想定外の使い方じゃないかと思うので、上記のコードの処理途中で止めようとしたりすると酷いことになったりします(まあ、電源断すりゃ終わるケド。)

実験は自己責任で。まあ、ヤバイところを触るほど面白れぇんだが。

Pico三昧(53) ラズパイPico2:RP2350、MicroPythonで2コア実行 へ戻る

Pico三昧(55) ラズパイPico2:RP2350、MicroPythonで排他制御再び へ進む