Pico三昧(7) Pico C/C++SDKでinterp その1、popとpeek

Joseph Halfmoon

ラズパイPicoの特長的なハードウエアのひとつが interpolator です。その名からすると補間をしてくれるハードウエアですが、実際にはいろいろな目的に使えそうなデータパス、計算エンジンと言う感じです。RP2040の2個あるコアに2個づつ合計4個のinterpolatorが搭載されています。今回は pop と peek 2つの基本操作の違いを見てみます。

ハードウエア interpolator は、ラズパイPicoに搭載されているRP2040マイコンでは、プロセッサに近いところにあるSIO(Single-cycle IO block)という構造の中に含まれます。ここに含まれるのはコア間FIFO、ハードウエア・スピンロック、割り算器などの高速性が要求される「スペシャル」なものどもばかりです。

C/C++SDKのドキュメントには、APIの説明は書かれていますが、APIの説明読んでも使い方は良く分かりません。RP2040のデータシートのSIOの中に説明があるので、それを読むべきなようです。

RP2040 Datasheet

上記のデータシートの中に詳しい説明があります。interpolator についての勝手な理解を書かせていただきます。

  1. コア1個に2個のinterpolatorが接続されている。
  2. 1個のinterpolatorに0,1,2の3個のレーンが存在する。
  3. 各レーンには、データの入口と出口がある
  4. 0/1レーンの入口と出口の間では、加算、右シフト(2のべき乗の割り算)、マスク、符号拡張などが行えるとともにアキュムレータ(積算器)が使える。
  5. データの通過経路は複数のマルチプレクサで制御できる
  6. 2レーンは加算器のみ

「多分」補間にも使えるのですが、さてこれを何に使ったら良いでしょうか?という感じです。うまい具合に設定すれば、毎サイクルデータを書き込んで、そして読み出すだけで、次々と処理済のデータが出てくるので超強力な演算器となるでしょう。

ハードウエアinterpolatorを使用する始めの一歩

例によって Raspberry Pi Pico Project Generator を使い、HW interpolation を使用にチェックを入れてProjectを生成すれば(冒頭のアイキャッチ画像参照)、以下のような雛形コードが自動生成されます(以下では VSCode 使用しています。)

sourceGenerated
上記のデフォルト設定では、インターポレータ内部のシフトとかマスクとかはバイパスされているみたいですが、加算器は必ず通るので使うことができます。そして、ソフトウエア上の重要なオペレーションが加算結果に対する以下2つの操作であるようです。

  1. POP
  2. PEEK

POPは加算器から読み出した結果をプログラムで取得するだけでなく、レーン内部にあるアキュムレータに書き戻す操作です。よってデータをPOPし続けると次々と加算が起こるはず。

これに対してPEEKは、加算器から読み出した結果を取得するだけで、アキュムレータには書き戻さない操作です。

そこで以下のサンプルプログラムを書いてみました。

POPの方は、アキュムレータを0クリアした後、データの入口のBaseレジスタに3をたてておき、POPを繰り返します。すると3から始まり公比3の等差数列が得られる筈。

PEEKの方も、アキュムレータを0クリアし、Baseに3を立てますが、PEEK操作なので、ずっと3が読める筈。

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/interp.h"

volatile int counter = 1;

int main()
{
    const uint LED_PIN = 25;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);
    stdio_init_all();

    interp_config cfg = interp_default_config();
    interp_set_config(interp0, 0, &cfg);

    printf("Interp training 001\n");
    while (1) {
        interp0->accum[0] = 0;
        interp0->base[0] = 3;
        printf("+3 Series (%d):", counter);
        for (int i = 0; i < 5; i++) {
            printf(" %d", interp0->pop[0]);
        }
        printf("\n");
        gpio_put(LED_PIN, 1);
        sleep_ms(500);
    
        interp0->accum[0] = 0;
        interp0->base[0] = 3;
        printf("Peek Series (%d):", counter++);
        for (int i = 0; i < 5; i++) {
            printf(" %d", interp0->peek[0]);
        }
        printf("\n");
        gpio_put(LED_PIN, 0);
        sleep_ms(500);
    }

    return 0;
}

「Lチカ」もさせていますが、ボードを目でみて動作していることを確認するためです。

上記コードをビルドしたときに使った CMakeLists.txt が以下に。

# Generated Cmake Pico project file

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# initalize pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/pi/pico/pico-sdk")

# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(interpTRAIN C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(interpTRAIN interpTRAIN.c )

pico_set_program_name(interpTRAIN "interpTRAIN")
pico_set_program_version(interpTRAIN "0.1")

pico_enable_stdio_uart(interpTRAIN 1)
pico_enable_stdio_usb(interpTRAIN 0)

# Add the standard library to the build
target_link_libraries(interpTRAIN pico_stdlib)

# Add any user requested libraries
target_link_libraries(interpTRAIN
        hardware_interp
        )

pico_add_extra_outputs(interpTRAIN)
動作確認

標準出力にでてきた結果が以下に。+3 SeriesがPOP操作、Peek SeriesがPEEK操作です。

Result
予定通り、POP操作であると等差数列が得られ、PEEK操作だと変わらぬ値が読み出されます。

さて、次回は内部の演算要素やマクチプレクサにコンフィギュレーションをかけながら、何に使えそうか考えていきたいと思います。

Pico三昧(6) Pico C/C++SDKでS93C46接続その2、PIO制御 へ戻る

Pico三昧(8) Pico C/C++SDKでinterp その2、DeadBeef例題 へ進む