จะเกิดอะไรขึ้นเมื่อโปรแกรมฝังตัวเสร็จสิ้น


29

จะเกิดอะไรขึ้นในตัวประมวลผลแบบฝังตัวเมื่อการดำเนินการมาถึงreturnคำสั่งสุดท้ายนั้นทุกอย่างก็ค้างเหมือนเดิม การใช้พลังงาน ฯลฯ มี NOP หนึ่งนิรันดร์ที่ยาวนานบนท้องฟ้า? หรือ NOP ถูกดำเนินการอย่างต่อเนื่องหรือโปรเซสเซอร์จะปิดพร้อมกันหรือไม่

ส่วนหนึ่งของเหตุผลที่ฉันถามคือฉันสงสัยว่าตัวประมวลผลจำเป็นต้องปิดเครื่องก่อนที่มันจะเสร็จสิ้นการประมวลผลหรือไม่และมันจะทำอย่างไรถ้าการประมวลผลเสร็จสิ้นลงก่อนที่มือจะทำ?


22
ขึ้นอยู่กับความเชื่อของคุณ บางคนบอกว่ามันจะกลับชาติมาเกิด
Telaclavo

9
มันเป็นขีปนาวุธหรือไม่?
Lee Kowalkowski

6
บางระบบสนับสนุนHCF (Halt และการใช้ไฟจับ)การเรียนการสอน :)
Stefan Paul Noack

1
มันจะแตกแขนงไปสู่กิจวัตรการทำลายตนเอง

คำตอบ:


41

นี่เป็นคำถามที่พ่อของฉันมักจะถามฉันเสมอ " ทำไมมันไม่ทำงานตามคำแนะนำทั้งหมดและหยุดในตอนท้าย "

ลองมาดูตัวอย่างทางพยาธิวิทยา โค้ดต่อไปนี้รวบรวมในคอมไพเลอร์ C18 ของ Microchip สำหรับ PIC18:

void main(void)
{

}

มันสร้างผลลัพธ์แอสเซมเบลอร์ดังต่อไปนี้:

addr    opco     instruction
----    ----     -----------
0000    EF63     GOTO 0xc6
0002    F000     NOP
0004    0012     RETURN 0
.
. some instructions removed for brevity
.
00C6    EE15     LFSR 0x1, 0x500
00C8    F000     NOP
00CA    EE25     LFSR 0x2, 0x500
00CC    F000     NOP
.
. some instructions removed for brevity
.
00D6    EC72     CALL 0xe4, 0            // Call the initialisation code
00D8    F000     NOP                     //  
00DA    EC71     CALL 0xe2, 0            // Here we call main()
00DC    F000     NOP                     // 
00DE    D7FB     BRA 0xd6                // Jump back to address 00D6
.
. some instructions removed for brevity
.

00E2    0012     RETURN 0                // This is main()

00E4    0012     RETURN 0                // This is the initialisation code

อย่างที่คุณเห็นมีการเรียก main () และท้ายที่สุดจะมีคำสั่ง return แม้ว่าเราจะไม่ได้ใส่ไว้ในนั้นเอง เมื่อผลตอบแทนหลัก CPU ดำเนินการคำสั่งต่อไปซึ่งเป็นเพียง GOTO เพื่อกลับไปที่จุดเริ่มต้นของรหัส main () ถูกเรียกอย่างง่าย ๆ ซ้ำแล้วซ้ำอีก

ตอนนี้ต้องบอกว่านี่ไม่ใช่วิธีที่คนทั่วไปจะทำสิ่งต่าง ๆ ฉันไม่เคยเขียนโค้ดที่ฝังไว้ใด ๆ ซึ่งจะทำให้ main () ออกจากนั้น ส่วนใหญ่รหัสของฉันจะเป็นดังนี้:

void main(void)
{
    while(1)
    {
        wait_timer();
        do_some_task();
    }    
}

ดังนั้นฉันจะไม่ปล่อยให้ทางออกหลัก ()

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

เห็นได้ชัดว่าซีพียูจะยังคงรันคำสั่งต่อไป สิ่งเหล่านั้นจะมีลักษณะเช่นนี้:

addr    opco     instruction
----    ----     -----------
00E6    FFFF     NOP
00E8    FFFF     NOP
00EA    FFFF     NOP
00EB    FFFF     NOP
.
. some instructions removed for brevity
.
7EE8    FFFF     NOP
7FFA    FFFF     NOP
7FFC    FFFF     NOP
7FFE    FFFF     NOP

ที่อยู่หน่วยความจำถัดไปหลังจากคำสั่งสุดท้ายใน main () ว่างเปล่า บนไมโครคอนโทรลเลอร์ที่มีหน่วยความจำ FLASH คำสั่งเปล่าประกอบด้วยค่า 0xFFFF อย่างน้อยใน PIC นั้นรหัส op นั้นถูกตีความว่าเป็น 'nop' หรือ 'no operation' มันไม่ทำอะไรเลย ซีพียูจะยังคงดำเนินการ nops เหล่านั้นต่อไปจนสุดความทรงจำ

หลังจากนั้นจะเกิดอะไรขึ้น

ที่คำสั่งสุดท้ายตัวชี้คำสั่งของ CPU คือ 0x7FFe เมื่อ CPU เพิ่ม 2 เข้ากับตัวชี้คำสั่งมันจะได้รับ 0x8000 ซึ่งถือว่าเป็นโอเวอร์โฟลว์บน PIC ที่มี 32k FLASH เท่านั้นและมันจะล้อมรอบกลับไปที่ 0x0000 และ CPU ก็ดำเนินการตามคำแนะนำที่จุดเริ่มต้นของรหัสต่อไปอย่างมีความสุข ราวกับว่ามันถูกรีเซ็ต


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

หากคุณมีแอปพลิเคชันที่จำเป็นต้องทำสิ่งใดสิ่งหนึ่งหลังจากเปิดเครื่องแล้วไม่ทำอะไรเลยคุณสามารถทำได้เพียงสักครู่ (1); ในตอนท้ายของ main () เพื่อให้ CPU หยุดทำสิ่งที่เห็นได้ชัดเจน

หากแอปพลิเคชันต้องการให้ CPU ปิดเครื่องแล้วขึ้นอยู่กับ CPU อาจมีโหมดสลีปต่างๆ อย่างไรก็ตามซีพียูมีนิสัยตื่นขึ้นมาอีกครั้งดังนั้นคุณต้องตรวจสอบให้แน่ใจว่าไม่มีการ จำกัด เวลาในการนอนและไม่ต้องใช้ Watch Dog Timer ทำงาน ฯลฯ

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


20

สำหรับการคอมไพล์โค้ดมันขึ้นอยู่กับคอมไพเลอร์ คอมไพเลอร์ Rowley CrossWorks gcc ARM ที่ฉันใช้ข้ามไปยังโค้ดในไฟล์ crt0.s ที่มีการวนซ้ำไม่สิ้นสุด คอมไพเลอร์ Microchip C30 สำหรับอุปกรณ์ dsPIC และ PIC24 แบบ 16 บิต (หรือตาม gcc) จะรีเซ็ตตัวประมวลผล

แน่นอนว่าซอฟต์แวร์ฝังตัวส่วนใหญ่จะไม่หยุดเช่นนั้นและรันโค้ดอย่างต่อเนื่องในลูป


13

มีสองจุดที่ต้องทำที่นี่:

  • โปรแกรมฝังตัวพูดอย่างเคร่งครัดไม่สามารถ "เสร็จสิ้น"
  • ไม่จำเป็นต้องเรียกใช้โปรแกรมฝังตัวในบางครั้งแล้วจึง "เสร็จสิ้น"

