Перейти к содержанию
    

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подскажите, возможно ли настроить IAR так, чтобы он сделал компиляцию кода (например какой-то отдельной функции или всего кода) строго в определенный участок флешь памяти (от сих до сих, так сказать)?

 

Дописать перед функцией pragma location, а в опциях линкера (ExtraOptions) определить это место, как сегмент.

Например, так было достигнуто размещение функции ReadFuse() в загрузочной оласти:

#pragma location="BOOT"
void ReadFuse() { ... }

Сам сегмент был определен так:

-D_..X_FLASH_BOOT=F000
-Z(CODE)BOOT=_..X_FLASH_BOOT-_..X_FLASH_END

Вместо BOOT можно написать какое-то другое слово, а вместо F000 другой адрес.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При попытке компиляции IAR выдает ошибку Fatal Error[e163]: The command line symbol "_..X_FLASH_END" in -Z(CODE)BOOT=_..X_FLASH_BOOT-_..X_FLASH_END is not defined.

Изменено пользователем d7d1cd

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за ответ. Что значит 2 точки в коде:

-D_..X_FLASH_BOOT=F000
-Z(CODE)BOOT=_..X_FLASH_BOOT-_..X_FLASH_END

То есть задается только адрес начала размещения?

Нет, здесь задается именно сегмент памяти от сих до сих, где будут размещены ВСЕ функции, которые к данному сегменту приписаны. Т.е. если у вас несколько функций приписаны к сегменту BOOT, то все они в него попадут друг за дружкой. И тогда строго формально нельзя будет предсказать, с какого адреса одна из тех функций начинается, хотя практически линкер укладывает их тела в тот сегмент в порядке упоминания в тексте программы. Поэтому, чтобы разместить функцию строго по определенному адресу, вам придется сделать уникальный сегмент только для нее! Тогда уж она точно окажется в том ряду первой и попадет на начало сегмента.

 

Точки и подчеркивание, думаю, нечего не означают, просто придают именам уникальность, чтобы те случайно не совпали с именами каких-то объектов в программе.

Т.е.

_..X_FLASH_BOOT

- это одно имя целиком, а директива D (от слова define) лишь приписывает ей значение.

Само же определение сегмента задано во второй строке, после директивы Z.

 

Вообще-то, я сама глубоко с этим не разбиралась, а просто механически переделала под свое имя (BOOT) определение какого-то другого сегмента в том же самом стиле (это видно в xcl-файле для данного типа МК). Рисковать не хотелось, а надо было сделать по-быстрому.

 

Думаю, что в данном случае вполне годилось бы самое примитивное определение без всяких дефиниций:

-Z(CODE)BOOT=F000-F100

где задается имя сегмента, а его границы выставлены прямо в числах.

 

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

\Program Files\IAR Systems\Embedded Workbench 6.5\430\config\lnk430f5529.xcl

(так этот файл называется для MSP430F5529, но у вас может быть другой МК)

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

 

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

 

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

 

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

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

 

При попытке компиляции IAR выдает ошибку Fatal Error[e163]: The command line symbol "_..X_FLASH_END" in -Z(CODE)BOOT=_..X_FLASH_BOOT-_..X_FLASH_END is not defined.

Дефиниции границ сегментов могут довольно сильно отличаться по именам у разных МК. Советую вам найти xcl-файл именно для своего типа МК и сделать определение, подобное тому, как там определены сегменты кода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А потому попытайте сначала самый легкий способ - на числах, вдруг сработает?

Определил сегмент так:

-Z(CODE)BOOT=F002-F0FF

И все получилось! Компилятор расположил код функции с адреса 0xF002. Спасибо.

 

По расположению кода теперь разобрался. Есть другой вопрос. В функции я использую переменные. Как указать компилятору при определении переменной, что эта переменная должна быть физически расположена по адресу 0x0200, например? Или как указать, что массив данных должен начинаться с адреса 0x0421?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По расположению кода теперь разобрался. Есть другой вопрос. В функции я использую переменные. Как указать компилятору при определении переменной, что эта переменная должна быть физически расположена по адресу 0x0200, например? Или как указать, что массив данных должен начинаться с адреса 0x0421?

 

