ラズパイPicoの動作クロック変更

##はじめに
電子工作界隈で話題になっている**Cortex-M0+**をコアにもつ**RP2040**を搭載した**Raspberry Pi Pico**（以後、ラズパイPico）ですが、公式に提供されている**Pico-SDK**の初期化では動作クロックが**125MHz**になっています。ただし、チップとしては**133MHz**まで使えることになっているので、インターフェース誌 2021年８月号で私が紹介した**FreeRTOS**の動作クロックを変更してみました。

・インターフェース誌 2021年８月号
　　https://interface.cqpub.co.jp/magazine/202108/
　FreeRTOSの記事のサンプル
　https://interface.cqpub.co.jp/wp-content/uploads/if2108_103.pdf

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/29ae3405-a3cb-6ad6-d597-857994143220.jpeg" width=500>

##FreeRTOSとSDKの関係
インターフェース誌で紹介したラズパイPico用のFreeRTOSは以下の３つのプログラムで構成されています。

・https://github.com/PicoCPP/RPI-pico-FreeRTOS
　FreeRTOS本体とPico-SDKを組み合わせるための定義やサンプルプログラム
・https://github.com/FreeRTOS/FreeRTOS-Kernel
　FreeRTOS本体
・https://github.com/raspberrypi/pico-sdk.git
　Pico-SDK

```
|--RPI-pico-FreeRTOS
|  |　　→https://github.com/PicoCPP/RPI-pico-FreeRTOS
|  |--.git
|  |--.gitignore
|  |--.gitmodules
|  |--CMakeLists.txt
|  |--Dockerfile
|  |--FreeRTOS-Kernel
|  |　　→ https://github.com/FreeRTOS/FreeRTOS-Kernel
|  |--README.md
|  |--include
|  |--pico-cpp
|  |--pico-sdk
|  |　　→https://github.com/raspberrypi/pico-sdk.git
|  |--src
```

SDKで提供されるベクタテーブルのリセットハンドラから以下のような流れでFreeRTOSの起動していきます。

```
pico-sdk/src/rp2_common/pico_standard_link/crt0.S
　_reset_handler:
　　↓
pico-sdk/src/rp2_common/pico_runtime/runtime.c
　runtime_init()
　　↓
pico-sdk/src/rp2_common/hardware_clocks/clocks.c
　clocks_init()
　　↓
RPI-pico-FreeRTOS/src/main.cpp
　main
　　↓
　xTaskCreate()
　vTaskStartScheduler()
```

FreeRTOSはライブラリとして提供され**main**関数内でタスクの生成やスケジューラーが起動されます。

##クロックの設定
起動処理中にラズパイPicoのクロックは**clocks_init**関数の中で設定されています。

``` clocks.c
145     /// \tag::pll_settings[]
146     // Configure PLLs
147     //                   REF     FBDIV VCO            POSTDIV
148     // PLL SYS: 12 / 1 = 12MHz * 125 = 1500MHZ / 6 / 2 = 125MHz
149     // PLL USB: 12 / 1 = 12MHz * 40  = 480 MHz / 5 / 2 =  48MHz
150     /// \end::pll_settings[]
151
152     reset_block(RESETS_RESET_PLL_SYS_BITS | RESETS_RESET_PLL_USB_BITS);
153     unreset_block_wait(RESETS_RESET_PLL_SYS_BITS | RESETS_RESET_PLL_USB_BITS);
154
155     /// \tag::pll_init[]
156     pll_init(pll_sys, 1, 1500 * MHZ, 6, 2);　・・・(1)
157     pll_init(pll_usb, 1, 480 * MHZ, 5, 2);　・・・(2)
158     /// \end::pll_init[]
```

この中の**(1)**の部分でシステムクロックが設定されています。

```
1500MHZ / 6 / 2 = 125MHz
```

RP2040の最高周波数 133MHzにするには、この式の「**1500**」を設定することで変更できます。

```
133 * 6 * 2 = 1596
```

**133MHz**にするには**(1)**の行の「1500」を「**1596**」へ変更します。

```
    pll_init(pll_sys, 1, 1596 * MHZ, 6, 2);
```


ここでシステムクロックが 125MHzから**133HMz**へ変更したため、125MHzを期待して作られている処理を変更する必要があります。
変更箇所は、clocks_init関数の以下の箇所になります。

・システムのクロック値

``` clocks.c
169     // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
170     clock_configure(clk_sys,
171                     CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
172                     CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
173                     125 * MHZ,
174                     125 * MHZ);
```
　↓

``` C++
169     // CLK SYS = PLL SYS (133MHz) / 1 = 133MHz
170     clock_configure(clk_sys,
171                     CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
172                     CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
173                     133 * MHZ,
174                     133 * MHZ);
```

・ペリフェラルのクロック値

