Bagian penting pada Cortex-M3

10

Saya bertanya-tanya sedikit tentang menerapkan bagian kode kritis pada Cortex-M3 di mana pengecualian tidak diperbolehkan karena kendala waktu atau masalah konkurensi.

Dalam kasus saya, saya menjalankan LPC1758 dan saya memiliki transceiver TI CC2500. CC2500 memiliki pin yang dapat digunakan sebagai jalur interupsi untuk data dalam buffer RX dan ruang kosong di buffer TX.

Sebagai contoh, saya ingin memiliki buffer TX di SRAM MCU saya dan ketika ada ruang kosong di buffer TX transceiver, saya ingin menulis data ini di sana. Tetapi rutinitas yang menempatkan data dalam buffer SRAM jelas tidak dapat diinterupsi oleh interupsi ruang-bebas-TX. Jadi yang ingin saya lakukan adalah untuk menonaktifkan sementara interupsi saat melakukan prosedur ini untuk mengisi buffer ini tetapi memiliki interupsi yang terjadi selama prosedur ini dijalankan setelah selesai.

Bagaimana ini dilakukan paling baik di Cortex-M3?

Emil Eriksson
sumber

Jawaban:

11

Cortex M3 mendukung pasangan operasi operasi yang berguna (umum di banyak mesin lain juga) yang disebut "Load-Exclusive" (LDREX) dan "Store-Exclusive" (STREX). Secara konseptual, operasi LDREX melakukan beban, juga menetapkan beberapa perangkat keras khusus untuk mengamati apakah lokasi yang dimuat mungkin ditulis oleh sesuatu yang lain. Melakukan STREX ke alamat yang digunakan oleh LDREX terakhir akan menyebabkan alamat itu ditulis hanya jika tidak ada yang menulisnya terlebih dahulu . Instruksi STREX akan memuat register dengan 0 jika toko berlangsung, atau 1 jika dibatalkan.

Perhatikan bahwa STREX sering pesimistis. Ada berbagai situasi di mana mungkin memutuskan untuk tidak melakukan toko bahkan jika lokasi yang dimaksud sebenarnya belum tersentuh. Misalnya, interupsi antara LDREX dan STREX akan menyebabkan STREX menganggap lokasi yang sedang ditonton mungkin terkena. Untuk alasan ini, biasanya ide yang baik untuk meminimalkan jumlah kode antara LDREX dan STREX. Misalnya, pertimbangkan sesuatu seperti berikut:

inline void safe_increment (uint32_t * addr)
{
  uint32_t new_value;
  melakukan
  {
    new_value = __ldrex (addr) + 1;
  } while (__ strex (new_value, addr));
}

yang mengkompilasi ke sesuatu seperti:

; Asumsikan R0 menyimpan alamat yang dimaksud; r1 dibuang
lp:
  ldrex r1, [r0]
  tambahkan r1, r1, # 1
  strex r1, r1, [r0]
  cmp r1, # 0; Tes jika tidak nol
  bp l
  .. kode berlanjut

Sebagian besar waktu kode dijalankan, tidak ada yang akan terjadi antara LDREX dan STREX untuk "mengganggu" mereka, sehingga STREX akan berhasil tanpa basa-basi lagi. Namun, jika interupsi terjadi segera mengikuti instruksi LDREX atau ADD, STREX tidak akan melakukan store, tetapi kode akan kembali untuk membaca nilai (mungkin diperbarui) dari [r0] dan menghitung nilai tambahan baru. berdasarkan itu.

Menggunakan LDREX / STREX untuk membentuk operasi seperti safe_increment memungkinkan untuk tidak hanya mengelola bagian kritis, tetapi juga dalam banyak kasus untuk menghindari kebutuhan mereka.

