Cortex-M3'teki kritik bölümler


10

Zamanlama kısıtlamaları veya eşzamanlılık sorunları nedeniyle istisnalara izin verilmeyen bir Cortex-M3 üzerinde kritik kod bölümlerini uygulama hakkında biraz merak ediyorum.

Benim durumumda bir LPC1758 kullanıyorum ve bir TI CC2500 alıcı-vericisi var. CC2500, RX arabelleğindeki veriler için kesme çizgileri ve TX arabelleğindeki boş alan olarak kullanılabilen pinlere sahiptir.

Örnek olarak, MCU'mun SRAM'ında bir TX arabelleği olmasını istiyorum ve alıcı-vericinin TX arabelleğinde boş alan olduğunda, bu verileri oraya yazmak istiyorum. Ancak, SRAM arabelleğine veri koyan rutin, TX'deki boş alan kesintisi ile kesilemez. Yani yapmak istediğim, bu tamponu doldurmak için bu prosedürü yaparken kesmeleri geçici olarak devre dışı bırakmak ama bu işlem sırasında meydana gelen herhangi bir kesintiyi bitirdikten sonra yürütmek.

Bu Cortex-M3'te en iyi nasıl yapılır?

Yanıtlar:


11

Cortex M3, "Load-Exclusive" (LDREX) ve "Store-Exclusive" (STREX) olarak adlandırılan yararlı bir çift işlem işlemini (diğer birçok makinede de yaygındır) destekler. Kavramsal olarak, LDREX işlemi bir yük gerçekleştirir, ayrıca yüklenen konumun başka bir şey tarafından yazılıp yazılmayacağını gözlemlemek için bazı özel donanımlar ayarlar. Son LDREX tarafından kullanılan adrese bir STREX uygulanması, bu adresin yalnızca önce başka bir şey yazmamışsa yazılmasına neden olur . STREX komutu, mağaza gerçekleşmişse 0 veya iptal edilmişse 1 içeren bir kayıt yükleyecektir.

STREX'in genellikle karamsar olduğunu unutmayın. Söz konusu konuma gerçekten dokunulmamış olsa bile mağazayı gerçekleştirmemeye karar verebileceği çeşitli durumlar vardır. Örneğin, bir LDREX ve STREX arasındaki bir kesinti, STREX'in izlenen konumun vurulmuş olabileceğini varsaymasına neden olur. Bu nedenle, LDREX ve STREX arasındaki kod miktarını en aza indirmek genellikle iyi bir fikirdir. Örneğin, aşağıdakine benzer bir şey düşünün:

satır içi geçersiz safe_increment (uint32_t * addr)
{
  uint32_t yeni_değer;
  yapmak
  {
    yeni_değer = __ldrex (addr) + 1;
  } while (__ strex (yeni_değer, adres));
}

hangi gibi bir şey derler:

; Varsayalım ki R0 söz konusu adresi tutuyor; r1 çöp kutusuna gönderildi
lp:
  ldrex r1, [r0]
  r1, r1, # 1 ekle
  strex r1, r1, [r0]
  cmp r1, # 0; Sıfırdan farklı olup olmadığını test edin
  bne lp
  .. kod devam ediyor

Kodun yürütüldüğü zamanın büyük çoğunluğu, LDREX ve STREX arasında bunları "rahatsız etmek" için hiçbir şey olmayacak, bu nedenle STREX daha fazla uzatmadan başarılı olacaktır. Bununla birlikte, LDREX veya ADD komutunun hemen ardından bir kesme gerçekleşirse, STREX depoyu gerçekleştirmez, bunun yerine kod [r0] 'nin (muhtemelen güncellenmiş) değerini okumak ve yeni bir artan değeri hesaplamak için geri döner buna dayanarak.

Safe_increment gibi işlemler oluşturmak için LDREX / STREX kullanmak, yalnızca kritik bölümleri yönetmekle kalmaz, aynı zamanda birçok durumda bunlardan kaçınmayı da mümkün kılar.


Bu nedenle, engelleri kaldırıldıktan sonra tekrar sunulabilmeleri için kesintileri "engellemenin" bir yolu yoktur? Bunun mümkünse bile muhtemelen yetersiz bir çözüm olduğunu anlıyorum, ancak sadece ARM kesinti kullanımı hakkında daha fazla bilgi edinmek istiyorum.
Emil Eriksson