แนวคิดของการปิดโปรแกรมไม่ปกติมีอยู่ในสภาพแวดล้อมแบบฝังตัว ในระดับต่ำ CPU จะดำเนินการคำสั่งในขณะที่สามารถ; ไม่มีสิ่งเช่น "คำสั่งการส่งคืนสุดท้าย" CPU อาจหยุดการประมวลผลหากพบข้อผิดพลาดที่ไม่สามารถกู้คืนได้หรือหากหยุดอย่างชัดเจน (เข้าสู่โหมดสลีปโหมดพลังงานต่ำ ฯลฯ ) แต่โปรดทราบว่าแม้กระทั่งโหมดสลีปหรือความผิดพลาดที่ไม่สามารถกู้คืนได้ ถูกประหารชีวิต คุณสามารถปลุกจากโหมดสลีป (นั่นคือวิธีการใช้งานตามปกติ) และแม้แต่ซีพียูที่ล็อกอยู่ก็ยังสามารถเรียกใช้ตัวจัดการ NMI ได้ (ในกรณีนี้คือ Cortex-M) จ้องจับผิดจะยังคงทำงานเช่นกันและคุณอาจไม่สามารถปิดการใช้งานบนไมโครคอนโทรลเลอร์บางตัวเมื่อเปิดใช้งาน รายละเอียดแตกต่างกันมากระหว่างสถาปัตยกรรม

ในกรณีที่เฟิร์มแวร์เขียนในภาษาเช่น C หรือ C ++ จะเกิดอะไรขึ้นหากการออกจาก main () ถูกกำหนดโดยรหัสเริ่มต้น ตัวอย่างเช่นนี่คือส่วนที่เกี่ยวข้องของรหัสเริ่มต้นจาก STM32 Standard Peripheral Library (สำหรับ toolchain ของ GNU ความเห็นเป็นของฉัน):

Reset_Handler:  
  /*  ...  */
  bl  main    ; call main(), lr points to next instruction
  bx  lr      ; infinite loop

รหัสนี้จะเข้าสู่วงวนไม่สิ้นสุดเมื่อ main () ส่งคืนแม้ว่าจะอยู่ในรูปแบบที่ไม่ชัดเจน ( bl mainโหลดlrด้วยที่อยู่ของคำสั่งถัดไปซึ่งมีประสิทธิภาพข้ามไปเอง) ไม่มีการพยายามหยุด CPU หรือเข้าสู่โหมดพลังงานต่ำ ฯลฯ หากคุณมีความต้องการที่ถูกต้องตามกฎหมายสำหรับแอปพลิเคชันของคุณคุณจะต้องทำด้วยตัวเอง

โปรดทราบว่าตามที่ระบุไว้ใน ARMv7-M ARM A2.3.1 การลงทะเบียนลิงก์ถูกตั้งค่าเป็น 0xFFFFFFFF เมื่อรีเซ็ตและการสาขาไปยังที่อยู่นั้นจะทำให้เกิดข้อผิดพลาด ดังนั้นนักออกแบบของ Cortex-M จึงตัดสินใจที่จะปฏิบัติตอบแทนจากตัวจัดการรีเซ็ตเป็นความผิดปกติและยากที่จะโต้แย้งกับพวกเขา

การพูดถึงความต้องการถูกต้องตามกฎหมายในการหยุด CPU หลังจากเฟิร์มแวร์เสร็จสิ้นแล้วก็ยากที่จะจินตนาการถึงสิ่งใดที่จะไม่ได้รับการบริการที่ดีขึ้นเมื่อปิดอุปกรณ์ของคุณ (หากคุณปิดการใช้งาน CPU "ดี" สิ่งเดียวที่สามารถทำได้กับอุปกรณ์ของคุณคือวงจรพลังงานหรือการรีเซ็ตฮาร์ดแวร์ภายนอก) คุณสามารถ deassert สัญญาณเปิดใช้งานสำหรับตัวแปลง DC / DC ของคุณหรือปิดไฟใน อย่างอื่นเช่น ATX PC


1
"คุณสามารถปลุกจากโหมดสลีป (นั่นคือวิธีการใช้งานตามปกติ) และแม้แต่ซีพียูที่ล็อกอยู่ก็ยังสามารถเรียกใช้ตัวจัดการ NMI ได้ (ในกรณีนี้คือ Cortex-M)" <- ฟังดูเหมือนส่วนที่น่ากลัวของ พล็อตหนังสือหรือภาพยนตร์ :)
Mark Allen

