รูปแบบที่ดีที่สุดสำหรับ WFI (รอการขัดจังหวะ) บนไมโครคอนโทรลเลอร์ Cortex (ARM)


18

ฉันกำลังมองหาการพัฒนาซอฟต์แวร์ที่ใช้พลังงานแบตเตอรีโดยใช้ตัวควบคุม EFM Gekko (http://energymicro.com/) และต้องการให้ตัวควบคุมหลับไปทุกครั้งที่ไม่มีอะไรทำ คำสั่ง WFI (Wait For Interrupt) ถูกใช้เพื่อจุดประสงค์นี้ มันจะทำให้ตัวประมวลผลเข้าสู่โหมดสลีปจนกว่าจะเกิดการขัดจังหวะ

หากการนอนหลับมีส่วนร่วมโดยการจัดเก็บบางแห่งบางแห่งสามารถใช้การดำเนินการโหลดพิเศษ / เก็บพิเศษเพื่อทำสิ่งที่ต้องการ:

  // dont_sleep โหลด 2 ครั้งเมื่อมีอะไรเกิดขึ้น
  // ควรบังคับให้วนรอบหลักหมุนเวียนอย่างน้อยหนึ่งครั้ง หากมีการขัดจังหวะ
  // เกิดขึ้นที่ทำให้มันถูกรีเซ็ตเป็น 2 ระหว่างคำสั่งต่อไปนี้
  // พฤติกรรมจะเหมือนกับว่ามีการขัดจังหวะเกิดขึ้นหลังจากนั้น

  store_exclusive (load_exclusive (dont_sleep) >> 1);

  ในขณะที่ (! dont_sleep)
  {
    // หากการขัดจังหวะเกิดขึ้นระหว่างคำสั่งถัดไปและ store_exclusive อย่านอนหลับ
    load_exclusive (SLEEP_TRIGGER);
    ถ้า (! dont_sleep)             
      store_exclusive (SLEEP_TRIGGER);
  }

หากการอินเตอร์รัปต์เกิดขึ้นระหว่างการดำเนินการ load_exclusive และ store_exclusive ผลจะเป็นการข้าม store_exclusive ดังนั้นทำให้ระบบทำงานผ่านลูปอีกครั้ง (เพื่อดูว่าอินเตอร์รัปต์ได้ตั้งค่า น่าเสียดายที่ Gekko ใช้คำสั่ง WFI แทนที่อยู่การเขียนเพื่อเปิดใช้งานโหมดสลีป เขียนรหัสเช่น

  ถ้า (! dont_sleep)
    WFI ();

จะเรียกใช้ความเสี่ยงที่การขัดจังหวะอาจเกิดขึ้นระหว่าง 'if' และ 'wfi' และการตั้งค่า dont_sleep แต่ wfi จะดำเนินต่อไปและดำเนินการต่อไป รูปแบบที่ดีที่สุดในการป้องกันนั้นคืออะไร? ตั้งค่า PRIMASK เป็น 1 เพื่อป้องกันการขัดจังหวะจากการขัดจังหวะตัวประมวลผลก่อนดำเนินการ WFI และล้างออกทันทีหรือไม่ หรือว่ามีเคล็ดลับที่ดีกว่า?

แก้ไข

ฉันสงสัยเกี่ยวกับเหตุการณ์บิต ตามคำอธิบายทั่วไปมันต้องการให้มีการสนับสนุนโปรเซสเซอร์แบบมัลติโปรเซสเซอร์ แต่สงสัยว่าสิ่งต่อไปนี้อาจทำงานได้หรือไม่:

  ถ้า (dont_sleep)
    SEV (); / * จะทำให้การล้างสถานะเหตุการณ์ WFE ชัดเจนขึ้น แต่ไม่นอน * /
  WFE ();

การขัดจังหวะทุกครั้งที่ตั้งค่าอย่า_sleepควรดำเนินการคำสั่ง SEV ด้วยดังนั้นหากการขัดจังหวะเกิดขึ้นหลังจากการทดสอบ "if" WFE จะล้างค่าสถานะเหตุการณ์ แต่ไม่เข้าสู่โหมดสลีป เสียงนั้นเหมือนกระบวนทัศน์ที่ดีหรือไม่?


1
คำสั่ง WFI ไม่ได้ทำให้แกนนอนหลับหากสภาพการปลุกของมันเป็นจริงเมื่อดำเนินการคำสั่ง ตัวอย่างเช่นหากมี IRQ ที่ไม่ชัดเจนเมื่อดำเนินการ WFI จะทำหน้าที่เป็น NOP
ทำเครื่องหมาย

@ Mark: ปัญหาจะเกิดขึ้นหากมีการขัดจังหวะระหว่าง "if (! dont_sleep)" และ "WFI" เงื่อนไขการขัดจังหวะจะไม่รอดำเนินการเมื่อ WFI ดำเนินการอีกต่อไป แต่การขัดจังหวะอาจตั้งค่า dont_sleep ไว้ ทำบางสิ่งบางอย่างที่จะพิสูจน์ว่าลูปหลักทำงานซ้ำอีก ในแอพพลิเคชั่นของ Cypress PSOC ของฉันการขัดจังหวะใด ๆ ที่ควรทำให้เกิดการขยายเวลาปลุกจะทำให้สแตกซ้อนหากรหัสบรรทัดหลักกำลังจะหลับ แต่ดูเหมือนว่าจะค่อนข้างลำบากและฉันเข้าใจว่า
supercat

@supercat การขัดจังหวะอาจหรือไม่อาจถูกล้างเมื่อ WFI ดำเนินการ มันขึ้นอยู่กับคุณและเมื่อ / ที่คุณเลือกที่จะล้างขัดจังหวะ กำจัดตัวแปร dont_sleep และใช้อินเทอร์รัปต์ masked เพื่อสื่อความหมายเมื่อคุณต้องการตื่นหรือหลับ คุณสามารถกำจัดคำสั่ง if ทั้งหมดเข้าด้วยกันและออกจาก WFI ที่ส่วนท้ายของลูปหลัก หากคุณให้บริการตามคำขอทั้งหมดให้ล้าง IRQ เพื่อให้คุณนอนหลับได้ หากคุณจำเป็นต้องตื่นตัวให้เรียก IRQ ซึ่งเป็นหน้ากากที่ไม่มีอะไรเกิดขึ้น แต่เมื่อ WFI พยายามเรียกใช้งานมันจะเป็น NOP
ทำเครื่องหมาย

2
@supercat ในระดับพื้นฐานมากขึ้นดูเหมือนว่าคุณพยายามที่จะผสมผสานการออกแบบที่ขัดจังหวะด้วยการออกแบบ 'ใหญ่หลักวง' ซึ่งมักจะไม่สำคัญเวลามักจะสำรวจตามและมีการขัดจังหวะน้อยที่สุด การผสมสิ่งเหล่านี้เข้าด้วยกันอาจจะค่อนข้างเร็ว หากเป็นไปได้ให้เลือกกระบวนทัศน์การออกแบบหนึ่งอันหรืออื่น ๆ ที่จะใช้ โปรดจำไว้ว่าด้วยตัวควบคุมการขัดจังหวะที่ทันสมัยโดยทั่วไปคุณจะได้รับการทำงานหลายภารกิจระหว่างการขัดจังหวะและจำนวนคิวงาน (บริการขัดจังหวะครั้งเดียว ใช้เพื่อประโยชน์ของคุณ
ทำเครื่องหมาย

@ Mark: ฉันพัฒนาระบบที่ใช้ PIC 18x ค่อนข้างดีในแอปพลิเคชันที่ใช้พลังงานจากแบตเตอรี่ เนื่องจากข้อ จำกัด ของสแต็กจึงไม่สามารถจัดการได้มากเกินไปในการขัดจังหวะดังนั้นเนื้อหาส่วนใหญ่จึงได้รับการจัดการในลูปหลักตามความสะดวก ส่วนใหญ่ใช้งานได้ดีแม้ว่าจะมีจุดสองจุดที่สิ่งต่าง ๆ ถูกบล็อกในช่วงหนึ่งหรือสองวินาทีเนื่องจากการทำงานที่ยาวนาน ถ้าฉันย้ายไปที่ ARM ฉันอาจใช้ RTOS แบบง่าย ๆ เพื่อให้ง่ายขึ้นในการแยกการดำเนินการที่ใช้เวลานาน แต่ฉันไม่แน่ใจว่าจะใช้มัลติทาสกิ้งแบบยึดเอาเสียหรือร่วมมือ
supercat

คำตอบ:


3

ฉันไม่เข้าใจdont_sleepสิ่งนั้นอย่างเต็มที่แต่สิ่งหนึ่งที่คุณสามารถลองได้คือ "งานหลัก" ในตัวจัดการ PendSV ตั้งค่าลำดับความสำคัญต่ำสุด จากนั้นเพียงกำหนดเวลา PendSV จากเครื่องจัดการอื่น ๆ ในแต่ละครั้งที่คุณต้องการทำอะไรสักอย่าง ดูที่นี่วิธีการทำ (สำหรับ M1 แต่ M3 ไม่แตกต่างกันเกินไป)

อีกสิ่งหนึ่งที่คุณสามารถใช้ (อาจร่วมกับแนวทางก่อนหน้า) คือคุณสมบัติ Sleep-on-exit หากคุณเปิดใช้งานโปรเซสเซอร์จะเข้าสู่โหมดสลีปหลังจากออกจากตัวจัดการ ISR ล่าสุดโดยไม่ต้องโทร WFI ดูตัวอย่างบางส่วนที่นี่


5
คำสั่ง WFI ไม่ต้องการการขัดจังหวะเพื่อเปิดใช้งานการปลุกโปรเซสเซอร์บิต F และ I ใน CPSR จะถูกละเว้น
ทำเครื่องหมาย

1
@ ทำเครื่องหมายฉันต้องพลาดบิตนั้นในเอกสารคุณมีลิงค์ / พอยน์เตอร์เกี่ยวกับเรื่องนี้หรือไม่? เกิดอะไรขึ้นกับสัญญาณขัดจังหวะที่ปลุกแกนกลางขึ้นมา? มันยังคงอยู่จนกว่าจะถูกเปิดใช้งานการขัดจังหวะอีกครั้ง?
Igor Skochinsky

คู่มืออ้างอิง ASM อยู่ที่นี่: infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/…ข้อมูลเฉพาะเพิ่มเติมสำหรับ cortex-m3 อยู่ที่นี่: infocenter.arm.com/help/ index.jsp? topic = / com.arm.doc.dui0552a / …ในระยะสั้นเมื่อการขัดจังหวะที่ถูกปกปิดกลายเป็นสิ่งที่ค้างอยู่ในแกนหลักจะตื่นขึ้นและดำเนินการต่อหลังจากคำสั่ง WFI หากคุณพยายามที่จะออก WFI อื่นโดยไม่ล้างข้อมูลที่รอการขัดจังหวะ WFI จะทำหน้าที่เป็น NOP (แกนกลางจะไม่หลับเนื่องจากสภาพการปลุกสำหรับ WFI นั้นเป็นจริง)
ทำเครื่องหมาย

@ Mark: สิ่งหนึ่งที่ฉันคิดว่าน่าจะมีตัวจัดการอินเตอร์รัปต์ซึ่งทำให้ชุด dont_sleep ยังใช้คำสั่ง SEV ("Set Event") จากนั้นใช้ WFE ("Wait For Event") แทน WFI ตัวอย่าง Gekko ดูเหมือนจะใช้ WFI แต่ฉันคิดว่า WFE อาจใช้งานได้เช่นกัน ความคิดใด ๆ
supercat

10

วางไว้ในส่วนที่สำคัญ ISR จะไม่ทำงานดังนั้นคุณจะไม่เสี่ยงต่อการเปลี่ยนแปลงของ dont_sleep ก่อน WFI แต่จะยังคงปลุกโปรเซสเซอร์และ ISR จะทำงานทันทีที่ส่วนวิกฤติสิ้นสุดลง

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

สภาพแวดล้อมการพัฒนาของคุณอาจมีฟังก์ชั่นส่วนที่สำคัญ แต่ก็เป็นดังนี้:

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
อยากรู้อยากเห็นห้องสมุด ARM หลายแห่งใช้การดำเนินการในส่วนวิกฤตที่ใช้ตัวนับทั่วโลกแทนที่จะเก็บรักษาสถานะไว้ในเครื่อง ฉันพบว่าใจสามารถจะเชื่อได้เนื่องจากวิธีการนับนั้นซับซ้อนกว่าและจะใช้ได้ก็ต่อเมื่อโค้ดทั้งหมดทั้งระบบใช้ตัวนับเดียวกัน
supercat

1
การขัดจังหวะจะไม่ถูกปิดใช้งานจนกว่าเราจะออกจากส่วนที่สำคัญหรือไม่ ถ้าเป็นเช่นนั้น WFI จะไม่ทำให้ CPU รออย่างไม่มีกำหนดใช่หรือไม่
Corneliu Zuzu

1
คำตอบของ @Kenzi Shrimp ชี้ไปที่หัวข้อสนทนาของ Linux ซึ่งตอบคำถามก่อนหน้าของฉัน ฉันแก้ไขคำตอบของคุณและของคุณเพื่อชี้แจงว่า
Corneliu Zuzu

@CorneliuZuzu การแก้ไขคำตอบของคนอื่นเพื่อแทรกแซงการสนทนาของคุณเองไม่ใช่ความคิดที่ดี การเพิ่มคำพูดเพื่อปรับปรุงคำตอบ 'ลิงค์เท่านั้น' เป็นเรื่องที่แตกต่าง หากคุณมีคำถามจริงเกี่ยวกับการชนะของคุณอาจถามคำถามนั้นและเชื่อมโยงกับคำถามนี้
Sean Houlihane

1
@SeanHoulihane ฉันไม่ได้ทำการยกเลิกหรือลบสิ่งใดออกจากคำตอบของเธอ คำอธิบายสั้น ๆ ว่าทำไมงานนี้จึงไม่ใช่การอภิปรายแยกต่างหาก สุจริตฉันไม่รู้สึกว่าคำตอบนี้สมควรได้รับการโหวตโดยไม่ต้องชี้แจง WFI แต่มันสมควรได้รับมันมากที่สุดกับมัน
Corneliu Zuzu

7

ความคิดของคุณก็โอเคนี่คือสิ่งที่ Linux ใช้ ดูที่นี่

ข้อความอ้างอิงที่เป็นประโยชน์จากเธรดการสนทนาที่กล่าวถึงข้างต้นเพื่อให้ความกระจ่างว่าเหตุใด WFI จึงทำงานได้แม้ปิดการใช้งานอินเตอร์รัปต์:

หากคุณตั้งใจที่จะไม่ใช้งานจนกว่าจะมีการขัดจังหวะครั้งต่อไปคุณต้องเตรียมการบางอย่าง ในระหว่างการเตรียมการนั้นอินเทอร์รัปต์อาจทำงาน การขัดจังหวะอาจเป็นเหตุการณ์ที่คุณกำลังมองหา

ไม่ว่ารหัสของคุณจะดีแค่ไหนหากคุณไม่ปิดการใช้งานอินเตอร์รัปต์คุณจะมีการแข่งขันระหว่างการเตรียมพร้อมที่จะเข้านอนและการเข้านอนซึ่งจริง ๆ แล้วส่งผลให้เกิดการตื่นขึ้นของเหตุการณ์ที่หายไป

นี่คือเหตุผลที่ ARM ซีพียูทั้งหมดที่ฉันทราบจะตื่นขึ้นมาแม้ว่าพวกเขาจะถูกปิดบังที่ซีพียูหลัก (CPSR I บิต)

สิ่งอื่นใดและคุณควรลืมโดยใช้โหมดว่าง


1
คุณหมายถึงปิดการใช้งานอินเตอร์รัปต์ในเวลาที่คำสั่ง WFI หรือ WFE หรือไม่ คุณเห็นความแตกต่างที่มีความหมายระหว่างการใช้ WFI หรือ WFE เพื่อจุดประสงค์หรือไม่
supercat

1
@supercat: ฉันจะใช้ WFI แน่นอน WFE IMO ส่วนใหญ่ใช้สำหรับคำแนะนำในการซิงโครไนซ์ระหว่างคอร์ในระบบมัลติคอร์ (ตัวอย่างเช่นการทำ WFE เมื่อการล็อกแบบ spinlock ล้มเหลวและการออก SEV หลังจากออกจาก spinlock) นอกจากนี้ WFE ยังคำนึงถึงการปิดบังสถานะการปิดบังดังนั้นจึงไม่มีประโยชน์ที่นี่เหมือนกับ WFI รูปแบบนี้ใช้งานได้ดีใน Linux
กุ้ง

2

สมมติว่า:

  1. เธรดหลักรันงานพื้นหลัง
  2. อินเทอร์รัปต์รันเฉพาะงานที่มีลำดับความสำคัญสูงและไม่มีงานพื้นหลัง
  3. เธรดหลักสามารถถูกขัดจังหวะได้ตลอดเวลา (โดยปกติจะไม่ปิดบังมาสก์)

จากนั้นโซลูชันคือการใช้ PRIMASK เพื่อบล็อกการอินเตอร์รัปต์ระหว่างการตรวจสอบการตั้งค่าสถานะและ WFI:

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

0

เกี่ยวกับโหมด Sleep on Exit สิ่งนี้จะเข้าสู่โหมดสลีปโดยอัตโนมัติเมื่อใดก็ตามที่ตัวจัดการ IRQ ออกดังนั้นจึงไม่มี "โหมดปกติ" ใด ๆ ทำงานหลังจากที่กำหนดค่าไว้ IRQ เกิดขึ้นมันตื่นขึ้นมาและเรียกใช้ตัวจัดการและกลับไปนอน ไม่จำเป็นต้องใช้ WFI


2
วิธีที่ดีที่สุดที่จะจัดการกับความจริงที่ว่าประเภทของการนอนหลับที่โปรเซสเซอร์ควรลดลงอาจแตกต่างกันไปตามสิ่งที่เกิดขึ้นระหว่างการขัดจังหวะ? ตัวอย่างเช่นเหตุการณ์การเปลี่ยนพินอาจบ่งชี้ว่าข้อมูลอนุกรมอาจกำลังจะมาถึงและตัวประมวลผลจึงควรให้ oscillator นาฬิกาหลักทำงานอยู่ในขณะที่รอข้อมูลอยู่ หากลูปหลักล้างค่าสถานะเหตุการณ์ให้ตรวจสอบสิ่งที่เกิดขึ้นและทำให้โปรเซสเซอร์เข้าสู่โหมดสลีปที่เหมาะสมด้วย WFI ดังนั้นการขัดจังหวะใด ๆ ที่อาจส่งผลกระทบต่อโหมดที่เหมาะสมจะตั้งค่าสถานะเหตุการณ์ ...
supercat

... และยกเลิกการนอนหลับ การมีตัวจัดการลูปหลักหนึ่งตัวควบคุมโหมดสลีปดูเหมือนสะอาดกว่าไม่ต้องกังวลกับมันในการขัดจังหวะทุกครั้ง การ "หมุน" ส่วนหนึ่งของลูปหลักในการขัดจังหวะทุกครั้งอาจไม่ได้ผลดีที่สุด แต่ก็ไม่ควรเลวร้ายนักโดยเฉพาะอย่างยิ่งหากการขัดจังหวะทั้งหมดที่อาจส่งผลต่อพฤติกรรมการนอนหลับจะกระทบธง
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.