Размер стека в MSP430

На форуме electronix.ru товарищ d7d1dc задал вопрос:

Привет всем. Подскажите, размер памяти, выделяемой под стек, в устройствах MSP430 всегда одинаков или все зависит от компилятора?

http://electronix.ru/forum/index.php?showtopic=108099

Отвечаю ему и другим людям. Отвечаю здесь, так как ответ длинный, и поэтому я не решаюсь его разместить на том форуме.

Обычно в оперативной памяти размещаются:

  1. Инициализированные и неинициализированные переменные. Инициализированные переменные — это такие, которые при входе в функцию main уже имеют значение. Неинициализированные — получают свои значения (инициализируются) в процессе работы программы. До этого момента, их значения, как правило, равны нулю. Обычно эти переменные объявлены вне какой-либо функции (то есть, имеют глобальную область видимости), либо объявлены внутри функции, но с модификатором static.
  2. Куча (heap). Если вы в программе используете функции malloc, calloc, realloc, free, то вы должны определить размер кучи, где эти функции буду резервировать и освобождать память.
  3. Стек, куда записываются адреса возврата, регистр состояния, параметры (передаваемые в вызываемые функции), а так же локальные переменные функций.

Если размер памяти в п.1 и п.2 можно определить еще на этапе компиляции и линковки, то размер памяти в п.3 определяется чисто экспериментальным путем.

Есть, правда, программы, которые пытаются чисто теоретически прикинуть размер стека. И у них это дело даже получается. На практике оно конечно будет не так, но хотя бы примерно оценить размер стека — эти проги позволяют. Это все лучше, чем совсем ничего. Но в любом случае, только практика дает правильный результат.

Захват (занимание) памяти под переменные в п.1 осуществляется с младших адресов ОЗУ. Стек же «растет» от старших адресов к младшим (иначе говоря — на встречу переменным). Таким образом всегда существует «дыра» между переменными п.1 и стеком. Пока памяти достаточно между переменными п.1 и стеком, все хорошо. Но может случится так, что стек исчерпает свободное пространство, залезет на глобальные данные и изменит их значения.

А может случится наоборот. Допустим, при вызове какой-то функции стек залез на глобальные данные и изменил их. А потом, эта функция еще раз изменила те же самые ячейки памяти, и тем самым сломала в стеке адрес возврата. Обычно в таких случаях прога без видимых причин (ни с того ни с сего) начинает «дурить», либо вообще «падает».

Против такого «злодества» можно задать компиляцию с контролем стека. Те есть прога сама себя контролирует на предмет, чтобы стек не «наехал» на данные. В этом случае прога будет работать медленнее и будет иметь несколько больший объем. Но это не снимает проблему исчерпания памяти, а только дает гарантированный ответ — что случилось.

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

Кроме того, Вы правильно заметили: компилятор компилятору — рознь. Так, например, avrgcc имеет единый стек для адресов возврата и для данных, а CodeVision — два стека: один только для адресов возврата, другой только для данных (которые размещаются на стеке). Получается, что в оперативной памяти образуются две «дыры» вместо одной. Я считаю, что решение не очень эффективное, хотя где-то даже интересное.

Ведь получается, что может случится так, что один стек исчерпал свое пространство, а у второго в это время может быть еще много свободной памяти. Ее бы сейчас использовать, но — нельзя! Земельные участки нарезаны и распределены между пайщиками. Прога падает, хотя вроде бы есть еще память для стека. А в какой-то другой момент у этой же проги, допустим, может произойти исчерпание второго стека, при том, что у первого будет резерв памяти. То есть заранее очень сложно угадать — какой стек в какой момент времени исчерпается.

Возьмем MSP430 — у этих процов единый стек для данных и для адресов возврата. Поэтому таких проблем (с двумя стеками) здесь в принципе нет. И если уж так получилось, что закончилось свободное пространство в оперативной памяти, то оно действительно закончилось. И никакими передвижками стеков тут не обойтись — всё это от Лукавого. Нужно переходить на камень с другим объемом памяти, либо мудроствовать над кодом программы.

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

Что такое константа (или константная строка)? Прежде всего — это данные. А во вторых это данные, которые не изменяются по жизни. Такие данные удобно держать во флешь-памяти. (Флешь ведь в процессе работы проги не меняется.) Собственно, так делается в MSP430, ARM-ах b Cortex-ах, но не в AVR!

AVR идет лесом своим путем. У AVR имеется два адресных пространства: RAM и Flash (точнее — три, но мы не будем сейчас затрагивать EEPROM). В оперативной памяти размещаются только данные, команды из ОЗУ не могут быть считаны и, соответственно, выполнены. Во флешь — размещаются команды и кое-какие данные. «Кое-какие» — это значит не все. AVR не умеет работать с данными, которые находятся во флешь. Максимум, на что способно ядро AVR  — так это только считать одно-байтовые данные из флешь в какой-нибудь регистр.

Поэтому, когда в AVR программе используются константные данные, они сначала (еще до входа в функцию main) считываются из флешь и размещаются в ОЗУ. Размеры ОЗУ и без того крошечные, так еще и константы из флешь в них дублируются.

