Любые советы по работе с очень маленьким стеком? - PullRequest
8 голосов
/ 02 сентября 2010

Мне было интересно, знают ли какие-либо разработчики во встроенном пространстве какие-либо интересные приемы, помогающие уменьшить боль при разработке для микроконтроллеров с очень ограниченным пространством стека.Недавно я писал некоторые прошивки для 8-битных ЦП (семейство Microchip PIC18F, 31-байтовый стек) и, как следствие, мне пришлось сгладить мои программы и уменьшить количество параметров, передаваемых функциям.Я также пытался минимизировать свою зависимость от больших локальных переменных.Уплощение было разработано так, чтобы меньше помещать в стек, а уменьшение локальных переменных помогает сэкономить место в программном разделе «автоматические переменные» (psect) в оперативной памяти.Гарвардская архитектура это не весело, я знаю, но это то, с чем я имею дело.Я заметил проблемы с вызовом более чем нескольких функций глубоко из ISR, что, вероятно, является результатом моего окна стека, затронутого сохранением контекста IRQ.Я знаю, что работаю с ограничивающей архитектурой, но мне интересно, есть ли у кого-нибудь какие-либо советы по уменьшению головной боли.Я использую указатели и проверку границ, когда это возможно, но я уверен, что есть кусочки мудрости, которые я сам не обнаружил.Как заявление об отказе от ответственности, в настоящее время я использую указатели функций для упрощения работы конечного автомата.Мне кажется, что я иду по канату между 90-строчными пустыми функциями и кодом, который на самом деле использует функции по назначению.

Ответы [ 7 ]

7 голосов
/ 02 сентября 2010

Используйте register переменные для параметров и местных жителей. Конечно, в зависимости от количества регистров, доступных в процессоре, и качества кода, который генерирует компилятор, это может быть бесполезным. Объявите местных жителей как static, где это возможно. Это удержит их от размещения в стеке.

2 голосов
/ 02 сентября 2010

Для нерекурсивных функций вы можете сделать ваши локальные переменные статическими.

Кстати, термин «Гарвардская архитектура» просто означает, что существуют отдельные адресные пространства для команд и данных, в отличие«Архитектура фон Неймана», которая загружает инструкции из того же адресного пространства, из которого поступают данные.Это не имеет отношения к вашей проблеме, поскольку все, что она означает, - это то, что вы не можете написать самоизменяющийся код.

1 голос
/ 02 сентября 2010

EDIT

Семейство PIC не является дружественной для компилятора архитектурой набора команд. Первый трюк для работы с небольшим стеком и вообще ограниченными ресурсами на PIC - это программирование на ассемблере. Вы можете выполнить больше задач в одном и том же программном пространстве и за одно и то же время выполнения или за одну и ту же задачу за меньшее время, чем программирование на языке C.

Второй - просто не использовать стек. Сделайте ваши переменные глобальными или локальными глобальными (локальные переменные со словом static впереди), как бы вы это ни называли или как бы вы ни объявили, вывод компилятора будет таким же, переменная имеет одну статическую ячейку памяти и не использует стек.

Для процессоров, отличных от семейства PIC, вы можете использовать оптимизатор или зарегистрировать объявление переменных, чтобы предотвратить использование стека. PIC имеет только два регистра, и для того, чтобы сделать что-нибудь полезное, вы должны постоянно высылать содержимое в оперативную память, так что это будет иметь минимальное влияние на PIC. Для процессоров без PIC, в дополнение к тому, чтобы компилятор как можно больше сохранял данные в регистрах, вы можете помочь в этом, ограничивая число переменных, передаваемых в функцию, а также количество дополнительных локальных переменных, используемых функция. В случаях, когда неясно, сколько регистров используется, разберите и изучите выходные данные компилятора, реорганизация выполнения вашего кода может изменить распределение и удаление регистров. Если это не сработает, рассмотрите возможность разделения функции на две части и вызова одной, а затем по очереди другой, чтобы каждая функция могла выполняться в доступных регистрах без использования стека.

Если этот проект предназначен для увлекательного образовательного процесса, то программирование для PIC само по себе является образовательным, а программирование PIC на C также является образовательным опытом. Стоит учиться, стоит попасть в ловушки и пробиться обратно.

