みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

Yocto bitbake-layers layerindex-fetch

はじめに

bitbake-layersコマンドの便利なサブコマンドを見つけた(認識した)。

レイヤ間の依存関係

bitbakeで取り扱うレイヤではlayer.confに下記のようにLAYERDEPENDSを記述することで、依存関係を定義することができる。

LAYERDEPENDS_gnome-layer = "core openembedded-layer networking-layer"

しかし、bitbake-layers add-layerをする場合、依存関係の順番を間違えると面倒なことになる。

$ bitbake-layers add-layer ../poky/meta-openembedded/meta-gnome

一見問題なく追加できるが、次回以降bitbake-layersのコマンドを実行すると下記のようにエラーが発生する。

$ bitbake-layers show-layers
NOTE: Starting bitbake server...
Traceback (most recent call last):
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/bin/bitbake-layers", line 95, in <module>
    ret = main()
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/bin/bitbake-layers", line 63, in main
    tinfoil.prepare(True)
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/tinfoil.py", line 414, in prepare
    self.run_command('parseConfiguration')
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/tinfoil.py", line 470, in run_command
    raise TinfoilCommandFailed(result[1])
bb.tinfoil.TinfoilCommandFailed: Traceback (most recent call last):
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/command.py", line 74, in runCommand
    result = command_method(self, commandline)
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/command.py", line 275, in parseConfiguration
    command.cooker.parseConfiguration()
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/cooker.py", line 433, in parseConfiguration
    self.handleCollections(self.data.getVar("BBFILE_COLLECTIONS"))
  File "/home/mickey/work/yocto/x86-dunfell/layers/poky/bitbake/lib/bb/cooker.py", line 1225, in handleCollections
    raise CollectionError("Errors during parsing layer configuration")
bb.cooker.CollectionError: Errors during parsing layer configuration

これはmeta-gnomeが依存しているレイヤがないため発生するのだが、この状態になるとテキストエディタなどでbblayers.confを修正する必要がある。

meta-gnomeに記載されている依存関係は下記。

  1. core
  2. openembedded-layer
  3. networking-layer

しかしここで厄介なのが、networking-layerがmeta-pythonに依存しているためそちらも記述しなければならない。 このようなネストした依存関係がある場合には、1つずつ追加していくのが面倒となってくる。

bitbake-layers layerindex-fetch

レイヤ間の依存関係をプログラム的にチェックできるのであれば、追加する前にエラーにするか、自動的に追加するなどしてほしいと思っていた。 しかし、layerindex-fetchサブコマンドを使用すると自動的に依存関係を解決して、必要であればレイヤのダウンロードまでおこなってくれる。

$ bitbake-layers layerindex-fetch meta-gnome
NOTE: Starting bitbake server...
Loading https://layers.openembedded.org/layerindex/api/;branch=dunfell...
Layer                                              Git repository (branch)                                 Subdirectory
=============================================================================================================================
local:HEAD:openembedded-core                       git://git.yoctoproject.org/poky.git (dunfell)           meta
  required by: meta-gnome meta-networking meta-python meta-oe
layers.openembedded.org:dunfell:meta-oe            git://git.openembedded.org/meta-openembedded (dunfell)  meta-oe
  required by: meta-python meta-networking meta-gnome
layers.openembedded.org:dunfell:meta-python        git://git.openembedded.org/meta-openembedded (dunfell)  meta-python
  required by: meta-networking
layers.openembedded.org:dunfell:meta-networking    git://git.openembedded.org/meta-openembedded (dunfell)  meta-networking
  required by: meta-gnome
layers.openembedded.org:dunfell:meta-gnome         git://git.openembedded.org/meta-openembedded (dunfell)  meta-gnome
Cloning into '/home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded'...
remote: Counting objects: 148262, done.
remote: Compressing objects: 100% (49118/49118), done.
remote: Total 148262 (delta 92673), reused 147741 (delta 92348)
Receiving objects: 100% (148262/148262), 39.27 MiB | 3.67 MiB/s, done.
Resolving deltas: 100% (92673/92673), done.
Adding layer "meta-oe" (/home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-oe) to conf/bblayers.conf
Adding layer "meta-python" (/home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-python) to conf/bblayers.conf
Adding layer "meta-networking" (/home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-networking) to conf/bblayers.conf
Adding layer "meta-gnome" (/home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-gnome) to conf/bblayers.conf

レイヤの設定状況を確認する。

$ bitbake-layers show-layers
NOTE: Starting bitbake server...
layer                 path                                      priority
==========================================================================
meta                  /home/mickey/work/yocto/x86-dunfell/layers/poky/meta  5
meta-poky             /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-poky  5
meta-yocto-bsp        /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-yocto-bsp  5
meta-oe               /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-oe  6
meta-python           /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-python  7
meta-networking       /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-networking  5
meta-gnome            /home/mickey/work/yocto/x86-dunfell/layers/poky/meta-openembedded/meta-gnome  7

meta-gnomeが依存する全てのレイヤが追加されている。

自動的にダウンロードできるレイヤについて

レイヤのダウロードにはhttps://layers.openembedded.org/layerindex/api/というWeb APIを使用している。