Точно так же! Только на этот раз "pragma location" прописывается не перед функцией, а перед определением переменной или массива. Только просите память не из сегмента CODE, а из сегмента DATA. У нас на форуме на такой вопрос уже отвечали:

http://electronix.ru/forum/index.php?s=&am...t&p=1020355

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попытался указать переменной конкретный адрес. Ничего не получается. В опциях линкера я указал:

-Z(DATA)VARIABLE=0200-020F

В коде прописал:

#pragma location = "VARIABLE"
char data = 1;

Однако при выполнении кода в симуляторе я вижу, что цифра 1 попадает в регистр R14, а не в диапазон 0x0200-0x020F. Ошибок нет. Что не так то опять?

 

P.S. Посмотрел файл io430x24x.h. Есть там много строк, подобных этой:

 /* ADC12 Interrupt Flag */
__no_init volatile unsigned short ADC12IFG @ 0x01A4;

Как я понимаю, здесь переменной ADC12IFG "назначается" адрес 0x01A4. Неужели я не могу так же в программе назначить свои переменные?

Изменено пользователем d7d1cd

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В коде прописал:

#pragma location = "VARIABLE"
char data = 1;

Однако при выполнении кода в симуляторе я вижу, что цифра 1 попадает в регистр R14, а не в диапазон 0x0200-0x020F. Ошибок нет. Что не так то опять?

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

 

Посмотрел файл io430x24x.h. Есть там много строк, подобных этой:

 /* ADC12 Interrupt Flag */
__no_init volatile unsigned short ADC12IFG @ 0x01A4;

Как я понимаю, здесь переменной ADC12IFG "назначается" адрес 0x01A4.

Неужели я не могу так же в программе назначить свои переменные?

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Сделал объявление переменной глобально, то есть вне функции. Все хорошо, но работает только с атрибутом __no_init:

#pragma location = "VARIABLE"
__no_init volatile char data;

int main( void )
{
  data = 1;
}

Далее я попробовал сделать как в стандартном файле. Все тоже заработало, но тоже только с атрибутом __no_init:

__no_init volatile char X @ 0x0210;

int main( void )
{
  data = 1;
}

В принципе то, что объявление переменных выше указанными способами не предусматривает их инициализации, мне и надо. По удобству я считаю, что второй способ лучше.

С объявлением глобальных переменных тоже теперь ясно. А можно ли локальную переменную объявить так, чтобы она была связана с конкретным регистром?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Все хорошо, но работает только с атрибутом __no_init

В принципе то, что объявление переменных выше указанными способами не предусматривает их инициализации, мне и надо. По удобству я считаю, что второй способ лучше.

С объявлением глобальных переменных тоже теперь ясно.

Так оно и быть должно. Все обнуляемые переменные и массивы попадают в сегмент NEAR_Z (у других МК может называться по иному), который обнуляется при запуске (в процедуре startup). А раз у вас данные расположены в другом/самодельном сегменте, то инициализации они не получат. То, что аттрибут __no_init требуется синтаксически не знала (в старых версиях не был обязателен).

 

А можно ли локальную переменную объявить так, чтобы она была связана с конкретным регистром?

Полагаю, что нельзя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хорошо. А возможно ли какой-то локальной переменной присвоить значение какого-то регистра? И еще: при объявлении локальной переменной ей назначается какой-то регистр. Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?

Оставьте эту работу компилятору, это его проблемы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хорошо. А возможно ли какой-то локальной переменной присвоить значение какого-то регистра? И еще: при объявлении локальной переменной ей назначается какой-то регистр. Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?

 

На этот вопрос мне трудно дать конкретный ответ, т.к. нас с вами разделяют архитектуры: я - преимущественно AVR-щица, а вы - пользователь MSP430. Обмениваться опытом нам позволяет лишь общий компилятор IAR, и то лишь в той мере, в которой существует подобие между реализациями для этих двух архитектур.

 

