Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вопрос по IAR
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
Страницы: 1, 2
d7d1cd
Привет всем. Подскажите, возможно ли настроить IAR так, чтобы он сделал компиляцию кода (например какой-то отдельной функции или всего кода) строго в определенный участок флешь памяти (от сих до сих, так сказать)?
Xenia
Цитата(d7d1cd @ Nov 8 2013, 22:19) *
Подскажите, возможно ли настроить 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 другой адрес.
d7d1cd
При попытке компиляции IAR выдает ошибку Fatal Error[e163]: The command line symbol "_..X_FLASH_END" in -Z(CODE)BOOT=_..X_FLASH_BOOT-_..X_FLASH_END is not defined.
Xenia
Цитата(d7d1cd @ Nov 9 2013, 08:51) *
Спасибо за ответ. Что значит 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, но у вас может быть другой МК)
переключить проект на него вместо умолчания, а потом внести в него исправления. Редактировать исходный файл конфигурации нельзя, можно только копию!

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

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

Цитата(d7d1cd @ Nov 9 2013, 08:51) *
А что будет, если скомпилированный код не умещается от указанного места расположения до конца флешь памяти?

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

Цитата(d7d1cd @ Nov 9 2013, 08:51) *
При попытке компиляции 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-файл именно для своего типа МК и сделать определение, подобное тому, как там определены сегменты кода.
d7d1cd
Цитата(Xenia @ Nov 9 2013, 19:56) *
А потому попытайте сначала самый легкий способ - на числах, вдруг сработает?

Определил сегмент так:
Код
-Z(CODE)BOOT=F002-F0FF

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

По расположению кода теперь разобрался. Есть другой вопрос. В функции я использую переменные. Как указать компилятору при определении переменной, что эта переменная должна быть физически расположена по адресу 0x0200, например? Или как указать, что массив данных должен начинаться с адреса 0x0421?
Xenia
Цитата(d7d1cd @ Nov 9 2013, 21:31) *
По расположению кода теперь разобрался. Есть другой вопрос. В функции я использую переменные. Как указать компилятору при определении переменной, что эта переменная должна быть физически расположена по адресу 0x0200, например? Или как указать, что массив данных должен начинаться с адреса 0x0421?


Точно так же! Только на этот раз "pragma location" прописывается не перед функцией, а перед определением переменной или массива. Только просите память не из сегмента CODE, а из сегмента DATA. У нас на форуме на такой вопрос уже отвечали:
http://electronix.ru/forum/index.php?s=&am...t&p=1020355
d7d1cd
Попытался указать переменной конкретный адрес. Ничего не получается. В опциях линкера я указал:
Код
-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. Неужели я не могу так же в программе назначить свои переменные?
Xenia
Цитата(d7d1cd @ Nov 10 2013, 14:30) *
В коде прописал:
Код
#pragma location = "VARIABLE"
char data = 1;

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

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

Цитата(d7d1cd @ Nov 10 2013, 14:30) *
Посмотрел файл io430x24x.h. Есть там много строк, подобных этой:
Код
/* ADC12 Interrupt Flag */
__no_init volatile unsigned short ADC12IFG @ 0x01A4;

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

А вы попробуйте. Думаю, что вполне можете и так. Только внутри фигурных скобок этого не делайте.
d7d1cd
Сделал объявление переменной глобально, то есть вне функции. Все хорошо, но работает только с атрибутом __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;
}

В принципе то, что объявление переменных выше указанными способами не предусматривает их инициализации, мне и надо. По удобству я считаю, что второй способ лучше.
С объявлением глобальных переменных тоже теперь ясно. А можно ли локальную переменную объявить так, чтобы она была связана с конкретным регистром?
Xenia
Цитата(d7d1cd @ Nov 10 2013, 17:42) *
Все хорошо, но работает только с атрибутом __no_init
В принципе то, что объявление переменных выше указанными способами не предусматривает их инициализации, мне и надо. По удобству я считаю, что второй способ лучше.
С объявлением глобальных переменных тоже теперь ясно.

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

Цитата(d7d1cd @ Nov 10 2013, 17:42) *
А можно ли локальную переменную объявить так, чтобы она была связана с конкретным регистром?

Полагаю, что нельзя.
d7d1cd
Хорошо. А возможно ли какой-то локальной переменной присвоить значение какого-то регистра? И еще: при объявлении локальной переменной ей назначается какой-то регистр. Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?
SSerge
Цитата(d7d1cd @ Nov 10 2013, 21:04) *
Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?