そのため、OpenEmbedded Layer Indexに登録されているもののみが対象となっていると推測される。

既存の設定について

layerindex-fetchでは既に追加してあるレイヤに関しては削除されないため、ローカルで開発中のレイヤなどがBBLAYERSに既にある場合でも問題なく使用することができる。

ブランチ

下記のようにすることで、ダウンロードする際に使用するブランチを指定することができる。

$ bitbake-layers layerindex-fetch meta-gnome -b dunfell

ブランチを指定しない場合は、現在チェックアウトしているpokyのブランチがデフォルトとなる。

例えば、pokyがgatesgarthである時にブランチを指定せずにlayerindex-fetchを実行するとgatesgarthブランチが選択される。

また、レイヤ側がmasterブランチしか持っていない場合などに、pokyがdunfellだった場合ではデフォルト指定ではエラーになる。

$ bitbake-layers layerindex-fetch meta-efi-secure-boot
NOTE: Starting bitbake server...
Loading https://layers.openembedded.org/layerindex/api/;branch=dunfell...
ERROR: Layer "meta-efi-secure-boot" not found in layer index

そのような場合にはブランチを明示する必要がある。

$ bitbake-layers layerindex-fetch meta-efi-secure-boot -b master

まとめ

bitbake-layers layerindex-fetchは便利。

Yocto meta-gnomeでpolkitのエラー

はじめに

デフォルトの設定でmeta-gnomeをビルドするとエラーが発生する。

エラーの内容

gnome-control-centerとsystmedがそれぞれで/usr/share/polkit-1/rules.dをインストールしようとしてコンフリクトしている。

Error: Transaction test error:
  file /usr/share/polkit-1/rules.d conflicts between attempted installs of gnome-control-center-3.36.4-r0.cortexa7t2hf_neon_vfpv4 and systemd-1:246.9-r0.cortexa7t2hf_neon_vfpv4

設定ファイル自体がかぶってエラーになるならともかく、格納場所であるディレクトリがかぶってエラーになってしまうのはどうかと思う。

原因

meta-gnomeはmeta-openembeddedの持ち物なので、こんな基本的なところがメンテナンスされないことはないだろうと思いつつ調べていくと、ここで似たようなエラーメッセージが引っかかった。

これはPC(サーバ)用のLinux上でrpmの処理中に似たような状況になっている。

これはbitbakeのエラーではなくRPMの制限なのかと推測した。

回避策

local.confに下記を追加し、パッケージシステムをrpmからdebにする。

PACKAGE_CLASSES = "package_deb"

まとめ

meta-gnomeを使う場合はパッケージシステムはRPM以外にする。

Raspberry Pi Pico Systick割り込みを使ってLチカをFlashに書く

はじめに

前回作ったプログラムをFlashから実行できるようにしてみる。

Flash対応

Makefile

diff -Narubp blink_ram_int_systick/Makefile blink_flash_int_systick/Makefile
--- blink_ram_int_systick/Makefile    2021-02-27 13:21:52.160613474 +0900
+++ blink_flash_int_systick/Makefile  2021-02-28 07:45:26.010697915 +0900
@@ -7,20 +7,26 @@ ASMOPT = $(MCPU) -g
 COPT = $(MCPU) -ffreestanding -g -O0
 LOPT = -nostdlib -nostartfiles
 
-all: ram
+all: led.uf2
 
 clean:
    rm -f *.o
    rm -f *.elf
    rm -f *.list
+   rm -f *.uf2
    rm -f *~
 
 start.o: start.S
    $(CROSS_COMPILE)as $(ASMOPT) start.S -o start.o
 
+boot2.o: boot2/bs2_default_padded_checksummed.S
+   $(CROSS_COMPILE)as $(ASMOPT) boot2/bs2_default_padded_checksummed.S -o boot2.o
+
 main.o: main.c
    $(CROSS_COMPILE)gcc $(COPT) -fpic -mthumb -c main.c -o main.o
 
-ram: start.o main.o
-  $(CROSS_COMPILE)ld $(LOPT) start.o main.o -T memmap_ram.ld  -o led.elf 
+led.elf: start.o boot2.o main.o
+   $(CROSS_COMPILE)ld $(LOPT) start.o boot2.o main.o -T memmap.ld  -o led.elf 
 
+led.uf2: led.elf
+   elf2uf2 led.elf led.uf2

Flashに書き込みできるようにビルド済みのBoot Stage2のリンクとelf2uf2でuf2に変換できるようにする。

リンカスクリプト

リンカスクリプトmemmap_ram.ldではなくmemmap.ldにリネームして下記のように変更する。

diff -Narubp blink_ram_int_systick/memmap_ram.ld blink_flash_int_systick/memmap_ram.ld
--- blink_ram_int_systick/memmap_ram.ld   2021-02-27 13:21:52.160613474 +0900
+++ blink_flash_int_systick/memmap_ram.ld 2021-02-28 08:24:07.117410109 +0900
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: MIT */
 MEMORY
 {
+    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
       RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
 }
 
