開發Raspberry Pi Pico 2的Arduino程式並比較ARM和RISC-V核心的效能

本文使用的Raspberry Pi Pico 2和Pico 2W開發板由台灣樹莓派Sosorry先生贊助提供,特此感謝!

Raspberry Pi Pico 2和Pico 2W是樹莓派(Raspberry Pi)公司推出,基於RP2350雙核心微控制器的開發板(以下簡稱Pico 2)。RP2350微控制器內部有雙核心的ARM Cortex-M33,以及另一個雙核心的Hazard3 RISC-V,開發人員可以自由選擇採用哪個架構的微控器來執行程式。本文將說明如何設定Arduino IDE 2和VS Code的“Arduino Maker Workshop”(創客工作坊)來開發Pico 2,並且透過執行梅森旋轉(Mersenne twister)演算法的程式比較ARM和RISC-V的效能。

Raspberry Pi Pico 2開發板的USB連接埠

Pico 2開發板的USB預設是大量儲存裝置模式,第一次連接電腦時,會呈現一個名叫“RP2350”的磁碟。

RP2350磁碟

在Windows的「裝置管理員」中,可看到它並未被指派序列連接埠,而是列舉在「通用序列匯流排裝置」底下。

裝置管理員

稍後上傳Arduino程式之後,Pico 2的USB介面就會切換為序列通訊模式。

使用Arduino IDE開發Pico 2專案

首先需要在Arduino IDE安裝支援Pico 2的開發板管理員。樹莓派公司沒有提供專門用於Arduino IDE的官方工具,本文採用的Arduino Pico專案是由Earle F. Philhower和社群人員基於官方Raspberry Pi SDK(支援C/C++語言)建立的開發板管理員,支援所有採用RP2040和RP2350微控制器的控制板,包含樹莓派官方的Pico, Pico W, Pico 2, Pico 2 W。

選擇Arduino IDE主功能表的「檔案→喜好設定」,在「其他開發板管理員網址」欄位,加入這個連結:

https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json

其他開發板管理員網址

接著,在「開發板管理員」面板查詢關鍵字“pico”,即可找到對應的開發環境:

開發板管理員

選擇Pico 2開發板

從Arduino IDE 2的「選擇開發板」選單可看到Pico板的UF2_Board名稱。點擊「選擇其他開發板及連接埠」:

選擇其他開發板及連接埠

搜尋“pico 2”關鍵字,選取“Raspberry Pi Pico 2”開發板,勾選右下角的「顯示所有連接埠」,然後點選“UF2 Board UF2 Devices”連接埠。

選擇其他開發板及連接埠

你也可以從「工具」主功能表選擇開發板和連接埠。

「工具」主功能表

用VS Code的“Arduino Maker Workshop”(創客工作坊)開發 Pico 2專案

若使用VS Code的“Arduino Maker Workshop”(創客工作坊)開發專案,請點擊「開發板管理員」,再點擊下方的“ADD URL”(新增URL)

Arduino Maker Workshop”(創客工作坊)

貼入Pico開發板管理員網址:

開發板管理員網址

按下Save儲存後,螢幕上會出現底下的訊息,請點擊“GO TO NOT INSTALLED TAB”(切換到未安裝標籤頁)

GO TO NOT INSTALLED TAB”(切換到未安裝標籤頁)

然後在“Not Installed”(尚未安裝)畫面,輸入“pico”關鍵字,安裝開發板。

“Not Installed”(尚未安裝)

安裝完畢後,即可在「選擇開發板」畫面選擇Pico 2開發板。

選擇開發板

編譯Pico 2程式

為了比較Pico 2 RP2350微控器內部的ARM和RISC-V的運算效能,此例採用實作梅森旋轉演算法(Mersenne twister)的隨機數生成器“Arduino-IDE-Benchmark”,它有32位元和64位元兩種隨機數字長度版本。底下程式透過測量產生1000筆64位元長度的隨機數所需時間,提供開發人員簡單的處理器效能評估基準。

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <inttypes.h>
#define N (624)
#define M (397)
#define K (0x9908B0DFU)
#define HighestBit(u) ((u) & 0x8000000000000000ULL)
#define LowestBit(u) ((u) & 0x0000000000000001ULL)
#define LowestBits(u) ((u) & 0x7FFFFFFFFFFFFFFFULL)
#define MixBits(u, v) (HighestBit(u)|LowestBits(v))
static uint64_t state[N + 1];
static uint64_t *nextRand;
static int left = -1;

