ส่วนที่สำคัญใน Cortex-M3


10

ฉันสงสัยเล็กน้อยเกี่ยวกับการติดตั้งส่วนรหัสที่สำคัญใน Cortex-M3 ที่ไม่อนุญาตให้มีข้อยกเว้นเนื่องจากข้อ จำกัด ด้านเวลาหรือปัญหาการเห็นพ้องด้วย

ในกรณีของฉันฉันใช้ LPC1758 และฉันมีตัวรับส่งสัญญาณ TI CC2500 บนเครื่อง CC2500 มีพินซึ่งสามารถใช้เป็นบรรทัดอินเตอร์รัปต์สำหรับข้อมูลในบัฟเฟอร์ RX และพื้นที่ว่างในบัฟเฟอร์ TX

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

วิธีนี้ทำได้ดีที่สุดใน Cortex-M3

คำตอบ:


11

Cortex M3 รองรับการทำงานของการดำเนินงานคู่ที่มีประโยชน์ (ทั่วไปในเครื่องอื่น ๆ อีกมากมาย) เรียกว่า "Load-Exclusive" (LDREX) และ "Store-Exclusive" (STREX) ตามหลักการแล้วการดำเนินการของ LDREX จะทำการโหลดรวมทั้งตั้งค่าฮาร์ดแวร์พิเศษบางอย่างเพื่อสังเกตว่าตำแหน่งที่โหลดนั้นอาจถูกเขียนโดยอย่างอื่น การแสดง STREX ไปยังที่อยู่ที่ใช้โดย LDREX สุดท้ายที่จะทำให้อยู่ว่าจะเขียนเท่านั้นถ้าไม่มีอะไรอื่นเขียนมันเป็นครั้งแรก คำสั่ง STREX จะโหลดการลงทะเบียนด้วย 0 หากร้านค้าเกิดขึ้นหรือ 1 ถ้าถูกยกเลิก

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

inline void safe_increment (uint32_t * addr)
{
  uint32_t new_value;
  ทำ
  {
    new_value = __ldrex (addr) + 1;
  } ขณะที่ (__ strex (new_value, addr));
}

ซึ่งรวบรวมสิ่งที่ชอบ:

; สมมติว่า R0 เก็บที่อยู่ที่เป็นปัญหา ถังขยะ r1
lp:
  ldrex r1, [r0]
  เพิ่ม r1, r1, # 1
  strex r1, r1, [r0]
  cmp r1, # 0; ทดสอบว่าไม่ใช่ศูนย์
  bne lp
  .. รหัสต่อไป

ส่วนใหญ่เวลาที่โค้ดทำงานไม่มีอะไรเกิดขึ้นระหว่าง LDREX และ STREX เพื่อ "รบกวน" พวกเขาดังนั้น STREX จะประสบความสำเร็จโดยไม่ต้องกังวลใจต่อไป อย่างไรก็ตามหากการขัดจังหวะเกิดขึ้นทันทีตามคำสั่ง LDREX หรือ ADD STREX จะไม่ทำการจัดเก็บ แต่รหัสจะกลับไปอ่านค่า (อาจมีการปรับปรุง) ของ [r0] และคำนวณค่าที่เพิ่มขึ้นใหม่ ขึ้นอยู่กับว่า

การใช้ LDREX / STREX เพื่อจัดรูปแบบการดำเนินการเช่น safe_increment ทำให้ไม่เพียง แต่จะจัดการส่วนที่สำคัญเท่านั้น แต่ในหลาย ๆ กรณีเพื่อหลีกเลี่ยงความต้องการเหล่านั้น


ดังนั้นจึงไม่มีวิธี "ขัดจังหวะ" การขัดจังหวะเพื่อให้พวกเขาสามารถให้บริการอีกครั้งเมื่อพวกเขาถูกยกเลิกการปิดกั้น? ฉันรู้ว่านี่อาจเป็นวิธีที่ไม่เหมาะสมแม้ว่าจะเป็นไปได้ แต่ฉันต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการจัดการขัดจังหวะ ARM
Emil Eriksson