Если вы делаете это для работы (карьера и / или средства к существованию зависают на волоске) и не хотите программировать на ассемблере, я рекомендую попробовать переключить проект на другую платформу. msp430, avr или один из них на базе ARM (возможно, stellaris). Знание ARM очень полезно для вашей карьеры в области встраиваемых систем, но детали ARM будут несколько громоздкими с точки зрения мощности и стоимости по сравнению с PIC. AVR и MSP430 части больше по номиналу. Все три из этих альтернативных архитектур гораздо больше подходят для программирования на C, и одна и та же программа будет потреблять меньше памяти / стека, чем эквивалент на PIC. Это означает, что вы можете заменить 8K PIC на 8K чем-то другим, вам не обязательно будет больше памяти на альтернативной архитектуре, чтобы сделать больше. Вы по-прежнему должны беспокоиться о своем стеке и должны избегать (неглобальных) локальных переменных, чтобы предотвратить рост стека. Эти архитектуры также отличаются от PIC отсутствием отдельного стека и оперативной памяти общего назначения. Вы можете балансировать глобально распределенные переменные и переменные, основанные на стеке, и не зависеть от архитектуры. Если в рабочей среде используются переменные на основе стека, просмотрите код, чтобы определить наихудший путь вложенных функций, и подсчитайте, сколько неглобальных переменных находится в этом пути, и вызывает ли это стек, сталкивающийся с глобально распределенными переменными.

Разборка - ваш лучший друг во встраиваемых системах, вам не обязательно программировать на ассемблере, но вы все равно сможете ее прочитать. Изучение сборки - лучший прием для уменьшения всех форм встроенных проблем: проблем со стеком, использования памяти, производительности, проблем с загрузкой и т. Д.

1 голос
/ 02 сентября 2010

Использование нелокального перехода (setjmp() и longjmp(), когда jmp_buf не хранится в стеке), чтобы избежать вызовов функций.

1 голос
/ 02 сентября 2010
  1. Используйте оптимизирующий компилятор всей программы, который может эффективно статически распределять «стек» - я считаю, что компиляторы Hi-Tech PICC могут сделать это. См. Раздел 5.9 в руководстве по PICC18

  2. Использование protothreads

0 голосов
/ 08 июля 2013

Некоторые общие кроссплатформенные советы (как уже упоминалось):

  • Использовать статические локальные переменные. Это применимо везде, если функция не реентерабельна (либо для рекурсии, либо для вызова из нескольких асинхронных потоков / прерываний). Вам просто нужно помнить, чтобы на них не использовались инициализаторы (так как они будут устанавливать свое значение только при запуске, а не при каждой записи в функции). Чтобы прояснить разницу с намеченными статическими переменными, вы можете использовать что-то вроде «#define fn_local static» для ваших местных жителей.

  • Сокращение передачи параметров в функции. В 8-битных микро, передавая символьную производную вместо производной типа int, обычно вы экономите байт в стеке (так как обычно целые 16 бит) Если у вас много параметров, попробуйте сгруппировать их в структуру и заставить функцию принимать указатель на эту структуру.

  • Будьте очень осторожны с прерываниями. Избегайте вызовов функций внутри. Попробуйте спроектировать свою программную архитектуру, чтобы вам не нужны вложенные прерывания. В зависимости от компилятора и от того, что / как он сохраняет данные при вводе прерывания, это может или не может быть значительным использованием стека.

Специальные советы по MPLAB / PIC18:

  • В функции MPLAB параметры также могут быть статическими. Эффект от этого тот же, что и у статических локальных элементов: им не нужен стек, и их использование делает функцию не входящей. Обратите внимание, что использование static на PIC18 также может значительно повысить производительность, если сделать это с осторожностью, поскольку относительная адресация указателя стека довольно сложна.

  • PIC18 имеет аппаратный стек для вызовов функций. Поддерживаемых 31 слоев обычно более чем достаточно для большинства сложных программ. Результатом этого является то, что сами вызовы не вносят вклад в программный стек, занимающий пространство из вашей драгоценной оперативной памяти: на самом деле вы можете писать программное обеспечение PIC, которое может даже не нуждаться в программном стеке, используя представленные выше методы.

  • Можно управлять тем, что компилятор сохраняет при вводе прерывания, что влияет не только на использование стека, но и на задержку прерывания. См. эту достойную статью на эту тему. В частности, вызовы внешних функций из кода прерывания делают компилятор неспособным определить, какие ресурсы ему понадобятся, поэтому он будет генерировать длительный код входа / выхода прерывания, сохраняя многие вещи в стеке.

0 голосов
/ 03 сентября 2010

Я когда-то программировал для 8051, и мы использовали для этого компилятор Keil C.Этот компилятор вызывал функции не с помощью стека, а с помощью передачи параметров с помощью глобальных функций (такое поведение можно было бы пометить, помечая функцию «входящая»).Я думаю, что это также уменьшило использование стека, делая больше вещей.

Я хочу сказать, что есть компиляторы, которые будут использовать меньше стекового пространства, чем другие.Если есть такие компиляторы для PIC, я не могу сказать.

...