@@ -8,11 +9,22 @@ ENTRY(reset)
 
 SECTIONS
 {
-    .text : {
-        *(.vectors)
-      *(.text*)
-    } > RAM
+    /* 
+       boot2 section is for embed the precompiled boot2.
+       this code from memmap_default.ld in pico-sdk
+    */
+    .boot2 : {
+        __boot2_start__ = .;
+        KEEP(*(.boot2))
+        __boot2_end__ = .;
+    } > FLASH
 
+    ASSERT(__boot2_end__ - __boot2_start__ == 256,
+        "ERROR: Pico second stage bootloader must be 256 bytes in size")
+    .text : {
+        KEEP (*(.vectors))
+       KEEP(*(.text*))
+    } > FLASH
     .bss : {
         __bss_start__ = .;
    *(.bss*)

スタートアップ

ベクターテーブルはBoot Stage2で設定されるので、VTORの設定は削除。

diff -Narubp blink_ram_int_systick/start.S blink_flash_int_systick/start.S
--- blink_ram_int_systick/start.S 2021-02-27 13:21:52.160613474 +0900
+++ blink_flash_int_systick/start.S   2021-02-28 07:51:40.922563814 +0900
@@ -28,10 +28,6 @@ __vectors:
    .thumb_func
    .global reset
 reset:
-  ldr r1, =0xE000ED08 /* VTOR */
-  ldr r0, =__vectors
-  str r0, [r1]
-
    ldr r0, =0x20001000
    mov sp, r0
    bl init_bss

実行(失敗)

ビルドして出来上がったled.uf2をPicoに書き込む。

LEDがチカチカするが波形取ってみると下記のようになる。

f:id:mickey_happygolucky:20210228155147p:plain
Lチカ(失敗)

本来は100msになっているべきWidthが223.6msになっている。

XOSC

RP2040 Datasheetからこの辺りのことを調べてみる。

「2.4.5.1.1. SysTick timer」によると、watchdogブロックのクロックを使用する。

The SysTick timer uses a 1μs pulse as a clock enable. This is generated in the watchdog block as timer_tick.

SysTickはクロックソースをSYST_CSRのCLKSOURCEによって選択できるが、Lチカプログラムでは外部クロック(watchdogのクロック)を使用するようにしている。

「4.7.2 Tick generation」によると、1usのtickを得るために使用しているclk_tickclk_refから作られているらしく、正確なリファレンスクロックを得るには、Crystal Oscillator(XOSC)を動かす必要があるらしい。

The watchdog reference clock, clk_tick, is driven from clk_ref. Ideally clk_ref will be configured to use the Crystal Oscillator (Section 2.16) so that it provides an accurate reference clock.

「2.7. Boot Sequence」によるとclk_refは最初Ring Oscillator(ROSC)によって、低周波数(6.5MHz)で駆動しているとのこと。

The Ring Oscillator (Section 2.17) is started, providing a clock source to the clock generators. clk_sys and clk_ref are now running at a relatively low frequency (typically 6.5MHz).

Pico SDKを使用している場合は、xosc_init()によってこれらが正しく設定された状態でプログラムが実行される。

RAMバージョンで問題なく動作していたのは、Flashに書き込まれていたプログラムがpico-examplesのblinkerで、Pico SDKによってXOSCの初期化が行われていたからではないかと推測される。

レジスタ設定の確認

XOSCのレジスタ

XORCレジスタで設定する。

f:id:mickey_happygolucky:20210228155305p:plain

f:id:mickey_happygolucky:20210228155324p:plain

f:id:mickey_happygolucky:20210228155343p:plain

クロックソース

CLOCKSレジスタで設定する。

f:id:mickey_happygolucky:20210228155358p:plain

f:id:mickey_happygolucky:20210228155411p:plain

XOSC初期化処理の追加

XOSCを初期化しclk_sysのクロックソースをROSCからXOSCに変更する。

regs.h

regs.hを下記のように修正する。

diff --git a/blink_flash_int_systick/regs.h b/blink_flash_int_systick/regs.h
index 2d650ec..51792c4 100644
--- a/blink_flash_int_systick/regs.h
+++ b/blink_flash_int_systick/regs.h
@@ -34,5 +34,13 @@
 #define SYSTICK_RVR (0xE000E014)
 #define SYSTICK_CVR (0xE000E018)
 
+#define XOSC_BASE (0x40024000)
+#define XOSC_CTRL (XOSC_BASE+0x00)
+#define XOSC_STATUS (XOSC_BASE+0x04)
+#define XOSC_STARTUP (XOSC_BASE+0x0c)
+
+#define CLOCKS_BASE (0x40008000)
+#define CLOCKS_REF_CTRL (CLOCKS_BASE+0x30)
+#define CLOCKS_SYS_CTRL (CLOCKS_BASE+0x3c)
 
 #endif //REGS_H

XOSC関連とclk_ref、clc_sysのコントロールレジスタを追加。

main

main.cを下記のように修正する。

diff --git a/blink_flash_int_systick/main.c b/blink_flash_int_systick/main.c
index 19539f9..6553c63 100644
--- a/blink_flash_int_systick/main.c
+++ b/blink_flash_int_systick/main.c
@@ -24,6 +24,26 @@ void init_bss() {
         }
 }
 
+void init_xosc() {
+        write_reg(XOSC_CTRL, 0xaa0); /* 1_15MHZ */
+        write_reg(XOSC_STARTUP, 47); /* (((12 * MHZ) / 1000) + 128) / 256 = 47 */
+        write_reg_op(XOSC_CTRL, 0xfab << 12, OP_SET); /* ENABLE */
+
+        /* Wait for XOSSC to be stable */
+        while (!(read_reg(XOSC_STATUS) & 0x80000000)) ;
+}
+
+void init_clocks() {
+        init_xosc();
+
+        /* source of clk_ref to xosc */
+        write_reg_op(CLOCKS_REF_CTRL, 0x2, OP_SET);
+
+        /* reset the source of clk_sys to clk_ref which references xosc */
+        write_reg(CLOCKS_SYS_CTRL, 0x0);
+}
+
+
 void init_systick() {
         write_reg(SYSTICK_RVR, 1000-1); /* Systick interrupt will issue per 1ms. */
         write_reg(SYSTICK_CVR, 0);

XOSCの初期化処理と、clk_refのクロックソースをXOSCに変更。 XOSCを参照するようにしたclk_refを、clk_sysのクロックソースとして再設定している。

スタートアップ

start.Sを下記のように修正する。

diff --git a/blink_flash_int_systick/start.S b/blink_flash_int_systick/start.S
index b39ba02..0eb8efc 100644
--- a/blink_flash_int_systick/start.S
+++ b/blink_flash_int_systick/start.S
@@ -31,6 +31,7 @@ reset:
        ldr r0, =0x20001000
        mov sp, r0
        bl init_bss
+       bl init_clocks
        bl main
        b hang

init_clocksを呼び出すように変更。

実行

ビルドして出来上がったled.uf2をPicoに書き込む。

LEDがチカチカするが波形取ってみると下記のようになる。

f:id:mickey_happygolucky:20210228155224p:plain
Lチカ

パルス幅が100msになっている。

まとめ

LチカをFlashに書くと、クロック周りの初期化をきちんとしないと正しいタイミングで点滅しない。

参考

Raspberry Pi Pico Systick割り込みを使ってLチカをする

はじめに

SysTick割り込みを使用してLチカをする。

2021-02-26 Raspberry Pi Pico Systickを使ってLチカを正確にするのコードをベースに作業する。

SysTick割り込み

SysTick割り込みはm0+のコアで用意されている割り込みで、この割り込みが発生するとベクターテーブルに飛んでくる。

ここによるとベクターテーブルは下記のような構造になっている。

ベクターテーブル

ベクターテーブルは通常0x00000000に配置されCPUのリセットがかかったり割り込みが入ったりした場合に参照される様になっているが、VTORレジスタにアドレスを設定することで任意の場所に配置することができる。

今回はPicoのSRAM上にベクターテーブルを配置するようにする。

ベクターテーブルと割り込みハンドラの追加

ベクターテーブルとSysTickの割り込みハンドラとなるisr_systickを追加する。

スタートアップ

start.Sを下記のように変更する。

--- a/blink_ram_int_systick/start.S
+++ b/blink_ram_int_systick/start.S
@@ -2,10 +2,36 @@
    .cpu cortex-m0plus
    .thumb

+/* vector table */
+   .section .vectors, "ax"
+   .align 2
+   .global __vectors
+__vectors:
+.word 0x20001000
+.word reset
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word hang
+.word isr_systick
+
 /* reset handler */
    .thumb_func
    .global reset
 reset:
+   ldr r1, =0xE000ED08 /* VTOR */
+   ldr r0, =__vectors
+   str r0, [r1]
+
    ldr r0, =0x20001000
    mov sp, r0
    bl main

ベクターテーブルを__vectorsとして定義する。 Systick割り込みハンドラの位置にisr_systickを登録している。他の割り込みは使用しないのでisr_systickまでしか定義していない。

reset関数の先頭でVTORレジスタ__vectorsのアドレスを設定している。

リンカスクリプト

memmap_ram.ldを下記の様に修正する。

--- a/blink_ram_int_systick/memmap_ram.ld
+++ b/blink_ram_int_systick/memmap_ram.ld
@@ -9,6 +9,7 @@ ENTRY(reset)
 SECTIONS
 {
     .text : {
+        *(.vectors)
        *(.text*)
     } > RAM
 }

.vectorセクションがRAMに配置されるようにする。

main

main.cを下記のように修正する。

--- a/blink_ram_int_systick/main.c
+++ b/blink_ram_int_systick/main.c
@@ -18,6 +18,11 @@ void init_systick() {
         write_reg(SYSTICK_CSR, 0x1);
 }

+static uint32_t tick_count = 0;
+void isr_systick() {
+        ++tick_count;
+}
+
 // us is up to max of 24bit (0xffffff)
 void delay_us(uint32_t us) {
         write_reg(SYSTICK_RVR, us);

Systickの割り込みハンドラとなるisr_systickを追加する。

割り込みが発生するたびにtick_countをインクリメントする。

ディレイ処理の改造

前回はマイクロ秒単位で作成したディレイ関数をミリ秒単位に変更する。

main.c

diff --git a/blink_ram_int_systick/main.c b/blink_ram_int_systick/main.c
index 830f1e4..77d1309 100644
--- a/blink_ram_int_systick/main.c
+++ b/blink_ram_int_systick/main.c
@@ -15,7 +15,9 @@ void write_reg_op(uint32_t addr, uint32_t value, uint32_t op) {
 }

 void init_systick() {
-        write_reg(SYSTICK_CSR, 0x1);
+        write_reg(SYSTICK_RVR, 1000-1); /* Systick interrupt will issue per 1ms. */
+        write_reg(SYSTICK_CVR, 0);
+        write_reg(SYSTICK_CSR, 0x3); /* TICKINT=1, ENABLE=1 */
 }

 static uint32_t tick_count = 0;
@@ -23,16 +25,10 @@ void isr_systick() {
         ++tick_count;
 }

-// us is up to max of 24bit (0xffffff)
-void delay_us(uint32_t us) {
-        write_reg(SYSTICK_RVR, us);
-        write_reg(SYSTICK_CVR, 0);
-
-        // wait for 0 to RVR
-        while (!read_reg(SYSTICK_CVR)) __asm__ __volatile__("");
-
-        // wait for RVR counting down to 0.
-        while (read_reg(SYSTICK_CVR)) __asm__ __volatile__("");
+void delay_ms(uint32_t ms) {
+        uint32_t expire = tick_count + ms;
+        while (tick_count != expire)
+                __asm__ __volatile__("");
 }

 int main(void) {
@@ -66,7 +62,7 @@ int main(void) {
         // Blink
         write_reg(SIO_GPIO_OE_SET, (1ul<<25));
         while (1) {
-                delay_us(100*1000); //100ms
+                delay_ms(100); //100ms
                 write_reg(SIO_GPIO_OUT_XOR, (1ul<<25));
         }
         return 0;

RVRを1000-1に設定することで、割り込みの周期を1ミリ秒にしている。

BSS領域を使えるようにする

変数tick_countはスタティックな広域変数となっている。0で初期化しているため、コンパイラによって.bssセクションに配置される様になっている。

$ readelf -s led.elf | grep tick_count
25:    21: 20000224     4 NOTYPE  LOCAL  DEFAULT    2 tick_count
$ readelf -S led.elf | grep bss
7:  [ 2] .bss              NOBITS          20000224 000278 000004 00  WA  0   0  4

余談だが初期値を0以外にすると.dataセクションに配置される。

現在は.bssセクションはリンカスクリプトでもケアしていないので、下記のようにCのコード上0に初期化していても実際にはおかしな値が設定されている。

static uint32_t tick_count = 0;

これを正しく動作させるためには下記の様な修正をする必要がある。

  • リンカスクリプトを修正し.bssセクションが正しくRAM上に配置されるようにする。
  • mainが実装されるまでに0で初期化する。

リンカスクリプト

リンカスクリプトを下記のように修正する。

diff --git a/blink_ram_int_systick/memmap_ram.ld b/blink_ram_int_systick/memmap_ram.ld
index 97530d9..c16e1ea 100644
--- a/blink_ram_int_systick/memmap_ram.ld
+++ b/blink_ram_int_systick/memmap_ram.ld
@@ -12,4 +12,11 @@ SECTIONS
         *(.vectors)
        *(.text*)
     } > RAM
+
+    .bss : {
+        __bss_start__ = .;
+       *(.bss*)
+       . = ALIGN(4);
+       __bss_end__ = .;
+    } > RAM
 }

__bss_start____bss_end__はロケーションカウンタと呼ばれるもので、リンク後にその位置をプログラムから参照できるようにするもの。ここでは.bssセクションの先頭と末尾の位置を保存している。

main

一般的にはBSS領域の初期化などの処理はスタートアップのアセンブリで実装されることが多いが、ここではC言語からロケーションカウンタを参照し0で初期化する。

diff --git a/blink_ram_int_systick/main.c b/blink_ram_int_systick/main.c
index 77d1309..19539f9 100644
--- a/blink_ram_int_systick/main.c
+++ b/blink_ram_int_systick/main.c
@@ -14,6 +14,16 @@ void write_reg_op(uint32_t addr, uint32_t value, uint32_t op) {
         write_reg(addr | op, value);
 }
 
+void init_bss() {
+        extern uint32_t __bss_start__;
+        extern uint32_t __bss_end__;
+        uint32_t *p = &__bss_start__;
+        uint32_t *end = &__bss_end__;
+        while (p != end) {
+                *p++ = 0;
+        }
+}
+
 void init_systick() {
         write_reg(SYSTICK_RVR, 1000-1); /* Systick interrupt will issue per 1ms. */
         write_reg(SYSTICK_CVR, 0);

スタートアップ

スタートアップからinit_bssを呼び出す。

diff --git a/blink_ram_int_systick/start.S b/blink_ram_int_systick/start.S
index 793d991..8419f1b 100644
--- a/blink_ram_int_systick/start.S
+++ b/blink_ram_int_systick/start.S
@@ -34,6 +34,7 @@ reset:
 
        ldr r0, =0x20001000
        mov sp, r0
+       bl init_bss
        bl main
        b hang

波形を見る

f:id:mickey_happygolucky:20210227132845p:plain
Lチカ

こちらも100msのパルス幅になっている。

まとめ

Systickの割り込みを使用してLチカを行った。

ベクターテーブルの場所はVTORレジスタによって適切に設定する必要がある。 また今回は実装の都合上、tick_countをBSSに置いたためBSSの初期化なども行う必要があった。

今回の成果物はここに置いてある。

参考

*Cortex-M0+ Devices Generic User Guide

Raspberry Pi Pico Systickを使ってLチカを正確にする

はじめに

「PicoでベアメタルのLチカ(RAMバージョン) その2」として。

これまで作ってきたLチカは、適当な値をforでビジーループするだけだったので、タイミングはいい加減だった。

Cortex-M0+ではコアにSysTickを持っていて、これは難しい設定することもなく使用可能なので、これを使ってLチカのタイミングを正確にする。

SysTickはPico SDKの中では基本的には使用されていない。

SysTick

RP2040 Datasheetの「2.4.5.1.1. SysTick timer」によると、

The SysTick timer uses a 1μs pulse as a clock enable. This is generated in the watchdog block as timer_tick.

watchdogのtimer_tickで生成される1usのパルスを使用するとのこと。

SYST_CSRCLKSOURCEの初期値が0になっており外部のクロックを使用するとのこと。これがtimer_tickのことかと思われる。

f:id:mickey_happygolucky:20210226084257p:plain

f:id:mickey_happygolucky:20210226084308p:plain

WATCHDOGのTICKレジスタRUNNINGENABLEが動いているので、timer_tickの1usのパルスは初期状態で動作している。

f:id:mickey_happygolucky:20210226084323p:plain

f:id:mickey_happygolucky:20210226084334p:plain

Lチカ(RAMバージョン)を改造

Lチカ(RAMバージョン)をSysTickを使用するように変更してみる。

regs.h

regs.hにSYSTICK関連のレジスタ定義を追加する。

diff --git a/blink_ram/regs.h b/blink_ram/regs.h
index dc0b11b..2d650ec 100644
--- a/blink_ram/regs.h
+++ b/blink_ram/regs.h
@@ -30,5 +30,9 @@
 
 #define IO_GPIO25_CTRL (IO_BANK0_BASE + IO_BANK_GPIO25_CTRL)
 
+#define SYSTICK_CSR (0xE000E010)
+#define SYSTICK_RVR (0xE000E014)
+#define SYSTICK_CVR (0xE000E018)
+
 
 #endif //REGS_H

main.c

main.cinit_systick()delay_us()を追加する。

初期状態ではSysTickでは動作していないので、init_systick()で動かすようにする。その際CLKSOURCEは0に設定し、timer_tickを使用するようにする。

delay_us()では、RVRに任意のディレイ時間をus単位で設定する。次にCVRに0を設定しRVRにリセットされるまで待つ。 次に、CVRが0になるまで待つことで設定した時間の分だけビジーループする。

diff --git a/blink_ram/main.c b/blink_ram/main.c
index 47f4935..f4b4135 100644
--- a/blink_ram/main.c
+++ b/blink_ram/main.c
@@ -14,6 +14,22 @@ void write_reg_op(uint32_t addr, uint32_t value, uint32_t op) {
         write_reg(addr | op, value);
 }
 
+void init_systick() {
+        write_reg(SYSTICK_CSR, 0x1);
+}
+
+// us is up to max of 24bit (0xffffff)
+void delay_us(uint32_t us) {
+        write_reg(SYSTICK_RVR, us);
+        write_reg(SYSTICK_CVR, 0);
+
+        // wait for 0 to RVR
+        while (!read_reg(SYSTICK_CVR)) __asm__ __volatile__("");
+
+        // wait for RVR counting down to 0.
+        while (read_reg(SYSTICK_CVR)) __asm__ __volatile__("");
+}
+
 int main(void) {
 
         /////////////////////
@@ -39,10 +55,13 @@ int main(void) {
         write_reg(IO_GPIO25_CTRL, 0x5);
         uint32_t v = read_reg(IO_GPIO25_CTRL);
 
+        // SysTick init
+        init_systick();
+
         // Blink
         write_reg(SIO_GPIO_OE_SET, (1ul<<25));
         while (1) {
-                for (int i = 100000; i != 0; i--) ;
+                delay_us(100*1000); //100ms
                 write_reg(SIO_GPIO_OUT_XOR, (1ul<<25));
         }
         return 0;

波形を見る

f:id:mickey_happygolucky:20210226091039p:plain
Lチカ

ちゃんと100ms幅になっている。

まとめ

今回のような単純な使い方では、SysTickは24ビットしかないのであまり長い時間待たせられない。

例えば割り込みを使用してTickカウンタを作って、その差分で待つようにすれば24ビットの壁は超えられると思うが、ちゃんとしたタイマー処理を使用したい場合は、やはりタイマーを使用するほうが良いと思われる。

Raspberry Pi Pico J-LlinkでOpenOCD

はじめに

J-Link EDUが手元にあるので、OpenOCDでPicoのデバッグができないか試してみた。

接続

下記のように接続する。

Pico ARM-JTAG-SWD
SWDIO SWDIO(7)
SWDCLK SWDCLK(9)
GND GND(4,6,8,10,12,14,16,18,20)
VSYS VCC(1)

PlatformIOのJ-LINK

設定ファイル

${PICO_SDK_PATH}/../openocd/tcl/pico-jlink.cfgを下記の内容で作成する。

source [find interface/jlink.cfg]
transport select swd

source [find target/rp2040.cfg]
adapter speed 4000

アダプタをSWDモードに設定し、アダプタのスピードを設定する。

スピードを設定する際にadapter_khzを使うとadapter speedを使えと怒られる。

OpenOCDの実行(失敗)

下記のコマンドでOpenOCDを実行する。

$ cd ${PICO_SDK_PATH}/../openocd
$ src/openocd -f pico-jlink.cfg -s tcl

下記のようなエラーになる。

Open On-Chip Debugger 0.10.0+dev-geb22ace-dirty (2021-02-24-18:16)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
DEPRECATED! use 'adapter speed' not 'adapter_khz'
adapter speed: 4000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V10 compiled Oct 22 2019 16:28:15
Info : Hardware version: 10.10
Info : VTarget = 4.808 V
Info : clock speed 4000 kHz
Error: Sequence 4 not supported.
Info : DAP init failed


Error: Sequence 3 not supported.
Error: Sequence 4 not supported.

エラーの箇所

src/jtag/drivers/jlink.cの下記の関数でエラーになっている。

static int jlink_swd_switch_seq(enum swd_special_seq seq)
{
    const uint8_t *s;
    unsigned int s_len;

    switch (seq) {
        case LINE_RESET:
            LOG_DEBUG("SWD line reset");
            s = swd_seq_line_reset;
            s_len = swd_seq_line_reset_len;
            break;
        case JTAG_TO_SWD:
            LOG_DEBUG("JTAG-to-SWD");
            s = swd_seq_jtag_to_swd;
            s_len = swd_seq_jtag_to_swd_len;
            break;
        case SWD_TO_JTAG:
            LOG_DEBUG("SWD-to-JTAG");
            s = swd_seq_swd_to_jtag;
            s_len = swd_seq_swd_to_jtag_len;
            break;
        default:
            LOG_ERROR("Sequence %d not supported.", seq);
            return ERROR_FAIL;
    }

    jlink_queue_data_out(s, s_len);

    return ERROR_OK;
}

このseqが3、4の場合にエラーになっている。

enum swd_special_seq {
    LINE_RESET,
    JTAG_TO_SWD,
    SWD_TO_JTAG,
    SWD_TO_DORMANT,
    DORMANT_TO_SWD,
}

SWD_TO_DORMANTDORMANT_TO_SWDに未対応ということらしい。

調べてみると、既にPR(Rp2040 jlink #19)は出されている。

patchは2つあり、下記の修正はドンピシャの内容となっている。

From ef234fa045dfae30e4557db0867d4720f46bbbd3 Mon Sep 17 00:00:00 2001
From: Liam Fraser <liam@raspberrypi.com>
Date: Sun, 24 Jan 2021 08:59:02 +0000
Subject: [PATCH] Add DORMANT_TO_SWD and SWD_TO_DORMANT sequences to jlink
 driver

---
 src/jtag/drivers/jlink.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c
index ae8ce49c6..5b2c1ea89 100644
--- a/src/jtag/drivers/jlink.c
+++ b/src/jtag/drivers/jlink.c
@@ -2149,6 +2149,16 @@ static int jlink_swd_switch_seq(enum swd_special_seq seq)
            s = swd_seq_swd_to_jtag;
            s_len = swd_seq_swd_to_jtag_len;
            break;
+       case DORMANT_TO_SWD:
+           LOG_DEBUG("DORMANT-to-SWD");
+           s = swd_seq_dormant_to_swd;
+           s_len = swd_seq_dormant_to_swd_len;
+           break;
+       case SWD_TO_DORMANT:
+           LOG_DEBUG("SWD-to-DORMANT");
+           s = swd_seq_swd_to_dormant;
+           s_len = swd_seq_swd_to_dormant_len;
+           break;
        default:
            LOG_ERROR("Sequence %d not supported.", seq);
            return ERROR_FAIL;

もう一つの方はack待ちが不要なコマンドに対応するものらしい。

From 7e5ea1861a118120a78dac1fd812f1bdcaedc0cc Mon Sep 17 00:00:00 2001
From: graham sanderson <graham.sanderson@gmail.com>
Date: Mon, 25 Jan 2021 12:09:03 -0600
Subject: [PATCH] Add handling of no-ack commands to jlink driver

Change-Id: I4f7b0dad37bc68cde168962d86e53d7f5ea1cad7
---
 src/jtag/drivers/jlink.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c
index 5b2c1ea89..e2537b78f 100644
--- a/src/jtag/drivers/jlink.c
+++ b/src/jtag/drivers/jlink.c
@@ -2002,6 +2002,8 @@ struct pending_scan_result {
    void *buffer;
    /** Offset in the destination buffer */
    unsigned buffer_offset;
+   /** true if the command has nmo acknowledgement */
+   bool no_ack;
 };
 
 #define MAX_PENDING_SCAN_RESULTS 256
@@ -2195,7 +2197,9 @@ static int jlink_swd_run_queue(void)
    }
 
    for (i = 0; i < pending_scan_results_length; i++) {
-      int ack = buf_get_u32(tdo_buffer, pending_scan_results_buffer[i].first, 3);
+       int ack = pending_scan_results_buffer[i].no_ack ?
+               SWD_ACK_OK :
+               buf_get_u32(tdo_buffer, pending_scan_results_buffer[i].first, 3);
 
        if (ack != SWD_ACK_OK) {
            LOG_DEBUG("SWD ack not OK: %d %s", ack,
@@ -2259,6 +2263,9 @@ static void jlink_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data, uint3
 
        jlink_queue_data_out(data_parity_trn, 32 + 1);
    }
+   pending_scan_results_buffer[pending_scan_results_length].no_ack =
+           (0 == ((cmd ^ swd_cmd(false, false, DP_TARGETSEL)) &
+                         (SWD_CMD_APnDP|SWD_CMD_RnW|SWD_CMD_A32)));
 
    pending_scan_results_length++;

Checkに引っかかっておりマージされないらしい。

パッチの適用

ローカルのソースコードにこれらの変更を適用してみる。手元ではとりあえず手で修正した。

$ make -j $(nproc)

ビルドが通った。

OpenOCDの実行(成功)

再度実行してみる

$ cd ${PICO_SDK_PATH}/../openocd
$ src/openocd -f pico-jlink.cfg -s tcl
Open On-Chip Debugger 0.10.0+dev-geb22ace-dirty (2021-02-24-18:16)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
DEPRECATED! use 'adapter speed' not 'adapter_khz'
adapter speed: 4000 kHz

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V10 compiled Oct 22 2019 16:28:15
Info : Hardware version: 10.10
Info : VTarget = 4.825 V
Info : clock speed 4000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections

問題なく起動した。

blinkを実行

pico-examplesのblinkを実行してみる。

OpenOCDとは別のターミナルで下記を実行する。

$ cd ${PICO_EXAMPLES_PATH}/build/blink
$ gdb-multiarch blink.elf
(gdb) target remote :3333
(gdb) mon reset init
(gdb) load
(gdb) c

LEDがチカチカした。

f:id:mickey_happygolucky:20210224193102g:plain
Lチカ

まとめ

J-Link対応のためのPRは既に出されているが 2021/2/24時点ではマージされていない。

修正内容自体は正しそうなので、自分でこれらの修正を取り込むか、PR用のブランチを使用すればJ-Link自体は使用できそう。

CIがしくじっているだけのような気もするので、もうすぐ取り込まれるかも。

Raspberry Pi Pico ARM-USB-TINY-H + ARM-JTAG-SWDでデバッグ

はじめに

ツイッターでRaspberryPi4が無いケースでOpenOCDでデバッグするにはどういう手段があるか。という話題が出た。

ARM-USB-TINY-H + ARM-JTAG-SWD

ARMのMCUデバッグで安価で使い勝手が良いものとしてはARM-USB-TINY-Hが挙げられる。

しかしこのデバイスはSWDには対応していない。JTAG-SWDは乗っかる信号線はTMSとTCLKだが、JTAGしか理解できないデバッガはTMSにSWDIOが送られてきても理解できない。

ARM-JTAG-SWDはSWDIOで扱う信号をJTAGのTMS、TDI、TDOに適宜振り分けるアダプタとなっている(はず)。

f:id:mickey_happygolucky:20210224070715p:plain
回路図

接続

下記のように接続する。

Pico ARM-JTAG-SWD
SWDIO SWDIO(7)
SWDCLK SWDCLK(9)
GND GND(4,6,8,10,12,14,16,18,20)
VSYS VCC(1)

PlatformIOのOlimex ARM-USB-TINY-H

デバッグ

OpenOCD

Pico SDKのOpenSDKを使用する。

${PICO_SDK_PATH}/../openocd/tcl/pico-jtag-swd.cfgを下記の内容で作成する。

source [find interface/ftdi/olimex-arm-usb-tiny-h.cfg]
source [find interface/ftdi/olimex-arm-jtag-swd.cfg]
source [find target/rp2040.cfg]
adapter speed 1000

クロック設定を忘れるとエラーになる。1000khzに得に根拠は無いので適宜最適値を探してほしい。

また、クロックを設定する際にadapter_khzを使うとadapter speedを使えと怒られる。

下記のコマンドで起動する。

$ cd ${PICO_SDK_PATH}/../openocd
$ src/openocd -f pico-jtag-swd.cfg -s tcl

blinkを実行

pico-examplesのblinkを実行してみる。

OpenOCDとは別のターミナルで下記を実行する。

$ cd ${PICO_EXAMPLES_PATH}/build/blink
$ gdb-multiarch blink.elf
(gdb) target remote :3333
(gdb) mon reset init
(gdb) load
(gdb) c

LEDがチカチカした。

f:id:mickey_happygolucky:20210224071249g:plain
Lチカ

まとめ

ARM-USB-TINY-HはARM-JTAG-SWDアダプタを使えばPicoのデバッグができる。

JLinkはまだ対応中のようだ

参考