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


25

Существуют разные сегменты памяти, в которые вводятся различные типы данных из кода C после компиляции. То есть: .text, .data, .bss, стек и куча. Я просто хочу знать, где каждый из этих сегментов будет находиться в памяти микроконтроллера. То есть, какие данные поступают в какой тип памяти, учитывая типы памяти: RAM, NVRAM, ROM, EEPROM, FLASH и т. Д.

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

Любая помощь высоко ценится. Заранее спасибо!


1
NVRAM, ROM, EEPROM и Flash - это разные имена для одной и той же вещи: энергонезависимой памяти.
Лундин

Немного касательно вопроса, но код может (исключительно) существовать во многих из них, особенно если вы рассматриваете использование патчей или калибровки. Иногда он будет перемещен перед выполнением, иногда выполняется на месте.
Шон Хулихейн

@SeanHoulihane ОП спрашивает о микроконтроллерах, которые почти всегда работают вне Flash (вы квалифицировали свой комментарий как исключительный). Микро процессоры с МБ - х внешнего ОЗУ под управлением Linux, например, будут копировать свои программы в оперативную память , чтобы выполнить их, возможно , от SD - карты , действующей в качестве монтируемого объема.
tcrosley

@tcrosley В настоящее время существуют микроконтроллеры с TCM, и иногда микроконтроллеры являются частью более крупного SoC. Я также подозреваю, что в таких случаях, как устройства eMMC, mcu запускается из оперативной памяти из собственной памяти (на основе памяти телефона, загруженного пару лет назад). Я согласен, это не прямой ответ - но я думаю, что очень важно, чтобы типичные отображения никоим образом не были жесткими правилами.
Шон Хулихейн

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

Ответы:


38

.текст

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

.bss и .data

Существует три типа данных, которые могут быть размещены вне функции или процедуры; первая - это неинициализированные данные (исторически называемые .bss, которые также включают в себя 0 инициализированных данных), а вторая - инициализированные (не-bss) или .data. Название «bss» исторически происходит от «Block Started by Symbol», использовавшегося в ассемблере около 60 лет назад. Обе эти области области расположены в оперативной памяти.

По мере компиляции программы переменные будут распределены в одну из этих двух общих областей. На этапе связывания все элементы данных будут собраны вместе. У всех переменных, которые необходимо инициализировать, будет выделена часть памяти программы для хранения начальных значений, и непосредственно перед вызовом main () переменные будут инициализированы, как правило, модулем с именем crt0. Раздел bss инициализируется для всех нулей одним и тем же кодом запуска.

С несколькими микроконтроллерами есть более короткие инструкции, которые разрешают доступ к первой странице (первые 256 мест, иногда называемые страницей 0) ОЗУ. Компилятор для этих процессоров может зарезервировать ключевое слово, например, nearдля обозначения переменных, которые должны быть там размещены. Аналогичным образом, существуют также микроконтроллеры, которые могут ссылаться только на определенные области через регистр указателя (требующий дополнительных инструкций), и такие переменные обозначаются far. Наконец, некоторые процессоры могут обращаться к разделу памяти побитно, и компилятор может указать это (например, ключевое слово bit).

Поэтому могут быть дополнительные сегменты, такие как .nearbss и .neardata и т. Д., Где собираются эти переменные.

.rodata

Третий тип данных, внешний по отношению к функции или процедуре, похож на инициализированные переменные, за исключением того, что он предназначен только для чтения и не может быть изменен программой. В языке Си эти переменные обозначаются с помощью constключевого слова. Они обычно хранятся как часть флеш-памяти программы. Иногда они идентифицируются как часть сегмента .rodata (данные только для чтения). На микроконтроллерах, использующих архитектуру Гарварда , компилятор должен использовать специальные инструкции для доступа к этим переменным.

стек и куча

Стек и куча находятся в оперативной памяти. В зависимости от архитектуры процессора, стек может увеличиваться или уменьшаться. Если он вырастет, он будет размещен в нижней части оперативной памяти. Если он уменьшается, он будет помещен в конец ОЗУ. Куча будет использовать оставшуюся память, не выделенную для переменных, и расти в противоположном направлении стека. Максимальный размер стека и кучи обычно можно указать в качестве параметров компоновщика.

