NuttX for Raspberry Pi PicoのSMP対応

[Raspberry Pi PicoでNuttXを動かす](https://qiita.com/yunkya2/items/55f1222e8b2b903dd7ff) で動かしたNuttXをSMPに対応させました。

**【追記】hackster.io で紹介されました。
[The Raspberry Pi Pico, and RP2040, Get Full Dual-Core SMP Support in Apache's NuttX RTOS Port](https://www.hackster.io/news/the-raspberry-pi-pico-and-rp2040-get-full-dual-core-smp-support-in-apache-s-nuttx-rtos-port-7ab07a04060c)**

PicoはCortex-M0+を2つ搭載していますが、通常の動作ではそのうち1つのCPUしか使われず、もう1つのCPUはPico SDKの[pico_multicore API](https://raspberrypi.github.io/pico-sdk-doxygen/group__pico__multicore.html)を使って明示的に動かしてやる必要がありました。
NuttX SMPは一般のPC用OSのようにスケジューラが複数コアを管理するので、複数タスク、スレッドの動作時にOSがその実行を各コアに割り振ってくれます。

# コード取得とビルド

Raspberry Pi Pico SMP対応も[公式リポジトリ](https://github.com/apache/incubator-nuttx)に[マージ](https://github.com/apache/incubator-nuttx/pull/2916)されました。

環境準備やビルドの手順は[以前](https://qiita.com/yunkya2/items/55f1222e8b2b903dd7ff)と同じです。唯一、ビルド時のコンフィグレーションが`raspberrypi-pico:nsh`から`raspberrypi-pico:smp`に変わります。

```
$ git clone https://github.com/apache/incubator-nuttx.git nuttx
$ git clone https://github.com/apache/incubator-nuttx-apps.git apps
$ cd nuttx
$ ./tools/configure.sh raspberrypi-pico:smp
$ make
```

# 実行

起動してpsを見て見ると、以下のように表示内容に"CPU"の項目が増えて、2つのCPUがそれぞれアイドルタスクを実行していることが分かります。

```
NuttShell (NSH) NuttX-10.0.1
nsh>
nsh> ps
  PID CPU PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK   STACK COMMAND
    0   0   0 FIFO     Kthread N-- Assigned           00000000 001024 CPU0 IDLE
    1   1   0 FIFO     Kthread N-- Running            00000000 002052 CPU1 IDLE
    3   0 100 RR       Task    --- Running            00000000 002036 init
nsh>
```

`smp`コマンドで、多数のスレッドを作ってSMP動作を確認するテストが走ります。

```
nsh> smp
  Main[0]: Running on CPU0
  Main[0]: Initializing barrier
Thread[1]: Started
  Main[0]: Thread 1 created
Thread[1]: Running on CPU0
  Main[0]: Now running on CPU1
Thread[2]: Started
Thread[2]: Running on CPU1
  Main[0]: Thread 2 created
  Main[0]: Now running on CPU0
Thread[3]: Started
...
Thread[2]: Now running on CPU0
Thread[6]: Done
  Main[0]: Thread 1 completed with result=0
Thread[2]: Done
Thread[3]: Now running on CPU0
Thread[7]: Now running on CPU0
Thread[6]: Now running on CPU0
Thread[2]: Now running on CPU1
  Main[0]: Now running on CPU1
  Main[0]: Thread 2 completed with result=0
  Main[0]: Thread 3 completed with result=0
  Main[0]: Thread 4 completed with result=0
  Main[0]: Thread 5 completed with result=0
  Main[0]: Thread 6 completed with result=0
  Main[0]: Thread 7 completed with result=0
  Main[0]: Thread 8 completed with result=0
nsh>
```

`getprime`で、素数を求めるベンチマークを引数で指定したスレッド数同時に走らせます。

```
nsh> getprime 2
Set thread priority to 10
Set thread policy to SCHED_RR
Start thread #0
thread #0 started, looking for primes < 10000, doing 10 run(s)
Start thread #1
thread #1 started, looking for primes < 10000, doing 10 run(s)
thread #0 finished, found 1230 primes, last one was 9973
thread #1 finished, found 1230 primes, last one was 9973
Done
getprime took 14440 msec
nsh>
```

SMPを有効にしなかった場合は、各スレッドを同じCPUで処理するため倍近くの時間がかかります。

```
nsh> getprime 2
Set thread priority to 10
Set thread policy to SCHED_RR
Start thread #0
Start thread #1
thread #2 started, looking for primes < 10000, doing 10 run(s)
thread #2 started, looking for primes < 10000, doing 10 run(s)
thread #2 finished, found 1230 primes, last one was 9973
thread #2 finished, found 1230 primes, last one was 9973
Done
getprime took 25610 msec
nsh>
```

RP2040にはCPUキャッシュがないので、2つのCPUが同時に走るとメモリアクセスが競合しまくるはずですが、この状況で 25610/14440 = 約1.8倍程度のパフォーマンスが出るのは、SMPシステムとしてはかなり優秀ではないでしょうか。