Здесь приведен перевод раздела даташита ARM® IAR C/C++ Compiler Reference Guide -> Segment reference, посвященный сегментам кода и данных.
Компилятор ARM IAR C/C++ размещает код и данные в именованные сегменты, которые используются линкером IAR XLINK при построении двоичного кода. Подробные знания сегментов необходимы при программировании модулей на языке ассемблера. Также эти знания полезны, когда нужно анализировать выходной код на языке ассемблера, генерируемый компилятором. Дополнительную информацию о сегментах можно получить из раздела "Placing code and data" (размещение кода и данных).
[Общие сведения о сегментах]
Таблица ниже показывает сегменты, которые доступны в среде компиляции ARM IAR C/C++ Compiler.
Сегмент |
Описание |
CODE |
Содержит код программы. |
CODE_I |
Содержит код программы, декларированный с атрибутом __ramfunc. |
CODE_ID |
Содержит код программы, который копируется в CODE_I (ROM, ПЗУ) при старте программы (startup). |
CSTACK |
Содержит стек, используемый программами на языке C или C++. |
DATA_AC |
Содержит данные инициализированных констант. |
DATA_AN |
Содержит неинициализируемые данные. |
DATA_C |
Содержит данные констант. |
DATA_I |
Содержит статические (static) и глобальные инициализируемые переменные. |
DATA_ID |
Содержит начальные значения для статических и глобальных переменных в DATA_I. |
DATA_N |
Содержит статические и глобальные переменные, определенные с атрибутом __no_init. |
DATA_Z |
Содержит статические и глобальные переменные, инициализируемые нулем. |
DIFUNCT |
Содержит указатели на код, обычно это конструкторы C++ constructors, которые должны быть выполнены кодом старта программы (system startup code) перед вызовом функции main. |
HEAP |
Содержит данные кучи, используемые вызовами malloc и free. |
ICODE |
Содержит код загрузки (startup code, код старта системы). |
INITTAB |
Содержит адреса и размеры сегментов, которые должны быть инициализированы при startup. |
INTVEC |
Содержит вектора сброса и прерывания (reset, interrupt vectors). |
IRQ_STACK |
Содержит стек для обработки запросов прерывания, IRQ и исключений (exceptions). |
SWITAB |
Содержит таблицу векторов программных прерываний (software interrupt vector table). |
[Описание сегментов]
Сегменты размещаются в памяти при использовании директив линкера для управления размещения сегментов -Z и -P, которые предназначены соответственно для последовательного и пакетного размещения. Некоторые сегменты не могут использовать пакетное размещение, потому что их содержимое должно быть непрерывным. В каждом описании тип памяти сегмента — CODE, CONST или DATA - показывает, должен ли сегмент быть помещен в ROM или RAM.
CODE Для исполняемого кода. CONST Для данных, которые помещаются в ROM. DATA Для данных, которые помещаются в RAM.
Подробнее про директивы -Z и -P можно узнать в даташите "IAR Linker and Library Tools Reference Guide". Чтобы узнать, как задать сегменты в файле команд линкера (linker command file), см. раздел "Управление линкером с помощью файла команд". Дополнительную информацию по специальному расширенному синтаксису IAR (extended keywords), см. раздел "Extended keywords".
CODE. Этот сегмент содержит код программы, за исключением кода инициализации системы и кода функций, которые определены с атрибутом __ramfunc. Этот код выполняется из ROM (обычно это память FLASH). Тип памяти сегмента: CODE Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
CODE_I. Этот сегмент содержит код функций, которые определены с атрибутом __ramfunc. Этот код выполняется из RAM (обычно этот тип памяти дает дополнительный прирост скорости выполнения). Код копируется в RAM из сегмента CODE_ID в процессе инициализации. Тип памяти сегмента: CODE Размещение в памяти: Этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
CODE_ID. В этом сегменте расположено хранилище кода, который определен с атрибутом __ramfunc. Этот код выполняется из RAM после того, как будет скопирован в сегмент CODE_I. Тип памяти сегмента: CODE Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
CSTACK. Содержит внутренние данные стека. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
Стек используется функциями для сохранения переменных и другой информации, которая используется в функции локально. Стек представляет собой блок непрерывной памяти, на содержимого которого указывает регистр указателя стека SP (stack pointer). Модуль начальной инициализации системы (код cstartup) инициализирует указатель стека, устанавливая его в конец сегмента стека. При помещении данных в стек указатель SP уменьшает свое значение, и стек растет вниз, в сторону уменьшения адреса.
По умолчанию в начале файла настройки линкера устанавливается константа, определяющая размер стека:
Имейте в виду, что здесь указано шестнадцатеричное значение адреса без префикса 0x. Компилятор использует стек для различных операций, которые происходят в программе, так что действительный требуемый размер стека зависит от сложности программы (глубине вложенности вызовов подпрограмм, количество используемых в них локальных переменных). Если указанное программистом значение размера стека слишком мало, то стек перезапишет область память глобальных и статических переменных, что приведет к сбою программы. Если же выбранный размер стека задан слишком большим, то память RAM будет потрачена впустую.
Далее в файле настройки линкера действительные сегменты определяются в соответствии с областью памяти, выделенной под стек:
-Z(DATA)CSTACK+_CSTACK_SIZE=RAMSTART-RAMEND
DATA_AC. Содержит данные инициализируемых констант.
Сегменты, содержащие located данные, не нуждаются ни в какой дальнейшей конфигурации, потому что они имеют уже присвоенные адреса до процесса линковки. Словечко "located" означает, что данные размещены по абсолютно заданному месту (location) с использованием оператора @ или директивы #pragma location.
DATA_AN. Содержит данные, которые определены с атрибутом __no_init. Все, что сказано для located-данных в описании сегмента DATA_AC, относится и к сегменту DATA_AN.
DATA_C. Содержит данные констант (объекты, определенные с ключевым словом const). Тип памяти сегмента: CONST Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
DATA_I. Содержит статические и глобальные инициализируемые переменные, которые инициализируются путем копирования из сегмента DATA_ID при старте приложения (startup). Этот сегмент не может быть размещен в памяти директивой -P пакетного размещения, потому что содержимое сегмента должно быть непрерывным. Вместо этого нужно использовать директиву -Z, когда Вы задаете этот сегмент в настроечном файле команд линкера. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
DATA_ID. Хранилище значений для статических и глобальных переменных сегмента DATA_I. Эти значения копируются из DATA_ID в DATA_I при старте приложения (startup). Этот сегмент не может быть размещен в памяти директивой -P пакетного размещения, потому что содержимое сегмента должно быть непрерывным. Вместо этого нужно использовать директиву -Z, когда Вы задаете этот сегмент в настроечном файле команд линкера. Тип памяти сегмента: CONST Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
DATA_N. Содержит статические и глобальные переменные, которые определены с атрибутом __no_init. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
DATA_Z. Содержит статические и глобальные переменные, инициализируемые нулем. Этот сегмент не может быть размещен в памяти директивой -P пакетного размещения, потому что содержимое сегмента должно быть непрерывным. Вместо этого нужно использовать директиву -Z, когда Вы задаете этот сегмент в настроечном файле команд линкера. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
DIFUNCT. Содержит вектор динамической инициализации, используемый C++. Тип памяти сегмента: CODE Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
HEAP. Содержит кучу, используемую для динамически выделяемых данных, другими словами для данных, выделяемых вызовами malloc и free, а также в C++, new и delete. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
Сегмент кучи будет использоваться в приложении только тогда, когда действительно используется динамическое выделение памяти. Размер кучи и её размещение в памяти определяются файлом команд линкера - точно так же, как и при определении сегмента стека. Пример:
-D_HEAP_SIZE=8000
...
-Z(DATA)HEAP+_HEAP_SIZE=RAMSTART-RAMEND
ICODE. Содержит специальный код запуска (startup); эти функции должны быть в пределах доступности инструкций ветвления из сегмента INTVEC. Тип памяти сегмента: CODE Размещение в памяти: этот сегмент может быть размещен в любом месте младшей 32-мегабайтной области памяти, так чтобы ассемблерные инструкции ветвления из сегмента INTVEC могли передать управление в ICODE. Тип доступа: только для чтения.
INITTAB. Содержит таблицу адресов и размеров сегментов, которые должны быть инициализированы при startup. Тип памяти сегмента: CONST Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
INTVEC. Содержит вектор сброса (reset) и вектора исключений (exceptions). В этих векторах размещены инструкции ветвления, делающие переходы в cstartup, обработчики прерываний и т. п. Тип памяти сегмента: CONST Размещение в памяти: этот сегмент должен быть размещен в диапазоне адресов от 0x00 до 0x3F. Тип доступа: только для чтения.
IRQ_STACK. Содержит стек, который используется при обработке исключений IRQ. Другие стеки могут быть добавлены, если это нужно для обработки других типов исключений FOQ, SVC, ABT и UND. Файл кода запуска cstartup.s79 должен быть модифицирован, чтобы инициализировать используемые указатели стека исключений. Тип памяти сегмента: DATA Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: чтение и запись.
Архитектура ARM поддерживает 5 режимов исключения, в которые входит процессор при разных событиях исключения (exception). Каждый режим исключения имеет свой собственный стек для того, чтобы избежать порчи стека режима пользователя/системы. В таблице показаны предлагаемые имена для различных стеков исключения, но могут использоваться и любые другие имена.
Режим процессора |
Рекомендуемое имя сегмента стека |
Описание |
Supervisor |
SVC_STACK |
Стек операционной системы. |
IRQ |
IRQ_STACK |
Стек для обработчиков прерывания (IRQ) общего назначения. |
FIQ |
FIQ_STACK |
Стек для обработчиков высокоскоростных прерываний (FIQ). |
Undefined |
UND_STACK |
Стек для прерываний от встретившейся неизвестной инструкции. Поддерживает программную эмуляцию аппаратных сопроцессоров и расширений системы команд. |
Abort |
ABT_STACK |
Стек обработчиков прерываний при событиях некорректной выборки инструкции и недопустимому доступу к данным. |
Для каждого режима процессора, которому нужен стек, в коде загрузки (startup code) должен быть инициализирован отдельный указатель стека, и в файле команд линкера должно быть задано размещение сегмента. В поставляемых файлах cstartup.s79 и lnkarm.xcl предварительно конфигурируется только стек IRQ, но можно легко добавить другие стеки исключений.
Чтобы просмотреть любой из этих стеков в окне Stack среды IAR Embedded Workbench IDE, эти предварительно сконфигурированные имена сегментов должны быть использованы вместо имен сегментов, заданных пользователем.
SWITAB. Содержит таблицу векторов программных прерываний. Тип памяти сегмента: CODE Размещение в памяти: этот сегмент может быть размещен в любом месте памяти. Тип доступа: только для чтения.
[Управление линкером с помощью файла команд]
Файл настроек линкера нужно изменять в том случае, когда нужно настроить целевую карту памяти программируемой системы (target system memory map). Предположим, целевая система должна использовать такую карту памяти:
0x000000–0x00003F ROM или RAM 0x008000–0x0FFFFF ROM (FLASH) или другая энергонезависимая память 0x100000–0x7FFFFF RAM или другая память, доступная на чтение и запись
ROM может использоваться для сохранения сегментов памяти, которая имеет тип CONST и CODE. Память RAM может содержать сегменты типа DATA. Основное назначение изменения файла команд линкера - проверить, что код и данные Вашего приложения не пересекают границы областей памяти (области памяти технологически ограничены в зависимости от типа процессора) - невыполнение этого условия приведет к отказу в работе приложения.
В файле команд линкера (см. пример по умолчанию lnkarm.xcl), адреса начала и конца сегментов ROM и RAM задаются директивой -D:
-DROMSTART=08000
-DROMEND=FFFFF
-DRAMSTART=100000
-DRAMEND=7FFFFF
Содержимое файла команд линкера. Каталог arm\config содержит готовые файлы команд линкера. Кроме того, Вы можете найти готовые файлы команд линкера для различных отладочных плат ARM в подкаталогах arm\src\examples. Настроечные файлы команд содержат информацию, требуемую линкеру, и они сразу готовы к использованию. Если, к примеру, Ваше приложение использует дополнительную внешнюю RAM, то нужно добавить подробную информацию, описывающую эту внешнюю область памяти RAM. Помните о том, что не следует вносить изменения в оригинальные настроечные файлы. Вместо этого рекомендуется сделать копию настроечного файла в рабочую директорию проекта, и модифицировать эту копию. Поставляемые файлы конфигурации линкера снабжены комментариями, описывающими содержимое.
Помимо всего прочего, командный файл линкера содержит три различных типа параметров командной строки XLINK:
• Тип используемого CPU (-carm), задает тип целевого ядра. • Определение констант, которые буду использоваться в файле далее. Константы задаются опцией -D. • Директивы размещения (это самая большая часть файла команд линкера). Сегменты могут быть размещены с использований опций -Z и -P. Опция -Z (последовательное размещение) поместит части сегмента в порядке их появления, а опция -P (пакетное размещение) приведет к тому, что линкер попытается перестроить части так, чтобы лучше использовать память. Однако -P полезно применять только тогда, когда не требуется обеспечить непрерывность размещения частей сегмента. Подробнее см. даташит "IAR Linker and Library Tools Reference Guide".
Использование команды -Z для последовательного размещения. Используйте команду -Z, когда Вам нужно обеспечить весь сегмент как непрерывный кусок памяти, когда нужно сохранить порядок частей в сегменте, или, что используется реже, чтобы разместить сегменты в определенном порядке.
В следующем примере показано, как использовать команду -Z для размещения сегмента MYSEGMENTA перед сегментом MYSEGMENTB в памяти CONST (т. е. в ROM), в диапазоне адресов 0x008000-0x0FFFFF.
-Z(CONST)MYSEGMENTA,MYSEGMENTB=008000-0FFFFF
Два сегмента разного типа можно разместить в той же самой области памяти, но для этого не нужно указывать диапазон второго сегмента. В следующем примере сегмент MYSEGMENTA будет размещен в памяти первым. Тогда остальная память будет использоваться MYCODE.
-Z(CONST)MYSEGMENTA=008000-0FFFFF
-Z(CODE)MYCODE
Два диапазона памяти могут перекрывать друг друга. Это позволяет сегментам с различными требованиями к размещению использовать разделяемое общее пространство памяти, пример:
-Z(CONST)MYSMALLSEGMENT=008000-000FFF
-Z(CONST)MYLARGESEGMENT=008000-0FFFFF
Несмотря на то, нет строгих рекомендаций на указание конца каждого диапазона памяти, все равно рекомендуется это делать. Когда Вы укажете конец диапазона памяти, то линковщик IAR XLINK Linker предупредит Вас, если Ваши сегменты не умещаются в памяти.
Использование команды -P для пакетного размещения. Команда -P отличается от -Z тем, что при использовании -P не требуется размещать сегменты (или части сегмента) последовательно. С использованием -P становится возможным размещать части сегмента в "дырки", оставленные от предыдущих размещений других сегментов.
Следующий пример показывает, как можно применить опцию XLINK -P, чтобы использовать память эффективнее. Команда разместит сегмент данных MYDATA в памяти DATA (т. е. RAM) в фиктивном диапазоне памяти:
-P(DATA)MYDATA=100000-101FFF,110000-111FFF
Если Ваше приложение имеет дополнительную область RAM в диапазоне памяти 0x10F000-0x10F7FF, то просто добавьте к оригинальному определению:
-P(DATA)MYDATA=100000-101FFF,10F000–10F7FF,110000-111FFF
|