Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: получение "чистого" кода функции
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
der Spomer
Допустим, я написал программу на C++, скомпилил, прошил. Теперь мне понадобилось дописать в ROM ещё одну функцию, написанную также на C++. Цель - скажем, допрошил 30-40 файлов-функций в ROM и пользуешься ими из основной программы.
Вопрос:
1) Возможно ли настроить среду (EWARM или любую другую) так, чтобы она выдала мне файл с "чистым" кодом функции, т.е. без кода инициализации процессора и т.п.?
2) Существуют ли стандарты файлов, предназначенных не для включения в проект, а для непостедственной прошивки в устройство?
MALLOY2
Цитата(der Spomer @ Dec 18 2007, 09:07) *
Допустим, я написал программу на C++, скомпилил, прошил. Теперь мне понадобилось дописать в ROM ещё одну функцию, написанную также на C++. Цель - скажем, допрошил 30-40 файлов-функций в ROM и пользуешься ими из основной программы.
Вопрос:
1) Возможно ли настроить среду (EWARM или любую другую) так, чтобы она выдала мне файл с "чистым" кодом функции, т.е. без кода инициализации процессора и т.п.?
2) Существуют ли стандарты файлов, предназначенных не для включения в проект, а для непостедственной прошивки в устройство?


1) Можно функции разместить в каком нить отдельном пространстве и выдрать потом и прошивочного файла.

2) конечно существуют, обычно имеют разширения .hex и .bin, но бывают и другие форматы smile.gif

P.S.
Цитата
дописать в ROM
обычно туда 1 раз записывают

И еще по ходу, а откуда основная программа узнает что вы что то куда то дописали ?
_dem
1) ROM = Read Only Memory
2) сразу на будущее ознакомьтесь с понятием трансляции адресов (как в DLL)

Если Вы будете дописывать в FLASH/EEPROM функции во время работы девайса, то таким же нехитрым способом можно переписать всю прошивку, что обычно и делают.

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

Да, эти функции должны хранится во внутренней флешке или во внешнем хранилище (CF card/FLASH....) ?
MALLOY2
Цитата
сразу на будущее ознакомьтесь с понятием трансляции адресов (как в DLL)


А это зачем ?
sergik_vrn
Цитата(MALLOY2 @ Dec 18 2007, 10:46) *
А это зачем ?

в результате работы компилятора получаются "относительные" адреса, абсолютные их значения становятся известны только на этапе линковки. соответственно, если Вы хотите использовать скомпилированный код, преобразовывать "относительные" адреса в абсолютные придется самостоятельно. эта процедура в DLL называется "трансляцией адреса"
MALLOY2
Цитата
в результате работы компилятора получаются "относительные" адреса, абсолютные их значения становятся известны только на этапе линковки. соответственно, если Вы хотите использовать скомпилированный код, преобразовывать "относительные" адреса в абсолютные придется самостоятельно. эта процедура в DLL называется "трансляцией адреса"


Может в DLL и так, а вот для микроконтроллера вы не преобразуете никак и никогда !!! незная MAP(файл для линкера который описывает пямять). А когда этот файл есть то и ничего преобразовывать не надо так как всегда можно посмотреть где эта функция находится.
Shuuura
Попробуйте заранее расположить 30-40 по фиксированным адресам. Получится если функции не использут никаких системных библиотек и внешних функций (или внешним функциям как-же присваивать адреса). И с оптимизацией поиграть. Возможно придется выключить перемещение кода.
alexander55
Цитата(Shuuura @ Dec 18 2007, 11:31) *
Попробуйте заранее расположить 30-40 по фиксированным адресам.

Совершенно бесполезное и ненужное занятие.
Зачем это, я что-то не понимаю. 07.gif
Shuuura
Цитата(alexander55 @ Dec 18 2007, 11:38) *
Совершенно бесполезное и ненужное занятие.
Зачем это, я что-то не понимаю. 07.gif

Я так понимаю, чтобы пользователь мог сам определять функционал устройства. что-то вроде ПЛК. Ядро отдельно , юзеровская программа отдельно. Можно даже несколько юзеровских программ хранить и практически на лету переключать. Остается придумать как подключить стандартные функции, которые уже есть в ядре. Например как можно присвоить функции cos фиксированый адрес? или если эта функция не используется в ядре, расположить ее в юзеровской области.
sergik_vrn
Цитата(MALLOY2 @ Dec 18 2007, 11:17) *
Может в DLL и так, а вот для микроконтроллера вы не преобразуете никак и никогда !!! незная MAP(файл для линкера который описывает пямять). А когда этот файл есть то и ничего преобразовывать не надо так как всегда можно посмотреть где эта функция находится.

