TinyGo言語は、ハード依存性のあるMachineパッケージを使っていてもピン名を変更するくらいで別なマイコンへ移植可能なことが多くお楽。しかしディープに機種依存なハードを使うためにはハードウエアを直接操作しないとなりません。そんなときでもunsafeなポインタは「隠蔽」可能であります。今回はCPUID読み出し。
※「GoにいればGoに従え」Go関連記事の総Index
※実機動作確認は Arm Cortex-M0+コアのRP2040チップ搭載、Raspberry Pi Pico機にTinyGoのオブジェクトを書き込んで行っています。ビルドはWindows11上です。
※RP2040のデータシート(PDF)はこちら
rp2040.go
SPIとかI2Cとか、ほとんどのマイコンが共通して持っているペリフェラルについてはmachineパッケージがその差を隠蔽してくれています。ほぼほぼ統一的なマナーでプログラミングが可能です。しかし、機種依存性が「はなはだしい」部分については直接ハードウエアレジスタを操作せざるを得ません。C言語などであればポインタの出番となります。TinyGo言語においてもポインタはありますが、そういうポインタは unsafe ということで差別されてます。しかし、自力で勝手なアドレスにアクセスするのに比べれば、事前に「中の人」が作っておいてくれたポインタ(最後のところは unsafeですが)を使う方が安心であります。
過去記事で、SAMD21マイコン上でのTinyGoでもハードウエアレジスタの直接アクセスをやってみてます。
AT SAMの部屋(9) XiaoでもGo!TinyGoでレジスタ直接アクセス、大丈夫か?
今回のターゲット機は、RP2040マイコン搭載のRaspberry Pi Pico機であるので、RP2040用のAPI関数を呼び出さねばなりません。そのファイルは以下にあります。
Tinygoのインストールディレクトリ/src/device/rp/rp2040.go
「書き変えるな」とお達しが書き込まれた2Mバイト以上のサイズのある、巨大な自動生成のソースファイルです。この中にRP2040の内蔵するハードウエアレジスタのほぼほぼ全てにアクセスできるポインタ群が含まれており、また、それを使ってレジスタ・フィールドへのアクセスするAPI関数が定義されております。
今回はこのrp2040.goを使ってみます。Picoのハードウエアに直接アクセスする第1回としてCPUIDレジスタを読み出してみます。前にも読みだしている気がするのだけれども。なにせ「読み出し期待値」は分かっているので、ちゃんと読めたことが即座に分かるという段取り。
なお、rp2040.go パッケージを使うときは importのところで以下のようにするだけで使えます。お楽。
import { "device/rp"
CPUIDとCHIP_ID
紛らわしいので、このボケ老人はときどきこんがらがるのです。RP2040マイコンには似た名前のハードウエア・レジスタが2個あります。
-
- CPUID
- CHIP_ID
CPUIDの方は、RP2040マイコンのコアである Arm社設計Cortex-M0+のレジスタです。Arm社が既定のもの。ソフトが走っているコアがどのようなコアであるのかをソフトウエアで判別することなどに使えます。Cortex-M0+の場合、組み込み向けのARMv6-Mというアーキテクチャであるのでシンプルです。1個の32ビットレジスタに以下の5フィールドが書き込まれてます(リード・オンリ。)
-
- IMPLEMENTER、 0x41 … Armを表す
- VARIANT、 0x0 … Revision 0
- ARCHITECTURE、 0xC … ARMv6-M
- PARTNO、 0xC60 … Cortex-M0+
- REVISION、 0x1 … Patch 1
-
- REVISION(4bit)
- PART(16bit)
- MANUFACTURER(12bit)
今回実験に使ったソースコード
読み出したハードウエアレジスタのフィールド値を繰り返し表示するだけのコードが以下に。
package main import ( "device/rp" "fmt" "time" ) func main() { cpuidIMP := rp.PPB.GetCPUID_IMPLEMENTER() cpuidVAR := rp.PPB.GetCPUID_VARIANT() cpuidARCH := rp.PPB.GetCPUID_ARCHITECTURE() cpuidPNO := rp.PPB.GetCPUID_PARTNO() cpuidREV := rp.PPB.GetCPUID_REVISION() chipREV := rp.SYSINFO.GetCHIP_ID_REVISION() chipPART := rp.SYSINFO.GetCHIP_ID_PART() chipMANU := rp.SYSINFO.GetCHIP_ID_MANUFACTURER() for { fmt.Println("CPUID") fmt.Printf("IMPLEMENTER: %08x\n", cpuidIMP) fmt.Printf("VARIANT: %08x\n", cpuidVAR) fmt.Printf("ARCHITECTURE: %08x\n", cpuidARCH) fmt.Printf("PARTNO: %08x\n", cpuidPNO) fmt.Printf("REVISION: %08x\n", cpuidREV) fmt.Println("CHIP_ID") fmt.Printf("REVISION: %08x\n", chipREV) fmt.Printf("PART: %08x\n", chipPART) fmt.Printf("MANUFACTURER: %08x\n", chipMANU) time.Sleep(time.Second * 4) } }
実機実行結果
目出度くハードウエアレジスタの値は読み出し成功しているみたい。
次回以降、ハードウエアレジスタを直接操作して何かやれよ、自分。