Оставьте эту работу компилятору, это его проблемы.
Xenia
Цитата(d7d1cd @ Nov 10 2013, 18:04) *
Хорошо. А возможно ли какой-то локальной переменной присвоить значение какого-то регистра? И еще: при объявлении локальной переменной ей назначается какой-то регистр. Возможно ли сделать так, чтобы значение этого регистра предварительно было сохранено в стеке, а после окончания работы функции восстановлено из него?


На этот вопрос мне трудно дать конкретный ответ, т.к. нас с вами разделяют архитектуры: я - преимущественно 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 пользоваться нельзя.
d7d1cd
Цитата(SSerge @ Nov 10 2013, 18:45) *
Оставьте эту работу компилятору, это его проблемы.


Я понимаю, но у меня задача очень специфическая. Нашел я тут как делать вставки на ассемблере:
Код
int main( void )
{
  
  int AAA;
  asm("MOV.W R15, &AAA");
    
}


Однако тут компилятор выдает ошибку:
Цитата
Error[Og005]: Unknown symbol in inline assembly: "AAA"


Если же переменную ААА объявить глобально (вне функции main), то ошибки нет. Хотя в справке к IAR переменная объявлена локально. Почему же ошибка?
rezident
Цитата(d7d1cd @ Nov 10 2013, 19:57) *
Если же переменную ААА объявить глобально (вне функции main), то ошибки нет. Хотя в справке к IAR переменная объявлена локально. Почему же ошибка?

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

Ну и еще раз напомню про отличия типов переменных. Тип переменной влияет на ее размещение в памяти и область видимости.
Переменные типа global (глобальная) и static (статическая) размещаются в памяти данных, т.е. под них выделяются ячейки памяти с постоянным адресом.
Переменные типа auto (автоматическая или локальная) размещаются на стеке или в РОН (по усмотрению компилятора), но постоянного адреса в памяти они не имеют.
Переменные типа register (регистровая) в принципе могут быть как глобальными (если объявлена вне функции с указанием конкретного регистра) так и локальными (объявлена без указания регистра внутри функции). Переменные типа register это тип данных с наиболее быстрым доступом. А для того, чтобы доступ к ним был быстрым, предполагается, что размещаться они должны в РОН. Но проблема состоит в том, что если заранее в опциях проекта для переменных типа register не зарезервировать конкретные регистры (в IAR EW430 возможно резервировать только R4 и R5), то компилятор обращается с ними очень вольно и в большинстве случаев пилюет на такую декларацию с высокой колокольни, трактуя локальные register как обычные переменные auto.
Область видимости global - вся программа.
Область видимости static - данный конкретный модуль или функция, если static объявлена внутри функции.
Область видимости auto - функция или блок оператора в котором она объявлена.
d7d1cd
Уважаемый rezident, объяснения как всегда на высоте! Спасибо!
Хочется подробнее узнать про функции, написанные полностью на ассемблере. Как я полагаю в этих функциях для переменных можно будет использовать любые РОН, а не только R4 и R5. Подскажите ссылку на документацию (если возможно - на русском языке).
Xenia
Цитата(d7d1cd @ Nov 12 2013, 09:02) *
Хочется подробнее узнать про функции, написанные полностью на ассемблере. Как я полагаю в этих функциях для переменных можно будет использовать любые РОН, а не только R4 и R5. Подскажите ссылку на документацию (если возможно - на русском языке).

IAR поддерживает сборку проекта, состоящего из C/C++ и ассеблерных файлов/модулей, различая их по расширению.

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

Я, кстати, сама так МК-шному ассемблеру училась - книг не читала, а писала по образу и подобию того, как компилятор компилирует сишные образцы. Правда, к этому времени я на других ассемблерах уже умела программировать - это, видимо, тоже помогало. А из книг было достаточно таблицы инструкций и IAR-ского руководства EW430_AssemblerReference.pdf