uint32_t startTime;
uint32_t elapsedTime;

void Seed(uint64_t seed) {
   register uint64_t x = (seed | 1ULL) & 0xFFFFFFFFFFFFFFFFULL;
   register uint64_t *s = state;
   register int j;
   for (left = 0, *s++ = x, j = N; --j; *s++ = (x *= 69069ULL) & 0xFFFFFFFFFFFFFFFFULL);
}

void Reload() {
   register uint64_t *p0 = state;
   register uint64_t *p2 = state + 2;
   register uint64_t *pM = state + M;
   register uint64_t s0;
   register uint64_t s1;
   register int j;
   if (left < -1) {
      Seed(4357ULL);
   }
   left = N -  1;
   nextRand = state +  1;
   for (s0 = state[0], s1 = state[1], j = N - M +  1; --j; s0 = s1, s1 = *p2++) {
      *p0++ = *pM++ ^ (MixBits(s0, s1) >> 1) ^ (LowestBit(s1) ? K : 0ULL);
   }
   for (pM = state, j = M; --j; s0 = s1, s1 = *p2++) {
      *p0++ = *pM++ ^ (MixBits(s0, s1) >> 1) ^ (LowestBit(s1) ? K : 0ULL);
   }
   s1 = state[0];
   *p0 = *pM ^ (MixBits(s0, s1) >>  1) ^ (LowestBit(s1) ? K : 0ULL);
   s1 ^= (s1 >> 11);
   s1 ^= (s1 << 7) & 0x9D2C5680ULL;
   s1 ^= (s1 << 15) & 0xEFC60000ULL;
}

uint64_t Random() {
   uint64_t y;
   if (--left < 0) {
      Reload();
   }
   y = *nextRand++;
   y ^= (y >> 11);
   y ^= (y << 7) & 0x9D2C5680ULL;
   y ^= (y << 15) & 0xEFC60000ULL;

   return (y ^ (y >> 18));
}

uint64_t MersenneTwisterRandom(uint64_t seed) {
   Seed(seed);
   return Random();
}

void MTR_1000() {
   uint64_t initial_seed =  28378U;
   uint64_t first_seed = MersenneTwisterRandom(initial_seed);
   for (uint32_t i = 1; i <= 1e6; i++) {
      first_seed = MersenneTwisterRandom(first_seed);
      // printf("random value no. %08d is -- %" PRIu64 "\n", i, first_seed);
      // uncomment this if you need to display each number to serial
      // but this slows everything down a lot
   }
}

void setup() {
  Serial.begin(115200);
}

void loop() {
   startTime = millis();
   MTR_1000();
   elapsedTime = millis() - startTime;
   Serial.print("time elapsed for calculating 1k 64-bit random numbers ");
   Serial.print(elapsedTime);
   Serial.println(" ms");
}

上傳程式之前,可從主功能表「工具→CPU Architecture(處理器架構)」選擇ARMRISC-V處理器核心。

CPU Architecture(處理器架構)

選擇序列埠

程式編譯上傳完畢後,第一次切換到序列埠監控窗,將會出現底下的訊息,因為Pico 2開發板此時的USB介面已從儲存媒介切換成USB序列通訊模式。

序列埠監控窗

請從主能表「工具→連接埠」選擇Pico 2開發板的連接埠:

工具→連接埠

序列埠監控窗的「鮑率」選擇115200,等一會兒,即可看到生成1000個64位元長度隨機數字的花費時間。根據測試,ARM核心耗時124347毫秒,RISC-V核心耗時158033毫秒。

序列埠監控窗

Posts created 495

2 thoughts on “開發Raspberry Pi Pico 2的Arduino程式並比較ARM和RISC-V核心的效能

  1. 不好意思, 不知如何聯繫您, 所以在此留言.
    想邀請您到學校演講, 如果方便再請回覆。
    如果不便, 麻煩刪除此留言。謝謝

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top