``` clocks.c
198     // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
199     // Normally choose clk_sys or clk_usb
200     clock_configure(clk_peri,
201                     0,
202                     CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
203                     125 * MHZ,
204                     125 * MHZ);
```
　↓

``` C++
198     // CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
199     // Normally choose clk_sys or clk_usb
200     clock_configure(clk_peri,
201                     0,
202                     CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
203                     133 * MHZ,
204                     133 * MHZ);
```

##FreeRTOSのタイマ設定
OSはタイマー割込みを使って単位時間を決めている。FreeRTOSも同様でタスクの待ち時間などの単位時間として用いられます。
**RP2040**では **Cortex-M0+**が内蔵する **systickタイマ**を使っているのでシステムクロックsystickタイマの入力となり、システム周波数がタイマの動作周波数となっています。
FreeRTOSは１ミリ秒を単位時間としているのでシステムクロックの 1/1000をタイマのカウント値としています。

実際には **include/FreeRTOSConfig.h**内で定義されている。
システムクロックが**133MHz**になるので、タイマのカウント値も以下のように変更します。


``` include/FreeRTOSConfig.h
  8 #define configCPU_CLOCK_HZ                      125000000/* Looking at runtime.c in the RPI 2040 SDK, the sys clock frequency is 125MHz */
```
　↓

``` C++
  8 #define configCPU_CLOCK_HZ                      133000000/* Looking at runtime.c in the RPI 2040 SDK, the sys clock frequency is 133MHz */
```


##検証
Pico-SDKとFreeRTOSの設定の変更前後で**RP2040**の動作速度がどのように変わるか確認します。
**RP2040**の動作速度がわかるように以下の何も処理しないループ（ビジーループ）を作成します。**125MHz**設定で１回の呼び出しで100マイクロ秒となるよう調整してループ回数を決めてあります。命令の実行時間が125MHz（1/125M 秒）単位となるので、ループ回数が1250回となったようです。ちなみにコンパイラの最適化により処理が消されないようループに用いる変数を**volatile**宣言してあります。

``` main.cpp
volatile  int  conter1;

static  inline  void  test_delay( void )
{
  for( conter1=0; conter1<1250 ; conter1++ ) {
  }
}
``` 

先に作成した**test_delay()**の呼び出し前後で**GPIO**を H/L (1/0)する処理を入れ、それを延々と繰り返すようにしました。
この GPIOをロジアナなどで観察されば**RP2040**の動作速度がわかります。


``` main.cpp
void vTaskCode( void * pvParameters )
{
 :
    for( ;; )
    {
      int  i;

      gpio_put(PIN_ONLED,1);
      for( i=0 ; i<500 ; i++ ) {
        gpio_put(PIN_GPIO0,1);
        test_delay();
        gpio_put(PIN_GPIO0,0);
        test_delay();
      }

      gpio_put(PIN_ONLED,0);
      for( i=0 ; i<500 ; i++ ) {
        gpio_put(PIN_GPIO0,1);
        test_delay();
        gpio_put(PIN_GPIO0,0);
        test_delay();
      }
    }

``` 

ロジアナの測定結果は以下のようになりました。

・125MHz設定時の test_delay()の測定結果
　![qiita_pico_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/dcb961cd-c732-141a-8b0d-18d3fac809bf.jpeg)

・133MHz設定時の test_delay()の測定結果（基準線は125MHz時）
　![qiita_pico_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/fa8b6177-6831-0f3c-cee7-3df66f2ef7a1.jpeg)

・133MHz設定時の test_delay()の測定結果（基準線は133MHz時）
　![qiita_pico_05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/b609a4c0-d14d-cfd1-95c0-5fbbf6f5f8de.jpeg)

##結果
ロジアナの結果からも 125MHzから133MHz設定へ変更したことで同じ処理（命令）の動作時間が早くなったことが確認できました。
ロジアナの結果ではタイマ割込みなどの影響からか多少の変化が出ますが、おおむね**106%**程度の速度の向上が見られました。これは動作周波数の違いと同等になることから、125MHzから 133MHzへの設定変更ができたと考えます。

``` 
100 /  94 * 100 ≒ 106.38 [%]

133 / 125 * 100 ≒ 106.40 [%]
``` 

##さいごに
今回はインターフェース誌で紹介した FreeRTOSで試しましたが、**RP2040**の初期化処理に**Pico-SDK**を使ったプログラムであれば、
**clocks.c**の**clocks_init()**内の設定値を変更することで性能の向上が見込めます。
ただし、ハードウェアのアクセスや通信速度などで 125MHzのシステムクロックを期待している処理の場合、誤動作の原因となるので注意が必要です。

この点を注意しながら**ラズパイPico**や**RP2040**の**133MHz**動作を試されてはいかがでしょうか。

－ 以上 －