вопрос тут не в том, КАК это сделать - достаточно разместить функцию-транслятор адресов в фиксированном месте памяти, и в ней хранить таблицу остальных адресов функций. вопрос в том, ЗАЧЕМ это может понадобиться
alexander55
Цитата(Shuuura @ Dec 18 2007, 11:41) *
Я так понимаю, чтобы пользователь мог сам определять функционал устройства. что-то вроде ПЛК. Ядро отдельно , юзеровская программа отдельно. Можно даже несколько юзеровских программ хранить и практически на лету переключать. Остается придумать как подключить стандартные функции, которые уже есть в ядре. Например как можно присвоить функции cos фиксированый адрес? или если эта функция не используется в ядре, расположить ее в юзеровской области.

Понятно.
Что-то типа BIOS и OS на борту. Юзеровские программы подгружаемые. Аналог наладоннина.
Так же BIOS и OS на борту. А юзеровские программы прошиваются в отведенную область. Аналог PLC.
der Spomer
Цитата
ЗАЧЕМ это может понадобиться
Цитата
чтобы пользователь мог сам определять функционал устройства


Цитата(MALLOY2 @ Dec 18 2007, 13:25) *
.hex и .bin

Можно ли настроить среду, чтобы она сгенерила .hex или .bin, содержащий только скомпилённую мою функцию?
Shuuura
Цитата(der Spomer @ Dec 18 2007, 12:15) *
Можно ли настроить среду, чтобы она сгенерила .hex или .bin, содержащий только скомпилённую мою функцию?

1 вариант. Попробуйте отключить стартап, все глобальные переменные no_init
2 вариант. Вручную линковать obj. Может получится
Результат сообщите, интересно
der Spomer
Мои соображения.

Если я всё правильно понимаю(в чём я сильно сомневаюсь) то стандартный исполняемый файл с пустым main, сгенерённый ewarm, имеет след. структуру:
------------------------------------
таблица векторов прерываний
сstartup
cmain
cexit
------------------------------------
Если структура верна, то, видимо, при отключении первых двух пунктов получается работоспособный код, который можно вызвать из основной программы без потери работоспособности обоих. Поправьте если не прав
Shuuura
Цитата(der Spomer @ Dec 18 2007, 13:04) *
Мои соображения.

Если я всё правильно понимаю(в чём я сильно сомневаюсь) то стандартный исполняемый файл с пустым main, сгенерённый ewarm, имеет след. структуру:
------------------------------------
таблица векторов прерываний
сstartup
cmain
cexit
------------------------------------
Если структура верна, то меня, видимо, при отключении первых двух пунктов получается работоспособный код, который можно вызвать из основной программы без потери работоспособности обоих. Поправьте если не прав

еще есть секция инициализации переменных, стека и т.д.
также надо разделить области ОЗУ ядра и приложения при использовании глобальных переменных
Более правильный путь - компиляция obj. Она позволит отключить все ненужные в данном случае секции и получить код с заданного адреса.

Надо проверить идею: разбираем map файл ядра, генерим из него файлик с указанием адресов всех использованных функций. Подключая полученый файл к новому проекту имеем все нужные адреса. Еслм функции в ядре нет, указываем линкеру, что ее надо подключать в пользовательскую область. Вызов пользовательских функций делаем через функцию-транслятор( если их много) или по жестким адресам (если мало). Функция -транслятор включается в пользовательский проект. По идее должно работать
der Spomer
Цитата(Shuuura @ Dec 18 2007, 16:22) *
компиляция obj

Так и сделал.
Плюс к глобальным переменным дописал __no_init.
Работает.
Спасибо.
Shuuura
Как указать линкеру, что используемая функция уже лежит по конкретному адресу и на этот адрес писать ничего не надо?
IgorKossak
Голую main можно получить если исключить startup и low_lewel_init.
Наблюдается некоторый оверхед. Если main использует хоть какие-нибудь библиотечные функции, то они тоже будут присутствовать в коде. В той части программы, которую здесь назвали BIOS, они также могут присутствовать, что слегка излишне. Если предполагается использовать таким образом несколько "подгружаемых" функций, то и они все будут содержать в себе полный набор используемых ими функций несмотря на наличие того же самого у других.
Если подобный расход памяти не критичен, то на это можно закрыть глаза. Иначе придётся таки воспользоваться механизмом DLL.

Цитата(Shuuura @ Dec 18 2007, 13:39) *
Как указать линкеру, что используемая функция уже лежит по конкретному адресу и на этот адрес писать ничего не надо?

