Как работает стек вызовов во время прерывания на AVR?

8

(Специально для Arduino Uno ...)

Что происходит со стеком, когда на микроконтроллере AVR происходит прерывание, и я вызываю функцию? Встроен ли компилятор в код? Кэширует ли он где-нибудь стек, а затем сбрасывает указатель стека? Есть ли у него вторичный стек только для прерываний?

Как я понимаю, вектор для прерывания - это прямая команда GOTO в сборке. Кроме того, я не думаю, что микроконтроллер будет автоматически связываться со стеком, поэтому он, вероятно, остался один. Однако это все еще не объясняет, как функции работают во время ISR.

Анонимный пингвин
источник
Ответ ниже велик, просто обратите внимание, что компилятор не может встроить вызовы прерывания, потому что ... Это вызовы прерывания: D
Владимир Краверо
1
@VladimirCravero Я имел в виду встроенные функции, вызываемые внутри прерывания (если я вызываю foo () в ISR, оно встроено, так что это на самом деле не вызов функции?)
Anonymous Penguin
Таким образом, ответ ниже не отвечает на ваш вопрос или нет? Caveman объясняет, что происходит, когда происходит прерывание, но не то, что происходит, когда функция вызывается в контексте прерывания. ответ на этот вопрос будет следующим: ничего особенного, функция вызывается и все. магия происходит, когда прерывание срабатывает, что происходит после (до iret) просто нормального, обычно непрерывного кода.
Владимир Краверо
@VladimirCravero да, это так (косвенно). Я говорил о том, как стек был изменен для ISR, думая, что его нужно было изменить, чтобы в первую очередь использовать функции. Я собираюсь догадаться, что функции работают точно так же, как и после настройки ISR.
Анонимный Пингвин
ну, тогда ты понял. после перехода к вектору прерываний все в порядке, и вы можете перемещаться куда угодно.
Владимир Краверо

Ответы:

16

AVR - это архитектура RISC, поэтому он имеет довольно простую аппаратную обработку прерываний. Большинство процессоров связываются со стеком во время прерываний, хотя есть пара, особенно ARM и PowerPC, которые используют разные методы.

В любом случае, это то, что AVR делает для прерываний:

При возникновении прерывания аппаратное обеспечение процессора выполняет следующие действия, которые не являются простым GOTO:

  1. Завершите текущую инструкцию.
  2. Отключить глобальный флаг прерывания.
  3. Вставьте адрес следующей инструкции в стек.
  4. Скопируйте адрес в правильном векторе прерывания (в соответствии с возникшим прерыванием) в программный счетчик.

Теперь на этом этапе оборудование сделало все, что оно собирается сделать. Программное обеспечение должно быть написано правильно, чтобы не сломать вещи. Как правило, следующие шаги идут по этим направлениям.

  1. Вставьте регистр состояния в стек. (Это должно быть сделано в первую очередь, прежде чем он будет изменен).

  2. Вставьте любые регистры процессора, которые будут (или могут быть) изменены в стек. Какие регистры, которые необходимо сохранить таким образом, определяются моделью программирования. Модель программирования определяется компилятором.

Теперь рабочий код прерывания можно запустить. Чтобы ответить на вопрос о вызове функции, она просто делает то, что всегда делает, помещает возвращаемое значение в стек, а затем возвращает его обратно, когда все готово. Это не влияет ни на одно из этих предыдущих значений, которые мы сохраняли в стеке до сих пор.

  1. Запустите ISR рабочий код.

Теперь мы закончили и хотим вернуться от прерывания. Сначала мы должны сделать очистку программного обеспечения.

  1. Вставьте регистры процессора, которые мы выдвинули на шаге 6.
  2. Вставьте сохраненное значение статуса обратно в регистр состояния. После этого мы должны быть осторожны, чтобы не выполнять никаких инструкций, которые могли бы изменить регистр состояния.
  3. Выполните инструкцию РТИ. Аппаратное обеспечение выполняет эти шаги для этой инструкции:

    а. Включить глобальный флаг прерывания. (Обратите внимание, что, по крайней мере, одна инструкция должна быть выполнена до того, как будет выполнено следующее прерывание. Это препятствует тому, чтобы тяжелые прерывания полностью блокировали фоновую работу.)

    б. Вставьте сохраненный обратный адрес в ПК.

Теперь мы вернулись к нормальному коду.

Обратите внимание, что есть некоторые моменты, в которых мы должны быть очень осторожны, особенно в отношении регистра состояния и сохранения регистров, которые могут быть изменены. К счастью, если вы используете компилятор C, все это обрабатывается под прикрытием.

Кроме того, вы должны следить за глубиной стека. В любой момент, когда разрешены прерывания, ISR может использовать больше стека, чем очевидно, если посмотреть на локальный код. Конечно, это мало что дает, если вы не используете свою память до предела.

Вот ссылка, описывающая этот процесс, если вы хотите ссылку.

троглодит
источник
Какова цель шагов 5/6? Мне кажется глупым не изменять регистры напрямую, хотя я полагаю, что неправильное использование некоторых регистров во время прерываний может привести к неприятным результатам.
Анонимный Пингвин
1
Шаги 5 и 6 сохраняют текущее (до прерывания) состояние системы, чтобы его можно было восстановить (шаги 8 и 9) после завершения прерывания, чтобы позволить основной программе продолжить работу, как если бы она не была прервана.
Питер Беннетт
1
Это отличное резюме.
Коннор Вольф
3
Также стоит отметить, что если вы действительно, действительно знаете, что делаете, вы можете отключить автоматическое сохранение статуса и других регистров с помощью флага GCC ( ISR_NAKED). Это может позволить вам пропустить шаги 5,6,8,9 в контексте, где вам действительно нужны эти циклы. Недостатком является то, что вы должны быть абсолютно уверены, что вы либо сохраните любые соответствующие регистры, либо сможете перезаписать их без вреда.
Коннор Вольф
2
Интересно знать, но перед использованием этого флага я бы занялся программированием на ассемблере. Так опасно ...
пещерный человек