อะไรคือวิธีที่ตั้งใจ / ถูกต้องในการจัดการการขัดจังหวะและการใช้คำสั่ง WFI risc-v cpu

Aug 19 2020

ฉันยังใหม่มากกับการเขียนโปรแกรมแบบ bare metal และไม่เคยมีการขัดจังหวะมาก่อน แต่ฉันได้เรียนรู้บนบอร์ด dev ที่ขับเคลื่อนด้วย RISC-V FE310-G002 SOC

ฉันได้อ่านเกี่ยวกับคำแนะนำ RISC-V WFI (รอการขัดจังหวะ) และจากคู่มือดูเหมือนว่าคุณจะไม่สามารถพึ่งพามันเพื่อนอนหลับเป็นหลักได้ แต่แนะนำเพียงว่าสามารถหยุดการดำเนินการกับระบบได้และควรปฏิบัติตามคำสั่งเช่น NOP อย่างไรก็ตามสิ่งนี้ดูเหมือนจะไม่มีประโยชน์สำหรับฉัน พิจารณาตัวอย่างโปรแกรม ASM ต่อไปนี้:

wfi_loop:
WFI
J wfi_loop

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

นอกจากนี้หากการใช้งานของคุณทำตามคำสั่ง WFI จริงและการขัดจังหวะถูกทริกเกอร์ก่อนที่จะดำเนินการตามคำสั่ง WFI คอร์ทั้งหมดจะหยุดทำงานจนกว่าการขัดจังหวะอื่น ๆ จะถูกเรียกใช้เนื่องจากจะกลับมาก่อนคำสั่ง WFI

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

ฉันยังคงวนเวียนอยู่กับสิ่งนี้ในหัวของฉันและดูเหมือนจะไม่สามารถใช้งานได้อย่างปลอดภัย บางทีถ้าคุณเป็นอะตอมให้เปิดใช้งานการขัดจังหวะด้วย CSRRS จากนั้นเรียก WFI ทันทีเช่นนี้:

CSRRSI zero, mie, 0x80
wfi_loop:
WFI
J wfi_loop
NOP
NOP

จากนั้นตรวจสอบให้แน่ใจว่าได้เพิ่มการลงทะเบียน mepc เป็น 8 ไบต์ก่อนที่จะเรียก MRET จากตัวจัดการขัดจังหวะ การขัดจังหวะจะต้องถูกปิดใช้งานอีกครั้งในการลงทะเบียน mie ภายในตัวจัดการขัดจังหวะก่อนที่จะกลับมา วิธีนี้จะปลอดภัยก็ต่อเมื่อ WFI, J และ NOP ถูกเข้ารหัสเป็นคำสั่ง 4 ไบต์ไม่ว่าจะใช้คำสั่งบีบอัดหรือไม่ก็ตาม นอกจากนี้ยังขึ้นอยู่กับตัวนับโปรแกรมที่ไปถึงคำสั่ง WFI ก่อนที่จะสามารถทริกเกอร์อินเทอร์รัปต์ได้หลังจากเปิดใช้งานโดยคำสั่ง CSRRSI จากนั้นจะอนุญาตให้การขัดจังหวะถูกทริกเกอร์ในที่ปลอดภัยในโค้ดและส่งคืนในลักษณะที่มันหลุดออกจากลูปที่รอมันอยู่

ฉันเดาว่าฉันแค่พยายามทำความเข้าใจกับพฤติกรรมที่ฉันคาดหวังได้จากฮาร์ดแวร์ดังนั้นจะเรียกและส่งคืนจากการขัดจังหวะและใช้คำสั่ง WFI ได้อย่างไร

คำตอบ

3 ErikEidt Aug 19 2020 at 18:22

ควรมีหนึ่งงาน / เธรด / กระบวนการที่มีไว้สำหรับการไม่ทำงานและควรมีลักษณะเหมือนรหัสบิตแรกของคุณ

เนื่องจากเธรดที่ไม่ได้ใช้งานได้รับการตั้งค่าให้มีลำดับความสำคัญต่ำสุดหากเธรดที่ไม่ได้ใช้งานกำลังรันอยู่นั่นหมายความว่าไม่มีเธรดอื่นให้รันหรือเธรดอื่นทั้งหมดถูกบล็อก

เมื่อการขัดจังหวะเกิดขึ้นที่ปลดบล็อกเธรดอื่น ๆ รูทีนเซอร์วิสการขัดจังหวะควรกลับมาทำงานต่อเธรดที่ถูกบล็อกแทนเธรดที่ไม่ได้ใช้งาน

โปรดทราบว่าเธรดที่บล็อกบน IO เองก็ถูกขัดจังหวะด้วยเช่นกันซึ่งถูกขัดจังหวะด้วยการใช้ecallไฟล์. ข้อยกเว้นนั้นเป็นคำร้องขอสำหรับ IO และทำให้เธรดนี้ถูกบล็อก - ไม่สามารถดำเนินการต่อได้จนกว่าจะตอบสนองคำขอ IO

ดังนั้นเธรดที่ถูกบล็อกบน IO จะถูกระงับเหมือนกับว่าถูกขัดจังหวะ - และการขัดจังหวะนาฬิกาหรือการขัดจังหวะ IO สามารถดำเนินการต่อกระบวนการที่แตกต่างจากที่ถูกขัดจังหวะทันทีซึ่งจะเกิดขึ้นในกรณีที่กระบวนการไม่ได้ใช้งาน กำลังทำงานอยู่และมีเหตุการณ์บางอย่างที่กระบวนการกำลังรอให้เกิดขึ้น