Она не только лежит по адресу, но и некоторый обьём памяти (в общем случае неизвестный) занимает.
А что касается линкера, то что ни линкер, то свои заморочки. У IAR, например, выделяется отдельный сегмент со своим диапазоном адресов.
Shuuura
Цитата(IgorKossak @ Dec 18 2007, 14:49) *
Иначе придётся таки воспользоваться механизмом DLL.


Не понимаю как им можно воспользоваться? Может как-то можно через линкер? Адреса же заранее известны.

Цитата(IgorKossak @ Dec 18 2007, 14:55) *
Она не только лежит по адресу, но и некоторый обьём памяти (в общем случае неизвестный) занимает.


Вот именно. Как заставить IAR-ий линкер НЕ ПИСАТЬ по данному адресу функцию, а только произвести вызов? Из расчета что библиотечная функция по данному адресу уже есть. Может это Keil делать умеет?
Т.е хочется прошить "BIOS" c библиотечными фукнциями, а потом маленькие пользовательские програмки, которые обращаются к этим функциям. Програмки делать отдельным проектом с голым main.
_dem
В общих чертах -

Выполняемый (загружаемый) код должен иметь таблицу импорта для библиотечных функций (id=address) и таблицу экспорта (id/name=address)

Загрузчик кода поправляет таблицу импорта и добавляет список экспорта в глобальный список функций. Это несложно в реализации и дает возможность динамической загрузки и выполнения кода.

Вместо таблицы импорта можно сделать callback и возвращать загружаемому объекту ссылку на нее (чтобы можно было выполнять код из flash, не переписывая в нем таблицу)

Вообще, вариантов реализации - море, но механизм везде общий smile.gif

ps Единая для всех модулей точка входа (инициализации), и больше ничего не надо - остальное сделает загрузчик
Такой код пишется в ИАРе в рабочем проекте (с рабочим загрузчиком объектов), потом либо перетаскивается в пустой проект, либо собирается в отдельном сегменте, чтобы его можно было легко выкусить. Просто вместо стандартных malloc/free/strcpy будут вызовы _malloc/_free/_strcpy - записи из локальной таблицы импорта
Shuuura
Как-то сложно и слишком по ПКшному получается. А если стандартных функций сотни? всего-то надо заставить линкер сделать вызов по конкретному адресу.

PS IAR похоже не умеет.
_dem
Да, IAR не предусматривает динамической загрузки кода
1. Если стандартных функций - сотни (сотнИ!), то, имхо, в проект просится серьезная ОС, в противном случае у вас где-то не там проведена граница между user code и BIOS.
2. Заставляя линкер делать прямые вызовы, вы потеряте возможность обновить bios без обновления user code
3. На самом деле код загрузчика довольно прост и не съест много памяти - адресное пространство-то единое

Таблица импорта для объекта, даже если там 100 ф-ций - это 400 байт, оверхеда на их вызов никакого (это ARM)

Неудобство в том, что нужно иметь единый .h файл с define-ми для всех функций BIOS
Shuuura
А как быть с системными функциями С? (те которые явно не определены?)
например __longToFloat::??rT , __fMul::??rT .
MALLOY2
Цитата
Как заставить IAR-ий линкер НЕ ПИСАТЬ по данному адресу функцию, а только произвести вызов? Из расчета что библиотечная функция по данному адресу уже есть.


В линкере создаем секцию типа MY_SPACE_PROGRAMM размером равным максимальному размеру подгружаемой программы.
С помощью команад section_begin и section_end можно получить начало и размер этой области.

далее создаем прототип нашей функции

Код
typedef int(*my_programm_func)(int argc, const char* argv[]);


определяем указатель

Код
my_programm_func my_exe;


инициализируем указатель
Код
my_exe  = __section_begin(MY_SPACE_PROGRAMM )


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

вызываем
Код
my_exe(param_count, ptr_param);



P.S. а создавая код своей ункции указываеш где и как еще размещать, причем я думаю дак если включить реижм компиляции в библиотеку, приэтом присвоив функции #pragma section="NAME" [align]
то может с этого что то и получится...
_dem
в общем, направление траншеи ясно, а как именно, чем и как глубоко копать - дело конкретной реализации

Мне, например, хочется красивую динамическую загрузку с глобальной таблицой функций ( легко делается много-объектность - фактически получаем примитивный запуск приложений на ARM7 smile.gif )

