Penjelasan terperinci tentang proses pemetaan memori STM32 dan bootloading.
Sementara semua contoh di bawah ini adalah untuk STM32F446, prinsip -prinsip dasar berlaku untuk sebagian besar MCU.
STM32CUBEMX menghasilkan STM32F446RETX_FLASH.LD yang akan kami lihat sebagai referensi untuk semua eksplorasi kami.
Linker mengambil file objek yang dihasilkan oleh kompiler dan menghasilkan output kompilasi akhir, yang dalam kasus kami adalah biner .elf . Linker selalu menggunakan file skrip linker; Bahkan jika Anda tidak menentukan satu, skrip default digunakan.
Penting untuk dicatat, bahwa skrip linker hanya menjelaskan memori berdasarkan spesifikasi MCU dan tidak mengubah adressing memori perangkat keras.
Kami tidak dapat berbicara tentang proses bootloader tanpa memahami struktur memori. Sebenarnya itu adalah tujuan dari bootloader untuk memiliki memori dalam keadaan siap untuk menjalankan metode main() aplikasi kami.
Memori program, memori data, register dan port I/O di STM32F4 diatur dalam ruang alamat linier yang sama.
...
+ -- -- 0x2001FFFF -- -- +
| |
| RAM |
| |
+ -- -- 0x20000000 -- -- +
| ... |
+ -- -- 0x1FFF7A0F -- -- +
| |
| System |
| |
+ -- -- 0x1FFF0000 -- -- +
| ... |
+ -- -- 0x081FFFFF -- -- +
| |
| Flash |
| |
+ -- -- 0x08000000 -- -- +
| ... |
+ -- -- 0x001FFFFF -- -- +
| |
| Alias |
| |
+ -- -- 0x00000000 -- -- + Di mana memori alias menunjuk ke memori flash, sistem atau RAM tergantung pada pin BOOT0 . Secara default itu flash.
Dua ruang paling menarik bagi kami untuk saat ini:
Namun, dalam praktiknya, pembagian fungsional dapat bervariasi. Misalnya, Anda mungkin perlu memuat data biner program ke RAM atau sebaliknya.
Struktur memori tercermin dalam file linker. Flash dimulai pada ORIGIN = 0x8000000 dan RAM di ORIGIN = 0x20000000 .
// linker file
MEMORY
{
RAM ( xrw ) : ORIGIN = 0x20000000 , LENGTH = 128 K
FLASH ( rx ) : ORIGIN = 0x8000000 , LENGTH = 512 K
}Perhatikan bahwa ini hanya implementasi default, Anda dapat dengan mudah membagi Flash dan RAM dan menambahkan blok atau bagian Anda sendiri segera setelah mereka mematuhi spesifikasi memori MCU.
Memori kemudian dibagi dalam file linker menjadi beberapa bagian, masing -masing memiliki alamat dan tujuannya (misalnya, flash atau RAM).
// linker file
SECTIONS
{
...
. text :
{
...
} > FLASH
...
}Untuk mengeksplorasi . Tabel simbolf.
arm-none-eabi-objdump -t stm32-boot-explained.elf | sortProgram ini menghasilkan output seperti yang dijelaskan dalam detail dalam manual:
Address Flag_Bits Section Size NameKode program yang dikompilasi masuk ke bagian ini dalam memori flash .
Bagian dimulai dari alamat ORIGIN Flash, dan _etext menunjuk ke alamat terakhir bagian.
08000000 l d . text 00000000 . text
08000 c00 g . text 00000000 _etextKita dapat melihat lokasi fungsi, yang kami gunakan di file bootloader kami:
080005 c8 g F . text 00000048 __libc_init_array
08000610 g F . text 000000d 8 main
080006e8 g F . text 00000064 Reset_Handler
08000b c4 g F . text 00000024 SystemInit Bagian berada di memori RAM dan memegang semua variabel dengan nilai yang ditentukan. Rentang alamat dari _sdata ke _edata .
20000000 g . data 00000000 _sdata
20000010 g . data 00000000 _edataMari kita periksa variabel apa dari Main.c yang berada di bagian ini:
20000000 l O . data 00000004 static_data_int
20000008 g O . data 00000008 data_double Nilai aktual harus diisi oleh skrip bootloader dengan menyalin data dari memori flash di alamat _sidata :
08000 c08 g * ABS * 00000000 _sidata Blokir bagian data simbol RAM untuk semua variabel tanpa nilai yang ditetapkan. Bootloader menangani pengaturan data blok ini ke 0 .
Rentang rentang memori dari _sbss ke _ebss .
20000010 g . bss 00000000 _sbss
20000044 g . bss 00000000 _ebssSemua variabel dari main.c tanpa nilai eksplisit berada di bagian ini:
2000002 c l O . bss 00000004 static_bss_int
20000030 g O . bss 00000008 bss_double
20000038 g O . bss 00000008 bss_my_struct
20000040 g O . bss 00000004 bss_my_unionMari kita periksa dengan GDB :
(gdb) p bss_double
$1 = 0
(gdb) p & bss_double
$2 = (double * ) 0x20000030 < bss_double > Setelah Anda menetapkan beberapa nilai ke variabel, alamatnya tidak berubah dan masih tetap di .bss :
(gdb) p bss_double
$3 = 86
(gdb) p & bss_double
$4 = (double * ) 0x20000030 < bss_double > Semua memori RAM di atas _end dan sampai _estack didedikasikan untuk menumpuk dan menumpuk memori.
20000048 g . _user_heap_stack 00000000 _end
20020000 g . isr_vector 00000000 _estack _estack Alamat dihitung sebagai ORIGIN(RAM) + LENGTH(RAM) . Sehingga untuk RAM 128KB:
_estack = 0x20000000 + 128 * 1024 # dec
= 0x20000000 + 0x20000 # hex
= 0x20020000 Stack adalah struktur LIFO yang dimulai di _estack dan tumbuh ke bawah. Ukuran tumpukan minimum didefinisikan dalam file linker sebagai _Min_Stack_Size . Memori tumpukan secara otomatis dibebaskan.
+ -- -- 0x20020000 -- -- + < -- _estack
| |
| Stack |
| |
+ - - 0x2001FC00 - - + < -- - _Min_Stack_Size
| |
+ -- -- 0x200 sssss -- -- + < -- $msp register
| |
| |
| Free space |
| |
| |
+ -- -- 0x200 hhhhh -- -- + < -- Actual heap end
| |
| Heap |
| |
+ - - 0x20000248 - - + < -- + _Min_Heap_Size
| |
+ -- -- 0x20000048 -- -- + < -- _end Tumpukan pada gilirannya dimulai dari _end dan tumbuh ke atas hingga _estack - _Min_Stack_Size saat diminta oleh malloc .
Kami memiliki variabel stack_int yang ditentukan dalam Main.c. Mari kita periksa alamat dengan GDB setelah diinisialisasi:
(gdb) p & stack_int
$1 = (unsigned short * ) 0x2001ffd6
(gdb) p $msp
$2 = (void * ) 0x2001ffd0 Yang cocok dengan harapan kami, ketika variabel berada di atas $msp .
Perlu disebutkan bahwa implementasi perpustakaan C standar yang biasa digunakan dalam aplikasi C tertanam adalah Newlib. Perpustakaan ini membutuhkan implementasi fungsi spesifik sistem tertentu. STM32CUBEMX menghasilkan file syscalls.c dengan implementasi default yang diperlukan.
Ini juga merupakan kasus untuk panggilan sbrk yang meningkatkan ruang data program. malloc menggunakan fungsi ini untuk mengalokasikan lebih banyak heap memori. Anda dapat menemukan implementasi yang dihasilkan oleh STM32CUBEMX di Sysmem.c. Ini hanya memungkinkan tumpukan tumbuh dari _end hingga _estack - _Min_Stack_Size .
Sekarang ketika kita memahami memori MCU, mari kita terhubung ke program kita dengan GDB , inilah yang kita lihat sebagai output pertama:
...
Reading symbols from ./stm32-boot-explained.elf...
Remote debugging using localhost:61234
Reset_Handler () at %PATH%/bootloader.c:25
25 void Reset_Handler () {
(gdb) info registers
...
pc 0x80006e8 0x80006e8 < Reset_Handler >
... Reset_Handler entah bagaimana diidentifikasi sebagai titik boot untuk aplikasi kami.
Anda mungkin telah memperhatikan bahwa ada instruksi ENTRY dalam file linker:
// linker file
ENTRY ( Reset_Handler ) Sebenarnya, ini menyimpan referensi ke alamat fungsi Reset_Handler di header file .elf :
arm-none-eabi-objdump -t -f stm32-boot-explained.elf | grep " start address "
start address 0x080006e9 Melihat tabel simbol kami, Reset_Handler hadir di bagian flash .text , sama seperti semua fungsi lainnya:
080006e8 g F . text 00000064 Reset_HandlerAlamat diselaraskan halaman, itulah sebabnya ada kemungkinan ketidaksesuaian antara header .elf dan alamat yang sebenarnya.
Meskipun informasi ini sebagian besar digunakan oleh penghubung untuk memverifikasi keberadaan simbol titik masuk dalam kode, ia tidak memiliki makna praktis untuk MCU.
Menurut spesifikasi STM32, CPU mengambil nilai _estack top-of-stack dari alamat 0x00000000 , kemudian memulai eksekusi kode dari memori boot mulai dari 0x00000004 .
+ -- -- 0x001FFFFF -- -- +
| |
| Alias |
| |
+ -- -- 0x00000000 -- -- + Di sinilah memori alias yang disebutkan di atas didefinisikan. Dengan konfigurasi default, ketika nilai pin BOOT0 = 0 , alias ke blok memori flash mulai dari 0x8000000 .
Opsi lain berdasarkan BOOT0 dan BOOT1 termasuk memori sistem dengan bootloader tertanam atau memori RAM. Bootloader tertanam diprogram oleh ST selama produksi dan di luar ruang lingkup manual ini.
Tapi Reset_Handler memiliki alamat 0x08000524 yang bukan awal dari memori flash, bagaimana MCU menemukan metode bootstrap?
Di sinilah Table Vector ikut bermain.
08000000 g O . isr_vector 000001 c4 Vector_Table MCU memperlakukan awal memori sebagai tabel vektor, yang berisi pointer untuk berbagai rutinitas layanan interupsi dan fungsi startup yang penting, termasuk Reset_Handler . Konsultasikan dengan spek untuk melihat struktur tabel yang tepat yang diharapkan MCU memuat dari 0x00000000 . Tabel yang sebenarnya harus diisi oleh bootloader.
| Alamat | Nama |
|---|---|
| 0x00000000 | Disimpan |
| 0x00000004 | Reset Handler |
| 0x00000008 | Interupsi yang tidak bisa menutupi |
| 0x00000012 | Kesalahan keras |
| ... | Interupsi lainnya |
Reset_Handler ini adalah fungsi bootloader yang dapat digunakan untuk banyak aplikasi, dari tugas khusus keamanan hingga pembaruan otomatis firmware. Di sini, kami akan menjelajahi implementasi default dasar untuk memahami interaksinya dengan memori MCU.
Secara default Reset_Handler metode didefinisikan dalam file asm startup_stm32f436xx.s yang disediakan oleh stm23cubemx. Implementasi bootloader.c aktual dalam proyek ini ditulis dalam C untuk kejelasan.
Perhatikan bahwa variabel yang ditentukan dalam skrip linker dapat diakses dalam kode C:
extern uint32_t _estack ;Sehingga mudah untuk mereplikasi versi ASM.
Proses pemuatan minimal kemudian dapat dibagi menjadi langkah -langkah berikut:
SystemInit() ).data dari flash ke RAM.bss__libc_init_array() Function)main() )Untuk Langkah #1 dan #4, STM32CUBEMX menyediakan implementasi fungsi, Anda dapat memeriksa detail di System_STM32F4XX.C.
Proyek ini memiliki satu set file minimal yang diperlukan untuk mem -boot STM32. Anda mungkin ingin mencobanya sendiri untuk memeriksa output dari ARM-NONE-EABI-OBJDUMP dan melangkah dengan GDB .
ARM GNU Toolchain diperlukan untuk membangun proyek.
Disarankan untuk menginstal paket alat perintah STM32CUBECLT all-in-one dengan ARM-none-eabi-GCC , STM32_PROGRAMMER_CLI dan alat ST-Link_GDBServer disertakan.
Bangun proyek menggunakan cmake :
mkdir build ; cd build
cmake ../ -DPROGRAMMER_CLI=/opt/ST/STM32CubeCLT_1.15.1/STM32CubeProgrammer/bin
-DGDB_SERVER=/opt/ST/STM32CubeCLT_1.15.1/STLink-gdb-server/bin
make VERBOSE=1Perhatikan bahwa perintah terakhir adalah tahap tautan. Jika kita melepas semua bendera kompiler lainnya, perintah itu bisa terlihat seperti berikut. Di sinilah penghubung diinstruksikan untuk menggunakan skrip linker kami.
arm-none-eabi-gcc
...
-T " %SCRIPT_DIR%/STM32F446RETx_FLASH.ld "
...
" %OBJ_DIR%/%OBJECT_NAME%.c.obj "
...
-o stm32-boot-explained.elfStm32_programmer_cli sudah dikonfigurasikan sebelumnya untuk prokotol SWD, cukup jalankan:
make flash Perhatikan output programmer, yang menggunakan alamat 0x08000000 sebagai titik awal:
...
Memory Programming ...
Opening and parsing file: stm32-boot-explained.elf
File : stm32-boot-explained.elf
Size : 1,46 KB
Address : 0x08000000
...Ini sebenarnya alamat awal dari memori flash, yang sudah kita ketahui.
Ada target khusus yang dikonfigurasi sebelumnya untuk menjalankan st-link_gdbserver :
# start ST-Link gdb server
make gdb-server
# connect with gdb debugger
gdb -ex ' target remote localhost:61234 ' ./stm32-boot-explained.elf