Думаю, что самая большая тут трудность не в том, чтобы на ассемблеру выучиться, а в том, чтобы соблюсти условности, позволяющие линкеру связывать объектные модули от двух разных языков. Т.к. проблема чаще возникает в том, что на ассемблере порой приходится искать доступ к глобальным переменным и массивам, определенным в сишной части проекта.
d7d1cd
Цитата(Xenia @ Nov 12 2013, 13:33) *
...написать на языке C функцию, нуждающуюся в переложении на ассемблер, в отдельном файле, откомпилировать проект с установкой, чтобы выдавало ассемблерный листинг. После чего редактируешь листинг по своему вкусу, используя те регистры, какие хочется, а затем подменяешь в проекте второй файл на ассемблерный.

Если Вас не затруднит, можете сделать пошаговое описание выше сказанного? Просто я в IAR совсем новичок. Ранее программировал только на C++Builder.
Например, на С написать функцию, которая принимает 2 параметра и возвращает сумму квадратов этих параметров. Как потом реализацию этой функции вывести в ассемблерный листинг?
MrYuran
Цитата(d7d1cd @ Nov 12 2013, 14:01) *
Как потом реализацию этой функции вывести в ассемблерный листинг?

Поставить в настройках галочку (вкладка compiler output или что-то в этом роде), будет формироваться .lst

Нажмите для просмотра прикрепленного файла
d7d1cd
Создал я проект, добавил в него файл. В этом файле создал функцию. Сделал компиляцию этого файла при установленной галочке Output assembler file. В итоге я получил файл с реализацией моей функции на ассемблере. Как я понимаю, я могу теперь отредактировать этот файл по своему усмотрению, поменять алгоритм реализации функции, использовать любые регистры.
Пока я с этим файлом ничего делать не буду. Подскажите, как мне из кода на С++ вызвать мою функцию, расположенную теперь в ассемблерном файле?
MrYuran
Прямо так и вызывать, предварительно объявив прототип.
Цитата
поменять алгоритм реализации функции, использовать любые регистры.

Если используются аргументы, необходимо соблюдать принятый формат их передачи в функцию и обратно.
d7d1cd
Попробовал следовать Вашему совету. Файл с функцией на ассемблере поместил в отдельную папку. Далее добавил этот файл в проект. Перед функцией main поместил прототип функции, которая находится в ассемблерном виде. При попытке запуска, компилятор выдает ошибку компиляции ассемблерного файла, хотя я в нем абсолютно ничего не менял. Скажите, что я не так делаю?
MrYuran
Цитата(d7d1cd @ Nov 13 2013, 14:19) *
Скажите, что я не так делаю?

Видимо, что-то не так sm.gif
А что за ошибка?

Я в ассемблере не слишком шарился, обычно си хватало.
Есть пара проектов, но сейчас времени нет искать.
Может, как-нибудь эту функцию надо глобально расшарить директивами? Типа паблик или что-то в этом роде
РТФМ, там много интересного
d7d1cd
Ошибка Bad instruction. Возникает в строке ассемблерного файла с якобы инструкцией ERROR. Кто знает, подскажите. пожалуйста, как осуществить желаемое...

P.S. В полученном ассемблерном файле есть комментарий (после этой непонятной инструкции ERROR). Там написано, что-то типа "Этот модуль использует возможности С++ не совместимые с кодом ассемблера..."
d7d1cd
Сделал я проект. Прикладываю его сюда. В этом проекте я получаю ассемблерный листинг, который потом не компилируется. Люди добрые, посмотрите, подскажите...
rezident
Цитата(d7d1cd @ Nov 14 2013, 12:48) *
Сделал я проект. Прикладываю его сюда. В этом проекте я получаю ассемблерный листинг, который потом не компилируется. Люди добрые, посмотрите, подскажите...

