Cortex (ARM) mikrodenetleyicilerinde WFI (kesinti için bekle) için en iyi model


18

EFM Gekko denetleyicilerini (http://energymicro.com/) kullanarak pille çalışan yazılımlar geliştirmeye çalışıyorum ve bunun için yararlı bir şey olmadığında denetleyicinin uykuda olmasını istiyorum. WFI (Kesmeyi Bekle) komutu bu amaç için kullanılır; bir kesinti gerçekleşinceye kadar işlemciyi uyku moduna geçirir.

Bir yerde bir şey saklayarak uyku meşgul olsaydı, biri gibi bir şey yapmak için yüke özel / mağazaya özel işlemleri kullanabilir:

  // dont_sleep bir şey olduğunda 2 ile yüklenir
  // ana döngüyü en az bir kez dönmeye zorlamalıdır. Bir kesinti varsa
  // aşağıdaki ifade sırasında 2'ye sıfırlanmasına neden olur,
  // davranış, kesintiden sonra gerçekleşmiş gibi olacaktır.

  store_exclusive (yük_exclusive (dont_sleep) >> 1);

  iken (! dont_sleep)
  {
    // Bir sonraki ifade ile store_exclusive arasında kesinti olursa uyuma
    load_exclusive (SLEEP_TRIGGER);
    eğer (! dont_sleep)             
      (SLEEP_TRIGGER) store_exclusive;
  }

Load_exclusive ve store_exlusive işlemleri arasında bir kesinti gerçekleşirse, etki store_exclusive'i atlamak ve böylece sistemin döngüden bir kez daha geçmesine (kesmenin dont_sleep ayarlayıp ayarlamadığını görmek) neden olur. Ne yazık ki, Gekko uyku modunu tetiklemek için bir yazma adresi yerine bir WFI talimatı kullanır; gibi kod yazmak

  eğer (! dont_sleep)
    WFI ();

'if' ve 'wfi' arasında bir kesinti oluşması ve dont_sleep ayarlaması riskini doğurur, ancak wfi devam eder ve yine de yürütür. Bunu önlemek için en iyi model nedir? Kesintilerin WFI'yi çalıştırmadan hemen önce işlemciyi kesmesini önlemek için PRIMASK'ı 1 olarak ayarlayın ve hemen ardından silinsin mi? Yoksa daha iyi bir hile var mı?

DÜZENLE

Olay bitini merak ediyorum. Genel açıklamaya göre, çoklu işlemci desteği için tasarlanmış gibi görünüyor, ancak aşağıdakine benzer bir şeyin işe yarayıp yaramayacağını merak ediyordu:

  eğer (dont_sleep)
    SEV (); / * WFE net etkinlik bayrağını takip edecek, ancak uyuyamayacak * /
  WFE ();

Don't_sleep ayarlayan her kesme de bir SEV komutu yürütmelidir, bu nedenle kesme "if" testinden sonra gerçekleşirse, WFE olay bayrağını temizler ancak uyku moduna geçmez. Bu iyi bir paradigma gibi mi görünüyor?


1
WFI komutu, komut yürütüldüğünde uyanma durumu doğruysa çekirdeği uyku durumuna geçirmez. Örneğin, WFI yürütülürken belirsiz bir IRQ varsa, bir NOP görevi görür.
Mark

@Mark: Sorun, "if (! Dont_sleep)" ile "WFI" arasında bir kesinti alınırsa, WFI yürütüldüğünde kesinti koşulunun artık beklemeyeceği, ancak kesinti dont_sleep'ı ayarlamış olabileceğinden dolayı olabilir. başka bir yineleme çalıştıran ana döngüyü haklı çıkaracak bir şey yaptı. Cypress PSOC uygulamamda, uzun bir uyanmaya neden olacak herhangi bir kesinti, ana hat kodu uyumak üzereyse yığını jinx ederdi, ancak bu oldukça yapışkan görünüyor ve ARM bu yığın manipülasyonlarını caydırıyor.
supercat

@supercat WFI yürütüldüğünde kesinti temizlenebilir veya silinmeyebilir. Bu size bağlı ve ne zaman / nerede kesmeyi temizlemek için seçin. Dont_sleep değişkeninden kurtulun ve uyanık kalmak veya uyumak istediğinizde belirtmek için maskeli bir kesinti kullanın. İf ifadesinden hep birlikte kurtulabilir ve WFI'yı ana döngünün sonunda bırakabilirsiniz. Tüm isteklere hizmet ettiyseniz, uyuyabilmeniz için IRQ'yu temizleyin. Uyanık kalmanız gerekiyorsa, IRQ'yu tetikleyin, maskelenir, böylece hiçbir şey olmaz, ancak WFI yürütmeye çalıştığında NOP olacaktır.
Mark

2
@supercat Daha temel bir seviyede, kesintiye dayalı bir tasarımı, genellikle zaman açısından kritik olmayan, genellikle yoklama tabanlı ve minimum kesintileri olan bir 'büyük ana döngü' tasarımı ile karıştırmaya çalıştığınız anlaşılıyor. Bunları karıştırmak oldukça çirkin oldukça hızlı olabilir. Mümkünse kullanmak için bir tasarım paradigması veya diğerini seçin. Modern kesme kontrolörleri ile temel olarak kesintiler ve görev kuyrukları için ne kadar miktarda (servis bir kesinti, sonra bir sonraki yüksek öncelik, vb.) Arasında önleyici çoklu görev elde edeceğinizi unutmayın. Bunu kendi avantajın için kullan.
Mark

@ Mark: Pille çalışan bir uygulamada PIC 18x'i oldukça iyi kullanan bir sistem geliştirdim; yığın sınırlamaları nedeniyle, bir kesinti içinde çok fazla işleyemezdi, bu nedenle malzemelerin büyük çoğunluğu ana döngüde uygun bir şekilde ele alınır. Çoğunlukla iyi çalışıyor, ancak uzun süren işlemler nedeniyle bir iki saniye boyunca işlerin engellendiği birkaç nokta var. Bir ARM'ye geçersem, uzun süren işlemleri bölmeyi kolaylaştırmak için basit bir RTOS kullanabilirim, ancak önleyici veya ortak çoklu görev kullanıp kullanmayacağından emin değilim.
supercat

Yanıtlar:


3

Bir şeyi tam olarak anlamadım dont_sleep, ancak deneyebileceğiniz bir şey, en düşük önceliğe ayarlanmış PendSV işleyicisindeki "ana işi" yapmaktır. Daha sonra, her şeyi yapmanız gerektiğinde diğer işleyicilerden bir PendSV planlayın. Nasıl yapılacağına bakın (M1 için ama M3 çok farklı değil).

Kullanabileceğiniz başka bir şey (belki de önceki yaklaşımla birlikte) Çıkışta Uyku özelliğidir. Bunu etkinleştirirseniz, işlemci WFI'yi aramak zorunda kalmadan son ISR işleyicisinden çıktıktan sonra uyku moduna geçer. Burada bazı örneklere bakın .


5
WFI komutu, işlemciyi uyandırmak için kesintilerin etkinleştirilmesini gerektirmez, CPSR'deki F ve I bitleri yok sayılır.
Mark

1
@Mark Dokümanlar'da bu biti kaçırmış olmalıyım, bununla ilgili bazı linkler / işaretçiler var mı? Çekirdeği uyandıran kesme sinyaline ne olur? Kesme tekrar etkinleştirilene kadar beklemede mi kalıyor?
Igor Skochinsky

ASM başvuru kılavuzu burada: infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/… korteks-m3 için daha spesifik bilgiler burada: infocenter.arm.com/help/ index.jsp? topic = / com.arm.doc.dui0552a /… kısaca maskelenmiş bir kesinti beklenirken çekirdek WFI komutundan sonra uyanır ve çalışmaya devam eder. Bekleyen kesintiyi temizlemeden başka bir WFI yayınlamaya çalışırsanız, WFI bir NOP gibi davranır (WFI için uyanma koşulu doğru olduğundan çekirdek uyku yapmaz).
Mark

@Mark: Düşündüğüm bir şey, dont_sleep da bir SEV ("Olay Ayarla") talimatı yürütmek ve daha sonra WFI yerine WFE ("Olay için Bekle") kullanmak herhangi bir kesme işleyicisi olması olacaktır. Gekko örnekleri WFI kullanıyor gibi görünüyor, ancak WFE'nin de işe yarayacağını düşünürdüm. Düşüncesi olan var mı?
supercat

10

Kritik bir bölümün içine koyun. ISR'ler çalışmaz, bu nedenle WFI'den önce dont_sleep değiştirme riskini çalıştırmazsınız, ancak işlemciyi yine de uyandıracaklar ve kritik bölüm sona erdiğinde ISR'ler yürütülecektir.

uint8 interruptStatus;
interruptStatus = EnterCriticalSection();
if (!dont_sleep)
  WFI();
ExitCriticalSection(interruptStatus);

Geliştirme ortamınızın muhtemelen kritik bölüm işlevleri vardır, ancak kabaca şu şekildedir:

EnterCriticalSection:

MRS r0, PRIMASK /* Save interrupt state. */
CPSID i /* Turn off interrupts. */
BX lr /* Return. */

ExitCriticalSection:

MSR PRIMASK, r0 /* Restore interrupt states. */
BX lr /* Return. */

2
İlginçtir ki, bir dizi ARM kütüphanesi, durumu yerel olarak korumak yerine global bir sayaç kullanan kritik bölüm uygulamasını kullanır. Ben sayaç yaklaşımı daha karmaşık ve sadece tüm sistem genelinde kod aynı sayacı kullanırsa işe yarayacak çünkü zihin boggling buluyorum.
supercat

1
Kritik bölümden çıkana kadar kesintiler devre dışı bırakılmayacak mı? Öyleyse, WFI CPU'nun süresiz olarak beklemesine neden olmaz mı?
Corneliu Zuzu

1
@Kenzi Shrimp'in yanıtı, önceki sorumu yanıtlayan bir Linux tartışma konusuna işaret ediyor. Cevabını ve sizinkini açıklığa kavuşturmak için düzenledim.
Corneliu Zuzu

@CorneliuZuzu Kendi tartışmanıza müdahale etmek için bir başkasının cevabını düzenlemek harika bir fikir değil. 'Yalnızca bağlantı' yanıtını iyileştirmek için teklif eklemek farklı bir konudur. Kazancınızla ilgili gerçek bir sorunuz varsa, belki de bir soru olarak sorun ve buna bağlantı verin.
Sean Houlihane

1
@SeanHoulihane Onun cevabını geçersiz kılmadı ya da hiçbir şey silmedim. Bunun neden işe yaradığına dair kısa bir açıklama ayrı bir tartışma değildir. Dürüst olmak gerekirse, bu cevabın WFI açıklaması olmadan bir yukarı oyu hak ettiğini düşünmüyorum, ancak en çok bunu hak ediyor.
Corneliu Zuzu

7

Fikriniz iyi, bu tam olarak Linux'un uyguladığı şey. Buraya bakın .

WFI'nin neden devre dışı bırakılmış kesintilerle bile çalıştığını açıklığa kavuşturmak için yukarıda belirtilen tartışma başlığından faydalı alıntı:

Bir sonraki kesintiye kadar rölantide kalmak istiyorsanız, biraz hazırlık yapmanız gerekir. Bu hazırlık sırasında bir kesinti aktif hale gelebilir. Böyle bir kesinti aradığınız bir uyanma olayı olabilir.

Kodunuz ne kadar iyi olursa olsun, kesintileri devre dışı bırakmazsanız, her zaman uyumaya hazırlanmak ile aslında uyumaya gitmek arasında bir yarış olacaktır ve bu da uyandırma olaylarının kaybolmasına neden olur.

Bu yüzden farkında olduğum tüm ARM CPU'lar çekirdek CPU'da maskelenmiş olsalar bile uyanacak (CPSR I bit.)

Başka bir şey ve boşta modunu kullanmayı unutmalısınız.


1
WFI veya WFE talimatı sırasında kesintileri devre dışı bırakmayı mı kastediyorsunuz? Bu amaçla WFI veya WFE kullanma arasında anlamlı bir ayrım görüyor musunuz?
Supercat

1
@supercat: Kesinlikle WFI kullanırdım. WFE IMO temel olarak çok çekirdekli sistemdeki çekirdekler arasındaki senkronizasyon ipuçları içindir (örneğin, spinlock almada WFE yapmak başarısız ve spinlock'tan çıktıktan sonra SEV vermek). Ayrıca, WFE kesme maskeleme bayrağını dikkate alır, bu yüzden burada WFI kadar yararlı değildir. Bu model Linux'ta gerçekten iyi çalışıyor.
Karides

2

Varsayım:

  1. Ana iş parçacığı arka plan görevlerini çalıştırır
  2. Kesmeler yalnızca yüksek öncelikli görevleri çalıştırır ve arka plan görevi gerçekleştirmez
  3. Ana iplik her zaman kesilebilir (normalde kesintileri maskelemez)

Ardından çözüm, bayrak doğrulaması ve WFI arasındaki kesintileri engellemek için PRIMASK kullanmaktır:

mask_interrupts();
if (!dont_sleep)
    wfi();
unmask_interrupts();

0

Çıkış Modunda Uyku ne olacak? Bu, bir IRQ işleyicisi her çıktığında otomatik olarak uyku moduna geçer, bu yüzden yapılandırıldıktan sonra gerçekten çalışan herhangi bir "normal mod" yoktur. Bir IRQ oluşur, uyanır ve işleyiciyi çalıştırır ve uyku moduna geri döner. WFI gerekmez.


2
İşlemcinin düşmesi gereken uyku türünün, bir kesinti sırasında meydana gelen bir şeye bağlı olarak değişebileceği gerçeğiyle en iyi nasıl başa çıkılmalıdır? Örneğin, bir pin değiştirme olayı seri verinin gelebileceğini ve bu nedenle işlemcinin verileri beklerken birincil saat osilatörünü çalışır durumda tutması gerektiğini gösterebilir. Ana döngü olay bayrağını temizler, neler olup bittiğini inceler ve işlemciyi bir WFI ile uygun bir uyku moduna geçirirse, hangi modun uygun olacağını etkileyebilecek herhangi bir kesinti olay bayrağını ayarlayacaktır ...
supercat

... ve uykuyu iptal et. Bir ana döngü işleyicinin kontrolüne sahip olması, uyku modunda her kesinti için endişelenmekten daha temiz görünür. Her döngüdeki ana döngünün bir kısmını "döndürmek" en iyi şekilde etkili olmayabilir, ancak özellikle uyku davranışını etkileyebilecek tüm kesintiler bir bayrak vurduğunda çok kötü olmamalıdır.
supercat
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.