Pico三昧(14) PicoでもGo! TinyGoのオブジェクトがPico上で走るまで

Joseph Halfmoon

別シリーズでGo言語を勉強したりしているのですが、インタプリットもでき、バイナリにビルドもできでなかなか良いものです。マイクロコントローラでもGoのオブジェクトを走らせられるということで今回やってみましたです。ターゲットはラズパイPico。その母艦は例によってラズパイ4です。C/C++SDKと同様で実行可能。

マイクロコントローラ(MCU、マイコン)向けにオブジェクトを生成できる環境は以下のサイトから手に入ります。TinyGoというお名前です。クロスコンパイラというより、クロス開発環境といった感じです。CLIですが。

TinyGo – Go compiler for small places

母艦の上でGo言語のソースをビルドし、生成されたオブジェクトコードをターゲットのマイコンのFlashに書き込んで走らせるもの。ターゲットにインタプリタ処理系が載っていてソースを書き込むMicroPythonとはそのありかたが異なります。

上記ページを拝見すると手元のボードでもTinyGoのターゲットにできるものが多いことが分かりました。人気順にサポートしてる?今回は、以下の組み合わせでトライアルを行いたいと思います。

    • ターゲットボード Raspberry Pi Pico
    • 開発用のホスト Raspberry Pi 4 model B (Raspberry Pi OS 32bitで動作)

いつも本シリーズで、Raspberry Pi Pico C/C++ SDKを使ってC言語開発?に使っている組み合わせです。その用途むけに、USBで接続されているだけでなく、常時SWDデバッグおよびUARTも接続してあります(冒頭のアイキャッチ画像をご覧ください。)かなり配線重なってます。あまり場所を移動させたくない雰囲気ありあり(私がですが。)

Raspberry Pi 4へのTinyGoのインストール

Raspberry Pi OS機へのTinyGoのインストール手順は簡単です。以下のインストールページの中の、Linux、Ubuntu/Debian向けの手順のその中に、Raspberry Pi  OSへのTinyGoのインストール方法も記されています。同じLinuxでもx86(x64)向けとはバイナリ異なります。

Quick install

書かれている通りにインストールすれば、以下のように tinygo のバージョン表示を出すところまでは何も障害ありません。

TinyGoVersion

しかし、ここでトラブリました。TinyGoはインストールできて立ち上がってくるのに、テキトーなソースを書いてビルドしようとするとエラーがでます。GOROOTがなんたらとか、ファイルが見当たらんとかあれこれ。しかし真の原因は、上記のバージョンメッセージの中にありました。

using go version <unknown>

です。TinyGoは、Go言語の処理系がインストールされている前提でそれに依存しているので、手元のラズパイ4機のようにGo言語をインストールしてなかった装置では動作しないようです。先にGo言語本体をインストールしておかなければ。

なお、手元のラズパイ4機は32ビットOSのままで、busterです。

RPiOSversion

いつののように apt で go言語処理系をインストールしてみました。

$ sudo apt install golang

TinyGoのバージョンを確認してみると、Unkownが消え、以下のようになりました。

TinyGoVersionAgain

しかし、TinyGoでビルドしようとするとダメです。こんなエラーメッセージ。

TinyGoVersionError

つまり、Goの処理系が入っていれば良い、というもんじゃなく、Goの処理系は1.15 から 1.18 の間のバージョンでないとダメ、という御指定のようです。busterのリポジトリには古いGo 1.11しか入っていないみたいなのでダメだったと。

古いGoを一端Removeし、以下のGoのページから新しい処理系をダウンロードすることにいたしました。面倒くさがりなのでまず「バイナリ」でトライであります。このごろはソースからビルドしないでも済むことが多いので大助かりです。

Go Downloads

ダウンロードしたのは、1.18.1です。これなら文句あるまい、と。これの ARMv6 用のバイナリをダウンロードしてHomeディレクトリ配下の適当なところに解凍、パスを通してみました。Raspberry Pi 4のCPUそのものは、AArch64でARMv8ですが、Raspberry Pi OSの32ビット版は昔のRaspberry Pi(ARMv6)からサポートしているOSなので、ARMv6用がよろしかろう、という判断です。

パスを通せばGo言語そのものは立ち上がりました。またTinyGoのバージョン表示でも裏のGo 1.18.1が検出されているようです。こんな感じ。

TinyGoVersion118

そして、TinyGo の info コマンドで pico(TinyGoの世界では、Raspberry Pi Picoは「pico」と呼ばれているようです)の情報も取り出せました。

PicoInfo

とりあえずのビルド用のGoソース作成

ラズパイ4上で簡単なTinyGo用のソースを作成いたしました。ぐるぐる回りながら5秒に一回 Hello!と叫ぶもの。

package main

import (
  "time"
  "fmt"
)

func main() {
  var idx int = 0
  for {
    fmt.Print("Hello! ")
    fmt.Println(idx)
    time.Sleep(time.Millisecond * 5000)
    idx++
  }
}
ビルドそしてデバイスへの書き込みと動作確認

ビルド時のコマンドラインはこんな感じ。これで hello なるオブジェクトファイルができます。

$ tinygo build -target=pico -o hello ./hello.go

TinyGoのインストールの解説ページで説明されているシーケンスは、ラズパイPicoのボードを一端取り外して再度ドライブとして認識させ、これに書き込む方式でした。しかし当方手元のターゲットはデバッガ等のため配線多くて取り外したくないです。そこで、いつもC/C++SDKを使って作成したバイナリを書き込むときに使用しているデバッガ経由にてGoのオブジェクトの書き込みを行ってみました。

まず第1のターミナルウインドウで openocd を走らせておきます。

$ openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg

別に第2のターミナルウインドウで gdb を以下のように起動。その引数に生成したオブジェクトを与えてみました。大丈夫か?

$ gdb-multiarch ./hello

GDB起動後は、GDBのプロンプトから 別ウインドウで動作中のopenocdのサーバに接続し、loadコマンドを発行します。こんな感じ。

$ gdb-multiarch ./hello
~途中略~
(gdb) target remote localhost:3333
(gdb) load
Loading section .boot2, size 0x100 lma 0x10000000
Loading section .text, size 0x99bc lma 0x10000100
Loading section .tinygo_stacksizes, size 0x4 lma 0x10009abc
Loading section .data, size 0x550 lma 0x10009ac0
Start address 0x10002064, load size 40976
Transfer rate: 40 KB/sec, 6829 bytes/write.

ちゃんと、ラズパイPicoに書き込んでいるみたい。

TinyGoのfmt.Printは、Raspberry Pi PicoのUART0に向いているようなので、いつもやっている通りでminicomを /dev/ttyS0に接続すると以下のごとし。

Hello

GoのオブジェクトがHello!を叫んでいるようです。端末側の改行設定と合っていないので、「復帰」が抜けてますが。

なんとか、Goで書いて、ラズパイ4でビルドして、ラズパイPicoで実行できた、と。折角だから次はデバッグできることの確認か?大丈夫か?

Pico三昧(13) Pico C/C++SDKで 4.096MHzクロック生成その2PIO へ戻る

Pico三昧(15) PicoでもGo!Flash書き込み方法教えてもらった+VSCode へ進む