3
มีความเป็นไปได้ที่จะปิดการใช้งานอินเตอร์รัปต์และใน Cortex-M0 มักจะไม่มีทางเลือกอื่นในการทำเช่นนั้น ฉันคิดว่าวิธีการของ LDREX / STREX นั้นจะสะอาดกว่าการปิดการใช้งานอินเตอร์รัปต์ แต่เป็นที่ยอมรับกันในหลายกรณีว่ามันจะไม่สำคัญ (ฉันคิดว่าการเปิดใช้งานและปิดการใช้งานจบลงด้วยการเป็นรอบละรอบ . โปรดทราบว่าวิธีการ ldrex / strex จะทำงานได้หากรหัสถูกย้ายไปยัง CPU แบบมัลติคอร์ในขณะที่วิธีการที่ปิดการใช้งานอินเตอร์รัปต์จะไม่ทำงาน นอกจากนี้โค้ดการทำงานของ RTOS บางตัวยังมีการอนุญาตที่ลดลงซึ่งไม่ได้รับอนุญาตให้ปิดการใช้งานอินเตอร์รัปต์
supercat

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

คำตอบเดียวไม่สามารถเชื่อถือได้เพราะรหัสที่เกี่ยวข้องนั้นไม่มีวงเล็บ: while(STREXW(new_value, addr); เราจะเชื่อในสิ่งที่คุณพูดว่าถูกต้องได้อย่างไรหากรหัสของคุณจะไม่ได้รวบรวม

@Tim: ขออภัยการพิมพ์ของฉันไม่สมบูรณ์ ฉันไม่มีรหัสจริงที่ฉันเขียนเพื่อเปรียบเทียบดังนั้นฉันจำไม่ได้ว่าระบบที่ฉันใช้ใช้ STREXW หรือ __STREXW แต่การอ้างอิงคอมไพเลอร์แสดงรายการ __strex ว่าเป็นเหมือนจริง (ไม่เหมือนกับ STREXW ซึ่ง จำกัด อยู่ที่ 32-bit STREX การใช้งาน __strex ภายในสร้าง STREXB, STREXH หรือ STREX ขึ้นอยู่กับขนาดตัวชี้ที่ให้มา)
supercat

4

ดูเหมือนว่าคุณต้องการบัฟเฟอร์แบบวงกลมหรือ FIFO ในซอฟต์แวร์ MCU ของคุณ โดยการติดตามดัชนีหรือพอยน์เตอร์สองตัวในอาร์เรย์เพื่ออ่านและเขียนคุณสามารถมีทั้งเบื้องหน้าและพื้นหลังในการเข้าถึงบัฟเฟอร์เดียวกันโดยไม่มีการรบกวน

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

รหัสพื้นหลัง (การจัดการขัดจังหวะ) ใช้ข้อมูลจากตัวชี้การอ่านและการเพิ่มตัวชี้การอ่าน

เมื่อพอยน์เตอร์การอ่านและเขียนเท่ากันบัฟเฟอร์จะว่างเปล่าและกระบวนการพื้นหลังจะไม่ส่งข้อมูล เมื่อบัฟเฟอร์เต็มกระบวนการเบื้องหน้าจะไม่ยอมเขียนอีกต่อไป (หรือสามารถเขียนทับข้อมูลเก่าขึ้นอยู่กับความต้องการของคุณ)

การใช้บัฟเฟอร์แบบวงกลมเพื่อแยกตัวอ่านและตัวเขียนควรลบความจำเป็นในการปิดการใช้งานอินเตอร์รัปต์


ใช่ฉันเห็นได้ชัดว่าจะใช้บัฟเฟอร์วงกลม แต่การเพิ่มและลดลงไม่ใช่การทำงานของอะตอม
Emil Eriksson

3
@Emil: พวกเขาไม่จำเป็นต้องเป็น สำหรับบัฟเฟอร์แบบวงกลมคลาสสิกที่มีสองพอยน์เตอร์และหนึ่งช่อง "ใช้ไม่ได้" สิ่งที่จำเป็นคือการเขียนหน่วยความจำนั้นเป็นแบบอะตอมมิกและมีการบังคับใช้ตามลำดับ ผู้อ่านเป็นเจ้าของตัวชี้หนึ่งตัวผู้เขียนเป็นเจ้าของอีกตัวหนึ่งและแม้ว่าทั้งคู่จะสามารถอ่านตัวชี้อย่างใดอย่างหนึ่งได้ แต่เจ้าของของตัวชี้เท่านั้นที่จะเขียนตัวชี้ของเขา ณ จุดนี้สิ่งที่คุณต้องมีก็คือการเขียนตามคำสั่งของอะตอม
John R. Strohm

2

ฉันจำตำแหน่งที่แน่นอนไม่ได้ แต่ในห้องสมุดที่มาจาก ARM (ไม่ใช่ TI, ARM ควรอยู่ภายใต้ CMSIS หรืออะไรทำนองนั้นฉันใช้ ST แต่ฉันจำได้ว่าอ่านไฟล์นี้มาจาก ARM ดังนั้นคุณควรมีเช่นกัน ) มีตัวเลือกการปิดการใช้งานอินเตอร์รัปต์ระดับโลก มันเป็นการเรียกฟังก์ชั่น (ฉันไม่ได้ทำงาน แต่ฉันจะค้นหาฟังก์ชันที่แน่นอนในวันพรุ่งนี้) ฉันจะสรุปมันด้วยชื่อที่ดีในระบบของคุณและปิดการใช้งานอินเตอร์รัปต์ทำสิ่งที่คุณและเปิดใช้งานอีกครั้ง ต้องบอกว่าตัวเลือกที่ดีกว่าคือการใช้เซมาฟอร์หรือโครงสร้างคิวแทนที่จะปิดใช้งานอินเตอร์รัปต์ชั่วคราว


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