В мнемонике ассемблера MSP430 нет команды ERROR. Она вставляется в листинг видимо как раз специально с той целью, чтобы этот asm потом не компилировали biggrin.gif
Я не знаю ни C++, ни как вставлять в проект для С++ ASM-функции. Специально только для этого курить документацию IAR мне лениво sm.gif
Но вот мой вариант вашего проекта без каких-либо "плюсов", pure C т.с. Функция DivisionOnTwo вырезана из вашего листинга и оформлена как законченная самостоятельная ASM-функция, которая вызывается из Си-шного модуля. Проект компилируется без ошибок и даже работает (в симуляторе).
Обратите внимание, что прототип функции DivisionOnTwo описан с квалификатором external.
d7d1cd
Спасибо всем за помощь! Наконец то я победил компилятор. Как мне подсказал rezident, необходимо функцию, которую надо переложить на ассемблер, написать в проекте на С (но не на С++). Тогда получается пригодный для компиляции ассемблерный листинг. Его я правлю как мне необходимо и добавляю к проекту на С++. Прототип функции, которая находится в ассемблере, необходимо объявить с квалификатором extern "C".
Почитав руководство к компилятору, я узнал, что при передаче в функцию параметров, всегда используются конкретные регистры, так же, как и при возврате функциями каких-либо значений.
d7d1cd
И снова здравствуйте. Теперь я умею в одном проекте использовать код на С++ и на ассемблере. Но вот возник вопрос...
У меня в проекте 3 функции, написанные на С++. Все они не принимают параметров и не возвращают их (работают с глобальными переменными). Вызываются они одна за другой. Настроив линкер, я указал, что функции у меня расположены в определенной области памяти. В отладчике я вижу, что мои функции расположились в указанной мной области памяти в таком же порядке, как происходит их вызов.
Далее я самую первую функцию пишу на ассемблере, так же указав сегмент расположения этой функции. Порядок вызова функций я не меняю, однако после всего этого я вижу, что функция, написанная на ассемблере, расположилась в памяти после функций, написанных на С++ (хотя вызывается первая).
Подскажите, почему эта функция на С++ была первая в памяти, а на ассемблере стала последней? И еще: конечно это не особо критично, но подскажите, возможно ли указать линкеру не только расположение функций, но и порядок их размещения в памяти?
Xenia
Цитата(d7d1cd @ Nov 17 2013, 13:56) *
Подскажите, почему эта функция на С++ была первая в памяти, а на ассемблере стала последней? И еще: конечно это не особо критично, но подскажите, возможно ли указать линкеру не только расположение функций, но и порядок их размещения в памяти?


У IAR компиляторы языка С/С++ и языка ассемблера раздельные! В вашем случае это icc430.exe и a430.exe. Поэтому сишные функции окажутся откомпилированными в одном объектном модуле, а ассемблерные функции в другом. А дальше все зависит от того, в каком порядке их соберет линкер. Видимо у него make такой, что первыми в очереди на сборку идут сишные модули, а ассемблерные за ними.

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

P.S. А не все ли вам равно, в каком порядке функции следуют внутри сегмента кода? Зарекаться на этот порядок в проектах, в общем-то, неприлично.
d7d1cd
Цитата(Xenia @ Nov 17 2013, 14:47) *
У IAR компиляторы языка С/С++ и языка ассемблера раздельные! В вашем случае это icc430.exe и a430.exe. Поэтому сишные функции окажутся откомпилированными в одном объектном модуле, а ассемблерные функции в другом. А дальше все зависит от того, в каком порядке их соберет линкер. Видимо у него make такой, что первыми в очереди на сборку идут сишные модули, а ассемблерные за ними.

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

P.S. А не все ли вам равно, в каком порядке функции следуют внутри сегмента кода? Зарекаться на этот порядок в проектах, в общем-то, неприлично.

Ну во поводу постскриптума: мне все равно, по сути... А поменяв порядок следования в Workspace, все получилось! Спасибо за совет.
Пока делал это, обнаружил следующую проблему. В функции на С++, для которой указано конкретное расположение, используется операция деления. Из отладчика я увидел, что операция деления - это тоже функция. И ее линкер размещает не в моем сегменте. Как сделать так, чтобы было в моем?
Xenia
Цитата(d7d1cd @ Nov 17 2013, 15:08) *
Пока делал это, обнаружил следующую проблему. В функции на С++, для которой указано конкретное расположение, используется операция деления. Из отладчика я увидел, что операция деления - это тоже функция. И ее линкер размещает не в моем сегменте. Как сделать так, чтобы было в моем?


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

В тех же случаях, когда библиотечная функция обособлена (не вызывает в своем теле других библиотечных функций), то вы можете взять ее код из сорцов библиотеки и присоединить к своему коду. Тогда она откомпилируется вместе с вашим кодом и окажется вашем сегменте. А по правилам линковки библиотека линкуется последней, а потому к коду будет пришит вами скомпилированный экземпляр, а не библиотечный.
d7d1cd
Цитата(Xenia @ Nov 17 2013, 16:01) *
...вы можете взять ее код из сорцов библиотеки и присоединить к своему коду. Тогда она откомпилируется вместе с вашим кодом и окажется вашем сегменте. А по правилам линковки библиотека линкуется последней, а потому к коду будет пришит вами скомпилированный экземпляр, а не библиотечный.

