Saya telah mengembangkan terinspirasi dari sini kode start-up logam telanjang untuk arm cortex M3. Namun, saya menghadapi masalah berikut: misalkan saya mendeklarasikan variabel global yang tidak diinisialisasi, misalnya tipe unsigned char di main.c
#include ...
unsigned char var;
...
int main()
{
...
}
ini membuat wilayah .bss di STM32 f103 mulai dari _BSS_START = 0x20000000 dan berakhir di _BSS_END = 0x20000001. Sekarang, kode mulai
unsigned int * bss_start_p = &_BSS_START;
unsigned int * bss_end_p = &_BSS_END;
while(bss_start_p != bss_end_p)
{
*bss_start_p = 0;
bss_start_p++;
}
mencoba menginisialisasi ke nol seluruh wilayah .bss. Namun, di dalam itu sementara loop pointer meningkat dengan 4 byte, oleh karena itu setelah satu langkah bss_start_p = 0x20000004 maka itu akan selalu berbeda dari bss_end_p yang mengarah ke loop infinite dll.
Apakah ada solusi standar untuk ini? Apakah saya seharusnya "memaksa" entah bagaimana dimensi wilayah .bss menjadi kelipatan 4? Atau haruskah saya menggunakan pointer ke unsigned char untuk berjalan melalui wilayah .bss? Mungkin sesuatu seperti:
unsigned char * bss_start_p = (unsigned char *)(&_BSS_START);
unsigned char * bss_end_p = (unsigned char *)(&_BSS_END);
while(bss_start_p != bss_end_p)
{
*bss_start_p = 0;
bss_start_p++;
}
```
Jawaban:
Seperti yang Anda duga, ini terjadi karena tipe data int yang tidak ditandatangani berukuran 4 byte. Setiap
*bss_start_p = 0;
pernyataan sebenarnya membersihkan empat byte dari area bss.Rentang memori bss perlu disejajarkan dengan benar. Anda cukup mendefinisikan _BSS_START dan _BSS_END sehingga ukuran totalnya adalah kelipatan empat, tetapi ini biasanya ditangani dengan memungkinkan skrip tautan untuk menentukan lokasi mulai dan berhenti.
Sebagai contoh, inilah bagian tautan di salah satu proyek saya:
The
ALIGN(4)
pernyataan mengurus hal-hal.Juga, Anda mungkin ingin berubah
while(bss_start_p != bss_end_p)
untuk
while(bss_start_p < bss_end_p)
.Ini tidak akan mencegah masalah (karena Anda mungkin menghapus 1-3 byte lebih banyak dari yang Anda inginkan), tetapi itu bisa meminimalkan dampak :)
sumber
while(bss_start_p < bss_end_p - 1)
diikuti oleh kliring byte-bijaksana dari sisa memori yang tersisa akan menghilangkan kekhawatiran terakhir.Solusi standar adalah
memset()
:Jika Anda tidak dapat menggunakan pustaka standar, maka Anda harus memutuskan apakah tidak masalah dalam kasus Anda untuk membulatkan ukuran area memori hingga 4 byte dan terus menggunakan
unsigned int *
; atau jika Anda harus tegas tentang hal itu, dalam hal ini Anda harus menggunakannyaunsigned char *
.Jika Anda benar-benar mengumpulkan ukuran, seperti pada putaran pertama Anda, maka
bss_start_p
memang mungkin berakhir lebih besar daribss_end_p
tetapi itu mudah untuk berurusan dengan perbandingan yang lebih rendah daripada<
tes ketidaksetaraan.Tentu saja, Anda juga dapat mengisi sebagian besar area memori dengan transfer 32-bit, dan hanya beberapa byte terakhir dengan transfer 8-bit, tetapi itu lebih berfungsi untuk mendapatkan sedikit keuntungan, terutama di sini jika hanya sepotong kode startup.
sumber
memset()
. Tapi penyelarasan ke 4 byte lebih atau kurang harus. Jadi mengapa tidak melakukannya?memset()
, dan C adalah apa yang tampaknya mereka pemrograman. Implementasi sederhanamemset()
juga cukup banyak hanya loop itu, tidak seperti itu tergantung pada banyak hal lain. Karena itu adalah mikrokontroler, saya juga berasumsi bahwa tidak ada tautan dinamis atau semacamnya yang terjadi (dan melihat tautannya, tidak ada, itu hanya panggilan untukmain()
setelah pengulangan zeroing), sehingga kompiler harus mampu menjatuhkanmemset()
di sana bersama dengan àny fungsi lain (atau untuk mengimplementasikannya sebaris).Ubah saja
!=
ke<
. Itu biasanya pendekatan yang lebih baik, karena ini berhubungan dengan masalah seperti ini.sumber
Ada banyak situs dan contoh lainnya. Ribuan jika tidak puluhan ribu. Ada c library yang terkenal dengan skrip linker dan kode boostrap, newlib, glibc khususnya tetapi ada yang lain yang dapat Anda temukan. Bootstraping C dengan C tidak masuk akal.
Pertanyaan Anda telah dijawab, Anda mencoba melakukan perbandingan tepat pada hal-hal yang mungkin tidak tepat, mungkin tidak dimulai pada batas yang diketahui atau berakhir pada batas yang diketahui. Jadi Anda dapat melakukan hal yang kurang dari itu tetapi jika kode tidak bekerja dengan perbandingan yang tepat maka itu berarti Anda mem-posting .bss ke bagian berikutnya yang mungkin atau mungkin tidak menyebabkan hal-hal buruk terjadi, jadi cukup ganti dengan yang kurang dari bukan solusinya.
Jadi begini TL; DR baik-baik saja. Anda tidak mem-bootstrap bahasa dengan bahasa itu, Anda bisa lolos dengan itu, tetapi Anda bermain dengan api saat melakukannya. Jika Anda baru belajar bagaimana melakukan ini, Anda harus berhati-hati, bukan keberuntungan bodoh atau fakta yang belum Anda temukan.
Script linker dan kode bootstrap memiliki hubungan yang sangat intim, mereka menikah, bergabung di pinggul, Anda tidak mengembangkan satu tanpa yang lain yang menyebabkan kegagalan besar. Dan sayangnya skrip linker ditentukan oleh linker dan bahasa assembly didefinisikan oleh assembler sehingga Anda mengubah toolchain berharap harus menulis ulang keduanya. Mengapa bahasa assembly? Tidak perlu bootstrap, bahasa yang dikompilasi umumnya dilakukan. C tidak jika Anda tidak ingin membatasi penggunaan langauge, saya akan mulai dengan sesuatu yang sangat sederhana yang memiliki persyaratan spesifik toolchain minimal, Anda tidak menganggap. , cobalah untuk menghindarinya, ini tidak berlaku untuk variabel lokal, jadi Anda harus tahu kapan Anda menggunakannya. jadi mengapa kita berbicara tentang .bs dan .data ??? (global bagus untuk pekerjaan level ini tapi itu topik lain)) aturan lain untuk solusi sederhana adalah jangan menginisialisasi variabel dalam deklarasi, lakukan dalam kode. ya membakar lebih banyak flash, Anda biasanya memiliki banyak, tidak semua variabel diinisialisasi dengan konstanta pula yang akhirnya menghabiskan instruksi.
Anda dapat mengetahui dari desain cortex-m bahwa mereka mungkin berpikir tidak ada kode bootstrap sama sekali sehingga tidak ada .data atau .bss yang mendukung. Kebanyakan orang yang menggunakan global tidak dapat hidup tanpa hal ini:
Saya bisa membuat ini lebih minimal tetapi contoh fungsional minimal untuk semua korteks-ms menggunakan gnu toolchain, saya tidak ingat versi apa yang bisa Anda mulai dengan 5.xx atau lebih hingga 9.xx saat ini. Saya mengganti skrip linker di suatu tempat sekitar 3. xx atau 4.xx ketika saya belajar lebih banyak dan ketika gnu mengubah sesuatu yang merusak yang pertama saya.
bootstrap:
titik masuk ke dalam kode C:
skrip tautan.
Semua ini bisa lebih kecil dan masih berfungsi, menambahkan beberapa hal tambahan di sini hanya untuk melihatnya di tempat kerja.
build dan tautan yang dioptimalkan.
untuk beberapa vendor Anda ingin menggunakan 0x08000000 atau 0x01000000 atau alamat serupa lainnya saat flash dipetakan di sana dan dicerminkan ke 0x00000000 dalam beberapa mode boot. beberapa hanya memiliki banyak flash yang dicerminkan pada 0x00000000 sehingga Anda ingin memiliki titik tabel vektor pada ruang flash aplikasi bukan nol. karena ini adalah tabel vektor semua bekerja.
Catatan pertama, korteks-ms adalah mesin ibu jari saja dan untuk alasan apa pun mereka menerapkan alamat fungsi ibu jari, yang berarti lsbit aneh. Ketahui alat Anda, arahan .thumb_func memberi tahu assembler gnu bahwa label berikutnya adalah alamat fungsi ibu jari. melakukan hal +1 dalam tabel akan menyebabkan kegagalan, jangan tergoda untuk melakukannya, lakukan dengan benar. ada cara assembler gnu lain untuk menyatakan fungsi ini adalah pendekatan minimal.
itu tidak akan boot jika Anda tidak mendapatkan tabel vektor dengan benar.
bisa dibilang hanya perlu stack pointer vector (bisa meletakkan apa saja di sana jika Anda ingin mengatur stack pointer sendiri dalam kode) dan reset vektor. Saya menempatkan empat di sini tanpa alasan tertentu. Biasanya menempatkan 16 tetapi ingin mempersingkat contoh ini.
Jadi, apa yang minimal yang perlu dilakukan bootstrap C? 1. mengatur penunjuk tumpukan 2. nol .bss 3. menyalin. Data 4. cabang ke atau memanggil titik entri C
titik masuk C biasanya disebut main (). tetapi beberapa toolchain melihat main () dan menambahkan sampah tambahan ke kode Anda. Saya sengaja menggunakan nama yang berbeda. YMMV.
salinan data tidak diperlukan jika ini semua berbasis ram. menjadi mikrokontroler korteks-m secara teknis dimungkinkan tetapi tidak mungkin sehingga salinan data diperlukan ..... jika ada. data.
Contoh pertama saya dan gaya pengkodean adalah untuk tidak bergantung pada data atau .bss, seperti dalam contoh ini. Arm menangani stack pointer sehingga satu-satunya yang tersisa adalah memanggil titik masuk. Saya suka memilikinya sehingga titik masuk dapat kembali, banyak orang berpendapat Anda tidak boleh melakukan itu. Anda bisa melakukan ini:
dan tidak kembali dari centry () dan tidak memiliki kode reset handler.
linker telah meletakkan hal-hal di mana kita bertanya Dan secara keseluruhan kami memiliki program yang berfungsi penuh.
Jadi pertama-tama kerjakan skrip tautan:
menekankan bahwa nama rom dan ram tidak berarti mereka hanya menghubungkan titik-titik untuk penghubung antar bagian.
tambahkan beberapa item sehingga kita dapat melihat apa yang dilakukan alat
tambahkan beberapa item untuk ditempatkan di bagian tersebut. dan dapatkan
inilah hal-hal yang kami cari dalam percobaan itu (perhatikan tidak ada alasan untuk benar-benar memuat atau menjalankan kode apa pun ... ketahui alat Anda, pelajari)
jadi apa yang kita pelajari di sini adalah bahwa posisi variabel sangat sensitif dalam skrip gnu linker. perhatikan posisi data_rom_start vs data_start tetapi mengapa data_end berfungsi? Aku akan membiarkan kamu mencari tahu itu. Sudah mengerti mengapa orang mungkin tidak mau harus mengacaukan dengan skrip linker dan hanya mendapatkan pemrograman sederhana ...
jadi hal lain yang kami pelajari di sini adalah bahwa linker menyelaraskan data_rom_start bagi kami, kami tidak memerlukan ALIGN (4) di sana. Haruskah kita berasumsi bahwa itu akan selalu berhasil?
Perhatikan juga bahwa padded pada jalan keluar ke, kita memiliki 5 byte. Data tetapi padded ke 8. Tanpa ALIGN () kita sudah bisa melakukan copy menggunakan kata-kata. Berdasarkan apa yang kita lihat dengan rantai alat ini di komputer saya hari ini, mungkinkah itu benar untuk masa lalu dan masa depan? Siapa tahu, bahkan dengan ALIGNs perlu memeriksa secara berkala untuk mengonfirmasi beberapa versi baru yang tidak merusak, mereka akan melakukannya dari waktu ke waktu.
dari percobaan itu mari kita beralih ke ini hanya untuk aman.
memindahkan ujung ke dalam agar konsisten dengan apa yang dilakukan orang lain. Dan itu tidak mengubahnya:
satu lagi tes cepat:
memberi
tidak perlu mengisi antara bouncing dan tanda
Ohh, benar, saya ingat sekarang mengapa saya tidak memasukkan _end__ di dalamnya. karena TIDAK BEKERJA.
beberapa kode sederhana, tetapi sangat portabel untuk menikah dengan skrip linker ini
memberi
kita bisa berhenti di situ atau terus berjalan. Jika kita menginisialisasi dalam urutan yang sama dengan skrip linker tidak apa-apa jika kita masuk ke hal berikutnya karena kita belum sampai di sana. dan stm / ldm hanya diperlukan / diinginkan untuk menggunakan alamat yang selaras kata, jadi jika Anda berubah ke:
dengan bss pertama di skrip linker, dan ya Anda mau ble bukan bls.
loop itu akan berjalan lebih cepat. sekarang saya tidak tahu apakah bus ahb bisa lebar 64 bit atau tidak tetapi untuk lengan berukuran penuh Anda ingin menyelaraskan hal-hal ini pada batas 64 bit. empat register ldm / stm pada batas 32 bit tetapi bukan batas 64 bit menjadi tiga transaksi bus terpisah, di mana selaras pada batas 64 bit adalah transaksi tunggal yang menghemat beberapa jam per instruksi.
karena kita melakukan baremetal dan kita sepenuhnya bertanggung jawab untuk semua yang kita dapat katakan katakanlah bss pertama maka data maka jika kita menumpuk melakukan itu maka tumpukan tumbuh dari atas ke bawah, jadi jika kita nol bss dan menumpahkan beberapa selama kita mulai dari tempat yang tepat yang baik-baik saja kita belum menggunakan memori itu. lalu kita salin. data lebih dan dapat tumpah ke tumpukan itu baik-baik saja, tumpukan atau tidak ada banyak ruang untuk tumpukan sehingga kita tidak menginjak siapa pun / apa pun (selama kita memastikan dalam skrip linker kita melakukan itu. jika ada kekhawatiran membuat ALIGN () lebih besar sehingga area kami selalu berada dalam ruang kami untuk pengisian ini.
jadi solusi sederhana saya, ambil atau tinggalkan. selamat datang untuk memperbaiki bug, saya tidak menjalankan ini pada perangkat keras maupun simulator saya ...
kumpulkan semuanya dan Anda mendapatkan:
perhatikan bahwa ini bekerja dengan arm-none-eabi- dan arm-linux-gnueabi dan varian lainnya karena tidak ada hal jagoan ghee yang digunakan.
Anda akan menemukan ketika Anda melihat-lihat bahwa orang-orang akan menjadi gila dengan hal-hal jagoan ghee di skrip linker mereka, hal-hal besar wastafel dapur. Lebih baik hanya tahu bagaimana melakukannya (atau lebih baik cara menguasai alat sehingga Anda dapat mengontrol apa yang terjadi) daripada mengandalkan barang-barang orang lain dan tidak tahu di mana itu akan rusak karena Anda tidak mengerti dan / atau ingin meneliti Itu.
sebagai aturan umum jangan bootstrap bahasa dengan bahasa yang sama (bootstrap dalam arti ini menjalankan kode tidak mengkompilasi kompiler dengan kompiler yang sama) Anda ingin menggunakan bahasa yang lebih sederhana dengan lebih sedikit bootstrap. Itulah sebabnya C dilakukan dalam perakitan, tidak memiliki persyaratan bootstrap yang baru Anda mulai dari instruksi pertama setelah reset. JAVA, tentu Anda bisa menulis jvm di C dan bootstrap C dengan asm lalu bootstrap JAVA jika Anda mau dengan C tetapi juga menjalankan JAVA di C juga.
Karena kami mengontrol asumsi pada loop salinan ini, mereka secara definisi lebih ketat dan lebih bersih daripada memcpy / memset disetel dengan tangan.
Perhatikan masalah Anda yang lain adalah ini:
jika ini denda lokal, tidak ada masalah, jika ini global maka Anda perlu. data diinisialisasi pertama untuk mereka untuk bekerja dan jika Anda mencoba trik itu untuk melakukan. data maka Anda akan gagal. Variabel lokal, baik itu akan berfungsi. jika Anda karena suatu alasan memutuskan untuk membuat penduduk lokal yang statis (global saya suka menyebutnya) maka Anda kembali dalam masalah lagi. Setiap kali Anda melakukan tugas dalam deklarasi meskipun Anda harus memikirkannya, bagaimana itu diterapkan dan apakah itu aman / waras. Setiap kali Anda menganggap suatu variabel adalah nol ketika tidak dideklarasikan, kesepakatan yang sama, jika variabel lokal tidak dianggap nol, jika global maka itu adalah. jika Anda tidak pernah menganggapnya nol maka Anda tidak perlu khawatir.
sumber