Що відбувається, коли вбудована програма закінчується?


29

Що відбувається у вбудованому процесорі, коли виконання досягне остаточного returnтвердження. Чи все просто застигає як є; споживання енергії тощо, з одним довгим вічним НОП на небі? або НПО виконуються постійно, або процесор взагалі вимкнеться?

Частина причини, про яку я запитую, - мені цікаво, чи потрібно процесору вимкнути живлення до того, як він закінчить виконання, і якщо він робить, як він коли-небудь закінчить виконання, якщо він вимкнувся з роботи перед рукою?


22
Це залежить від ваших переконань. Деякі кажуть, що це перевтілиться.
Телаклаво

9
Це для ракети?
Лі Ковальковський

6
деякі системи підтримують інструкцію HCF (Halt and Catch Fire) . :)
Стефан Пол Ноак

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 () викликається, а в кінці містить зворотній вислів, хоча ми його прямо там не вказали. Коли основна віддача, процесор виконує наступну інструкцію, яка просто 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 цей код коду трактується як "nop", або "no operation". Він просто нічого не робить. Центральний процесор продовжував би виконувати ці носочки до кінця пам'яті.

Що після цього?

В останній інструкції покажчик інструкції процесора становить 0x7FFe. Коли ЦП додає 2 до свого вказівника інструкції, він отримує 0x8000, що вважається переповненням на ПОС із лише 32k FLASH, і тому він обертається назад до 0x0000, і процесор із задоволенням продовжує виконувати інструкції ще на початку коду , так, як ніби він був скинутий.


Ви також запитували про необхідність відключення живлення. В основному ви можете робити все, що завгодно, і це залежить від вашої заявки.

Якщо у вас був додаток, який потребував лише однієї справи після ввімкнення живлення, а потім нічого не робити, ви могли просто покласти час (1); наприкінці main (), щоб процесор перестав робити щось помітне.

Якщо програма вимагала від центрального процесора живлення, тоді, залежно від процесора, ймовірно, будуть доступні різні режими сну. Однак процесори мають звичку прокидатися знову, тому вам доведеться переконатися, що не існує обмеження часу уві сні, а також немає активного таймера для сторожових собак тощо.

Ви навіть можете організувати якусь зовнішню схему, яка дозволила б процесору повністю скоротити власну потужність після її закінчення. Дивіться це запитання: Використання миттєвої кнопки як перемикача тумблера для вимкнення .


20

Для компільованого коду це залежить від компілятора. Роулі CrossWorks компілятор gcc ARM, який використовую стрибки для кодування у файлі crt0.s, який має нескінченний цикл. Компілятор Microchip C30 для 16-бітних пристроїв dsPIC і PIC24 (також заснований на gcc) скидає процесор.

Звичайно, більшість вбудованих програмного забезпечення ніколи не закінчується таким чином і виконує код постійно в циклі.


13

Тут слід зробити два моменти:

  • Вбудована програма, строго кажучи, не може «закінчити».
  • Дуже рідко виникає необхідність запустити вбудовану програму деякий час, а потім "закінчити".

Концепція відключення програми зазвичай не існує у вбудованому середовищі. На низькому рівні процесор виконує інструкції, поки це може; не існує такого поняття, як "остаточна заява про повернення". Центральний процесор може зупинити виконання, якщо він зіткнеться з невиправною помилкою або явно зупинився (переведений у сплячий режим, режим низької потужності тощо), але зауважте, що навіть режими сну або непоправні помилки, як правило, не гарантують, що більше ніякого коду не буде бути виконаним. Ви можете прокинутися з режимів сну (саме так вони зазвичай використовуються), і навіть заблокований процесор все ще може виконати обробник NMI (це стосується Cortex-M). Також сторожовий пес все ще буде працювати, і ви, можливо, не зможете його відключити на деяких мікроконтролерах після його ввімкнення. Деталі сильно різняться між архітектурами.

У разі вбудованого програмного забезпечення, написаного на такій мові, як C або C ++, що відбувається, якщо вихід main () визначається кодом запуску. Наприклад, ось відповідна частина стартового коду зі стандартної периферійної бібліотеки STM32 (для інструментальної мережі GNU коментарі є моїми):

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

