www.mtkn.jp

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

rp2040_2.html (23968B)


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