"bl main" จะโหลด "lr" ด้วยที่อยู่ของคำสั่งต่อไปนี้ ("bx lr") ใช่ไหม? มีเหตุผลใดที่จะคาดหวังว่า "lr" จะมีสิ่งอื่นใดเมื่อดำเนินการ "bx lr" หรือไม่
supercat

@supercat: ถูกต้องแน่นอน ฉันแก้ไขคำตอบเพื่อลบข้อผิดพลาดและขยายออกเล็กน้อย เมื่อคิดถึงสิ่งนี้แล้ววิธีที่พวกเขาใช้ลูปนี้ก็แปลกดี loop: b loopพวกเขาจะได้ทำได้อย่างง่ายดาย ฉันสงสัยว่าพวกเขาเป็นจริงหมายถึงการทำผลตอบแทน lrแต่ลืมที่จะบันทึก
หนาม

มันอยากรู้อยากเห็น ฉันคาดหวังว่ารหัส ARM จำนวนมากจะออกโดยที่ LR มีค่าเหมือนกันกับที่ถือไว้ในการเข้า แต่ไม่ทราบว่ามันรับประกัน การรับประกันดังกล่าวมักจะไม่เป็นประโยชน์ แต่การส่งเสริมนั้นจำเป็นต้องเพิ่มคำแนะนำในการทำตามขั้นตอนที่คัดลอก r14 ไปยังการลงทะเบียนอื่นแล้วเรียกกิจวัตรอื่น ๆ หาก lr ถูกพิจารณาว่า "ไม่ทราบ" ในการส่งคืนหนึ่งสามารถ "bx" การลงทะเบียนที่เก็บสำเนาที่บันทึกไว้ ที่จะทำให้เกิดพฤติกรรมที่แปลกมากกับรหัสที่ระบุแม้ว่า
supercat

ที่จริงฉันค่อนข้างแน่ใจว่าฟังก์ชั่นที่ไม่ใช่ใบไม้คาดว่าจะบันทึก lr สิ่งเหล่านี้มักจะกด lr ลงบนสแต็คในคำนำและกลับมาโดยการ popping ค่าที่บันทึกไว้ในพีซี นี่คือสิ่งที่จะเป็นเช่น C หรือ C ++ main () จะทำ แต่นักพัฒนาของห้องสมุดที่สงสัยนั้นไม่ได้ทำอะไรเช่นนี้ใน Reset_Handler
หนาม

9

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

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


7

ปัญหาไม่ได้ถูกฝังอยู่ (ระบบสมองกลฝังตัวสามารถรัน Linux หรือแม้กระทั่ง Windows) แต่สแตนด์อะโลนหรือโลหะเปลือย: โปรแกรมแอปพลิเคชั่น (รวบรวม) เป็นสิ่งเดียวที่ทำงานบนคอมพิวเตอร์ (ไม่สำคัญว่าจะเป็น ไมโครคอนโทรลเลอร์หรือไมโครโปรเซสเซอร์)

สำหรับภาษาส่วนใหญ่ภาษาไม่ได้กำหนดว่าจะเกิดอะไรขึ้นเมื่อ 'main' ยกเลิกและไม่มี OS ที่จะกลับไป สำหรับ C มันขึ้นอยู่กับสิ่งที่อยู่ใน startupfile (มัก crt0.s) ในกรณีส่วนใหญ่ผู้ใช้สามารถ (หรือต้อง) ใส่รหัสเริ่มต้นดังนั้นคำตอบสุดท้ายคือ: สิ่งที่คุณเขียนคือรหัสเริ่มต้นหรือสิ่งที่เกิดขึ้นในรหัสเริ่มต้นที่คุณระบุ

ในทางปฏิบัติมี 3 วิธี:

  • ไม่มีมาตรการพิเศษ จะเกิดอะไรขึ้นเมื่อผลตอบแทนหลักไม่ได้ถูกกำหนด

  • ข้ามไปที่ 0 หรือใช้วิธีการอื่นเพื่อรีสตาร์ทแอปพลิเคชัน

  • ป้อนลูปแบบแน่น (หรือปิดการใช้งานอินเตอร์รัปต์และสั่งการหยุดทำงาน) ล็อคโปรเซสเซอร์ตลอดไป

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


