www.mtkn.jp

Manuscripts for my personal webpage.
git clone https://git.mtkn.jp/www.mtkn.jp
Log | Files | Refs | LICENSE

rp2040_2.html (22153B)


      1 <h1>RP2040 SDKなし2 Clock, UART</h1>
      2 <time>2023-05-10</time>
      3 <p>
      4 今回はClockとUARTを設定してパソコンに繋ぎ、\
      5 キーボードからの入力をオウム返しするプログラムを作成する。
      6 <p>
      7 </p>
      8 前回: <a href="rp2040_1.html">RP2040 SDKなしでLチカ</a><br>
      9 ソースコード: <a href="https://git.mtkn.jp/rp2040">git</a>/ex2
     10 </p>
     11 
     12 <h2>動作環境</h2>
     13 <ul>
     14 <li>Void Linux
     15 	<ul>
     16 	<li>cross-arm-none-eabi-binutils-2.32_2</li>
     17 	<li>GNU Make 4.4.1</li>
     18 	<li>minicom version 2.7.1</li>
     19 	</ul>
     20 </li>
     21 <li><a href="https://akizukidenshi.com/catalog/g/g108461/">FT234X 超小型USBシリアル変換モジュール</a>
     22 </li>
     23 </ul>
     24 
     25 <h2>Clock</h2>
     26 
     27 <h3>リング発振回路</h3>
     28 <p>\
     29 RP2040にはリング発振回路というのが内蔵されている。\
     30 これは自分の出力を反転させようとするもので、\
     31 不安定だが高速で消費電力の少ないクロックとして用いられる。\
     32 RP2040は電源を入れると、このリング発振回路を動作用のクロックとして\
     33 用いている。この発振回路の周波数は、チップの製造過程での誤差、動作時の電圧、\
     34 動作温度によって変動するので、正確な周波数が必要な用途には向かない。\
     35 </p>
     36 
     37 <h3>水晶発振子</h3>
     38 <p>\
     39 秋月電子通商で購入したRP2040マイコンボードには外部クロックとして、\
     40 12MHzの水晶発振子が付属する。水晶発振子はリング発振回路より電力を消費するが、\
     41 より正確である。\
     42 </p>
     43 
     44 <h3>PLL</h3>
     45 <p>\
     46 水晶振動子を入力として、周波数を数倍にしたものを出力するもの。\
     47 電気的な話はよく知らない。データシートの「2.18.2. Calcurating PLL parameters」\
     48 によると、入力周波数を<code>FREF</code>としたときの出力周波数は\
     49 <code>(FREF / REFDIV) × FBDIV / (POSTDIV1 × POSTDIV2)</code>となる。\
     50 これらの変数はそれぞれ設定用のレジスタに値を保存することで変更できる。\
     51 </p>
     52 
     53 <h2>UART</h2>
     54 <p>
     55 Universal Asynchronous Receiver/Transmitterの略。\
     56 </p>
     57 
     58 <h2>プログラム</h2>
     59 <h3>初期設定</h3>
     60 <p>
     61 後で見るように、UARTの動作には多分水晶発振子とPLLが必要なので、\
     62 まずはそれを設定する。\
     63 起動後、メインのプログラムが読み込まれるまでの<code>boot2</code>は\
     64 前回と同じものである。\
     65 <code>main.s</code>ではまず前回と同様に初期スタックポインタとエントリーポイント\
     66 を定義する:
     67 </p>
     68 <pre><code>	.section .vectors
     69 vectors:
     70 	.word 0x20040000 // initial SP
     71 	.word(reset+1)   // entry point
     72 </code></pre>
     73 <p>
     74 続いて利用するサブシステムのリセットを解除する。\
     75 PLLとUARTが追加されている。\
     76 今回使うUARTはUART0だけである:
     77 </p>
     78 <pre><code>	.section .text
     79 reset:
     80 	// unreset gpio, pll_sys, uart0
     81 	ldr r0, =(1 &lt;&lt; 22 | 1 &lt;&lt; 12 | 1 &lt;&lt; 5) // uart0 | pll_sys | io_bank0
     82 	ldr r3, resets_base
     83 	ldr r1, atomic_clr
     84 	str r0, [r3, r1] // RESETS: RESET
     85 unreset_chk:
     86 	ldr r1, [r3, #0x8] // RESETS: RESET_DONE
     87 	tst r0, r1
     88 	beq unreset_chk
     89 
     90 /* ... */
     91 
     92 atomic_clr:
     93 	.word 0x00003000
     94 resets_base:
     95 	.word 0x4000c000
     96 </code></pre>
     97 
     98 <h3>GPIOの設定</h3>
     99 <p>
    100 次にGPIOの役割を設定する。\
    101 前回はLEDを点滅させるためにGPIO25をSIOに設定したが、\
    102 今回はGPIO0とGPIO1をUART0にする:
    103 </p>
    104 <pre><code>	// set gpio functions
    105 	ldr r3, io_bank0_base
    106 	mov r0, #2 // uart0
    107 	mov r1, #0x4
    108 	str r0, [r3, r1] // IO_BANK0: GPIO0_CTRL
    109 	mov r1, #0xc
    110 	str r0, [r3, r1] // IO_BANK0: GPIO1_CTRL
    111 
    112 /* ... */
    113 
    114 io_bank0_base:
    115 	.word 0x40014000
    116 </code></pre>
    117 
    118 <h3>Clockの設定</h3>
    119 <p>
    120 Clockの設定をする。\
    121 まずは水晶発振子を起動する。\
    122 推奨発振子は起動してから周波数が安定するまで少し時間がかかるようで、\
    123 その間待たないといけない。\
    124 この時間は1msあれば十分だとデータシートに書いている。\
    125 この待ち時間はXOSC: STARTUPレジスタに、256サイクル単位で記述する。\
    126 データシートによると初期のリング発振子は最大で12MHzなので、\
    127 <code>(12 * 10^6 * 1 * 10^-3) / 256 = 47</code>をこのレジスタにセットする。\
    128 ところでデータシートではこの計算は水晶発振子の周波数で書かれている。\
    129 起動直後でまだ使えない水晶発振子の周波数を使うのはなんでやろ。\
    130 SDKでも<code>pico-sdk/src/rp2_common/hardware_xosc/xosc.c</code>で、
    131 </p>
    132 <pre><code>\
    133 #define STARTUP_DELAY (((((XOSC_MHZ * MHZ) / 1000) + 128) / 256) * PICO_XOSC_STARTUP_DELAY_MULTIPLIER)
    134 </code></pre>
    135 <p>
    136 と定義されている(PICO_XOSC_STARTUP_DELAY_MULTIPLIERは1)。\
    137 とりあえず47に設定しているが、試しに0や1にしても動いた。よくわからん。\
    138 </p>
    139 <p>
    140 待ち時間を設定したら、起動する。\
    141 XOSC: CTRLに起動用のコマンド的なものを入力し、周波数が安定するのを待つ。\
    142 </p>
    143 <p>
    144 以上を実装したのが以下のコード:
    145 </p>
    146 <pre><code>	// setup xosc
    147 	ldr r3, xosc_base
    148 	mov r0, #47 // start up delay for 12MHz rosc (xosc?)
    149 	str r0, [r3, #0xc] // XOSC: STARTUP
    150 	ldr r0, =(0xfab << 12 | 0xaa0)
    151 	str r0, [r3, #0] // XOSC: CTRL
    152 wait_xosc:
    153 	ldr r0, [r3, #0x4] // XOSC: STATUS
    154 	lsr r0, r0, #31 // STABLE bit
    155 	beq wait_xosc
    156 
    157 /* ... */
    158 
    159 xosc_base:
    160 	.word 0x40024000
    161 </code></pre>
    162 
    163 <h3>PLLの設定</h3>
    164 <p>
    165 水晶発振子が起動できたので、次にPLLを設定する。\
    166 CPUが133MHzまで対応しているので133MHzになるようにした。\
    167 </p>
    168 <p>
    169 PLLは入力となる振動(ここでは水晶発振子の振動)を加工して\
    170 周波数を上げたり下げたりする。\
    171 出力の周波数は以下の式で決まる:
    172 </p>
    173 <pre>(FREF / REFDIV) * FBDIV / (POSTDIV1 * POSTDIV2)</pre>
    174 <p>
    175 FREFは入力の周波数(ここでは12MHz)で、その他の変数はプログラマが設定できる。\
    176 ただしデータシートによると(FREF / REFDIV)は5MHz以上でないといけないので、\
    177 REFDIVは1である。\
    178 また、FBDIVは16~320、POSTDIV1とPOSTDIV2は1~7で、POSTDIV1とPOSTDIV2に違う値を\
    179 代入する場合、POSTDIV1に大きい方を入れたほうが消費電力が少なくなるとのことなので、\
    180 133MHzにするには、FBDIV=133、POSTDIV1=6、POSTDIV=2とすればいい\
    181 (POSTDIV1=4、POSTDIV2=3も可能だが、pico-sdkに付属するvcocalc.pyというスクリプト\
    182 のコメントには、この2つの値の差が大きい方がいいと書いている)。
    183 </p>
    184 <p>
    185 PLL設定の手順は、FBDIVの設定、PLLとVCOの起動、VOCが安定するまで待機、\
    186 POSTDIV1とPOSTDIV2の設定、Post Dividerの起動、そして最後にシステムとUARTのクロック\
    187 を今設定したPLLに変更、である。\
    188 以上を実装したのが以下のコード:
    189 </p>
    190 <pre><code>	// setup pll_sys 133MHz
    191 	ldr r3, pll_sys_base
    192 	// set feedback divider
    193 	mov r0, #133
    194 	str r0, [r3, #0x8] // PLL: FBDIV_INT
    195 	// power on pll and vco
    196 	ldr r0, =(1 &lt;&lt; 5 | 1) // VCOPD | PD
    197 	ldr r1, atomic_clr
    198 	add r1, r1, #0x4
    199 	str r0, [r3, r1] // PLL: PWR
    200 	// wait vco to lock
    201 wait_vco:
    202 	ldr r0, [r3, #0] // PLL: CS
    203 	lsl r0, r0, #31
    204 	beq wait_vco
    205 	// setup post dividers
    206 	ldr r0, =(4 &lt;&lt; 16 | 3 &lt;&lt; 12)
    207 	str r0, [r3, #0xc] // PLL: PRIM
    208 	// power on post divider
    209 	mov r0, #8 // POSTDIVPD
    210 	str r0, [r3, r1] // PLL: PWR
    211 
    212 	// set system clock clksrc_pll_sys
    213 	ldr r3, clocks_base
    214 	ldr r0, =(0x0 &lt;&lt; 5 | 0x1)
    215 	str r0, [r3, #0x3c] // CLOCKS: CLK_SYS_CTRL
    216 	// enable clk_peri
    217 	mov r0, #1
    218 	lsl r0, r0, #11
    219 	str r0, [r3, #0x48] // CLOCKS: CLK_PERI_CTRL
    220 
    221 /* ... */
    222 
    223 atomic_clr:
    224 	.word 0x00003000
    225 clocks_base:
    226 	.word 0x40008000
    227 pll_sys_base:
    228 	.word 0x40028000
    229 </code></pre>
    230 
    231 <h3>UARTの設定</h3>
    232 <p>
    233 データシートによるとUART設定の手順は以下の通り:
    234 </p>
    235 <ul>
    236 <li>リセットの解除</li>
    237 <li>clock_periの設定</li>
    238 <li>UARTの有効化</li>
    239 <li>FIFOの有効化</li>
    240 <li>転送速度の設定</li>
    241 <li>フォーマットの設定</li>
    242 </ul>
    243 <p>
    244 上の2つは既に終えている。\
    245 残りの部分はこの順番どおりに設定しても動かなかった。\
    246 C言語で書かれたサンプルを見ると、クロックを設定した後、\
    247 転送速度の設定、UARTの有効化、FIFOの有効化の順になっている。\
    248 そのとおりにすると動いた。\
    249 理由はよく理解していないが、変数を設定してから起動するほうが素直ではある。\
    250 </p>
    251 <p>
    252 転送速度はminicomのデフォルトである115200 baudに設定する。\
    253 データシート「4.2.7.1. Baud Rate Calculation」の計算式において、\
    254 クロック周波数を125MHzから133MHzに変えて計算して、\
    255 BRDI=72、BDRF=0.157(=10/64)となる。\
    256 この数値をUART: UARTIBRD、UART: UARTFBRDレジスタにそれぞれ代入する。
    257 </p>
    258 <p>
    259 UARTの有効化はUART: UARTCRレジスタのUARTENビットをセットすることで行う。\
    260 C言語のサンプルでは同じレジスタのRXE、TXEビットもセットしているが、\
    261 この2つはもともと1になっているのでほっといてよさそう。\
    262 </p>
    263 <p>
    264 FIFOの有効化はUART: UARTLCR_HレジスタのFENビットをセットすることで行う。\
    265 また、同じレジスタの他のビットで、データーのフォーマットを設定できる。\
    266 ここではminicomのデフォルトに合わせてWLENを8bitにする。\
    267 </p>
    268 <p>
    269 以上をまとめると以下のようになる:
    270 </p>
    271 <pre><code>	// setup uart0
    272 	ldr r3, uart0_base
    273 	// set baudrate 115200
    274 	// BDRI = 72, BDRF = 0.157 (10 / 64)
    275 	mov r0, #72
    276 	str r0, [r3, #0x24] // UART: UARTIBRD
    277 	mov r0, #10
    278 	str r0, [r3, #0x28] // UART: UARTFBRD
    279 	// enable uart0
    280 	mov r0, #1 // UARTEN
    281 	ldr r1, atomic_set
    282 	add r1, r1, #0x30
    283 	str r0, [r3, r1] // UART: UARTCR
    284 	// enable FIFO and set format
    285 	ldr r0, =(3 &lt;&lt; 5 | 1 &lt;&lt; 4) // WLEN = 8, FEN = 1
    286 	str r0, [r3, #0x2c] // UART: UARTLCR_H
    287 
    288 /* ... */
    289 
    290 atomic_set:
    291 	.word 0x00002000
    292 uart0_base:
    293 	.word 0x40034000
    294 </code></pre>
    295 
    296 <h3>UARTの入出力</h3>
    297 <p>
    298 設定が終わったので実際にUARTの入出力を処理するコードを書く。\
    299 まずUARTからの出力は、出力したいバイトをUART: UARTDRに書き込むことで\
    300 行う。\
    301 その際、書き込まれたデータは一時的に出力用FIFOに保持されるので、このFIFO\
    302 が満杯でないことを確認する必要がある。\
    303 FIFOの状態はUART: UARTFRレジスタで確認できる。\
    304 このレジスタのTXFFの値が1であればデータを書き込めないので、\
    305 0になるまで待機する。\
    306 関数名は<code>putchar</code>にした。\
    307 また出力したいデータは<code>r0</code>レジスタにの下位8ビットに\
    308 入れられているものとした。\
    309 書き込めるデーターは8ビットだけなので、<code>0xff</code>と論理積をとってから\
    310 書き込んでいる:
    311 </p>
    312 <pre><code>putchar:
    313 	ldr r3, uart0_base
    314 	mov r1, #1
    315 	lsl r1, r1, #5 // TXFF
    316 txff:
    317 	ldr r2, [r3, #0x18] // UART: UARTFR
    318 	tst r1, r2
    319 	bne txff
    320 	mov r1, #0xff
    321 	and r0, r0, r1
    322 	str r0, [r3, #0] // UART: UARTDR
    323 	bx lr
    324 
    325 /* ... */
    326 
    327 uart0_base:
    328 	.word 0x40034000
    329 </code></pre>
    330 
    331 <p>
    332 入力はUART: UARTDRの下位8ビットを読むことで得られる。\
    333 UARTからの入力は、一時的に入力用FIFOに保存される。\
    334 このFIFOが空の状態でデータを読んでも意味がないので、\
    335 FIFOが空でないことを確認する必要がある。\
    336 これはUART: UARTFRレジスタのRXFEを読むことで確認できる。\
    337 本来は入力があったときに割り込みを発生させて、それまでは\
    338 CPUを休ませるか別の処理をさせておくべきだが、とりあえずここでは\
    339 ループでFIFOの状態を確認し続けている。\
    340 関数名は<code>getchar</code>にした。
    341 読み込んだデータは<code>r0</code>レジスタに保存している:\
    342 </p>
    343 <pre><code>getchar:
    344 	ldr r3, uart0_base
    345 	mov r1, #1
    346 	lsl r1, r1, #4 // RXFE
    347 rxfe:
    348 	ldr r2, [r3, #0x18] // UART: UARTFR
    349 	tst r1, r2
    350 	bne rxfe
    351 	ldr r0, [r3, #0] // UART: UARTDR
    352 	mov r1, #0xff
    353 	and r0, r0, r1
    354 	bx lr
    355 
    356 /* ... */
    357 
    358 uart0_base:
    359 	.word 0x40034000
    360 </code></pre>
    361 <p>
    362 あとはこの2つの関数をループの中で交互に呼び出せば、\
    363 オウム返しするだけのプログラムが完成する:
    364 </p>
    365 <pre><code>loop:
    366 	bl getchar
    367 	bl putchar
    368 	b loop
    369 </code></pre>
    370 
    371 <h2>リング発振回路でUARTは動くんかな?</h2>
    372 <p>\
    373 UARTの通信には正確なクロックが必要である。\
    374 その為上では<code>clk_peri</code>として水晶発振子とPLLを用いた。\
    375 ところがpico-examplesのhello_uartでは<code>main()</code>関数で\
    376 水晶発振子を設定していない。そこでリング発振回路を用いてみたのだが、\
    377 どうもうまく通信できない。出力されている正確な周波数も分からないので\
    378 あきらめることにした。オシロスコープなんていうものは持っていない。\
    379 </p>
    380 
    381 <h3>pico-sdk</h3>
    382 <p>
    383 ところがどうも調べているとSDKを使った場合、\
    384 デフォルトではクロック周波数は125MHzになっているらしい。\
    385 どうやら水晶発振子もPLLも<code>main()</code>が呼ばれる前に\
    386 設定されているようである。\
    387 </p>
    388 <p>
    389 pico-examplesのサンプルプログラムはビルドすると自動で逆アセンブリした\
    390 ファイルを出力してくれる。これを見ると、最初の256バイトは前回説明した\
    391 boot2のコードで、その後ろにベクターテーブルが続く。\
    392 ベクターテーブルの最初は初期スタックポインタで、<code>0x20042000</code>\
    393 になっている。次はエントリーポイントで、<code>0x100001f7</code>である:\
    394 </p>
    395 <pre><code>\
    396 10000100 &lt;__VECTOR_TABLE&gt;:
    397 10000100:	20042000 	.word	0x20042000
    398 10000104:	100001f7 	.word	0x100001f7
    399 </code></pre>
    400 <p>
    401 Thumbモードなので実際のエントリーポイントは<code>1</code>引いた、\
    402 <code>0x100001f6</code>である。この場所ではまず自分のCPUIDを調べて、\
    403 <code>1</code>であれば待機状態に移行する。\
    404 RP2040はデュアルコアである。起動直後はCPUIDが<code>0</code>のコアだけで処理をして、\
    405 CPUIDが<code>1</code>のコアはプログラマが必要に応じて起動することになっている。\
    406 このためCPUIDが<code>1</code>のコアは起動してすぐに待機状態に入ることがデータシート\
    407 に書かれている。しかしこの処理はユーザーの書いたプログラムじゃなくて\
    408 内蔵ROMにある起動用プログラムが担当するみたいに書かれてるんやけど、\
    409 なんでSDKではユーザープログラムの一部として組み込んでるんかな?
    410 </p>
    411 <pre><code>\
    412 100001f6 &lt;_reset_handler&gt;:
    413 100001f6:	481d      	ldr	r0, [pc, #116]	; (1000026c &lt;hold_non_core0_in_bootrom+0xe&gt;)
    414 100001f8:	6800      	ldr	r0, [r0, #0]
    415 100001fa:	2800      	cmp	r0, #0
    416 100001fc:	d12f      	bne.n	1000025e &lt;hold_non_core0_in_bootrom&gt;
    417 </code></pre>
    418 <p>\
    419 上のコードの最初の<code>ldr</code>は、<code>0xd0000000</code>\
    420 (M0PLUS: CPUIDレジスタ)をロードしている。\
    421 最後の飛び先<code>0x1000025e</code>はCPUIDが<code>1</code>のCPUを待機させる処理\
    422 である:\
    423 </p>
    424 <pre><code>\
    425 1000025e &lt;hold_non_core0_in_bootrom&gt;:
    426 1000025e:	4809      	ldr	r0, [pc, #36]	; (10000284 &lt;hold_non_core0_in_bootrom+0x26&gt;)
    427 10000260:	f001 fb9c 	bl	1000199c &lt;rom_func_lookup&gt;
    428 10000264:	4700      	bx	r0
    429 10000266:	0000      	.short	0x0000
    430 /* ... */
    431 10000284:	00005657 	.word	0x00005657
    432 </code></pre>
    433 <p>\
    434 内蔵フラッシュに書きこまれた関数を呼びだしている。呼びだしに使うコードは\
    435 <code>0x00005657</code>(<code>'W' | 'V' &lt;&lt; 8</code>)である。\
    436 データシートを見ると、この関数は<code>_wait_for_vector()</code>という名前で、\
    437 CPUIDが1のCPUを寝かしつけるのに使われると書いている。\
    438 この部分のソースコードをpico-sdkで探すと\
    439 <code>pico-sdk/src/rp2_common/pico_standard_link/crt0.S</code>\
    440 というのが見付かった:\
    441 </p>
    442 <pre><code>\
    443 $ find pico-sdk/src -type f | xargs grep -l _reset_handler
    444 pico-sdk/src/rp2_common/pico_standard_link/crt0.S
    445 </code></pre>
    446 <p>\
    447 このファイルによると:
    448 </p>
    449 <pre><code>	// Only core 0 should run the C runtime startup code; core 1 is normally
    450 	// sleeping in the bootrom at this point but check to be sure
    451 </code></pre>
    452 <p>\
    453 だそうである。やっぱり無駄やん。内蔵フラッシュのプログラムにバグがあっても\
    454 このコードのせいで見付かりにくくなってない?知らんけど。\
    455 </p>
    456 
    457 <p>\
    458 続いて<code>.data</code>領域と<code>.bss</code>領域のコピー、初期化のようである。\
    459 多分OSの本かなんかで習ったメモリマップの話:\
    460 </p>
    461 <pre><code>\
    462 100001fe:	a40d      	add	r4, pc, #52	; (adr r4, 10000234 &lt;data_cpy_table&gt;)
    463 10000200:	cc0e      	ldmia	r4!, {r1, r2, r3}
    464 10000202:	2900      	cmp	r1, #0
    465 10000204:	d002      	beq.n	1000020c &lt;_reset_handler+0x16&gt;
    466 10000206:	f000 f812 	bl	1000022e &lt;data_cpy&gt;
    467 1000020a:	e7f9      	b.n	10000200 &lt;_reset_handler+0xa&gt;
    468 1000020c:	4918      	ldr	r1, [pc, #96]	; (10000270 &lt;hold_non_core0_in_bootrom+0x12&gt;)
    469 1000020e:	4a19      	ldr	r2, [pc, #100]	; (10000274 &lt;hold_non_core0_in_bootrom+0x16&gt;)
    470 10000210:	2000      	movs	r0, #0
    471 10000212:	e000      	b.n	10000216 &lt;bss_fill_test&gt;
    472 
    473 10000214 &lt;bss_fill_loop&gt;:
    474 10000214:	c101      	stmia	r1!, {r0}
    475 
    476 10000216 &lt;bss_fill_test&gt;:
    477 10000216:	4291      	cmp	r1, r2
    478 10000218:	d1fc      	bne.n	10000214 &lt;bss_fill_loop&gt;
    479 </code></pre>
    480 
    481 <p>\
    482 最後にいろいろ呼びだす:\
    483 </p>
    484 <pre><code>\
    485 1000021a &lt;platform_entry&gt;:
    486 1000021a:	4917      	ldr	r1, [pc, #92]	; (10000278 &lt;hold_non_core0_in_bootrom+0x1a&gt;)
    487 1000021c:	4788      	blx	r1
    488 1000021e:	4917      	ldr	r1, [pc, #92]	; (1000027c &lt;hold_non_core0_in_bootrom+0x1e&gt;)
    489 10000220:	4788      	blx	r1
    490 10000222:	4917      	ldr	r1, [pc, #92]	; (10000280 &lt;hold_non_core0_in_bootrom+0x22&gt;)
    491 10000224:	4788      	blx	r1
    492 10000226:	be00      	bkpt	0x0000
    493 10000228:	e7fd      	b.n	10000226 &lt;platform_entry+0xc&gt;
    494 /* ... */
    495 10000278:	10001819 	.word	0x10001819
    496 1000027c:	100002dd 	.word	0x100002dd
    497 10000280:	10001909 	.word	0x10001909
    498 </code></pre>
    499 <p>\
    500 一つめの<code>blx</code>は<code>0x10001818</code>(<code>runtime_init</code>)を、\
    501 二つめは<code>0x100002dc</code>(<code>main</code>)を、\
    502 最後のは<code>0x10001908</code>(<code>exit</code>)を、\
    503 それぞれ呼んでいる。\
    504 この<code>runtime_init</code>はアセンブリでは分かりにくいので\
    505 ソースコードを探してみると、以下のものが見付かった:\
    506 </p>
    507 <pre><code>\
    508 $ find pico-sdk/src -type f | xargs grep -l runtime_init
    509 pico-sdk/src/rp2_common/pico_runtime/runtime.c
    510 pico-sdk/src/rp2_common/pico_standard_link/crt0.S
    511 pico-sdk/src/common/pico_sync/include/pico/mutex.h
    512 </code></pre>
    513 <p>\
    514 最後の<code>mutex.h</code>は関係なさそう。\
    515 二つめの<code>crt0.S</code>は呼びだしてるだけ。\
    516 一つめの<code>runtime.c</code>が多分探しているものである。\
    517 これを見るとまず各種周辺機器を一度リセットし、リセット状態を解除している。\
    518 使わんやつも初期化してない?\
    519 その後<code>clocks_init()</code>を呼んでいる。\
    520 この関数は<code>pico-sdk/src/rp2_common/hardware_clocks/clocks.c</code>\
    521 で定義されている。これを見ると、<code>xosc_init()</code>を呼んで\
    522 水晶発振子を初期化した後、<code>clk_peri</code>を125MHzに設定している:\
    523 </p>
    524 <pre><code>    clock_configure(clk_peri,
    525                     0,
    526                     CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
    527                     125 * MHZ,
    528                     125 * MHZ);
    529 </code></pre>
    530 <p>\
    531 やっぱり水晶発振子じゃないとあかんのかな。\
    532 </p>
    533 
    534 <h2>CMake</h2>
    535 <p>\
    536 上ではビルドしたバイナリを逆アッセンブルして読んだ。\
    537 わざわざこんなことをしなくてもMakefile読めばなにがどう\
    538 なって最終生成物に辿りつくのか分かればいいのだが、そうもいかない。\
    539 このSDKとpico-examplesにはビルドシステムとしてCMakeなるものが使われている。\
    540 これがどうも複雑でよく分からない。勉強する気にもならん。\
    541 上で見た<code>crt0.S</code>や<code>runtime.c</code>といったファイルも\
    542 <code>hello_uart</code>で本当に使われているものなのかもよく分からない。\
    543 こんな煩雑なものは本当に必要なのかな。無駄に複雑にしてるだけとちゃうんかな。\
    544 特に僕は勉強用に使ってるので、ソースコードの依存関係をもっと\
    545 分かりやすくしてくれないと、内部でなにがどうなってるのか理解しにくい。\
    546 何度か頑張って読もうとしたが、面白くないのでやめた。\
    547 数百行のファイルをあっちからこっちから<code>include</code>してるし、\
    548 大文字ばかりの変数だらけで目が痛い。\
    549 こんなものを扱えるというのはえらいええ頭してはるんやね。\
    550 </p>
    551 
    552 
    553 <h2>参考</h2>
    554 <ul>
    555 <li>
    556 <a href="https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf">RP2040 Datasheet.Raspberry Pi Foundation</a>
    557 </li>
    558 <li>
    559 <a href="https://github.com/raspberrypi/pico-sdk">pico-sdk.github</a>
    560 </li>
    561 <li>
    562 <a href="https://developer.arm.com/documentation/ddi0419/c/">ARMv6-M Architecture Reference Manual</a>
    563 </li>
    564 <li>
    565 <a href="https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%BB%E3%82%AA%E3%82%B7%E3%83%AC%E3%83%BC%E3%82%BF">リング・オシレータ.Wikipedia</a>
    566 </li>
    567 <li>
    568 <a href="https://www5.epsondevice.com/ja/information/technical_info/osc.html">水晶発振器とは? 原理と仕組み、水晶振動子との違い、選び方のポイントを解説.エプソン水晶デバイス</a>
    569 </li>
    570 </ul>