Apa yang dimaksudkan / cara yang benar untuk menangani interupsi dan menggunakan instruksi cpu risc-v WFI?
Saya sangat baru dalam pemrograman bare metal dan tidak pernah delt dengan interupsi sebelumnya, tapi saya telah belajar di papan dev bertenaga RISC-V FE310-G002 SOC.
Saya telah membaca tentang instruksi RISC-V WFI (Tunggu interupsi) dan dari manual, sepertinya Anda tidak dapat mengandalkannya untuk benar-benar tidur inti. Sebaliknya, ini hanya menyarankan bahwa eksekusi dapat dihentikan ke sistem dan bahwa instruksi tersebut harus diperlakukan lebih seperti NOP. Namun, ini sepertinya tidak berguna bagi saya. Pertimbangkan cuplikan program ASM berikut:
wfi_loop:
WFI
J wfi_loop
Ini harus dilakukan karena WFI tidak bisa diandalkan. Namun, setelah MRET dari penangan interupsi, Anda masih akan terjebak dalam loop. Jadi Anda harus membuatnya bersyarat terhadap variabel global yang nilainya diperbarui di penangan interupsi. Ini sepertinya sangat berantakan.
Juga, jika implementasi Anda benar-benar menghormati instruksi WFI dan interupsi dipicu tepat sebelum eksekusi instruksi WFI, seluruh inti akan terhenti sampai beberapa interupsi lainnya dipicu karena akan kembali sebelum instruksi WFI.
Tampaknya satu-satunya penggunaan instruksi yang benar akan berada di dalam penjadwal kernel ketika tidak ada pekerjaan yang harus diselesaikan. Tetapi meskipun demikian, saya tidak berpikir Anda ingin kembali dari penangan interupsi ke kode seperti itu, melainkan memulai ulang algoritme penjadwal dari awal. Tetapi itu juga akan menjadi masalah karena Anda entah bagaimana harus memutar kembali tumpukan, dll ....
Saya terus berputar-putar dengan ini di kepala saya dan saya tidak bisa menemukan cara yang aman untuk digunakan. Mungkin, jika Anda secara atomik, aktifkan interupsi dengan CSRRS dan kemudian segera panggil WFI seperti ini:
CSRRSI zero, mie, 0x80
wfi_loop:
WFI
J wfi_loop
NOP
NOP
Kemudian pastikan untuk menambah mepc register sebesar 8 byte sebelum memanggil MRET dari penangan interupsi. Interupsi juga harus dinonaktifkan lagi di register mie di dalam pawang interupsi sebelum kembali. Solusi ini hanya akan aman jika WFI, J, dan NOP semuanya dikodekan sebagai instruksi 4 byte, terlepas dari apakah instruksi terkompresi digunakan. Ini juga tergantung pada penghitung program yang mencapai instruksi WFI sebelum memungkinkan untuk memicu interupsi, setelah diaktifkan oleh instruksi CSRRSI. Ini kemudian akan memungkinkan interupsi untuk dipicu di tempat yang aman dalam kode dan untuk kembali sedemikian rupa sehingga keluar dari loop yang menunggunya.
Saya kira saya hanya mencoba memahami perilaku apa yang dapat saya harapkan dari perangkat keras dan, oleh karena itu, bagaimana cara memanggil dan kembali dengan benar dari interupsi dan menggunakan instruksi WFI?
Jawaban
Harus ada satu tugas / utas / proses yang untuk pemalasan, dan itu harus terlihat seperti kode pertama Anda.
Karena utas menganggur diatur untuk memiliki prioritas terendah, jika utas menganggur sedang berjalan, itu berarti tidak ada utas lain untuk dijalankan atau semua utas lainnya diblokir.
Ketika terjadi interupsi yang membuka blokir beberapa utas lainnya, rutinitas layanan interupsi harus melanjutkan utas yang diblokir itu, bukan utas diam yang terputus.
Perhatikan bahwa utas yang memblokir IO itu sendiri juga terputus - itu terputus melalui penggunaannya sendiri ecall
. Pengecualian itu adalah permintaan untuk IO dan menyebabkan utas ini diblokir - tidak dapat dilanjutkan hingga permintaan IO dipenuhi.
Dengan demikian, utas yang diblokir pada IO ditangguhkan sama seperti jika itu terganggu - dan interupsi jam atau interupsi IO mampu melanjutkan proses yang berbeda dari yang langsung terputus, yang akan terjadi jika proses idle sedang berjalan dan beberapa peristiwa yang proses sedang menunggu terjadi.
Apa yang saya lakukan adalah menggunakan scratch
csr untuk menunjuk ke blok konteks untuk proses / utas yang sedang berjalan. Saat interupsi, saya menyimpan jumlah register paling sedikit yang diperlukan untuk (mulai) melayani interupsi. Jika hasil interupsi dalam beberapa proses / utas lain menjadi dapat dijalankan, maka ketika melanjutkan dari interupsi, saya memeriksa prioritas proses, dan dapat memilih sakelar konteks daripada melanjutkan apa pun yang terputus. Jika saya melanjutkan apa yang terputus, ini adalah pemulihan cepat. Dan untuk mengganti konteks, saya selesai menyimpan konteks CPU utas yang terputus, lalu melanjutkan proses / utas lain, mengganti scratch
register.
(Untuk interupsi bersarang, saya tidak mengizinkan sakelar konteks pada resume, tetapi pada interupsi setelah menyimpan konteks saat ini, saya menyiapkan scratch
csr ke tumpukan interupsi blok konteks sebelum mengaktifkan kembali interupsi prioritas yang lebih tinggi. Juga, sebagai yang sangat kecil optimasi kita dapat mengasumsikan bahwa utas diam tertulis khusus tidak memerlukan apa pun kecuali pcnya disimpan / dipulihkan.)
Jadi Anda harus membuatnya bersyarat terhadap variabel global yang nilainya diperbarui di penangan interupsi.
Anda harus melakukan itu terlepas dari pelaksanaannya wfi
karena Anda tidak tahu peristiwa apa yang menyebabkan jantung itu bangun.
Anda mungkin memiliki n interupsi yang diaktifkan saat menjalankan wfi
dan salah satunya mungkin telah dimunculkan.
wfi
adalah pengoptimalan , menghemat daya hingga terjadi sesuatu. Seperti yang Anda catat, penjadwal OS mungkin menemukan dirinya dalam kondisi bahwa tidak ada utas yang dapat dijadwalkan (misalnya mereka semua menunggu IO atau hanya tidak ada) dalam hal itu harus melakukan sesuatu seperti (dengan semua semantik visibilitas dan atomisitas yang diperlukan):
while ( ! is_there_a_schedulable_thread());
Itu hanya menunggu .
Namun alih-alih memutar putaran yang ketat (yang dapat mengganggu kinerja dan daya), penjadwal dapat menggunakan:
while ( ! is_there_a_schedulable_thread())
{
__wfi();
}
Paling buruk itu seperti loop ketat, paling-paling itu akan menghentikan hart sampai interupsi eksternal terjadi (artinya berpotensi IO selesai dan dengan demikian utas mungkin bebas untuk berjalan).
Bahkan dalam kasus tidak ada utas, bangun setiap x mikrodetik (karena interupsi pengatur waktu) lebih baik daripada membuang-buang daya.
wfi
juga dapat berguna pada pemograman embedding jika Anda kebetulan memiliki semua pekerjaan pada penangan interupsi (misalnya ketika tombol ditekan atau serupa).
Dalam kasus ini, main
fungsi hanya akan mengulang selamanya, seperti penjadwal tetapi tanpa kondisi keluar.
Sebuah wfi
instruksi akan sangat meningkatkan masa pakai baterai.
Anda tidak dapat menggunakannya di wfi
mana-mana atau Anda mungkin menemukan diri Anda menunggu interupsi yang tidak pernah terjadi (sebenarnya, ini adalah instruksi yang diistimewakan).
Anggap saja sebagai pengoptimalan untuk berkoordinasi dengan perangkat keras.
Secara khusus, ini tidak dirancang sebagai cara untuk memastikan interupsi diaktifkan:
void wait_for_int(int int_num)
{
//Leave only interrupt int_num enabled
enable_only_int(int_num);
__wfi();
restore_interrupts();
}
Ini bisa digunakan dengan cara itu mengingat implementasi spesifik RISC-V tetapi seperti yang Anda lihat dari pseudo-code, itu tidak terlalu nyaman.
Menonaktifkan semua kecuali satu interupsi umumnya adalah sesuatu yang tidak mampu dilakukan oleh OS.
Aplikasi yang disematkan bisa.