Использование стека ISR ATTiny2313

Я использую ATTiny2313 в качестве последовательного концентратора. Оперативной памяти всего 128 байт. Я думаю, что у меня заканчивается оперативная память во время ISR. Мой вопрос заключается в том, сколько оперативной памяти (стека) использует ISR для сохранения контекста (регистров). Т.е. если я использую ISR, сколько у меня останется из 128 байт. Есть ли способ обнаружить переполнение стека?

Ответы (1)

Что ж, проверяя документацию ATTiny2313 , на странице 15 говорится:

Реакция на выполнение прерывания для всех разрешенных прерываний AVR составляет минимум четыре тактовых цикла. После четырех тактов выполняется адрес вектора программы для фактической процедуры обработки прерывания. В течение этих четырех тактов счетчик команд помещается в стек. Вектор обычно представляет собой переход к процедуре прерывания, и этот переход занимает три такта. Если во время выполнения многоцикловой инструкции возникает прерывание, эта инструкция завершается до того, как будет обслужено прерывание. Если прерывание происходит, когда MCU находится в спящем режиме, время отклика на выполнение прерывания увеличивается на четыре такта. Это увеличение происходит в дополнение к времени запуска из выбранного спящего режима.

Возврат из процедуры обработки прерывания занимает четыре такта. В течение этих четырех тактов программный счетчик (два байта) извлекается из стека, указатель стека увеличивается на два, а бит I в SREG устанавливается.

Таким образом, вы действительно просматриваете только 2 байта в стеке во время прерывания (ПК); все остальное, что ISR помещает в стек, зависит от самого ISR. Я бы не ожидал, что хорошо написанный обработчик прерываний потребует много места в стеке.

Что касается самого указателя стека, на странице 13 говорится:

Стек в основном используется для хранения временных данных, для хранения локальных переменных и для хранения адресов возврата после прерываний и вызовов подпрограмм. Регистр указателя стека всегда указывает на вершину стека. Обратите внимание, что стек реализован как растущий от более высоких ячеек памяти к более низким ячейкам памяти. Это означает, что команда Stack PUSH уменьшает указатель стека.

Указатель стека указывает на область стека данных SRAM, где расположены стеки подпрограмм и прерываний. Это пространство стека в SRAM данных должно быть определено программой до выполнения каких-либо вызовов подпрограмм или разрешения прерываний. Указатель стека должен быть установлен выше 0x60. Указатель стека уменьшается на единицу, когда данные помещаются в стек с помощью инструкции PUSH, и уменьшается на два, когда адрес возврата помещается в стек вызовом подпрограммы или прерыванием. Указатель стека увеличивается на единицу, когда данные извлекаются из стека с помощью инструкции POP, и увеличивается на два, когда данные извлекаются из стека с возвратом из подпрограммы RET или возвратом из прерывания RETI.

Указатель стека AVR реализован в виде двух 8-битных регистров в пространстве ввода-вывода. Фактически используемое количество битов зависит от реализации. Обратите внимание, что пространство данных в некоторых реализациях архитектуры AVR настолько мало, что требуется только SPL. В этом случае регистр SPH будет отсутствовать.

В вашем случае, я думаю, присутствует только SPL (128 байт ОЗУ = 7 бит).

Помимо аппаратного обеспечения, это зависит от вашего фреймворка, который для большинства частей AVR будет включать GCC, GNU Binutils и avr-libc . Беглый взгляд на часто задаваемые вопросы по avr-libc выявил два хороших вопроса:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

