前回はMicroPython処理系が備えているlockをつかって2コアの排他制御を試みました。今回はラズパイPico/Pico2に特有なハードウエア・スピンロック機構を使ってみます。RP2040のときには有用であったHWスピンロックですが、RP2350では後ろ向きの機能になってます。でも使えることに変わりありませぬ。
※Pico関係投稿一覧は こちら 『Pico三昧』は一覧の末尾付近にひっそりと。
※Pico2対応のMicroPython処理系(バイナリ、uf2形式)は以下のURLからダウンロード可能です。
https://micropython.org/download/RPI_PICO2/
※動作確認に使用しているMicroPython処理系は以下です。
MicroPython-1.24.0-riscv–with-newlib4.3.0
上記はRISC-Vコア用のバイナリですが、Arm用でも同様に動くかと(確かめてないケド。)
HWスピンロック
ラズパイPicoのRP2040ではArm Cortex-Mシリーズの末弟 Cortex-M0+を2個搭載してました。もともとマルチコアを狙っていないと思われるM0+は排他制御向けのATOMIC命令を備えていませぬ。ATOMIC命令なしに「カッコよく、効率的な」制御を実現するためだと思いますが、RP2040の2コア間に存在するSIO(シングル・サイクルIO)ブロック内に32個のHW spinlock機構が備えつけられています。
一方、ラズパイPicoのRP2350では、Arm Cortex-M33も、RISC-V Hazard3も、ATOMIC命令を備えてます。フツーのメモリ上でフツーに「効率的な」排他制御機構を実装できる筈。よってRP2350では、わざわざHW spinlock機構を使わなくてもよいと思われます。しかし、互換性のためにこの機構はRP2350でも生かされておるようです。
そこで今回は「前回 MicroPython処理系の lock 機構で行った制御をHWスピンロックで書き替えたらどうよ」ということで実験してみます。
なお、以下のように考えました。
-
- RP2350のHWスピンロック機構は、セキュア/ノンセキュアで2重化されている
- MicroPython処理系上のスクリプトは「ノンセキュア」な側で動作しているみたい
- よって「ノンセキュア」な側のHWスピンロック機構を使用する
- 過去回のPicoで行った実験では、MicroPython処理系自身がHWスピンロック機構を使う場合がある(目撃したのは真ん中ちかくの2つ)
- そこで「被らない」ように一番端の0番のHWスピンロック機構を指定してみる(ソース読んだわけでないので、被るかもだけれども。)
今回実験のMicroPythonコード
前回実験のコード(_Threadモジュール内のlockを使用)を、HWスピンロックで書き替えただけのものです。
import time, machine, _thread from micropython import const SIO_BASE = const(0xd0000000) SIO_NONSEC_BASE = const(0xd0020000) SPINLOCK_ST = const(0x05c) SPINLOCK0 = const(0x100) def taskA(): cnt = 0 while cnt < 10: while machine.mem32[SIO_NONSEC_BASE + SPINLOCK0] == 0: pass print("Task A gets HW spinlock 0.") for i in range(1, 10): print(i+100," ",end="") print() print("Task A SPINLOCK_ST: {0:08x}".format(machine.mem32[SIO_NONSEC_BASE + SPINLOCK_ST])) machine.mem32[SIO_NONSEC_BASE + SPINLOCK0] = 1 time.sleep(0.5) cnt += 1 _thread.exit() def taskB(): cnt = 0 while cnt < 20: while machine.mem32[SIO_NONSEC_BASE + SPINLOCK0] == 0: pass print("Task B gets HW spinlock 1.") for i in range(1, 10): print(i+200," ",end="") print() print("Task B SPINLOCK_ST: {0:08x}".format(machine.mem32[SIO_NONSEC_BASE + SPINLOCK_ST])) machine.mem32[SIO_NONSEC_BASE + SPINLOCK0] = 1 time.sleep(0.5) cnt += 1 print("END.") def main(): print("HW spinlock test:") _thread.start_new_thread(taskA, ()) taskB() if __name__ == "__main__": main()
実機実験結果
上記のソースをPico2上で走らせた様子(の上の方)が以下に。
HWスピンロック機構を使っても、交通整理は上手くいっておるようです。