Apa yang dimaksudkan / cara yang benar untuk menangani interupsi dan menggunakan instruksi cpu risc-v WFI?

Aug 19 2020

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

3 ErikEidt Aug 19 2020 at 18:22

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 scratchcsr 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 scratchregister.

(Untuk interupsi bersarang, saya tidak mengizinkan sakelar konteks pada resume, tetapi pada interupsi setelah menyimpan konteks saat ini, saya menyiapkan scratchcsr 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.)

4 MargaretBloom Aug 19 2020 at 18:22

Jadi Anda harus membuatnya bersyarat terhadap variabel global yang nilainya diperbarui di penangan interupsi.

Anda harus melakukan itu terlepas dari pelaksanaannya wfikarena Anda tidak tahu peristiwa apa yang menyebabkan jantung itu bangun.
Anda mungkin memiliki n interupsi yang diaktifkan saat menjalankan wfidan salah satunya mungkin telah dimunculkan.

wfiadalah 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.

wfijuga dapat berguna pada pemograman embedding jika Anda kebetulan memiliki semua pekerjaan pada penangan interupsi (misalnya ketika tombol ditekan atau serupa).
Dalam kasus ini, mainfungsi hanya akan mengulang selamanya, seperti penjadwal tetapi tanpa kondisi keluar.
Sebuah wfiinstruksi akan sangat meningkatkan masa pakai baterai.

Anda tidak dapat menggunakannya di wfimana-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.