5

ฉันกำลังดูรหัส ATtiny45 ที่ถอดแยกชิ้นส่วน (C ++ ที่คอมไพล์โดย avr-gcc) เมื่อวันก่อนและสิ่งที่ทำในตอนท้ายของรหัสคือข้ามไปที่ 0x0000 โดยทั่วไปทำการรีเซ็ต / รีสตาร์ท

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

บน AVR 00 ไบต์ (ค่าเริ่มต้นเมื่อเซลล์ว่างเปล่า) คือ NOP = ไม่มีการดำเนินการ ดังนั้นมันจึงทำงานได้อย่างรวดเร็วไม่ทำอะไรเลย แต่ใช้เวลาพอสมควร


1

โดยทั่วไปmainโค้ดที่คอมไพล์แล้วจะเชื่อมโยงกับรหัสเริ่มต้น (อาจรวมเข้ากับ toolchain จัดทำโดยผู้จำหน่ายชิปเขียนโดยคุณเป็นต้น)

ตัวเชื่อมโยงจะวางแอปพลิเคชันและรหัสเริ่มต้นทั้งหมดในส่วนหน่วยความจำดังนั้นคำตอบสำหรับคำถามของคุณขึ้นอยู่กับ: 1. รหัสจากการเริ่มต้นเนื่องจากตัวอย่างเช่น:

  • จบด้วย empty loop ( bl lrหรือb .) ซึ่งจะคล้ายกับ "program end" แต่อินเตอร์รัปต์และอุปกรณ์ต่อพ่วงที่เปิดใช้ก่อนหน้านี้จะยังคงทำงานอยู่
  • จบลงด้วยการข้ามไปที่จุดเริ่มต้นของโปรแกรม (ทั้งเริ่มต้นทำงานอีกครั้งอย่างสมบูรณ์หรือ jsut ไปmain)
  • เพียงแค่ละเว้น "สิ่งที่จะเป็นถัดไป" หลังจากโทรmainกลับ

    1. ในหัวข้อย่อยที่สามเมื่อโปรแกรมตัวนับเพิ่มขึ้นหลังจากกลับมาจากmainพฤติกรรมจะขึ้นอยู่กับตัวเชื่อมโยงของคุณ (และ / หรือสคริปต์ตัวเชื่อมโยงที่ใช้ระหว่างการเชื่อมโยง)
  • หากมีการวางฟังก์ชั่น / รหัสอื่น ๆ หลังจากmainนั้นจะมีการดำเนินการกับค่าอาร์กิวเมนต์ที่ไม่ถูกต้อง / ไม่ได้กำหนด

  • หากหน่วยความจำต่อไปนี้เริ่มต้นด้วยการสร้าง migh exception การสอนที่ไม่ดีและ MCU จะถูกรีเซ็ตในที่สุด (ถ้า exception สร้าง reset)

หากเปิดใช้งาน watchdog ในที่สุดมันจะรีเซ็ต MCU แม้จะมีลูปไม่สิ้นสุดทั้งหมดที่คุณอยู่ (แน่นอนถ้ามันจะไม่ถูกโหลดซ้ำ)


-1

วิธีที่ดีที่สุดในการหยุดอุปกรณ์ฝังตัวคือรอตลอดไปพร้อมกับคำแนะนำ NOP

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


นั่นไม่ได้ตอบคำถามจริงๆ
Matt Young

-4

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

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

ใน x86 CPU เราปิดคอมพิวเตอร์โดยส่งคำสั่งไปยังคอนโทรลเลอร์ ACIP เข้าสู่โหมดการจัดการระบบ ดังนั้นคอนโทรลเลอร์นั้นเป็นชิป I / O และคุณไม่จำเป็นต้องปิดมันเอง

อ่านข้อกำหนด ACPI สำหรับข้อมูลเพิ่มเติม


3
-1: TS ไม่ได้พูดถึง CPU เฉพาะใด ๆ ดังนั้นอย่าคิดมากเกินไป ระบบที่แตกต่างกันจัดการกรณีนี้ในวิธีที่แตกต่างกันมาก
Wouter van Ooijen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.