Jump to content

    
_Desh_

Разместил таблицу векторов и код обработчика в ОЗУ, при прерывании вылетает в HardFault

Recommended Posts

1 час назад, _Desh_ сказал:

на первой странице все написано.

Никакого атрибута, который бы указывал "в какой области памяти находится код функции", там нет.

Share this post


Link to post
Share on other sites
1 hour ago, jcxz said:

Никакого атрибута, который бы указывал "в какой области памяти находится код функции", там нет.

https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

Quote

Normally, the compiler places the code it generates in the text section. Sometimes, however, you need additional sections, or you need certain particular functions to appear in special sections. The section attribute specifies that a function lives in a particular section. For example, the declaration:


extern void foobar (void) __attribute__ ((section ("bar")));

puts the function foobar in the bar section.

__attribute__((section(".RAMFUNC")))
void
systick_config(void)
{
  ...
}

Размещение секции указано здесь:

RAMFUNC (rx)  : ORIGIN = 0x20000000, LENGTH = 8K

и здесь:

.ramfunc :
{
  ...
  *(.RAMFUNC.ISR)
  *(.RAMFUNC)
  *(.RAMFUNC*)
  ...
} >RAMFUNC AT> FLASH

 

Share this post


Link to post
Share on other sites
1 час назад, _Desh_ сказал:

И что? Всеми этими телодвижениями Вы указали компилятору только имя секции в которую помещать код функции.

Но про то, что функция будет выполняться из ОЗУ - ничего не сказали.

1 час назад, _Desh_ сказал:

Размещение секции указано здесь:

И что? 

Во-первых: Разницу между компилятором и компоновщиком понимаете? А вопрос звучал "откуда тогда компилятор узнает, что функция будет выполняться из ОЗУ?". Именно компилятор, потому как именно компилятор создаёт код функции (а не компоновщик). А то что вы показали - это командный файл компоновщика, про который компилятор ничего не знает.

Во-вторых: Даже если бы компилятор знал про этот файл, и что с того? Этот файл указывает в какую область адресного пространства памяти скомпоновать данную секцию. И ничего не говорит об особенных характеристиках этой секции. А значит вполне допустимо часть кода (или данных) вынести за пределы этой секции. Так как нет никаких указаний от вас, что этого делать нельзя.

А значит - код секции вы скопируете в ОЗУ, а то что изнутри этого кода идут обращения во флешь - так и останется во флешь. Или ещё хуже, если изнутри другого (флешевого) кода, будут обращения в область .RAMFUNC когда вы ещё не скопировали туда код.

 

PS: Чтобы такого не происходило, нужно:

1. Или явно указать компилятору, что функция выполняется в режиме отдельного адресного пространства от остального кода когда остальной код недоступен. В IAR для этого имеется специальное ключевое слово __ramfunc.

2. Или скомпилировать весь код, который будет выполняться из ОЗУ при неработающей флешь, как отдельную программу, поместить результирующий бинарный образ внутрь главной программы, и когда нужно - копировать целиком этот образ в ОЗУ и запускать его (или его отдельные функции через таблицу импорта/экспорта).

Есть конечно ещё 3-й путь, колхозный: Выключить полностью оптимизацию, а также отключить опции оптимизации такие как "объединение одинаковых констант" и подобные ей. Ну и всё равно при этом  нужно после каждого изменения кода (или обновления версии компилятора) - проверять листинг всех RAM-функций чтобы не было лишних связей.

Share this post


Link to post
Share on other sites
 38 минут назад, jcxz сказал:

про который компилятор ничего не знает.

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

42 минуты назад, jcxz сказал:

А значит вполне допустимо часть кода (или данных) вынести за пределы этой секции. Так как нет никаких указаний от вас, что этого делать нельзя.

Нет, не допустимо. Я даже цитату из документации по компилятору для этого привел - он разместит мой код там, где ему указывает атрибут.

43 минуты назад, jcxz сказал:

А значит - код секции вы скопируете в ОЗУ, а то что изнутри этого кода идут обращения во флешь - так и останется во флешь.

Не останется, если я точно так же скопирую весь код и переменные, к которым идет обращение, туда же, при необходимости. Это неудобно, не спорю. В документации на линкер написано, что можно даже целиком объектный файл указывать для размещения в данной секции, но с этим я пока не разобрался.

45 минут назад, jcxz сказал:

если изнутри другого (флешевого) кода, будут обращения в область .RAMFUNC когда вы ещё не скопировали туда код

Копирование специально сделано перед вызовом функций, которые работают с периферией, как мне и посоветовали здесь.

57 минут назад, jcxz сказал:

Есть конечно ещё 3-й путь, колхозный: Выключить полностью оптимизацию

Я включил оптимизацию Os, затем O2, обычная функция и функция-обработчик прерывания работают из ОЗУ, поведение программы соответствует ожидаемому.

Я не использую IAR, используемую среду разработки я указал в первом же посте первой же строкой.

Share this post


Link to post
Share on other sites
57 минут назад, _Desh_ сказал:

Я даже цитату из документации по компилятору для этого привел - он разместит мой код там, где ему указывает атрибут.

Вам немного не про это говорят.
Компоновщик тупо разместит функцию в нужном куске ОЗУ, это да. Но он знать не знает, что делает ваша функция.
А она может явными и неявными зависимостями обратиться за пределы этого куска ОЗУ.
Например, вызвав другую функцию, либо взяв значение из какого-нибудь константного массива, который во Flash.

На Keil с ARM CLang v6 я, почему-то, ключевого слова наподобие __ramfunc не встретил:scratch_one-s_head:Может, там как-то все более хитро и этот ключ не нужен, хз.

Share this post


Link to post
Share on other sites
11 minutes ago, Arlleex said:

Может, там как-то все более хитро и этот ключ не нужен

Не нужен. Для разбирательств с явными и неявными зависимостями и существует прокладка между стулом и клавиатурой.

Share this post


Link to post
Share on other sites
31 minutes ago, Arlleex said:

А она может явными и неявными зависимостями обратиться за пределы этого куска ОЗУ.

Если она обратится к библиотечным функциям, у которых исходные коды недоступны, у меня будут проблемы. Но наверняка они тоже решаемы, просто я пока не знаю как (оно мне сейчас не надо).

31 minutes ago, Arlleex said:

На Keil с ARM CLang v6 я, почему-то, ключевого слова наподобие __ramfunc не встретил

Последний раз я пользовался Keil 4 с ARMCC и там можно было прямо в свойствах исходника мышкой указать размещение в конкретной области памяти. Либо так же с помощью атрибута, аналогичного GCC, если для отдельной функции или переменной. Но там еще можно было явно адрес указывать, например, __attribute__((at(0x20002000))). А в GCC так нельзя, только через линкер и секции.

Edited by _Desh_

Share this post


Link to post
Share on other sites

Ещё не забывайте функцию, размещаемую в RAM, объявлять как "no inline", в противном случае, если оптимизатор её сочтет пригодной для inline, запросто разместит её внутри другой функции (inline), которая размещена во флэше.

Share this post


Link to post
Share on other sites
2 часа назад, dimka76 сказал:

Вот еще советуют при размещении функции в ОЗУ использовать ключ –mlong-calls

Интересно, чем руководствуются авторы таких советов. "Сделайте свою маленькую быструю программу большой и медленной". Компоновщик сам в состоянии использовать длинные вызовы только там, где это необходимо. Зачем мешать ему делать свою работу?

Share this post


Link to post
Share on other sites
19 часов назад, _Desh_ сказал:

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

В скрипте линкёра нет и не может быть переменных.

Цитата

Нет, не допустимо. Я даже цитату из документации по компилятору для этого привел - он разместит мой код там, где ему указывает атрибут.

Включите оптимизацию (особенно - по размеру и максимальную) и загляните в листинги. Удивитесь. :wink:

Цитата

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

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

Но у меня нет уверенности, что ваш компилятор при максимальной оптимизации не может просто часть данных/кода (вызываемых из вашей ОЗУ-функции) поместить в чужую секцию. Ведь ничего не запрещает ему это сделать. То что секция кода именованная (с именем отличным от .text) - ну и что? Компилятор просто поместит адрес вызова из такой секции в таблицу внешних связей, а линкер - подставит на место этого вызова нужный адрес из другой секции (которая во flash).

Цитата

Копирование специально сделано перед вызовом функций, которые работают с периферией, как мне и посоветовали здесь.

Вы не поняли что о чём я пишу.... :unknw:

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

Если вы пишете на си например 2 функции:

void f1() { ... }

void f2() { ... }

то совсем не факт, в результате на выходе компилятора будут две ассемблерные функции. Вообще не нужно ожидать полного соответствия си-функциям ассемблерных. Выходных функций после компилятора может быть любое количество - от 0 (при полном инлайнинге кода функций в другой код), до множества функций (>2 - при оптимизации по размеру). Да, возможно они все будут в указанной вами секции кода, но это надо проверить. И кроме кода есть ещё и const-данные - они точно также могут объединяться с другими данными.

 

Цитата

Я включил оптимизацию Os, затем O2, обычная функция и функция-обработчик прерывания работают из ОЗУ, поведение программы соответствует ожидаемому.

Я не использую IAR, используемую среду разработки я указал в первом же посте первой же строкой.

Ну так если в барабан револьвера зарядить только один патрон и сыграть в русскую рулетку, то тоже скорей всего останешься живым. Но если делать то же самое много раз в разных комбинациях - то когда-нить выстрел всё-таки прозвучит. :suicide2:

Причём тут IAR / не IAR? Ваш компилятор ведь умеет оптимизировать? А способы оптимизации у всех современных компиляторов примерно одинаковые.

 

18 часов назад, Arlleex сказал:

А она может явными и неявными зависимостями обратиться за пределы этого куска ОЗУ.

Вот именно!

13 часов назад, x893 сказал:

Плесну керосина в костёр

veneer

Да, есть и такое при длинных вызовах. И как я понимаю - создаются они линкером и компилятор о них вообще не в курсе (хотя может в этом ошибаюсь).

Share this post


Link to post
Share on other sites
45 минут назад, jcxz сказал:

Ну так если в барабан револьвера зарядить только один патрон и сыграть в русскую рулетку, то тоже скорей всего останешься живым

Отвлечение от темы: читал где-то версию, что в исправном и правильно смазанном револьвере барабан под весом патрона всегда поворачивается отсеком с единственным патроном вниз. Выглядит правдоподобно.

Share this post


Link to post
Share on other sites
17 hours ago, Сергей Борщ said:

Интересно, чем руководствуются авторы таких советов. 

Вот здесь

https://ez.analog.com/dsp/software-and-development-tools/linux-blackfin/f/q-a/68624/run-a-specific-function-from-sram-gnu-gcc-compilation-tools

Share this post


Link to post
Share on other sites
1 час назад, dimka76 сказал:

Вот здесь

Все равно выглядит вредным советом. Ибо далее предлагается

Цитата

Then you need the following in an accessible header file or at the top of a C file that will use it

#define RAMFUNC __attribute__ ((long_call, section (".ramfunctions")))

А если прочитать документацию, то 

Цитата
long_call/short_call
This attribute specifies how a particular function is called on ARM and Epiphany. Both attributes override the -mlong-calls (see ARM Options) command-line switch and #pragma long_calls settings. The long_call attribute indicates that the function might be far away from the call site and require a different (more expensive) calling sequence. The short_call attribute always places the offset to the function from the call site into the `BL' instruction directly.

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.