где в микроконтроллере хранятся постоянные переменные?

В соответствии со структурой памяти программы C постоянные переменные хранятся в сегменте инициализированных данных ОЗУ. Но согласно некоторым схемам памяти микроконтроллера константные переменные хранятся во флэш-памяти. Я также видел, что размер BIN-файла моего кода увеличивается, когда я меняю константные переменные на определения макросов? Помогите мне устранить эту двусмысленность. Я использовал Nuc2240 Cortex M0 .

ОЗУ в большинстве микроконтроллеров является энергозависимым, поэтому константы хранятся во флэш-памяти и могут (или не могут) копироваться в ОЗУ во время инициализации (это зависит от микроконтроллера). «Разметка памяти программы C», на которую вы ссылаетесь, не является стандартом как таковым, многие из этих деталей зависят от реализации. C сам по себе является языком высокого уровня, использование памяти будет определяться компилятором и компоновщиком с некоторым знанием целевой платформы. Я не вижу в этом никакой "двусмысленности", разве я неправильно понял вашу озабоченность?
Здесь в ответах обсуждается несколько разных схем (загрузить константу из flash или RAM, либо закодировать в инструкции). Вы можете увидеть, что делает ваша цепочка инструментов для различных случаев, просмотрев листинг дизассемблирования программы и следуя инструкциям ассемблера. Многие IDE позволяют генерировать дизассемблированные списки, или вы можете использовать отдельную программу.

Ответы (5)

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

Когда вы пишете C, используемая вами цепочка инструментов C будет хранить материал там, где он был запрограммирован. Переменные, очевидно, располагаются в ОЗУ, константы могут храниться либо в ОЗУ, либо во флэш-памяти, а программы — во флэш-памяти.

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

Прочитайте вашу конкретную документацию. Это не физика, это все зависит от прихоти человека.

Задача не компилятора, а генератора кода и компоновщика размещать сегменты с константами в месте внутри EPROM, FLASH или RAM с буферизацией от батареи. Но константы также могут помещаться в энергозависимую память, если они инициализируются при запуске программы специальным кодом, сгенерированным компилятором.
да, компилятор - это я ленив, цепочка инструментов.

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

ARM Cortex представляет собой конструкцию, основанную на регистрах, поэтому в основном любые операнды (постоянные или нет) должны каким-то образом попасть в один из внутренних регистров, прежде чем их можно будет использовать в инструкциях программы. Это важно для ответа на ваш вопрос, потому что есть несколько разных способов, которыми архитектура позволяет это сделать.

Для небольших констант (обычно менее 8 или 10 бит) значение может быть закодировано непосредственно в определенных инструкциях. Например, есть инструкция ADD #imm, которая добавит в регистр 12-битное значение, закодированное в слове инструкции. Это можно использовать для прямого указания константы в вашей программе. Если бы вы добавляли одну и ту же константу в разных местах своего кода, а значение было бы 12-битным или меньше, тогда компилятор просто использовал бы инструкцию ADD #imm везде, где требуется ваша константа. Другими словами, фактическая константа будет реплицироваться в нескольких местах по всему коду, хранящемуся во FLASH.

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

Однако теперь мы сталкиваемся с потенциальной проблемой. На большинстве чипов Cortex флэш-память работает с той же скоростью или медленнее, чем ядро ​​ЦП. Если мы попытаемся выполнить инструкцию LDR из ячейки FLASH, эта операция чтения физической памяти должна конкурировать с чтением следующей инструкции. Это создает узкое место, из-за которого процессору приходится останавливаться до тех пор, пока данные не будут прочитаны. Хуже того, во многих реализациях FLASH работает очень медленно, а конвейерный кеш используется для более быстрой подачи инструкций в ЦП. В этих устройствах чтение, скажем, таблицы постоянных значений может выявить фундаментальное узкое место доступа к FLASH, вызывающее серьезное снижение производительности.

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

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

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

Теперь, если вы используете определения макросов, это совершенно другое. Макросы заменяются в исходном коде препроцессором, что означает, что константа будет перекомпилироваться независимо каждый раз, когда вы используете ее в своем коде. И, если вы используете его несколько раз, действительно, код будет больше. Это, конечно, не должно иметь место для простых целочисленных констант. Для строковых констант хорошие компиляторы попытаются объединить несколько одинаковых значений, чтобы уменьшить этот эффект и сохранить небольшой размер, но это не гарантируется и может зависеть от того, используются ли строки только в одной и той же единице компиляции (исходный файл .c) или между ними. несколько блоков компиляции. Для постоянных структур/объектов компиляторы обычно мало оптимизируют.

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

ответ зависит от вашего определения «постоянной переменной» и используемого компилятора.

если это действительно «постоянная переменная», большинство компиляторов сохранит ее как во флэш-памяти.

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

Похоже, вы путаете константы с переменными . Это особенно важно, если вы используете C, а не C++.

В C то, что вы называете константной переменной (например, const int), на самом деле будет выброшено в ОЗУ. Вот как компилятор справляется с необходимостью иногда ссылаться на адрес переменной (указатели).

Если это глобальная переменная, компилятор поместит ее в секцию инициализированных данных , что означает, что значения передаются во флэш-память в .etextсекции и копируются в .sdataсекцию (часть .data) сценарием запуска . Если это локальная переменная, стартовый код функции будет делать это так же, как и любую другую переменную.

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

С другой стороны, если вы используете C++, компилятор при оптимизации использует значение непосредственно из флэш-памяти. Это не сделано в C по замыслу. Смотрите этот ответ для получения дополнительной информации.

Кстати, именно так Cortex-M3, который я использую, работает в GCC.