А по другому никак нельзя, ведь язык С — это язык для процессоров с Фон-Неймановской архитектурой. Фон-Неймоновская архитектура подразумевает единое адресное пространство для кода и для любых видов данных. А у AVR — этих пространств — аж три!

Язык С понятия не имеет о том, как работать с «другими мирами» (с другими адресными пространствами). Язык С даже не предполагает, что могут быть какие-то еще адресные пространства. В понятии языка С адресное пространство едино: коды команд, данные, стек — все они находятся в едином адресном пространстве, но на разных (разумеется!) адресах. Поэтому процесс копирования данных из области кодов программы в область данных (например, начальная инициализация инициализированных переменных, простите тавтологию) выглядит как обычное копирование — из памяти в память. Ядро MSP430, ядро Cortex — отвечают этому требованию. А вот с AVR тут проблемы. Приходится строить костыли.

В качестве костылей для AVR была разработана библиотека функций, которая позволяет динамически (то есть — по мере необходимости, по особому требованию разработчика) вытягивать из флешь заданные данные и размещать их в ОЗУ (опять же в ОЗУ!). После чего с этими данными уже можно работать обычным способом, то есть с помощью стандартной С-ишной библиотеки.

Положительным моментом этой методы является значительное сокращение бешеного перерасхода ОЗУ. Ведь, как  правило, требуются не сразу все данные из флешь, а только какая-то их порция. Таким образом, «нецелевой» расход ОЗУ хоть и остался, но все же он уже не такой убойный, как если бы все константы были скопированы в ОЗУ сразу.

Еще раз подчеркну, что другие архитектуры, от этой «террористической бомбы» свободны. Там пишешь себе в радость и горя не знаешь!

Что же касается стека, то я вам советую почитать про линковщик и области памяти. Погуглите по связке слов «data bss text» и попробуйте разобраться что где лежит в памяти, и как вообще память компонуется.

Advertisements

4 responses to “Размер стека в MSP430

  1. Интересная статья, где человек заново изобрел Си, просто написав макроассемблер для PDP11: http://vinxru.livejournal.com/129992.html
    Сразу стало понятно, как был создан этот язык программирования.

  2. Прочитал. Спасибо за ссылку.

    Во-во! А я о чем говорю, что программировать для MSP430 (он же PDP11 в прошлой жизни) — очень и очень приятно.

  3. AVR и MSP430 это разные вещи и для разного применения. А то что вы работаете, используя язык высокого уровня — это не значит, что AVR плохи. «Костыли» были придуманы не для AVR, как вы выразились, а для тех, кто пишет на Си. А вообще статья ни о чем, и коню понятно, что стэк может расти по мере необходимости, в т.ч. балгодаря и прерываниям, в т.ч. вложенным. ПС. Это мнение любителя (и AVR и MSP, включая 5е семейство, ценю и уважаю).

    • Позвольте не согласиться. AVR, MSP430, STM32F — конечно, разные по своим возможностям микроконтроллеры. Но не для абсолютно «разного» применения. Области их применения существенно перекрываются. Поэтому в жизни мы сплошь и рядом видим, что разные разработчики для одной и той же задачи выбирают разные процессоры, а не какой-то один и тот же (согласно задаче). Причем на выбор процессора в большей степени влияет не столько параметры процессора, сколько его изученность разработчиком. Конечно, есть очень редкие случаи, когда под конкретную задачу подпадает только такой проц и больше никакой. Но это скорее исключения из правил, чем сами правила. Поэтому, если основывать свои утверждения не на правилах, а на исключениях, то можно вообще потерять направление на истину.

      Я не говорю, что AVR плохие. Я лишь говорю о том, что из-за того, что у них разные адресные пространства и отсутствуют команды для обработки данных, находящихся в адресном пространстве команд (т.е. во флешь-памяти), то возникают проблемы. И если в ассемблере эти проблему еще как-то можно решать, то в С эти проблемы обрастают костылями.

      Кроме этого, я утверждаю, что сейчас программы для микроконтроллеров пишутся в основном на С. На ассемблере проги для МК тоже пишутся, но не столь часто и не столь повсеместно. Поэтому проблема костылей для AVR все же «стоит», чем «не стоит».

      Я могу Вас понять, «… статья ни о чем, и коню понятно …». К стати, с Годом Лошади Вас! Может быть Вы не догадываетесь, но я статью писал не только для Вас, а для других тоже. Как говорится, спасибо за Ваше мнение!

      AVR — хороший проц. Народный. Таким он был 10 лет назад. Он красиво смотрелся на фоне 51-го ядра (скорость работы, удобная система памяти, богатая периферия), на фоне MSP430 (корпуса с крупным шагом ножек, цена, дешевый повторяемый в домашнем хозяйстве программатор STK200). Но время безжалостно. На рынок пришли новые, более «вкусные» МК.

      История повторяется. Ранее я наблюдал это явление в среде разработчиков, которые не захотели отказываться от 51-го, не захотели осваивать AVR. Теперь тоже самое происходит по отношению AVR-Cortex. Люди вообще странные по своей сути. Одни считают, что «жрать кактус» — нормально. Другие — что это есть дурно. Не хочу судить ни тех, ни других.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s