Вам, видимо, нужно что-то попроще. В любом случае, написано в этой ветке достаточно smile.gif
Сергей Борщ
Цитата(Shuuura @ Dec 18 2007, 13:55) *
Как заставить IAR-ий линкер НЕ ПИСАТЬ по данному адресу функцию, а только произвести вызов? Из расчета что библиотечная функция по данному адресу уже есть.
Определяете линкеру символ с именем функции и адресом ( -Dfunc_name=address, без пробелов) в .xcl, командной строке или на вкладке в оболочке. И все, собственно. Но более гибко - создать таблицу указателей на все такие "bios"-функции и программе давать указатель на эту таблицу.
MALLOY2
Цитата
Мне, например, хочется красивую динамическую загрузку с глобальной таблицой функций ( легко делается много-объектность - фактически получаем примитивный запуск приложений на ARM7 )


Купи наладонник, и программируй его под WinCE и будет тибе щастье, а на ARM7 делать такие вещи извращение
_dem
Текущий проект - SAM7X, IAR 4.41a, uCOS/II, активных полтора десятка задач, использована почти вся периферия процессора.
библиотечных функций я насчитал примерно на 632 байта, из которых 260 - ?div_module, такой объем вполне можно прилинковать и статически.

Хотя тут все равно надо думать в сторону линкера и адресов.


Цитата(MALLOY2 @ Dec 18 2007, 18:04) *
Купи наладонник, и программируй его под WinCE и будет тибе щастье, а на ARM7 делать такие вещи извращение


Потерять полкилобайта памяти на таблицу и столько же на код загрузчика и получить полноценную run-time загрузку модулей кода - вы не поверите, но есть задачи, где такое решение сильно облегчает жизнь и работу.
MALLOY2
Я когда то делал один проэкт в котором было много разных девайсов по функционалу, но состояли они
на 60% из одинаковых функций. Я сделал так отладил все функции, потом создал к ним один хидер, собрал все функции в один файл и закомпилил в библиотеку. Потом просто ее подключал и пользовал нужные функции.

думаю
Цитата
с полноценной run-time загрузкой
не все так просто.

Полноценная загрузка должна быть защищенна от неправельного кода и недолжна вызывать сбой устройства. А это дело такое... да же у винды синий экран бывает smile.gif так что полноценная загрузка это куча кода.
_dem
мне кажется, о защите речь не заходила
тем более понятно, так как у ARM7 отродясь не было MCU, от сбойного кода никакая защита не поможет. Watchdog - наш ответ кривому коду.

Давайте на некоторое время спустимся с небес с Windows CE/Linux embedded на грешную землю к ARM7. Имеем девайс, который должен иметь десяток простых алгоритмов обработки поступающих данных + возможность добавления новых. Причем SAM7 нас вполне устраивает по скорости/периферии. А теперь предложим заказчику проект на ARM7 и рядом на ARM9/Cortex/etc (подчеркнуть) с суммами и сроками.

На сим предлагаю дискуссию о целесообразности закончить и перейти к рассмотрению вопроса по существу.

[off] А то станем как на ixbt.com - сначала 3 страницы "а нафига", потом 2 страницы "купи готовое", потом один нормальный совет.[/off]
Сергей Борщ
Цитата(_dem @ Dec 18 2007, 16:30) *
На сим предлагаю дискуссию о целесообразности закончить и перейти к рассмотрению вопроса по существу.
Совсем недавно была подобная дискуссия. Для не-ARM архитектур нормальным решением является таблица функций, по типу таблицы векторов INT в DOS. В ARM-архитектуре для решения подобных задач как нельзя лучше подходит SWI, поскольку SWI-функция может принимать параметры и возвращать значения. Через SWI вы можете вызывать до 256 разных функций "в лоб" и сколько угодно - с некоторыми извращениями. Механизм реализации SWI-функций реализован в каком-то виде в любом компиляторе. Единственной точкой связи между приложением и "биосом" становится вектор SWI, что, как мне кажется, наиболее расширяемое и универсальное решение.
Shuuura
Цитата(_dem @ Dec 18 2007, 17:14) *
библиотечных функций я насчитал примерно на 632 байта, из которых 260 - ?div_module, такой объем вполне можно прилинковать и статически.

Поддержка операций с плавающей точкой займет несколько килобайт (__fMul, __fSub, __fDiv ... )
Не хочется с такие функции в юзеровскую часть тащить, тем более приложений хотется зашить несколько десятков
Как я понял системные функции нигде не определены, а встроены в компилятор и использовать предложеные решения для них не получится. Можно ли как-то использовать эти функции, кроме как разбирать map файл BIOSа извлекать их адреса? ( с дальнейшим присвоением в юзеровском проекте -Dfunc_name=address )

С библиотечными и собственными функциями все ясно, решений - любое из предложеных на выбор.
_dem
Цитата
Поддержка операций с плавающей точкой займет несколько килобайт (__fMul, __fSub, __fDiv ... )


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

В противном случае,
Цитата
кроме как разбирать map файл BIOSа извлекать их адреса
, решения вроде бы не видно...

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