Переменные, помещенные в стек, - это любые переменные, определенные в функции или процедуре без ключевого слова static. Когда-то их называли автоматическими переменными ( autoключевое слово), но это ключевое слово не нужно. Исторически autoсуществует, потому что он был частью языка B, который предшествовал C, и там он был необходим. Параметры функции также помещаются в стек.

Вот типичный макет для оперативной памяти (при условии отсутствия специального раздела на странице 0):

введите описание изображения здесь

EEPROM, ROM и NVRAM

До появления флэш-памяти EEPROM (электрически стираемое программируемое постоянное запоминающее устройство) использовалось для хранения данных программы и const (сегменты .text и .rodata). Теперь имеется только небольшой объем (например, от 2 КБ до 8 КБ) EEPROM, если таковой вообще имеется, и он обычно используется для хранения данных конфигурации или других небольших объемов данных, которые необходимо сохранить при отключении питания. цикл. Они не объявляются в программе как переменные, а записываются в специальные регистры микроконтроллера. EEPROM также может быть реализован в виде отдельного чипа и доступен через шину SPI или I²C.

ПЗУ по сути такой же, как Flash, за исключением того, что он запрограммирован на заводе (не программируется пользователем). Он используется только для устройств с очень большим объемом.

NVRAM (энергонезависимая RAM) является альтернативой EEPROM и обычно реализуется как внешняя микросхема. Обычное ОЗУ может считаться энергонезависимым, если оно работает от батареи; в этом случае никаких специальных методов доступа не требуется.

Хотя данные могут быть сохранены во Flash, Flash-память имеет ограниченное количество циклов стирания / программирования (от 1000 до 10000), поэтому она не предназначена для этого. Это также требует одновременного удаления блоков памяти, поэтому неудобно обновлять всего несколько байтов. Он предназначен для кода и переменных только для чтения.

ЭСППЗУ имеет гораздо более высокие ограничения на циклы стирания / программирования (от 100 000 до 1 000 000), поэтому для этой цели гораздо лучше. Если на микроконтроллере имеется EEPROM, и он достаточно большой, это место, где вы хотите сохранить энергонезависимые данные. Однако вам также придется сначала стереть в блоках (обычно 4 КБ) перед записью.

Если EEPROM отсутствует или он слишком мал, тогда необходим внешний чип. ЭСППЗУ объемом 32 КБ составляет всего 66 ¢ и может быть стерто / записано до 1 000 000 раз. NVRAM с тем же числом операций стирания / программирования намного дороже (x10). NVRAM обычно быстрее для чтения, чем EEPROM, но медленнее для записи. Они могут быть записаны в один байт за раз или в блоках.

Лучшей альтернативой обоим из них является FRAM (сегнетоэлектрическое ОЗУ), который имеет практически бесконечные циклы записи (100 триллионов) и не имеет задержек при записи. Это примерно такая же цена, как у NVRAM, около 5 долларов за 32 КБ.


Это была действительно полезная информация. Не могли бы вы дать ссылку на ваше объяснение? Как учебники или журналы, на случай, если я захочу больше узнать об этом ..?
Soju T Varghese

еще один вопрос, не могли бы вы дать представление о преимуществах или недостатках одной памяти над другой (EEPROM, NVRAM и FLASH)?
Soju T Varghese

Хороший ответ. Я опубликовал дополнительную статью, в которой более подробно остановился на том, что конкретно происходит в языке Си.
Лундин

1
@ SojuTVarghese Я обновил свой ответ и также включил некоторую информацию о FRAM.
tcrosley

@Lundin мы использовали одни и те же имена сегментов (например, .rodata), поэтому ответы хорошо дополняют друг друга.
tcrosley

21

Обычная встроенная система:

Segment     Memory   Contents

.data       RAM      Explicitly initialized variables with static storage duration
.bss        RAM      Zero-initialized variables with static storage duration
.stack      RAM      Local variables and function call parameters
.heap       RAM      Dynamically allocated variables (usually not used in embedded systems)
.rodata     ROM      const variables with static storage duration. String literals.
.text       ROM      The program. Integer constants. Initializer lists.

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


