Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Перемещение функции в памяти ...
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Алгоритмы ЦОС (DSP)
3.14
Как в C коректнее скопировать функцию из одной области памяти в другую и затем вызвать из нового местоположения.
alex_k
Размышления вслух... Если после компиляции си-компилятором (не известно ж
какой компилятор и что за процессор), мы получаем код, адресация переходов в котором выполнена в относительной системе, относительно текущеего адреса, тогда такую функцию в принципе можно копировать из
одного места памяти в другое. Дальше, получить адрес функции в си не проблема - просто указатель на функцию. Остается проблема, как узнать длинну кода занимаемого функцией. Если длинна изместна, то поидее можно копировать в выделенный буфер, например memcpy, ну а далее вызывать по новому указателю...
makc
Цитата(3.14 @ Mar 24 2005, 12:35)
Как в C коректнее скопировать функцию из одной области памяти в другую и затем вызвать из нового местоположения.
*


Абсолютно корректно это сделать не получится, т.к. этот вопрос не предусмотрен стандартом, но можно сделать следующее:

Объявить две функции - копируемую и еще одну, вспомогательную:
Код
static void source_f()
{
....
}

static void source_f_end()
{
}


После этого в программе можно объявить буфер и скопировать туда нужную функцию и вызвать ее:
Код
typedef  void   (*call_pointer)(void)
char  buffer[ desired ];
call_pointer   pointer;
memcpy( buffer, source_f, (size_t)(source_f_end - source_f) );
pointer = (call_pointer)buffer;
pointer();


но подобный метод накладывает сильные ограничения на то, что может быть написано в копируемой функции. Так, например, если в ней есть вызовы других функций, то необходимо изменить их непосредственные вызовы на вызовы по указателям, переданным вызываемой функции. И т.д.
olefil
От части этот механизм представлен в оверлеях, когда несколько функций могут занимать одно и то же место в памяти, это решает вопрос маленькой памяти, а копирование кода, точно не к чму не приведет, правда некоторые функции можно переносить таким образом, но только те которые представленны в понятном asm эквиваленте, т.е если там нет каких либо видов оптимизаии и вызовов функции по косвенному смещению от точки вызова. Тут конечно можно примеров много написать чего можно, а чего нельзя, но лучше таких вариантов с кодом не проделывать, а иначе программа может начать вести себя совершенно не вероятным образом.
3.14
2 max
Я правильно понял Ваш пример?
Копируемая функция source_f() переносится по адресу функции source_f_end(), вызов пересенной функции осуществляется через pointer() ?

Попробовал пример сунуть в VDSP4.0.
В дебуггере после memcpy() функция source_f_end() не меняет своего содержимого. В железе все то же виснет sad.gif
olefil
Компилятор в VDSP может не понимать того, что вы делаете и я больше чем увероен, что это так!!!
3.14
2 olefil
Я по большому счету, пытаюсь перенести asm примеры с оверлеями на C. Пока не получается sad.gif
olefil
А вчем конкретная проблема? Может чем помогу?
bve
Есть еще одна проблема, зависящая от компилятора и оптимизации -
передача параметров в подпрограмму. Т.е. необходимо копировать не только
тело самой функции, но и кусок кода ее вызывающий, либо запихивать параметры самому в необходимые регистры/ячейки. Не надо также забывать об изменении указателя фрейма, если таковой имеется.
makc
Цитата(3.14 @ Mar 24 2005, 14:20)
2 max
Я правильно понял Ваш пример?
Копируемая функция source_f() переносится по адресу функции source_f_end(), вызов пересенной функции осуществляется через pointer() ?

Попробовал пример сунуть в VDSP4.0.
В дебуггере после memcpy() функция source_f_end() не меняет своего содержимого. В железе все то же виснет sad.gif
*


Не совсем так. При решении этой задачи есть две проблемы:
1. Определение размеров копируемой функции.
2. Вызов функции из того места, куда она была скопирована.