Согласно стандартам языка C/C++, вы имеете возможность добавить декларатор "register", если хотите, чтобы локальная переменная заводилась не на стеке, а в регистре:

register char data;

или

char register data;

 

Только эта декларация является лишь пожеланием, которое компилятор может проигнорировать, даже не выдав по этому поводу никакого сообщения. Что же касается массивов, то это почти безнадега. То, насколько у компилятора есть возможности удовлетворить данное пожелание, сильно зависит от числа свободных регистров в данной архитектуре. В архитектуре AVR имеется 32 регистра, и хотя младшие из них специализированы, то все равно остается дофига других. В таких случаях компилятор довольно охотно удовлетворяет просьбу "register", хотя и трудно заранее предвидеть, какой по номеру регистр он для этой цели выберет. А, скажем, на архитектуре x86 такие пожелания удовлетворяются крайне редко, т.к. там всего 7 регистров, причем, совсем не лишних. Поэтому мне трудно предсказать, насколько компилятор для MSP430 окажется покладистым.

 

Еще в AVR-ном варианте есть возможность зарезервировать отдельные регистры (начиная с R15 и ниже) под глобальные переменные. Например:

__regvar __no_init volatile long data@12; // data занимает 4 регистра: R12,R13,R14,R15

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

При этом пользоваться можно только библиотекой clib (которая регистры R5-R15 либо не использует, либо сохраняет/восстанавливает их значения), а библиотекой dlib пользоваться нельзя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Оставьте эту работу компилятору, это его проблемы.

 

Я понимаю, но у меня задача очень специфическая. Нашел я тут как делать вставки на ассемблере:

int main( void )
{
  
  int AAA;
  asm("MOV.W R15, &AAA");
     
}

 

Однако тут компилятор выдает ошибку:

Error[Og005]: Unknown symbol in inline assembly: "AAA"

 

Если же переменную ААА объявить глобально (вне функции main), то ошибки нет. Хотя в справке к IAR переменная объявлена локально. Почему же ошибка?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если же переменную ААА объявить глобально (вне функции main), то ошибки нет. Хотя в справке к IAR переменная объявлена локально. Почему же ошибка?

Компилятор строку с inline assembler сам не разбирает. Он просто вставляет ее в текст объектного (ассемблерного) модуля. А поскольку в вашем примере переменная AAA в Си-шном модуле нигде более не используется, то оптимизатор компилятора выкидывает ее за ненадобностью. И вот когда объектный модуль попадает "на стол" к линкеру, то линкер приходит в недоумение, что это за объект AAA вдруг обнаружился? Т.к. компилятор про объект AAA ничего (адрес объекта) линкеру не сообщил и более того - совсем выкинул из программы.

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

 

Ну и еще раз напомню про отличия типов переменных. Тип переменной влияет на ее размещение в памяти и область видимости.

Переменные типа global (глобальная) и static (статическая) размещаются в памяти данных, т.е. под них выделяются ячейки памяти с постоянным адресом.

Переменные типа auto (автоматическая или локальная) размещаются на стеке или в РОН (по усмотрению компилятора), но постоянного адреса в памяти они не имеют.

Переменные типа register (регистровая) в принципе могут быть как глобальными (если объявлена вне функции с указанием конкретного регистра) так и локальными (объявлена без указания регистра внутри функции). Переменные типа register это тип данных с наиболее быстрым доступом. А для того, чтобы доступ к ним был быстрым, предполагается, что размещаться они должны в РОН. Но проблема состоит в том, что если заранее в опциях проекта для переменных типа register не зарезервировать конкретные регистры (в IAR EW430 возможно резервировать только R4 и R5), то компилятор обращается с ними очень вольно и в большинстве случаев пилюет на такую декларацию с высокой колокольни, трактуя локальные register как обычные переменные auto.

Область видимости global - вся программа.

Область видимости static - данный конкретный модуль или функция, если static объявлена внутри функции.

Область видимости auto - функция или блок оператора в котором она объявлена.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...