supercat
sumber
Jadi tidak ada cara "memblokir" interupsi sehingga mereka dapat dilayani lagi setelah diblokir? Saya menyadari bahwa ini mungkin solusi yang tidak bagus bahkan jika mungkin tetapi saya hanya ingin belajar lebih banyak tentang penanganan interupsi ARM.
Emil Eriksson
3
Dimungkinkan untuk menonaktifkan interupsi, dan pada Cortex-M0 seringkali tidak ada alternatif praktis untuk melakukannya. Saya menganggap pendekatan LDREX / STREX lebih bersih daripada menonaktifkan interupsi, meskipun diakui dalam banyak kasus itu tidak terlalu penting (saya pikir mengaktifkan dan menonaktifkan akhirnya menjadi satu siklus masing-masing, dan menonaktifkan interupsi selama lima siklus mungkin bukan masalah besar) . Perhatikan bahwa pendekatan ldrex / strex akan berfungsi jika kode dimigrasi ke CPU multi-core, sedangkan pendekatan yang menonaktifkan interupsi tidak akan. Juga, beberapa kode jalankan RTOS dengan izin dikurangi yang tidak diizinkan untuk menonaktifkan interupsi.
supercat
Saya mungkin akan berakhir dengan FreeRTOS, jadi saya tidak akan melakukan ini sendiri, tetapi saya ingin tetap belajar. Metode apa yang menonaktifkan interupsi yang harus saya gunakan untuk memblokir interupsi seperti yang dijelaskan sebagai lawan untuk menghilangkan interupsi yang terjadi selama prosedur? Bagaimana saya melakukannya jika saya ingin membuangnya?
Emil Eriksson
Satu jawaban di atas tidak dapat dipercaya karena kode yang terkait tidak memiliki tanda kurung: while(STREXW(new_value, addr); Bagaimana kami dapat meyakini apa yang Anda katakan benar jika kode Anda bahkan tidak dapat dikompilasi?
@Tim: Maaf pengetikan saya tidak sempurna; Saya tidak memiliki kode aktual yang saya tulis berguna untuk perbandingan, jadi saya tidak ingat apakah sistem yang saya gunakan menggunakan STREXW atau __STREXW, tetapi daftar referensi kompiler __strex sebagai intrinsik (tidak seperti STREXW yang terbatas pada STREX 32-bit, penggunaan intrinsik __strex menghasilkan STREXB, STREXH, atau STREX tergantung pada ukuran pointer yang disediakan)
supercat
4

Sepertinya Anda memerlukan beberapa buffer bundar atau FIFO di perangkat lunak MCU Anda. Dengan melacak dua indeks atau petunjuk ke dalam array untuk membaca dan menulis, Anda dapat memiliki latar depan dan latar belakang mengakses buffer yang sama tanpa gangguan.

Kode latar depan bebas untuk menulis ke buffer lingkaran kapan saja. Ini memasukkan data pada pointer tulis, kemudian menambahkan pointer tulis.

Kode latar belakang (penanganan interupsi) mengkonsumsi data dari pointer baca dan menambah pointer baca.

Ketika pointer baca dan tulis sama, buffer kosong dan proses latar belakang tidak mengirim data. Ketika buffer penuh, proses latar depan menolak untuk menulis lagi (atau dapat menimpa data lama, tergantung pada kebutuhan Anda).

Menggunakan buffer bundar untuk memisahkan pembaca dan penulis harus menghilangkan kebutuhan untuk menonaktifkan interupsi.

Toby Jaffey
sumber
Ya, saya jelas akan menggunakan buffer lingkaran tetapi kenaikan dan penurunan bukan operasi atom.
Emil Eriksson
3
@ Emil: Tidak harus begitu. Untuk buffer melingkar klasik, dengan dua pointer dan satu slot "tidak dapat digunakan", semua yang diperlukan adalah bahwa penulisan memori bersifat atomik dan diterapkan secara berurutan. Pembaca memiliki satu pointer, penulis memiliki yang lain, dan, meskipun keduanya dapat membaca kedua pointer, hanya pemilik pointer yang menulis pointernya. Pada titik itu, semua yang Anda butuhkan adalah atom-in order.
John R. Strohm
2

Saya tidak ingat lokasi tepatnya tetapi di perpustakaan yang berasal dari ARM (Bukan TI, ARM, itu harus di bawah CMSIS atau sesuatu seperti itu, saya menggunakan ST tapi saya ingat membaca di suatu tempat bahwa file ini berasal dari ARM sehingga Anda harus memilikinya juga ) ada opsi menonaktifkan interupsi global. Ini adalah panggilan fungsi. (Saya tidak di tempat kerja tapi besok saya akan mencari fungsi yang tepat). Saya akan membungkusnya dengan nama yang bagus di sistem Anda dan menonaktifkan interupsi, lakukan hal Anda dan aktifkan lagi. Karena itu, opsi yang lebih baik akan menerapkan semaphore atau struktur antrian daripada global interrupt disable.

Ktc
sumber