สิ่งที่ฉันทำคือใช้scratchcsr เพื่อชี้ไปที่บล็อกบริบทสำหรับกระบวนการ / เธรดที่กำลังทำงานอยู่ ในการขัดจังหวะฉันบันทึกการลงทะเบียนจำนวนน้อยที่สุดที่จำเป็นในการ (เริ่มต้น) ให้บริการการขัดจังหวะ หากการขัดจังหวะส่งผลให้กระบวนการ / เธรดอื่น ๆ สามารถรันได้เมื่อดำเนินการต่อจากการขัดจังหวะฉันจะตรวจสอบลำดับความสำคัญของกระบวนการและอาจเลือกสวิตช์บริบทแทนการดำเนินการต่อสิ่งที่ถูกขัดจังหวะ หากฉันดำเนินการต่อสิ่งที่ถูกขัดจังหวะการกู้คืนอย่างรวดเร็ว และเพื่อสลับบริบทฉันทำการบันทึกบริบท CPU ของเธรดที่ถูกขัดจังหวะเสร็จสิ้นจากนั้นดำเนินการต่อกระบวนการ / เธรดอื่นโดยสลับการscratchลงทะเบียน

(สำหรับอินเทอร์รัปต์ที่ซ้อนกันฉันไม่อนุญาตให้เปิดการสลับบริบทในประวัติย่อ แต่ในการขัดจังหวะหลังจากบันทึกบริบทปัจจุบันฉันจะตั้งค่าscratchcsr เป็นสแต็กของบล็อกบริบทที่ขัดจังหวะก่อนที่จะเปิดใช้งานอินเทอร์รัปต์ที่มีลำดับความสำคัญสูงกว่าอีกครั้งนอกจากนี้ในฐานะผู้เยาว์มาก การเพิ่มประสิทธิภาพเราสามารถสันนิษฐานได้ว่าเธรดว่างที่เขียนเองไม่ต้องการอะไรเลยนอกจากพีซีที่บันทึก / กู้คืน)

4 MargaretBloom Aug 19 2020 at 18:22

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

คุณต้องทำเช่นนั้นโดยไม่คำนึงถึงการนำไปใช้wfiเนื่องจากคุณไม่รู้ว่าเหตุการณ์ใดที่ทำให้ฮาร์ตตื่นขึ้นมา
คุณอาจเปิดใช้งานnอินเตอร์รัปต์ขณะดำเนินการwfiและอาจมีการเพิ่มการขัดจังหวะ

wfiเป็นการเพิ่มประสิทธิภาพซึ่งจะช่วยประหยัดพลังงานจนกว่าจะมีอะไรเกิดขึ้น ตามที่คุณสังเกตตัวกำหนดตารางเวลาระบบปฏิบัติการอาจพบว่าตัวเองอยู่ในสภาพที่ไม่มีเธรดที่กำหนดตารางเวลาได้ (เช่นพวกเขาทั้งหมดรอ IO หรือไม่มีเลย) ในกรณีนั้นจะต้องทำบางสิ่งเช่น (ด้วยการมองเห็นที่จำเป็นทั้งหมดและความหมายของอะตอมมิก):

while ( ! is_there_a_schedulable_thread());

รอเพียงเท่านี้
แต่แทนที่จะหมุนวงแน่น (ซึ่งอาจส่งผลเสียต่อประสิทธิภาพและพลังงาน) ตัวกำหนดตารางเวลาสามารถใช้:

while ( ! is_there_a_schedulable_thread())
{
  __wfi();
}

ที่แย่ที่สุดก็เหมือนกับการวนซ้ำที่ดีที่สุดมันจะหยุดฮาร์ทชั่วคราวจนกว่าการขัดจังหวะภายนอกจะเกิดขึ้น (หมายความว่า IO อาจเสร็จสมบูรณ์และเธรดอาจทำงานได้ฟรี)

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

wfiนอกจากนี้ยังมีประโยชน์ในการฝังโปรแกรมหากคุณมีงานทั้งหมดบนตัวจัดการขัดจังหวะ (เช่นเมื่อกดปุ่มหรือคล้ายกัน)
ในกรณีนี้mainฟังก์ชันจะวนซ้ำตลอดไปเช่นเดียวกับตัวกำหนดตารางเวลา แต่ไม่มีเงื่อนไขการออก การเรียนการสอนจะช่วยปรับปรุงชีวิตของแบตเตอรี่
wfi

คุณไม่สามารถใช้ได้wfiทุกที่หรือคุณอาจพบว่าตัวเองกำลังรอการขัดจังหวะที่ไม่เคยเกิดขึ้น (อันที่จริงมันเป็นคำสั่งที่มีสิทธิพิเศษ)

คิดว่าเป็นการเพิ่มประสิทธิภาพสำหรับการประสานงานกับฮาร์ดแวร์

โดยเฉพาะอย่างยิ่งมันไม่ได้ออกแบบมาเพื่อให้แน่ใจว่าการขัดจังหวะถูกยิง:

void wait_for_int(int int_num)
{
   //Leave only interrupt int_num enabled
   enable_only_int(int_num);
   __wfi();
   restore_interrupts();
}

มันอาจจะใช้วิธีการที่กำหนดเฉพาะการดำเนินงานของ RISC-V แต่อย่างที่คุณเห็นได้จากหลอกรหัสที่มันไม่ได้จริงๆที่สะดวก
การปิดใช้งานการขัดจังหวะทั้งหมดยกเว้นอย่างเดียวมักเป็นสิ่งที่ระบบปฏิบัติการไม่สามารถจ่ายได้
แม้ว่าแอปพลิเคชันที่ฝังไว้สามารถทำได้