はじめに
前回作ったプログラムをFlashから実行できるようにしてみる。
RESUSについて追記(2021/3/10)
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がチカチカするが波形取ってみると下記のようになる。
本来は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_tick
はclk_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レジスタで設定する。
クロックソース
CLOCKSレジスタで設定する。
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のクロックソースとして再設定している。
RESUS
クロックの初期化処理を追加したのに、実際に波形を取ってみると思ったとおりに動かない(パルス幅が広くなる)ことがある。
これは初期化の最中にRESUSという機能が働いた結果おかしい動作になっているっぽいことがわかった。
RESUSはRP2040が持っているセーフティの機能で、clk_sysが止まってしまった場合、clk_refを使用して無理やり動かすというものらしい。よってclk_refまで止まるとRESUSでもどうにもできなくなるとのこと。
それはそれとして、クロックの初期化で一度clk_sysを止めるのでその際にRESUSが動いたままだと挙動がおかしくなることがあるらしい。そのためクロックの初期化をする前にRESUSを止めておく必要がある。
main.cのinit_clocks()を下記のように修正する。
void init_clocks() { /* Disable resus that may be enabled from previous software */ write_reg(CLOCKS_CLK_SYS_RESUS_CTRL, 0); 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); }
CLOCKS_CLK_SYS_RESUS_CTRL
の定義をrefs.hに追加する。
#define CLOCKS_CLK_SYS_RESUS_CTRL (CLOCKS_BASE+0x78)
スタートアップ
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がチカチカするが波形取ってみると下記のようになる。
パルス幅が100msになっている。
まとめ
LチカをFlashに書くと、クロック周りの初期化をきちんとしないと正しいタイミングで点滅しない。