Как же мне это сделать (взять код из сорцов библиотеки), если, как Вы сказали, библиотека уже скомпилирована? Можно попросить привести примерчик?
Xenia
Цитата(d7d1cd @ Nov 17 2013, 16:49) *
Как же мне это сделать (взять код из сорцов библиотеки), если, как Вы сказали, библиотека уже скомпилирована? Можно попросить привести примерчик?

Если у вас полная версия, то исходники библиотек она тоже должна включать. Лежат тут:
\IAR Systems\Embedded Workbench X.x\430\src\lib\
Только сам компиятор в эти исходники не лазает, а пользуется уже скомпилированным вариантом под конкретно ваш МК:
\IAR Systems\Embedded Workbench X.x\430\lib\
d7d1cd
И снова здравствуйте. Подскажите, пожалуйста, как в одном заголовочном файле объявить константы и для модуля С++ и для модуля на ассемблере? Пытался разместить в таком файле следующие константы:
Код
#define VAR (0x0200)
__no_init volatile int Variable @ VAR;

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

Стандартные заголовочные файлы при их инклуде к модулю на любом языке при компиляции ошибок не создают. Я так же хочу сделать свой модуль...
Xenia
Цитата(d7d1cd @ Nov 22 2013, 11:29) *
Подскажите, пожалуйста, как в одном заголовочном файле объявить константы и для модуля С++ и для модуля на ассемблере? Пытался разместить в таком файле следующие константы:
Код
#define VAR (0x0200)
__no_init volatile int Variable @ VAR;

Если такой заголовочный файл добавить в модуль на С++, то компиляция ошибок не вызывает. А вот в модуле на ассемблере происходит ругань на вторую строку.
Стандартные заголовочные файлы при их инклуде к модулю на любом языке при компиляции ошибок не создают. Я так же хочу сделать свой модуль...


Стандартные заголовочные файлы только потому "интернациональны", что содержат в себе условную компиляцию:
Код
#ifdef  __IAR_SYSTEMS_ICC__
   для C/C++ компилятора
#endif

#ifndef __IAR_SYSTEMS_ASM__
   для ассемблера
#endif

Тогда как определения констант через #define оба компилятора понимают одинаково хорошо (через один и тот же препроцессор текст проходит).
Отсюда и рекомендация: определять общие константы через #define в начале хидера, а если приходится что-то добавлять специфическое, отчего у одного из компиляторов возникает несварение желудка, то ставите это под условие __IAR_SYSTEMS_ICC__или __IAR_SYSTEMS_ASM__.

Поэтому ваш пример будет выглядеть как-то так:
Код
  #define VAR  0x0200
#ifdef  __IAR_SYSTEMS_ICC__
  __no_init volatile int Variable @ VAR;
#endif

Тогда оба компилятора получат одинаковые знания про константу VAR, но ассемблер не увидит сишную строку, которая его станет раздражать.
d7d1cd
Спасибо, Xenia. Я пока не пробовал в компиляторе, но уже полностью Вам доверяю.
d7d1cd
Все получилось. Спасибо. Есть еще вопрос по расположению кода. Есть 2 функции. Мне необходимо чтобы точки входа в эти функции располагались строго по определенным адресам (например, точка входа в первую функцию по адресу 0xFC60, во вторую - по адресу 0xFC64). Возможно ли такое?
Xenia
Цитата(d7d1cd @ Nov 23 2013, 20:53) *
Спасибо. Есть еще вопрос по расположению кода. Есть 2 функции. Мне необходимо чтобы точки входа в эти функции располагались строго по определенным адресам (например, точка входа в первую функцию по адресу 0xFC60, во вторую - по адресу 0xFC64). Возможно ли такое?


За чем дело стало? Повторите снова тот же приём. Вы же уже раньше определяли сегмент
-Z(CODE)BOOT=F002-F0FF
и у вас всё получилось. Так определите теперь две таких штуки. С разными именами сегментов, конечно.

Вот только если точки входа так близко расположены: 0xFC60 и 0xFC64, вы врядли запихаете первую из функций в 4 байта. Следовательно у вас там есть место только для jmp-перехода. На C такого кода не написать, а на ассемблере - вполне. Напишите на ассемблере два джампа и вставьте пустые байты данных между ними, если это потребуется, чтобы второй джамп пришелся на 0xFC64. Тогда это будет всего один сегмент. Опять же на ассемблере гораздо проще расположить код в требуемых местах памяти.