Цей код увійде в нескінченний цикл, коли main () повернеться, хоча неочевидним чином ( bl mainзавантажує lrадресу наступної інструкції, що фактично є стрибком до себе). Не робиться спроб зупинити процесор або змусити його перейти в режим низької потужності і т. Д. Якщо у вас є законна потреба в будь-якому з цього в додатку, вам доведеться це зробити самостійно.

Зауважте, що, як зазначено в ARMv7-M ARM A2.3.1, регістр посилань встановлюється на 0xFFFFFFFF після скидання, і гілка за цією адресою викликає помилку. Тож дизайнери Cortex-M вирішили трактувати повернення з оброблювача скидання як ненормальне, і важко з цим посперечатися.

Якщо говорити про законну необхідність зупинки процесора після закінчення прошивки, важко уявити будь-який, який не міг би краще обслуговуватися при відключенні пристрою. (Якщо ви відключите ваш процесор "назавжди", єдине, що можна зробити на вашому пристрої - це цикл живлення або скидання зовнішнього обладнання.) Ви можете залишити сигнал ENABLE для перетворювача постійного та постійного струму або відключити джерело живлення іншим способом, як це робить ATX ПК.


1
"Ви можете прокинутися з режимів сну (саме так вони зазвичай використовуються), і навіть заблокований процесор все ще може виконати обробник NMI (це стосується Cortex-M)." <- звучить як дивовижна частина книгу чи сюжет фільму. :)
Марк Аллен

"Bl main" буде завантажувати "lr" з адресою наступної інструкції ("bx lr"), чи не так? Чи є якась причина очікувати, що "lr" буде містити щось інше, коли "bx lr" виконується?
supercat

@supercat: ти, звичайно, маєш рацію. Я відредагував свою відповідь, щоб видалити помилку та трохи розширити її. Думаючи про це, те, як вони реалізують цю петлю, досить дивне; вони могли легко зробити loop: b loop. Цікаво, чи вони насправді мали намір повернути, але забули зберегти lr.
Торн

Це цікаво. Я б очікував, що багато ARM-коду вийде, коли LR матиме те саме значення, що і при вході, але не знаю, що це гарантовано. Така гарантія часто не буде корисною, але дотримання її вимагає додати інструкцію до процедур, які копіюють r14 в інший реєстр, а потім викликають якусь іншу процедуру. Якщо lr вважається "невідомим" при поверненні, можна "bx" реєстру, що містить збережену копію. Це може викликати дуже дивну поведінку із зазначеним кодом.
supercat

Насправді я майже впевнений, що очікується, що функції, що не стосуються листя, дозволять економити lr. Зазвичай вони штовхають lr на стек у пролозі та повертаються, додаючи збережене значення на ПК. Це те, що, наприклад, робив C або C ++ main (), але розробники відповідної бібліотеки, очевидно, не зробили нічого подібного в Reset_Handler.
Торн

9

Запитуючи про те return, ти думаєш надто високий рівень. Код С переводиться в машинний код. Отже, якщо ви замість цього думаєте про процесор, сліпо витягуючи інструкції з пам'яті та виконуючи їх, він не має уявлення, який з них є "остаточним" return. Отже, процесори не мають властивого кінця, але натомість програміст повинен обробити кінцевий випадок. Як вказує Леон у своїй відповіді, компілятори запрограмували поведінку за замовчуванням, але часто програміст може захотіти власної послідовності відключення (я робив різні речі, такі як введення в режим низької потужності та зупинка або очікування підключення USB-кабелю в, а потім перезавантаження).

Багато мікропроцесорів мають інструкції щодо зупинки, яка зупиняє процесор, не впливаючи на периферійні пристрої. Інші процесори можуть розраховувати на "зупинку", просто просто перестрибуючи на ту саму адресу. Можливі варіанти, але це залежить від програміста, оскільки процесор буде просто тримати вказівки з читання інструкцій, навіть якщо ця пам'ять не мала наміру.


7

Проблема не вбудована (вбудована система може запускати Linux або навіть Windows), а автономна або гола-металева: (компільована) прикладна програма - це єдине, що працює на комп’ютері (не важливо, чи це мікроконтролер або мікропроцесор).