Какие регистры используются компилятором C?

  • Типы данных: char — 8 бит, int — 16 бит, long — 32 бита, long long — 64 бита, float и double — 32 бита (это единственный поддерживаемый формат с плавающей запятой), указатели — 16 бит (указатели функций — слова). адресов, что позволяет адресовать до 128 КБ памяти программ). Существует параметр -mint8 (см. Параметры компилятора C avr-gcc), чтобы сделать int 8-битным, но он не поддерживается avr-libc и нарушает стандарты C (int должен быть как минимум 16-битным). Он может быть удален в будущем выпуске.

  • Используемые вызовом регистры (r18-r27, r30-r31): могут быть выделены gcc для локальных данных. Вы можете свободно использовать их в подпрограммах ассемблера. Вызов подпрограмм C может стереть любую из них — вызывающая сторона отвечает за сохранение и восстановление.

  • Регистры, сохраненные при вызове (r2-r17, r28-r29): могут быть выделены gcc для локальных данных. Вызов подпрограмм C оставляет их без изменений. Подпрограммы ассемблера отвечают за сохранение и восстановление этих регистров в случае их изменения. r29:r28 (указатель Y) используется в качестве указателя кадра (указывает на локальные данные в стеке), если это необходимо. Требование к вызываемому объекту сохранять/сохранять содержимое этих регистров применяется даже в ситуациях, когда компилятор назначает их для передачи аргументов.

  • Фиксированные регистры (r0, r1): никогда не выделялись gcc для локальных данных, но часто используются для фиксированных целей:

    r0 - временный регистр, может быть затерт любым кодом C (кроме обработчиков прерываний, которые его сохраняют), может использоваться для запоминания чего-либо на некоторое время в рамках одного фрагмента кода на ассемблере

    r1 — предполагается, что он всегда равен нулю в любом коде C, может использоваться для запоминания чего-либо на некоторое время в одном фрагменте кода на ассемблере, но затем должен быть очищен после использования (clr r1). Это включает в себя любое использование инструкций [f]mul[s[u]], которые возвращают свой результат в r1:r0. Обработчики прерываний сохраняют и очищают r1 при входе и восстанавливают r1 при выходе (если он не равен нулю).

  • Соглашения о вызовах функций: Аргументы располагаются слева направо, с r25 по r8. Все аргументы выравниваются, чтобы начинаться с четных регистров (аргументы нечетного размера, включая char, имеют над собой один свободный регистр). Это позволяет лучше использовать инструкцию movw на расширенном ядре.

Если слишком много, те, которые не подходят, передаются в стек.

Возвращаемые значения: 8 бит в r24 (не r25!), 16 бит в r25:r24, до 32 бит в r22-r25, до 64 бит в r18-r25. 8-битные возвращаемые значения расширяются от нуля/знака до 16 бит вызываемой функцией (символ без знака более эффективен, чем знаковый char — просто clr r25). Все аргументы функций с переменным списком аргументов (printf и т. д.) передаются в стек, а char расширяется до int.

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_ramoverlap

Как обнаружить проблемы с оперативной памятью и перекрытием переменных? Вы можете просто запустить avr-nm для вашего выходного (ELF) файла. Запустите его с параметром -n, и он отсортирует символы по цифрам (по умолчанию они отсортированы по алфавиту).

Ищите символ _end, это первый адрес в ОЗУ, который не выделен переменной. (avr-gcc внутренне добавляет 0x800000 ко всем адресам переменных data/bss, поэтому игнорируйте это смещение.) Затем код инициализации во время выполнения инициализирует указатель стека (по умолчанию), чтобы он указывал на последний доступный адрес во (внутренней) SRAM. . Таким образом, область между _end и концом SRAM — это то, что доступно для стека. (Если ваше приложение использует malloc(), что, например, также может происходить внутри printf(), куча для динамической памяти также находится там. См. разделы «Области памяти» и «Использование malloc()».)

Объем стека, необходимый для вашего приложения, не так просто определить. Например, если вы рекурсивно вызываете функцию и забываете прервать эту рекурсию, требуемый объем стека бесконечен. :-) Вы можете посмотреть сгенерированный код ассемблера (avr-gcc ... -S), в каждом сгенерированном файле ассемблера есть комментарий, в котором указан размер кадра для каждой сгенерированной функции. Это количество стека, необходимое для этой функции, вы должны добавить его для всех функций, где вы знаете, что вызовы могут быть вложенными.

Да, но компилятор avr gcc запрограммирует сохранение/восстановление регистра в ISR, не так ли?
Может быть, посмотрите, что я добавил о регистре, стеке и использовании ОЗУ. Если вы действительно обеспокоены, я бы предложил создать исходный код сборки (gcc -S foo.c) и подробно изучить его.