重置向量(reset vector)
當你打開微控制器的電源開關時,處理器完全不曉得你的程式是什麼、從哪裡開始。它需要一個固定、事先約定好的地方去查看,告訴它「從這裡開始跑」。那個固定的指引就是重置向量:一個刻在晶片設計裡的已知位址,處理器在重置後會在那裡找到你程式中要最先執行的那段碼的位址。
在 ARM Cortex-M 晶片上這非常具體。記憶體最底部,晶片預期會有一張小小的表。最前面的 4 個位元組(位於重置位址,常是別名到快閃的 0x00000000)放的是要載入堆疊指標的初始值。「接下來」的 4 個位元組放的是重置向量:一個指向重置處理常式(reset handler,要最先執行的函式)的 32 位元位址。所以重置時硬體自動做兩件事:先從第 0 個字組載入堆疊指標,再跳到第 1 個字組裡的位址。那個處理常式通常是你的啟動程式碼(有時叫 Reset_Handler),它先準備好記憶體,然後呼叫 main()。在較舊的處理器上,「重置向量」則是一個固定位置,內含一道跳躍指令,但概念完全相同:CPU 最先讀取的一個硬接位址。
它之所以重要,是因為它是從原始矽片通往你軟體的唯一橋樑——若重置向量錯了、不見了,或你的程式沒放在向量所指之處,晶片就根本無法啟動(可能卡死、觸發錯誤或執行垃圾)。常見的陷阱:重置向量與初始堆疊指標通常由你的連結器指令稿與啟動檔放在硬體會讀取的確切位址,所以你很少手寫這個值,但你必須確保你的向量表真的位於晶片查看的位址——這個擺放搞錯,你寫的其他一切都永遠不會執行。
// Cortex-M 向量表的前兩個字組(放在快閃起始處): __attribute__((section(".isr_vector"))) const uint32_t vectors[] = { (uint32_t)&_estack, // 第 0 字組:初始堆疊指標 (uint32_t)&Reset_Handler, // 第 1 字組:重置向量 /* ... NMI、HardFault,接著各 IRQ ... */ }; // 上電時:SP <- vectors[0];PC <- vectors[1]。
在 Cortex-M 上,硬體從第 0 字組載入堆疊指標,並跳到第 1 字組的重置向量——這發生在你任何 C 程式碼執行之前。
在 Cortex-M 上,重置位址的第一個字組是初始堆疊指標,重置向量是「第二」個字組——順序或擺放搞錯,晶片在還沒抵達 main() 之前就會觸發錯誤。