Тем не менее, сдается мне, что вы собираетесь таким вычурным способом писать таблицу прерываний для загрузочного режима sm.gif. И если моя догадка верна, то всего этого делать не надо. А надо создать другой проект под бутовую часть, т.е. писать загрузчик в чистом виде, так чтобы и таблица прерываний, и кодовый сегмент были в верхней памяти. А уж потом объедините нижнюю часть кода (от проекта приложения) и верхнюю часть кода (от проекта загрузчика) на уровне нех-прошивки или бинарного кода.
d7d1cd
Я пытался свою задачу выполнить следующим образом. В проекте на С++ в файле main я создал 2 функции. Для этих функций я задал сегмент расположения, который начинался с адреса 0xFC60. В телах этих функций я вызываю по 1 функции, которые содержат нужный рабочий код, и больше ничего. Сами эти функции я по порядку вызываю в функции main(). Выглядит это так:
Код
// Сегмент CODEPLACE начинается с адреса 0xFC60

#pragma location = "CODEPLACE"
void _Function1()
{
  Function1();      // Часть рабочего кода расположена в этой функции
}

#pragma location = "CODEPLACE"
void _Function2()
{
  Function2();      // Часть рабочего кода расположена в этой функции
}

int main()
{
  _Function1();      // У этой функции адрес вызова всегда должен быть 0xFC60
  _Function2();      // У этой функции адрес вызова всегда должен быть 0xFC64
}

Все задуманное работает... но только в конфигурации Debug. В конфигурации Release линкер располагает мои функции _Function1() и _Function2() не в начале сегмента CODEPLACE, а в конце. Соответственно, при изменении рабочего кода, адреса этих функций меняются. Может как то можно решить эту проблему?
Xenia
Цитата(d7d1cd @ Nov 24 2013, 11:04) *
Все задуманное работает... но только в конфигурации Debug. В конфигурации Release линкер располагает мои функции _Function1() и _Function2() не в начале сегмента CODEPLACE, а в конце. Соответственно, при изменении рабочего кода, адреса этих функций меняются. Может как то можно решить эту проблему?


Проблема решается просто: ... так и оставайтесь в конфигурации Debug, если она вас устраивает sm.gif. Лично я именно так всегда и пишу. Или сравните между собой обе конфигурации по каждому из параметров и найдите разницу, которая дает этот эффект.
d7d1cd
А чем, собственно, отличаются конфигурации Debug и Release? Только опциями проекта?
Xenia
Цитата(d7d1cd @ Nov 25 2013, 19:21) *
А чем, собственно, отличаются конфигурации Debug и Release? Только опциями проекта?


Полагаю, что да. Ведь один же компилятор это компилит. Значит, либо в опциях разница или файл конфигурации другой (последний тоже в опциях задается, но по умолчанию может оказаться разным).
d7d1cd
Привет всем. Снова вопрос по среде программирования. Есть проект на ассемблере. В проекте 2 файла. Один главный, второй с функциями (умножение, деление и т. д.). Оба файла добавлены в проект. Из первого файла функции второго видно посредством PUBLIC и EXTERN-ов.

Теперь вопрос. Во втором файле много функций, но в первом файле используется, например, одна. Однако при компиляции проекта, в исполняемый файл попадают все функции из 2 файла (хотя, повторюсь, используется только одна). Возможно ли сделать так, чтобы в конечный файл прошивки компилировались только используемые функции?
d7d1cd
Разобрался в своем вопросе. Необходимо перед объявлением функции указать сегмент и флаг NOROOT:
Код
RSEG     CODE:NOROOT
d7d1cd
Решил один вопрос, появился другой. Задам тут, может так быстрее сам решу или знающие люди подскажут.
Итак, вопрос. Есть директива ассемблера RSEG. Она указывает в каком сегменте памяти должен располагаться код, который идет после директивы. Имя сегмента и его "область действия" я могу задать либо в опциях линкера либо в конфигурационном файле .xcl. Если под код не будет хватать места, то при сборке линкер выдаст ошибку. А нельзя ли как то указать линкеру два сегмента для размещения кода? Когда в одном сегменте закончится место, линкер будет располагать оставшийся код во втором сегменте.
rezident
Цитата(d7d1cd @ Jan 4 2014, 14:09) *
А нельзя ли как то указать линкеру два сегмента для размещения кода? Когда в одном сегменте закончится место, линкер будет располагать оставшийся код во втором сегменте.

