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


はじめに

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

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

FreeRTOSとSDKの関係

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

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);

 ↓

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);

 ↓

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ミリ秒を単位時間としているのでシステムクロックの 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 */

 ↓

  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設定で1回の呼び出しで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()の測定結果
 

・133MHz設定時の test_delay()の測定結果(基準線は125MHz時)
 

・133MHz設定時の test_delay()の測定結果(基準線は133MHz時)
 

結果

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

100 /  94 * 100 ≒ 106.38 [%]

133 / 125 * 100 ≒ 106.40 [%]

さいごに

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

この点を注意しながらラズパイPicoRP2040133MHz動作を試されてはいかがでしょうか。

- 以上 -