第1章: ブートシーケンス#
リセット直後の CPU が何をするかを追いかけ、ベクタテーブル(例外や割り込みが起きたときに飛ぶ先のアドレスを並べた表)から main() に至る道筋を自分の手でたどります。
サンプルコード#
ディレクトリ:
docs/os-on-arduino/code/01_bootビルドと書き込み:
uv run pio run -d 01_boot -t upload
uv run pio device monitor -b 115200
コードを見る: docs/os-on-arduino/code/01_boot
ソースコード#
この章のスケッチです。ファイル名をクリックすると開きます。コードは原稿リポジトリから自動で同期されているので、ここに出ているものが常に最新です。
docs/os-on-arduino/code/01_boot#
src/main.cpp — 63 行
1/**
2 * Chapter 1: Boot Sequence Check
3 *
4 * This sketch demonstrates the boot sequence by examining:
5 * - BSS section (zero-initialized variables)
6 * - DATA section (initialized variables copied from Flash)
7 * - Vector table location
8 * - Stack pointer
9 */
10
11#include <Arduino.h>
12
13// Global variable in BSS section (uninitialized, should be zero)
14int bss_var;
15
16// Global variable in DATA section (initialized, copied from Flash)
17int data_var = 12345;
18
19void setup()
20{
21 Serial.begin(115200);
22 while (!Serial) { }
23 while (Serial.read() != 'S') { delay(1); }
24
25 Serial.println("=== Boot Sequence Check ===");
26 Serial.println();
27
28 // BSS section variable (should be zero-cleared)
29 Serial.print("bss_var (should be 0): ");
30 Serial.println(bss_var);
31
32 // DATA section variable (initial value should be copied)
33 Serial.print("data_var (should be 12345): ");
34 Serial.println(data_var);
35
36 Serial.println();
37 Serial.println("=== Memory Addresses ===");
38
39 // Show variable addresses
40 Serial.print("&bss_var: 0x");
41 Serial.println((uint32_t)&bss_var, HEX);
42
43 Serial.print("&data_var: 0x");
44 Serial.println((uint32_t)&data_var, HEX);
45
46 // Vector table location (VTOR register)
47 Serial.print("VTOR: 0x");
48 Serial.println(SCB->VTOR, HEX);
49
50 // Stack pointer
51 uint32_t sp;
52 __asm volatile ("MRS %0, MSP" : "=r" (sp));
53 Serial.print("MSP: 0x");
54 Serial.println(sp, HEX);
55
56 Serial.println();
57 Serial.println("If you see this, boot sequence completed!");
58}
59
60void loop()
61{
62 // Nothing to do
63}
docs/os-on-arduino/code/01_boot_vector_dump#
src/main.cpp — 60 行
1// vector_dump.ino
2// 第1章 演習1-2: ベクタテーブルの内容をダンプ
3
4#include <Arduino.h>
5
6void setup()
7{
8 Serial.begin(115200);
9 while (!Serial) { }
10 while (Serial.read() != 'S') { delay(1); }
11
12 Serial.println("=== Vector Table Dump ===");
13 Serial.println();
14
15 // ベクタテーブルのアドレス(VTORから取得)
16 uint32_t *vtor = (uint32_t *)SCB->VTOR;
17
18 const char *names[] = {
19 "Initial SP",
20 "Reset",
21 "NMI",
22 "HardFault",
23 "MemManage",
24 "BusFault",
25 "UsageFault",
26 "Reserved",
27 "Reserved",
28 "Reserved",
29 "Reserved",
30 "SVCall",
31 "DebugMon",
32 "Reserved",
33 "PendSV",
34 "SysTick"
35 };
36
37 for (int i = 0; i < 16; i++) {
38 Serial.print("[");
39 if (i < 10) Serial.print(" ");
40 Serial.print(i);
41 Serial.print("] ");
42
43 // アドレス
44 char hex[16];
45 snprintf(hex, sizeof(hex), "0x%08lX", (unsigned long)vtor[i]);
46 Serial.print(hex);
47
48 // 名前
49 Serial.print(" ");
50 Serial.println(names[i]);
51 }
52
53 Serial.println();
54 Serial.println("Note: Handler addresses point to Flash (0x000xxxxx)");
55 Serial.println(" Initial SP points to RAM top (0x200xxxxx)");
56}
57
58void loop()
59{
60}
実機での出力#
Arduino UNO R4 WiFi の実機で実際に取得した出力です(macOS / Windows の 両方で同じ結果を確認しています)。手元の出力と見比べてください。
S を 1 文字送ると出力が始まります。
モニタを開いてしばらくは何も出ず、S を送った瞬間にまとめて出てくるところ。
data_var が 12345 になっていれば、初期化済みデータのコピーが効いています#
=== Boot Sequence Check ===
bss_var (should be 0): 0
data_var (should be 12345): 12345
=== Memory Addresses ===
&bss_var: 0x200000C4
&data_var: 0x20000000
VTOR: 0x20007F00
MSP: 0x20007EC8
If you see this, boot sequence completed!
...
VTOR(Vector Table Offset Register)はベクタテーブルの置き場所を示すレジスタ、
MSP(Main Stack Pointer)はいま使っているスタックの位置を指すレジスタの値です。
演習1-2: ベクタテーブルの読み取り#
この出力は別のプロジェクト 01_boot_vector_dump のものです。書き込んでモニタを開き、
S を送ってください。
uv run pio run -d 01_boot_vector_dump -t upload
uv run pio device monitor -b 115200
=== Vector Table Dump ===
[ 0] 0x20007F00 Initial SP
[ 1] 0x000098DD Reset
[ 2] 0x00009BA1 NMI
[ 3] 0x00006651 HardFault
[ 4] 0x000098D9 MemManage
[ 5] 0x000098D9 BusFault
[ 6] 0x000098D9 UsageFault
[ 7] 0x000098D9 Reserved
[ 8] 0x00000000 Reserved
[ 9] 0x00000000 Reserved
[10] 0x00000000 Reserved
[11] 0x000098D9 SVCall
[12] 0x000098D9 DebugMon
[13] 0x00000000 Reserved
[14] 0x000098D9 PendSV
[15] 0x000098D9 SysTick
Note: Handler addresses point to Flash (0x000xxxxx)
Initial SP points to RAM top (0x200xxxxx)
...
右の名前は例外の種類です。主なものを挙げます。
NMI: 止める(マスクする)ことができない割り込みHardFault: ほかで処理できない重大なエラーのときの例外。第6章で詳しく扱いますSVCall:svc命令で呼び出す例外。応用編2でシステムコールの入り口に使いますPendSV: 「あとで実行して」と予約できる例外。第3章でタスクの切り替えに使いますSysTick: 一定周期で起きるタイマ割り込み。第4章でスケジューラの時計に使います
つまずきやすいポイント#
(読者からの質問に応じて随時追記します。質問は GitHub Issues へどうぞ)