Вам просто нужно для сегмента CODE указать несколько диапазонов адресов, перечислив их через запятую. Вот там я пояснял как правильно отредактировать XCL-файл.
d7d1cd
Спасибо за помощь!
d7d1cd
Уже устал биться с линкером. Помогите...
У меня в проекте на ассемблере 2 файла (1 и 2). В 1 основные функции программы, во 2 так сказать рутинные функции (умножение, деление и прочее). Функции из файла 2 вызываются в 1 файле посредством директив PUBLIC и EXTERN. XCL-файл я использую свой. Его содержимое, состоящее из одной строки, такое:
Код
-Z(CODE)CODE=FC60-FFDF,9668-97EB          // Весь мой код должен быть в этих диапазонах адресов

Перед каждой функцией в файлах 1 и 2 я указываю сегмент размещения. Делаю это вот так (на примере функции умножения):
Код
RSEG    CODE:NOROOT:REORDER:SORT
Mul16u16uTo32u:                            
        DINT
        NOP
        MOV     R12, &MPY
        MOV     R13, &OP2
        MOV     &RESLO, R12
        MOV     &RESHI, R13
        EINT
        RET

Все было хорошо, пока мой код был мал. Линкер его размещал начиная с адреса FC60. Я думал, что когда закончится место в диапазоне FC60-FFDF, то линкер начнет размещать код в диапазоне 9668-97EB. Однако этого не произошло. Когда размер скомпилированного кода стал превышать размер диапазона FC60-FFDF, то линкер выдал мне ошибку:
Цитата
Linking
Error[e16]: Segment CODE (size: 0x384 align: 0x1) is too long for segment definition. At least 0x4 more bytes needed. The problem
occurred while processing the segment placement command "-Z(CODE)CODE=FC60-FFDF,9668-97EB", where at the moment of
placement the available memory ranges were "CODE:fc60-ffdf,CODE:9668-97eb"
Reserved ranges relevant to this placement:
9668-97eb CODE
fc60-ffdf CODE
Error while running Linker

Total number of errors: 1
Total number of warnings: 0

Я попробовал изменить порядок диапазонов адресов в XCL-файле. Однако это не помогло. Линкер выдает ту же ошибку. Что самое интересное, после изменения порядка адресов в XCL-файле, я убрал некоторые строки кода (чтобы "все стало помещаться"). После компиляции ошибки не стало, однако линкер все равно размещает код в диапазоне FC60-FFDF, хотя в XCL-файле этот диапазон прописан после диапазона 9668-97EB. Подскажите, что за напасть и как с ней бороться... smile3046.gif

P.S. Для информации:
CODE
NOROOT, ROOT
NOROOT means that the segment part is discarded by the linker if no symbols in this segment part are referred to.
Normally, all segment parts except startup code and interrupt vectors should set this flag.
The default mode is ROOT which indicates that the segment part must not be discarded.

REORDER, NOREORDER
REORDER allows the linker to reorder segment parts. For a given segment, all segment parts must specify the same state for this flag.
The default mode is NOREORDER which indicates that the segment parts must remain in order.

SORT, NOSORT
SORT means that the linker sorts the segment parts in decreasing alignment order. For a given segment,
all segment parts must specify the same state for this flag. The default mode is NOSORT
which indicates that the segment parts are not sorted.
rezident
Цитата(d7d1cd @ Jan 6 2014, 18:29) *
Я попробовал изменить порядок диапазонов адресов в XCL-файле. Однако это не помогло. Линкер выдает ту же ошибку. Что самое интересное, после изменения порядка адресов в XCL-файле, я убрал некоторые строки кода (чтобы "все стало помещаться"). После компиляции ошибки не стало, однако линкер все равно размещает код в диапазоне FC60-FFDF, хотя в XCL-файле этот диапазон прописан после диапазона 9668-97EB. Подскажите, что за напасть и как с ней бороться... smile3046.gif

Приложите в сообщению весь ваш XCL-файл полностью.
d7d1cd
Цитата(rezident @ Jan 6 2014, 17:52) *
Приложите в сообщению весь ваш XCL-файл полностью.

Приложил...
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.