Для більшості мов мова не визначає, що відбувається, коли закінчується "головне", і немає ОС, до якої можна повернутися. Для C це залежить від того, що є в стартовому файлі (часто crt0.s). У більшості випадків користувач може (або навіть зобов’язаний) надати код запуску, тому остаточна відповідь - це те, що ви пишете - це код запуску, або те, що відбувається у вказаному вами стартовому коді.

На практиці існує 3 підходи:

  • не вживайте спеціальних заходів. що відбувається, коли основна віддача не визначена.

  • перейти до 0 або скористатися будь-яким іншим способом для перезавантаження програми.

  • введіть тугий цикл (або відключивши переривання та виконуючи інструкцію зупинки), блокуючи процесор назавжди.

Що підходить, залежить від програми. Віртуальна листівка з хутра-елізи та система управління гальмом (лише кажучи про дві вбудовані системи), ймовірно, повинні перезапуститись. Мінус перезавантаження полягає в тому, що проблема може залишитися непоміченою.


5

Днями я дивився на якийсь розібраний ATtiny45 (C ++, складений avr-gcc) кодом, і те, що він робить в кінці коду, переходить до 0x0000. В основному робимо скидання / перезапуск.

Якщо останній перехід до 0x0000 компілятор / асемблер не вистачає, всі байти в пам'яті програми інтерпретуються як "дійсний" код машини, і він працює до тих пір, поки лічильник програм не перекинеться на 0x0000.

На AVR байт 00 (значення за замовчуванням, коли комірка порожня) - це NOP = немає операції. Тож він працює дуже швидко, нічого не роблячи, але просто забираючи деякий час.


1

Зазвичай скомпільований mainкод після цього пов'язується із стартовим кодом (він може бути інтегрований у ланцюжок інструментів, наданий постачальником мікросхем, написаним вами тощо).

Потім Linker розміщує всі програми та код запуску у сегментах пам'яті, тому відповідь на ваші запитання залежить від: 1. коду від запуску, оскільки він може, наприклад:

  • закінчується порожнім циклом ( bl lrабо b .), який буде подібний до "завершення програми", але перерви та периферійні пристрої, включені раніше, все ще працюватимуть,
  • закінчується переходом до початку програми (або повністю повторно запустіть, або jsut до main).
  • просто ігноруйте "що буде далі" після дзвінка до mainповернення.

    1. У третій кулі, коли програмовий лічильник просто збільшується після повернення від mainповедінки, буде залежати від вашого лінкера (та / або сценарію лінкера, який використовується під час посилання).
  • Якщо після функції буде розміщена інша функція / код, mainвона буде виконана з недійсними / невизначеними значеннями аргументів,

  • Якщо наступна пам'ять починається з поганої інструкції, буде створено виключення, і MCU згодом буде скинутий (якщо виняток генерує скидання).

Якщо ввімкнено сторожовий дог, він з часом скине MCU, незважаючи на всі нескінченні петлі, в яких ви перебуваєте (звичайно, якщо він не буде перезавантажений).


-1

Найкращий спосіб зупинити вбудований пристрій - це назавжди почекати з інструкціями NOP.

Другий спосіб - закрити пристрій за допомогою самого пристрою. Якщо ви можете управляти реле з вашими вказівками, ви можете просто відкрити вимикач , який призводить в русі вашого вбудованого пристрою і так ваш вбудований прилад Віднесений НЕ енергоспоживання.


Це насправді не відповідає на питання.
Метт Янг

-4

Це було чітко пояснено в посібнику. Як правило, загальний виняток буде кинутий процесором, оскільки він отримає доступ до місця пам'яті, яке знаходиться поза сегментом стека. [виняток із захисту пам'яті].

Що ви мали на увазі під вбудованою системою? Мікропроцесор або мікроконтролер? У будь-якому випадку це визначено в керівництві.

У процесорі x86 ми вимикаємо комп'ютер, передаючи команду на контролер ACIP. Вхід у режим управління системою. Таким контролером є мікросхема вводу / виводу, і його не потрібно вручну вимикати.

Прочитайте специфікацію ACPI для отримання додаткової інформації.


3
-1: TS не згадав жодного конкретного процесора, тому не варто вважати занадто багато. Різні системи розглядають цей випадок дуже по-різному.
Wouter van Ooijen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.