www.mtkn.jp

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

rp2040_2.html (23454B)


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