1. Для решения этой проблемы вводится функция source_f_end(), которая будет лежать после копируемой функции, т.е. адрес source_f_end - это адрес следующего байта после тела копируемой функции. Таким образом, разность source_f_end - source_f нам даст размер копируемой функции.
2. Функцию копируем в объявленный буфер. Поэтому функция source_f_end и не должна изменить своего содержимого - это только маркер. Буфер для копирования лучше объявить не локальным, а глобальным, тогда будет меньше проблем, т.к. не все процессоры умеют исполнять код из стека. Вызов осуществляется через pointer, значение которого должно быть равно адресу начала буфера, куда была скопирована функция.

А что есть в самой копируемой функции? Ведь если там есть прямые вызовы других функций и обращение к глобальным переменным, то она работать не будет. Т.к. как правило их смещения вычисляются линкером при линковке и едут непредсказуемым образом при копировании функции. Может быть в этом проблема?
makc
Цитата(bve @ Mar 24 2005, 14:51)
Есть еще одна проблема, зависящая от компилятора и оптимизации -
передача параметров в подпрограмму. Т.е. необходимо копировать не только
тело самой функции, но и кусок кода ее  вызывающий, либо запихивать параметры самому в необходимые регистры/ячейки. Не надо также забывать об изменении указателя фрейма, если таковой имеется.
*


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

Что касается фрейма (я так понимаю, что речь идет о фрейме стека), то он вычисляется исходя из текущего значения указателя стека. А в некоторых случаях он вообще не используется (например у gcc при -fomit-frame-pointer).

Так что, как мне кажется, проблемы передачи параметров нет.
olefil
Скорее это так, проблем с передачей параметров действительно нет, но вот от компиляции конкретным оброзом внутренности программы Sharc очень сильно изменяются. И если использовать оптимизацию, то что эта функция делает, знает только компилятор.
3.14
2 maks
<1. Для решения этой проблемы вводится функция source_f_end(), которая будет лежать после копируемой функции...>
В VDSP такая фишка не проходит, он в памяти располагает не по объявлению а по вызову.
Еще имеется специфика ADSP, регистры имеют разрядность 40 бит, ширина инструкций 48 бит. В моем случае копируемая функция находится в SDRAM, разрядность данных 32 разряда, надо упаковывать в 48 бит.

<Есть стандартный способ передачи параметров в подпрограмму>
А в чем он заключается?
Я решил сделать так, фунции которые будут копироваться в место исполнения общатся с внешней (програмной) средой будут посредством глобального массива аргументов, элементы которого для определенных функций будут являться входными и выходными параметрами.

2 olefil
В общем, взял пример fft_ovly2
Вот кусок менеджера оверлеев отвечающий за DMA передачу
Код
 i8 = runAddresses;
 i0 = liveAddresses;
 r0=0;      
//   Disable DMA
 dm(DMAC0) = r0;  
//   Set DMA external pointer to overlay live address
 r0=dm(m0,i0);
 dm(EIEP0)=r0;\
// Set DMA internal pointer to overlay run address  
 r0=pm(m8,i8);
 dm(IIEP0)=r0;\
 i0=runWordSize;  //  Number of words stored in internal memory  
//                   Most likely the word size will be 48 bits  
//                   for instructions.        
//   Set DMA external modifier
 r0=1;
 dm(EMEP0)=r0;
 i8=liveWordSize; //  Number of words stored in external memory
//                   Most likely the word size will be 32 or16  
//                   bits for external storage.    
//   Set DMA internal modify to 1
 dm(IMEP0)=r0;
//   Set DMA internal count to Overlay run size.    
 r0=dm(m0,i0);
 dm(CEP0)=r0;
//   Set DMA external count to Overlay live size.    
 r0=pm(m8,i8);
 dm(ECEP0)=r0;
//   DMA enabled, instruction word, Master, 48-32 packing    
 r0=0x2e1;
 dm(DMAC0)=r0;


Переписал это на C

Код
 *pDMAC0=0;
 *pEIEP0=pointerDest;
 *pIIEP0=pointerTarget;
 *pEMEP0=1;
 *pIMEP0=1;
 *pCEP0=0x20;
 *pECEP0=0x20;
 *pDMAC0=0x2e1;

Размер функции в которую копируется 1kword.
Копируемая функция размером 10word.

Разместил копируемую функцию во внутренней памяти, содержимое первых шести слов :
Код
043ff8000000 0f0200000008 ad02fffffffe
10010000c009 013e00001012 11000000c009


Далее разместил копируемую функцию в SDRAM, после завершения работы DMA, содержимое скопированной функции:
Код
00000000F800 00000008043F 0F020000FFFE
AD02FFFF0000 0000C0090000 000010121001


???
sweetin
Цитата(3.14 @ Mar 24 2005, 16:06)
<Есть стандартный способ передачи параметров в подпрограмму>
А в чем он заключается?


через general-purpose регистры. а если параметров много - через стековый фрейм функции из которой производится вызов.
olefil
У меня ща все платы в доработке не фига не попробовать. Но вроде код на первый взгляд правельный. Правда на счет упаковки 48-32 трудно сказать, я сейчас немного другой работой занимаюсь, но вроде похоже на правду. А че показывает VDSP, если в debugger код посмотреть. Блин у меня где-то был исходник для overley'ев копировал функции из внутренней памяти во внешнюю правда не по DMA, а через PX регистр все работало.
3.14
2 olefil
<А че показывает VDSP...>
Листинг машинного кода я выше привел, ну а дизассемблер соотвественно показывает не то что должно быть.
Из приведенного кода видно, что дело в упаковке, но почему никак не пойму и как ее изменить, то же не знаю. Если применить PACKING к секции, дык другие функции в ней перестанут выполняться.
olefil
Если disasm показывает фигню, то функция паковки 48-32 работает неверно. Мне тут через пару часиков подкинут устройство на котором можно будет все быстро проверить. Если до этого момента тут никто не скажет, чего там в коде не то, тогда я проверю как там поковка работает.
3.14
Кстати, а как из С обратиться к PX регистру?
olefil
Плату мне так и не подогнали. А на счет PX, глянул Include там походу нет загрузки PX, но это можно легко сделать через asm.

global _px_loader;

_px_loader:
px1 = r4;
px2 = r8;
i0 = r12;
pm(i12,m12) = px;
exit;

Только i12,m12 надо сохранить, а потом востановить. С функция будет следующая px_loader(int px_1, int px_2, int add)
Ну и там еще когда компилится будет имя функции надо уточнить, т.к. в С это скорее всего так как я написал, а в С++ там имя другое будет.
3.14
Спасибо, разобрался, заменил DMA на самодельную пересылку, заработало.
Осталась одна загвоздка - как определить размер функции?
olefil
Хороший вопрос. Конечно можно провести поиск конца функции по характерным данным например макросу exit - он всегда используется при выходе из С-функции. А можно сделать по другому. Т.к. все функции заранее известны, то можно составить таблицу, в которую включить все необходимые данные. А потом просто гулять по ней и смотреть чего где и куда. Вообще ваша задача стала мне интересна, там можно было бы написать красивый механиз для собственных оверлеев и плюс ко всем продумать проблему динамической линковки в глобальном масштабе... Если еще интресно почему DMA не пашет я гляну?
3.14
<Если еще интресно почему DMA не пашет я гляну?>
Не стоит, пока не актуально, потом разберусь.

Насчет размера функции, решил сделать так.
Вычисляю абсолютное значение разницы указателей двух соседних функций, пока считает правильно.
Еще есть вариант - при использовании оверлеев линковщик сам создает "_ov_word_size_run_1", потом попробую прикрутить.

Еще озадачился, библиотечные функции (типа "sprintf()") не хотят располагаться во внешней SDRAM sad.gif
olefil
Очень странно... потому как я их там только и держу. Скорее всего в LDF не правильно прописана загрузка библиотек.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.