3
Kesmeleri devre dışı bırakmak mümkündür ve Cortex-M0'da bunu yapmak için genellikle pratik bir alternatif yoktur. LDREX / STREX yaklaşımının kesintileri devre dışı bırakmaktan daha temiz olduğunu düşünüyorum, ancak birçok durumda itiraf etmek gerçekten önemli olmayacak (bence her birinin bir döngü olmasını etkinleştirin ve devre dışı bırakın ve beş döngü için kesintileri devre dışı bırakmak muhtemelen önemli değil) . Kod çok çekirdekli bir CPU'ya geçirilirse ldrex / strex yaklaşımının işe yarayacağını, kesintileri devre dışı bırakan bir yaklaşımın çalışmadığını unutmayın. Ayrıca, bazı RTOS'ların kesintileri devre dışı bırakmasına izin verilmeyen azaltılmış izinlerde çalıştırma kodu.
Supercat

Muhtemelen FreeRTOS ile devam edeceğim, bu yüzden bunu kendim yapmayacağım ama yine de öğrenmek istiyorum. Prosedür sırasında meydana gelen kesintileri atmak yerine, kesintileri engellemek için hangi kesintileri devre dışı bırakma yöntemini kullanmalıyım? Onları atmak istersem nasıl yaparım?
Emil Eriksson

İlişkili kodda bir parantez bulunmadığından yukarıdaki yanıtlara güvenilemez: while(STREXW(new_value, addr); Kodunuz derlenmeyecekse söylediğiniz şeyin doğru olduğuna nasıl inanabiliriz?

@Tim: Maalesef yazım mükemmel değil; Karşılaştırma için kullanışlı yazdığım gerçek kod yok, bu yüzden kullandığım sistemin STREXW veya __STREXW kullanılıp kullanılmadığını hatırlamıyorum, ancak bir derleyici referansı __strex'i içsel olarak listeler (STREXW'nin aksine 32-bit STREX, __strex intrinsic kullanımları, sağlanan işaretçi boyutuna bağlı olarak bir STREXB, STREXH veya STREX üretir)
21

4

MCU yazılımınızda bazı dairesel arabelleklere veya FIFO'lara ihtiyacınız olduğu anlaşılıyor. Okuma ve yazma için diziye iki dizin veya işaretçi izleyerek, hem ön planın hem de arka planın aynı ara belleğe parazit olmadan erişmesini sağlayabilirsiniz.

Ön plan kodu her zaman dairesel arabelleğe yazılabilir. Yazma işaretçisine veri ekler, ardından yazma işaretçisini artırır.

Arka plan (kesme işleme) kodu okuma işaretçisinden veri tüketir ve okuma işaretçisini artırır.

Okuma ve yazma işaretçileri eşit olduğunda, arabellek boştur ve arka plan işlemi veri göndermez. Arabellek dolduğunda, ön plan işlemi artık yazmayı reddeder (veya ihtiyaçlarınıza bağlı olarak eski verilerin üzerine yazabilir).

Okuyucuları ve yazarları ayırmak için dairesel arabelleklerin kullanılması kesintileri devre dışı bırakma ihtiyacını ortadan kaldırmalıdır.


Evet, açıkçası dairesel tampon kullanacağım ama artış ve azalma atomik işlemler değil.
Emil Eriksson

3
@Emil: Olmak zorunda değiller. İki işaretçi ve bir "kullanılamaz" yuvaya sahip klasik dairesel bir tampon için gerekli olan tek şey, bellek yazma işlemlerinin atomik ve sırayla uygulanmasıdır. Okuyucu bir işaretçiye sahiptir, yazar diğerine sahiptir ve her ikisi de her iki işaretçiyi de okuyabilmesine rağmen, yalnızca işaretçinin sahibi işaretçisini yazar. Bu noktada, tek ihtiyacınız olan atomik sıralı yazmalardır.
John R. Strohm

2

Tam konumu hatırlayamıyorum ama ARM'den gelen kütüphanelerde (TI, ARM değil, CMSIS veya benzeri bir şey altında olmalı, ST kullanıyorum ama bu dosyanın ARM'den geldiğini bir yerde okuduğumu hatırlıyorum, bu yüzden de sahip olmalısınız ) bir genel kesme devre dışı bırakma seçeneği vardır. Bu bir işlev çağrısıdır. (Ben işte değilim ama yarın tam işlevi arayacağım). Bunu sisteminizde güzel bir adla tamamlar ve kesintileri devre dışı bırakır, işinizi yapar ve tekrar etkinleştiririm. Bunu söyledikten sonra, daha iyi seçenek küresel kesme devre dışı bırakmak yerine bir semafor veya kuyruk yapısı uygulamak olacaktır.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.