Объяснение:

Переменная имеет статическую продолжительность хранения, если она объявлена ​​как staticили если она находится в области видимости файла (иногда небрежно называемой «глобальной»). В C есть правило, гласящее, что все статические переменные продолжительности хранения, которые программист не инициализировал явно, должны быть обнулены.

Каждая статическая переменная продолжительности хранения, которая инициализируется в ноль, неявно или явно, заканчивается в .bss. В то время как те, которые явно инициализированы ненулевым значением, заканчиваются в .data.

Примеры:

static int a;                // .bss
static int b = 0;            // .bss      
int c;                       // .bss
static int d = 1;            // .data
int e = 1;                   // .data

void func (void)
{
  static int x;              // .bss
  static int y = 0;          // .bss
  static int z = 1;          // .data
  static int* ptr = NULL;    // .bss
}

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

Примеры других сегментов:

const int a = 0;           // .rodata
const int b;               // .rodata (nonsense code but C allows it, unlike C++)
static const int c = 0;    // .rodata
static const int d = 1;    // .rodata

void func (int param)      // .stack
{
  int e;                   // .stack
  int f=0;                 // .stack
  int g=1;                 // .stack
  const int h=param;       // .stack
  static const int i=1;    // .rodata, static storage duration

  char* ptr;               // ptr goes to .stack
  ptr = malloc(1);         // pointed-at memory goes to .heap
}

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

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

int* j=0;                  // .bss
const int* k=0;            // .bss, non-const pointer to const data
int* const l=0;            // .rodata, const pointer to non-const data
const int* const m=0;      // .rodata, const pointer to const data

void (*fptr1)(void);       // .bss
void (*const fptr2)(void); // .rodata
void (const* fptr3)(void); // invalid, doesn't make sense since functions can't be modified

В случае целочисленных констант, списков инициализаторов, строковых литералов и т. Д. Они могут оказаться в .text или .rodata в зависимости от компилятора. Вероятно, они заканчиваются как:

#define n 0                // .text
int o = 5;                 // 5 goes to .text (part of the instruction)
int p[] = {1,2,3};         // {1,2,3} goes to .text
char q[] = "hello";        // "hello" goes to .rodata

Я не понимаю, в вашем первом примере кода, почему «static int b = 0;» идет в .bss и почему 'static int d = 1;' переходит в .data ..? В моем понимании, обе являются статическими переменными, которые были инициализированы программистом .. тогда в чем же разница? @Lundin
T Varghese

2
@SojuTVarghese, потому что .bss данные инициализируются в 0 как блок; конкретные значения, такие как d = 1, должны храниться во флэш-памяти.
tcrosley

@SojuTVarghese Добавил некоторые разъяснения.
Лундин

@Lundin Кроме того, из вашего последнего примера кода означает ли это, что все инициализированные значения идут в .text или .rodata, а их соответствующие переменные сами по себе идут в .bss или .data? Если да, то как переменные и соответствующие им значения отображаются друг на друга (т. Е. Между сегментами .bss / .data и .text / .rodata)?
Soju T Varghese

@SojuTVarghese Нет, .dataобычно имеет так называемый адрес загрузки во флэш-памяти, где хранятся начальные значения, и так называемый виртуальный адрес (не совсем виртуальный в микроконтроллере) в оперативной памяти, где переменная хранится во время выполнения. Перед mainзапуском начальные значения копируются с адреса загрузки на виртуальный адрес. Вам не нужно хранить нули, поэтому .bssне нужно хранить его начальные значения. sourceware.org/binutils/docs/ld/…
starblue

1

Хотя любые данные могут помещаться в любую память, которую выбирает программист, обычно система работает лучше всего (и предназначена для использования), когда профиль использования данных сопоставляется с профилями чтения / записи памяти.

Например, программный код - WFRM (пишите мало, много читайте), и его очень много. Это хорошо подходит для FLASH. ROM OTOH является W раз RM.

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.