singlskv 0 16 декабря, 2008 Опубликовано 16 декабря, 2008 · Жалоба Господа, Вы занимаетесь просто какой-то х.р..й ... :) :) :) Вот это обертка в асм файле isr.s : .extern curr_isr .global TIMER1_COMPA_vect; прерывание с переключаемым обработчиком TIMER1_COMPA_vect: push r30 push r31 lds r30, curr_isr lds r31, curr_isr + 1 icall pop r31 pop r30 reti а вот это сам файл с обработчиками и примером: #include <avr/io.h> #include <avr/interrupt.h> volatile unsigned char tmp; void INT0_vect(void) __attribute__((interrupt)); // свободный вектор прерывания void INT0_vect(void) { tmp = 0; // тело обработчика 0 } void INT1_vect(void) __attribute__((interrupt)); // свободный вектор прерывания void INT1_vect(void) { tmp = 1; // тело обработчика 1 } //Указатель на функцию обработчик void (* curr_isr)(void) = INT1_vect; int main(void) { curr_isr = INT1_vect; asm("sei"); while (1); } Накладные расходы примерно 16МЦ... на фсе... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 16 декабря, 2008 Опубликовано 16 декабря, 2008 · Жалоба Господа, Вы занимаетесь просто какой-то х.р..й ... :) :) :) Кому х.р.я, а кому - сын ошибок трудных. :) Т.е. получается, что __attribute__((interrupt)) и заставляет все call-used регистры сохранять внутри функции? Кстати Ваш ужас ... Как приятно иногда сказать: знатоки, блин. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 16 декабря, 2008 Опубликовано 16 декабря, 2008 · Жалоба Т.е. получается, что __attribute__((interrupt)) и заставляет все call-used регистры сохранять внутри функции?Справедливости ради, такой подход имеет один побочный эфект, прерывания будут разрешены еще до окончательного выхода из нашего главного обработчика, НО, если прерывания идут с таким темпом что это может вызвать переполнение стека, при других вариантах обработки мы просто будем пропускать прерывания, так что такой вариант ИМХО вполне адекватный... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
733259 0 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Справедливости ради, такой подход имеет один побочный эфект, прерывания будут разрешены еще до окончательного выхода из нашего главного обработчикаС __attribute__((interrupt)) прерывания будут разрешены сразу после входа в INT0_vect (INT0_vect), первой командой будет sei. C __attribute__((signal)) такого не будет, ИМХО правильнее __attribute__((signal,used)). К тому же вариант singlskv не универсален - переключаются векторы INT0, INT1, а если они нужны? В случае "просто" функций - компилятор выдаст предупреждение "misspelled interrupt handler" - из-за этого данный вариант мне показался не подходящим. ИМХО в реале накладные расходы на if будут меньше push r30, icall и т.д. Как приятно иногда сказать: знатоки, блин.Не совсем понял иронию. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Не знаю, можно ли все, что я тут напишу, реализовать в GCC, но в IAR'е я бы делал так static void (*IntProc_Pointer)(void); static UREG save_r16; #pragma diag_suppress=Ta006 __interrupt void Proc1(void) { extern void foo(UINT16); foo(1234); } __interrupt void Proc2(void) { extern void foo(UINT16); foo(4566); } #pragma diag_default=Ta006 #pragma required=save_r16 #pragma required=IntProc_Pointer #pragma vector=PCINT0_vect __raw __interrupt void Proc_JMP(void) { asm( " STS save_r16,R16\n" " LDS R16,IntProc_Pointer+0\n" //Возможно, необходимо поменять местами +0 и +1 " PUSH R16\n" " LDS R16,IntProc_Pointer+1\n" " PUSH R16\n" " LDS R16,save_r16\n" " RET\n" ); } __monitor void Write_IntProc_Pointer(void(__interrupt *p)(void)) { IntProc_Pointer=(void(*)(void))p; } void Set_Proc1(void) { Write_IntProc_Pointer(Proc1); } void Set_Proc2(void) { Write_IntProc_Pointer(Proc2); } Так меньше всего стека уйдет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
733259 0 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Что Ваш вариант в 20 тактов, что через icall в 21. Тогда как банальный if(test) - 5 или 6 тактов - lds, and, breq. ИМХО для 2 функций ни к чему. Навороты хороши не тогда, когда в комлекте ещё тормоза. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Rst7 5 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Что Ваш вариант в 20 тактов, что через icall в 21. Дык я про то, что в моем варианте лишний стек не используется. По скорости это конечно тормоза. ИМХО для 2 функций ни к чему. А если их 22? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба правильнее __attribute__((signal,used)). К тому же вариант singlskv не универсален - переключаются векторы INT0, INT1, а если они нужны? Следовательно, как компромисс, можно просто заюзать все неиспользуемые вектора. Есть у меня еще в стадии тестирования субвекторизация. Допустим, у нас есть функция, при должном внимании к построению которой можно добиться, что она состоит из набора независимых блоков, между которыми компилятор не будет осуществлять операции по сохранению используемых регистров void foo(void) { { block 1 } //........ { block N } } Сделаем необходимый инструментарий typedef uint16_t pc_type; // пока обошел указатели стороной uint8_t wherePC(void* pcreg) __attribute__((naked)); void setPC(pc_type newPC) __attribute__((naked)); /*Обертка для субвекторов*/ #define SubVector(t_name) if(wherePC(&t_name)) WherePC заточено таким образом, чтобы возвращать 0 при вызове, а в параметре - адрес, с которого ее вызывали, исключая обработку возвращаемого значения. void setPC(pc_type newPC) { asm volatile("ijmp" : : "z" (newPC)); } uint8_t wherePC(void* pcreg) { asm volatile ( "pop r25" "\n\t" "pop r24" "\n\t" "adiw r24,2" "\n\t" "st Z, r24" "\n\t" "std Z+1,r25" "\n\t" "movw r30,r24" "\n\t" "sbiw r30,2" "\n\t" "clr r24" "\n\t" "ijmp" "\n\t" ::"z" (pcreg)); } Используя SubVector(имя_переменной_указателя_на_блок) void foo(void) { static pc_type sv[3]; static uint8_t work = 0; if(work) {// код, использующий косв. переход на sv[n] set_PC(sv[1]); } else { SubVector(sv[0]) { block 1 } SubVector(sv[1]) { block 2 } SubVector(sv[2]) { block 3 } work = 1; } return; } В итоге при первом вызове получаем адреса блоков кода в переменных sv[], но сами блоки не выполняем, по аналогии с setjmp, а при последующих вызовах - переходим в кратчайшие сроки к независимому блоку. В данном контексте это должно обеспечить выигрыш по сравнению со switch() Просьба потестить эти примитивы и высказать мнения о применимости. Заранее спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
singlskv 0 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Следовательно, как компромисс, можно просто заюзать все неиспользуемые вектора.Конечно там нужен signal а не interrupt. На самом деле можно заюзать и несуществующие вектора, и с учетом варианта обертки от Rst7 можно сделать вот так: обертка в асм файле isr.s : #include <avr/interrupt.h> .extern curr_isr .extern save_r16 .global TIMER1_COMPA_vect; прерывание с переключаемым обработчиком TIMER1_COMPA_vect: sts save_r16, r16 lds r16, curr_isr push r16 lds r16, curr_isr + 1 push r16 lds r16, save_r16 ret а вот это сам файл с обработчиками и примером: #include <avr/io.h> #include <avr/interrupt.h> #define Isr1 __vector_Isr1 #define Isr2 __vector_Isr2 void Isr1(void) __attribute__((signal)); void Isr1(void) { // тело обработчика 0 } void Isr2(void) __attribute__((signal)); void Isr2(void) { // тело обработчика 1 } // указатель на функцию обработчик void (* curr_isr)(void) = Isr1; // для сохранения r16 в обработчике unsigned char save_r16; // установить обработчик void Set_ISR(void (* p)(void)) { unsigned char sreg = SREG; cli(); curr_isr = p; SREG = sreg; } int main(void) { sei(); Set_ISR(Isr1); Set_ISR(Isr2); while (1); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
MrYuran 26 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Вызов функции по указателю внутри обработчика ведет к сохранению ВСЕХ регистров в стеке. И восстановлению их при выходе. Не быстрый обработчик получается :( а что если "раздеть" обработчик (__naked__), а на подставляемые функции наоборот, навесить сохранение контекста? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба а что если "раздеть" обработчик (__naked__), а на подставляемые функции наоборот, навесить сохранение контекста? Не, там надо подставляемую функцию, к которой будут по указателю обращаться, делать (__naked__), а в нее заворачивать уже то, что нужно. С проктологической точки зрения - нормально :) двойной вложенный вызов. А с другой стороны оптимизация может выкинуть лишний call/ret.. надо попробовать Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 136 17 декабря, 2008 Опубликовано 17 декабря, 2008 · Жалоба Не, там надо подставляемую функцию, к которой будут по указателю обращаться, делать (__naked__),И потерять резервирование места под локальные переменные. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
defunct 0 20 декабря, 2008 Опубликовано 20 декабря, 2008 · Жалоба Ну хоть как-то. Не очень хорошо конечно, но что делать. Может есть способ лучше? Правда, в моем случае, быстродействия достаточно. На мой взгляд, это отличный способ. Почему я так думаю - потому что: 1. Сохранение и восстановление всех регистров это всего лишь 128 тактов вход-выход в обработчик. 2. Как правило, необходимость в подмене обработчика прерывания происходит тогда, когда кардинально что-то меняется в работе программы, например - замена протокола обмена. В таких случаях логично предположить, что подменяемые обработчики "тяжеловесны" и по времени выполнения занимают гораздо больше 128 тактов. 3. Не надо гнаться за скоростью там где ее хватает, тем более в ущерб переносимости и читаемости кода. Из чего делаем вывод - всего за 128 тактов получаем простой, прозрачный (легко читаемый), относительно безопасный, и прекрасно портируемый код на любую платформу. Хорошо это, не очень хорошо, или совсем плохо - судить Вам ;> Правда есть еще один тупой и топорный способ, о котором почему-то забыли упомянуть, хотя он обладает еще таким свойством как "абсолютная" безопасность :) : ISR() { prefix stuff if (...) handler1(); else if (..) handler2(); else common_handler(); postfix stuff } при "сказочной" тупизне и кажущейся неоптимальности, вот какими плюсами сие обладает: - Никакого гемороя с ассемблером. - Тривиальная простота. - Абсолютная безопастность - есть уверенность, что из обработчика выйдем всегда, и всегда выйдем правильно, нет риска неверного указателя в RAM. - Возможность не только "подменять" обработчик, но и "параллельно" выполнять несколько обработчиков (например, по одному интерфейсу обслуживать несколько протоколов обмена). - Легко портируемый. - Нативно оптимизируемый - если все функции handler1, 2, и т.п. объявить в одном файле с обработчиком как static, то результирующий код получится эффективным - т.к. оптимизатор сделает свое дело сам. Все шаманства с ассемблером и оптимизации "ради оптимизации" - это Зло. Лучше делать упор на функциональность, портируемость, читаемость. А вот когда действительно не будет хватать производительности и нет возможности поставить более шустрый кварц / камень, тогда (и только тогда) можно прибегнуть к извращениям на асм'е. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ReAl 0 20 декабря, 2008 Опубликовано 20 декабря, 2008 · Жалоба Есть у меня еще в стадии тестирования субвекторизация.Есть несколько менее переносимый, зато более эффективный способ. В том смысле, что переписать эту субвекторизацию на другой асм другого процессора+компилятора можно, как можно и посмотреть реализацию структуры jump_buf и сделать переключатель, как у Rst7, привязанный к процессору+компилятору, но переписываемый под любую другую компбинацию. А тут - привязка к конкретному инструменту. Но раз в примере всё равно использован gcc, то... в нём можно взять адрес метки. Во-первых, при этом можно WherePC сделать макросом, адрес вычисляется линкером: #define glue2(a,b) a##b #define WPC_LABEL(a,b) glue2(a,b) #define WherePC(a) { a = &&WPC_LABEL(WPC,__LINE__); WPC_LABEL(WPC,__LINE__):; } volatile unsigned char a; void *p1; void *p2; void foo() { ++a; WherePC(p1); ++a; WherePC(p2); ++a; } И результат компиляции: foo: lds r24,a subi r24,lo8(-(1)) sts a,r24 ldi r24,lo8(gs(.L2)) ldi r25,hi8(gs(.L2)) sts (p1)+1,r25 sts p1,r24 .L2: lds r24,a subi r24,lo8(-(1)) sts a,r24 ldi r24,lo8(gs(.L3)) ldi r25,hi8(gs(.L3)) sts (p2)+1,r25 sts p2,r24 .L3: lds r24,a subi r24,lo8(-(1)) sts a,r24 ret Во вторых, этот макрос при такой постановке вопроса не нужен вообще: http://forum.sources.ru/index.php?showtopi...p;#entry1929594 В переводе на AVR-ский язык ISR(INT0_vect) { static void *next_state = &&start; goto *next_state; start: next_state = &&idle; return; idle: if (PINB & 0x01) { PORTB |= 0x80; next_state = &&drain; } return; drain: if (PINB & 0x02) { PORTB &= ~0x80; next_state = &&idle; } return; } И результат: .text .size foo, .-foo .global __vector_1 __vector_1: push __zero_reg__ push __tmp_reg__ in __tmp_reg__,__SREG__ push __tmp_reg__ clr __zero_reg__ push r24 push r25 push r30 push r31 lds r30,next_state.1519 lds r31,(next_state.1519)+1 ijmp .L6: .L13: ldi r24,lo8(gs(.L7)) ldi r25,hi8(gs(.L7)) sts (next_state.1519)+1,r25 sts next_state.1519,r24 rjmp .L12 .L7: sbis 35-0x20,0 rjmp .L12 sbi 37-0x20,7 ldi r24,lo8(gs(.L10)) ldi r25,hi8(gs(.L10)) sts (next_state.1519)+1,r25 sts next_state.1519,r24 rjmp .L12 .L10: sbis 35-0x20,1 rjmp .L12 cbi 37-0x20,7 rjmp .L13 .L12: pop r31 pop r30 pop r25 pop r24 pop __tmp_reg__ out __SREG__,__tmp_reg__ pop __tmp_reg__ pop __zero_reg__ reti .data next_state.1519: .word gs(.L6) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_Pasha 0 20 декабря, 2008 Опубликовано 20 декабря, 2008 · Жалоба посмотреть реализацию структуры jump_buf и сделать переключатель Да-а... На разных платформах количество переключаемого контекста настолько разное... У AVR- многовато :( #define WherePC(a) { a = &&WPC_LABEL(WPC,__LINE__); WPC_LABEL(WPC,__LINE__):; } ................................ static void *next_state = &&start; goto *next_state; Спасибо! Т.е. задача-то решается по-прежнему макросами(для совместимости), но адрес метки в gcc имхо самое не-кривое решение. :beer: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться