Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Две таблицы векторов в одном софте
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
xelax
Привет всем!

Тут некоторых коллег посетила нездоровая идея иметь два вектора прерывания на одну периферию (NVM eeprom и flash).
Одна таблица в application другая в bootloader секции. И всё это должно жить в одной программе. В возможности иметь две таблице векторов прерывания для xmegа я не вижу никаких технических препятствий, то как это реализовать в одном софте средствами компилятора и линкера пока не понимаю. Не могу же я иметь две функции
Код
ISR(NVM_EE_vect)


Пока не вижу другого способа кроме как для application секции формировать таблицу посредством макросов ISR, а для bootloader формировать её в рукопашную, размещая jmp на обработчики по фиксированным адресам из bootloader и добавляя в функции обработки рукописные прологи и эпилоги.

Но уж очень этот вариант на камасутру похож. Может кто-то знает более красивые варианты решения?
msalov
на счёт хмеги не знаю, но можно создать массив в озу по фиксированному адресу куда класть нужные вектора, а для всех фиксированных векторов сделать затычки, перенаправляющие программу по адресу из озу.
xelax
Так камни с ядром avr имеют гарвардскую архитектуру и умеют исполнять код только из FLASH области, к сожалению это не cortex crying.gif .
Сергей Борщ
QUOTE (xelax @ Apr 20 2011, 11:58) *
Тут некоторых коллег посетила нездоровая идея
Вот именно, что нездоровая. Загрузчик и приложение - две совершенно независимые программы и сливать их в один проект не здорово. Хотя бы по той простой причине, что вы можете в процессе работы что-то непреднамеренно поменять в загрузчике и сделать новое приложение несовместимым со старым загрузчиком. Имея отдельный проект загрузчика вы можете административными методами или взведением атрибута read-only сказать "вот это работает - не трогай!" и таким образом обезопасить себя от неприятных сюрпризов. Да и как после компиляции отделять приложение от загрузчика для заливки его в те устройства, в которых загрузчик уже прошит? Гораздо более логичным будет иметь два отдельных проекта и сливать вместе уже выходные hex-файлы.
xelax
Полностью со всем согласен, и именно почти такие же аргументы и я приводил. Если бы за мной было бы последнее слово, никогда бы такую хрень дальше идеи не пропустил бы. Но к сожалению некоторые люди здравому смыслу не внемлют. Но это уже тема для других форумов, с медицинской тематикой laughing.gif .
vvppvv
Цитата(xelax @ Apr 20 2011, 14:19) *
Так камни с ядром avr имеют гарвардскую архитектуру и умеют исполнять код только из FLASH области, к сожалению это не cortex crying.gif .


Ну и пусть исполняют код из Flash. Можно использовать указатель на функцию (-ции), хранить сами указатели в ОЗУ и/или регистрах, и играть-тусовать их в рантайме, насколько фантазии хватит.
V_G
vvppvv
Тут, по-моему, как раз тот случай, когда помимо хороших знаний языка Си стоит хорошо знать конкретную архитектуру конкретного процессора. Да, в его ассемблере есть команда косвенного перехода IJMP по адресу из r31:30. Но не думаю, что IAR C умеет ее использовать для целей, потребных топикстартеру.
Все-таки 2 отдельных проекта реальнее, чем так извращаться на Си
vvppvv
Цитата(V_G @ May 11 2011, 18:04) *
vvppvv
Тут, по-моему, как раз тот случай, когда помимо хороших знаний языка Си стоит хорошо знать конкретную архитектуру конкретного процессора. Да, в его ассемблере есть команда косвенного перехода IJMP по адресу из r31:30. Но не думаю, что IAR C умеет ее использовать для целей, потребных топикстартеру.


Ну, во-первых, корректней использовать не косвенный переход, а косвенный вызов, раз уж мы о функциях wink.gif

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

__no_init __regvar void (* volatile Ufunc_ptr)(void) @14;

Использование 3х разных прерываний для вызова этих функций (а какая будет вызвана функция, т.е. какой загружен указатель, зависит от предыдущего контекста):

__interrupt __raw void irqOverflowTimer2 (void)
{
interrupt_entry ();
TCCR2=0; // остановить Timer2
(* Ufunc_ptr)();
interrupt_exit ();
}

__interrupt __raw void irqUSART0_UDRE (void)
{
interrupt_entry ();
(* Ufunc_ptr)();
interrupt_exit ();
}

__interrupt __raw void irqUSART0_RXC (void)
{
interrupt_entry ();
(* Ufunc_ptr)();
interrupt_exit ();
}

сам вызов (* Ufunc_ptr)() очень изящен, если он из регистров:

1436 (* Ufunc_ptr)();
\ 0000000E 01F7 MOVW R31:R30, R15:R14
\ 00000010 9509 ICALL


Как-то так.. sm.gif)

defunct
Цитата(xelax @ Apr 20 2011, 12:19) *
Так камни с ядром avr имеют гарвардскую архитектуру и умеют исполнять код только из FLASH области, к сожалению это не cortex crying.gif .

И что с того?
В ОЗУ для каждого вектора пусть лежит адрес обработчика, а по реальному вектору прерывания во флеш пусть лежит буквально следующий код:
Код
lds zh, high( vector_i )
lds zl, low( vector_i)
ijmp

Где vector_i - переменная в ОЗУ.

Если с асмом туго, переместите все вектора в область бутлоадера, и состряпайте Сишные wrapper'ы:

Код
ISR( vect_xxx)
{
     if (handlers[ vect_xxx ])
         handlers[ vect_xxx ]();
}


где "handlers" массив в ОЗУ, где каждый элемент - адрес обработчика.

Цитата(V_G @ May 11 2011, 16:04) *
Но не думаю, что IAR C умеет ее использовать для целей, потребных топикстартеру.
Все-таки 2 отдельных проекта реальнее, чем так извращаться на Си

Toolchain от IAR включает еще и транслятор Asm'а. Создаете .s файл, пишете в нем код таблицы векторов, и линкером кладете в секцию таблицы векторов вот и всего делов.


Цитата(vvppvv @ May 13 2011, 16:00) *
__interrupt __raw void irqUSART0_RXC (void)
{
interrupt_entry ();
(* Ufunc_ptr)();
interrupt_exit ();
}

Не вижу смысла в ручных entry / exit.
Надо просто тупо вызвать функцию.
vvppvv
Цитата
Не вижу смысла в ручных entry / exit.
Надо просто тупо вызвать функцию.


Тупо можно на этапе изучения контроллера, системы команд и написания первых "Hello, World". Дальше "тупо" - не надо. У меня перед объявлением программ прерываний стоИт __raw. Это совсем не просто так. Оптимизатор IAR спотыкается о косвенный взов, и начинает, как Вы говорите, "тупо" сохранять все регистры. Поэтому были составлены специальные inline-функции interrupt_entry (); и interrupt_exit (); Просто я выдрал пример из конкретного проекта, и немного его "не очистил" от лишнего, не относящегося к теме wink.gif)
defunct
Цитата(vvppvv @ May 31 2011, 21:14) *
У меня перед объявлением программ прерываний стоИт __raw.

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

Цитата
как Вы говорите, "тупо" сохранять все регистры.

Отлично, и пусть сохраняет. Обеспечивается максимальная гибкость - там где требуется гибкость, там нет требований к скорости исполнения, потому что это взаимоисключающие вещи.

Цитата
У меня перед объявлением программ прерываний стоИт __raw. Поэтому были составлены специальные inline-функции interrupt_entry (); и interrupt_exit ();

Вопрос в том насколько легко подцепить туда любую другую функцию после этого - тобиш вопрос поддержки проекта и расширения функционала.
Гораздо проще объявить __interrupt без __raw, и без ручных пролога и эпилога, тогда он сохранит все регистры из-за используемого внутри косвенного вызова, а саму, вызываемую косвенно, функцию обьявить с __raw.

Цитата
Тупо можно на этапе изучения контроллера, системы команд и написания первых "Hello, World". Дальше "тупо" - не надо.

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

Бывают и другие цели, для их достижения приходится жертвовать гибкостью, - в этом случае лучше просто использовать по максимуму возможности конкретной архитектуры и используемого инструментария. В контексте IAR под AVR это - вообще не вызывать никаких функций из обработчика прерываний, и иметь всегда строго один обработчик на одно прерывание. Еще лучше возможно будет писать весь проект на ASM, но это подходит только для задач по сложности типа контроллера светодиода.
vvppvv
Цитата
Вопрос в том насколько легко подцепить туда любую другую функцию после этого - тобиш вопрос поддержки проекта и расширения функционала.


Ответ - подцепить нелегко. Но в данном конкретном месте проекта вопрос про "любые другие функции" не стоял. Там была задача достичь максимального быстродействия этого, конкретного, специфического куска кода. Грубо говоря, написать его на ассемблере. Что позволило с меги128 вытянуть пол-мегабитный проприетарный протокол, вместо 128кБит, когда "обеспечивается универсальность и хорошая переносимость кода".

Цитата
Гораздо проще объявить __interrupt без __raw, и без ручных пролога и эпилога,


Разумеется. Это и делается в 99% случаев.

Цитата
тогда он сохранит все регистры из-за используемого внутри косвенного вызова,


Да, именно так. Причем сохранять будет в памяти, используя обычный "сишный" стек. А мой "ручной" пролог-эпилог использует команды MOVW, копирования слов (пар регистров), это самый быстрый способ сохранения-восстановления регистров в системе команд AVR.

Цитата
а саму, вызываемую косвенно, функцию обьявить с __raw.


А это зачем? sm.gif
Рав нужен только для прерываний. Вот, выдержка из мануала:
"This keyword prevents saving used registers in interrupt functions.
Interrupt functions preserve the contents of all used processor registers at function
entrance and restore them at exit. However, for some very special applications, it can be
desirable to prevent the registers from being saved at function entrance. This can be
accomplished by the use of the keyword __raw."

Вот у меня как раз пример был из "very special applications".

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


Совершенно верно! Что я и сделал.

Цитата
В контексте IAR под AVR это - вообще не вызывать никаких функций из обработчика прерываний, и иметь всегда строго один обработчик на одно прерывание.


Не пойдет. Не бывает универсальных правильных решений. Тогда мне бы пришлось семафорить, развешивать флажки, статусы и пр. В итоге - раздутость кода и потеря производительности и потребительских свойств проектируемой железки.
CDT
Цитата(xelax @ Apr 20 2011, 12:58) *
Тут некоторых коллег посетила нездоровая идея иметь два вектора прерывания на одну периферию .......

Как - то не врубаюсь: по одному событию бросаться выполнять два куска кода одновременно или по какому-то переключателю выбирать, в какую таблицу векторов прыгать?

Если первое - то все равно это будет делаться по очереди или в вперемешку, с неким интервалом, что организовывается в обработчике, доступном через имеющуюся таблицу векторов.

Если второе - зачем две таблицы векторов, если переключатель можно проверить в обработчике, доступном через имеющуюся таблицу векторов и пойти выполнять, то, что заказано.
defunct
Цитата(vvppvv @ Jun 5 2011, 13:22) *
А это зачем? sm.gif
Рав нужен только для прерываний. Вот, выдержка из мануала:

Прочитайте вашу выдержку, прочитайте что я предложил, подумайте, порисуйте на листочке.

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


Код
__raw void (*cb)(void);

__interrupt void GenericIntHandler(void)
{
     cb();
}


__raw void Handler1(void)
{
    //  do whatever you need
}

__raw void Handler2(void)
{
    //  do whatever you need
}

...
     cb = Handler1;



Цитата(vvppvv @ Jun 5 2011, 13:22) *
Не бывает универсальных правильных решений. Тогда мне бы пришлось семафорить, развешивать флажки, статусы и пр.

Все зависит от задачи, фантазии и опыта разработчика.
Вам бы пришлось, а кому-то не придется. Гораздо чаще достаточно одного обработчика для одного прерывания, в таком случае - это будет одновременно правильное, универсальное и оптимальное решение.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.