|
|
  |
Разместить часть кода в прологе функции |
|
|
|
Nov 27 2012, 07:52
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Пишу SPI slave, платформа AVR. Обмен по прерываниям. Чтобы хоть как-то увеличить быстродействие, следующий байт передаваемых в мастер данных подготавливаю заранее, а в прерывании его первым делом загружаю в регистр данных SPDR. Т.е. передался байт -> прерывание -> загружаю очередной байт до прихода первого CLK. Но пролог у прерывания получился довольно большой и новая порция данных не успевает загрузиться до прихода первого синхроимпульса нового байта. CODE #pragma vector=SPI_STC_vect __interrupt void SPI_TransferComplete_ISR(void) { SPDR = SPI_NextDataByte; //Без промедление загрузить очередной байт. Если не хотели передавать данные, //...................... //...................... //...................... }
CODE SPI_TransferComplete_ISR: /* Пролог */ 00042C 93BA ST -Y,R27 00042E 93AA ST -Y,R26 000430 93FA ST -Y,R31 000432 93EA ST -Y,R30 000434 933A ST -Y,R19 000436 932A ST -Y,R18 000438 931A ST -Y,R17 00043A 930A ST -Y,R16 00043C B73F IN R19,SREG /* ----Пролог */ /* Загрузка данных */ SPDR = SPI_NextDataByte; //Без промедление загрузить очередной байт. 000440 EBE4 LDI R30,0xB4 000442 E0F1 LDI R31,0x01 000444 8103 LDD R16,Z+3 000446 BD0E OUT SPDR,R16 /* ----Загрузка данных */ /* остальная менее критичная к скорости выполнения рутина */
Как видно пролог составляет 17 тактов. Это очень много. Два пути решения в лоб: уменьшить частоту тактирования SPI (точнее увеличить интервал между отправкой байт) или оптимизировать код стараясь уменьшить число используемых регистров. Но хотелось бы получить на выходе что-то такое CODE SPI_TransferComplete_ISR: /* Пролог, сохраняю регистры, которые изменят состояния при загрузке байта в SPDR */ 00043A 930A ST -Y,R16 000430 93FA ST -Y,R31 000432 93EA ST -Y,R30 /* Мой критичный к скорости выполнения код */ 000440 EBE4 LDI R30,0xB4 000442 E0F1 LDI R31,0x01 000444 8103 LDD R16,Z+3 000446 BD0E OUT SPDR,R16 /* Остаток пролога */ 00042C 93BA ST -Y,R27 00042E 93AA ST -Y,R26 000430 93FA ST -Y,R31 000432 93EA ST -Y,R30 000434 933A ST -Y,R19 000436 932A ST -Y,R18 000438 931A ST -Y,R17
00043C B73F IN R19,SREG
/* остальная менее критичная к скорости выполнения рутина */
Можно ли это реализовать в IAR? PS да, добавлю, собирал как без оптимизации, так и с максимальной оптимизацией по скорости, размер пролога не уменьшается.
|
|
|
|
|
Nov 27 2012, 08:18
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Да там этой рутины - кот наплакал. Анализ состояния SPI (приём команды, передача команды) и работа с кольцевым буфером. Функции из прерывания никакие не вызываю, даже inline. Единственно, много volatile переменных, надо их число посокращать бы. А выбрасывать из прерывания не могу, подготовка очередного байта данных критична к скорости, не так, как его загрузка в регистр ВВ, но тем не менее. Эх, был бы DMA или кварц пожирнее. Ещё в голову пришло, объявить мою SPI_NextDataByte как регистровую, но боюсь, что оптимизатор с ума сойдёт тогда.
|
|
|
|
|
Nov 27 2012, 12:46
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Сделал следующее. Выделил под мою SPI_NextDataByte РОН R15 (регистровая переменная) и сделал по Вашему совету jmp CODE #pragma vector=SPI_STC_vect __interrupt void SPI_TransferComplete_ISR(void) { SPDR = SPI_NextDataByte; asm("jmp SPI_IRQ_Routine"); }
void SPI_IRQ_Routine(void) { //Всё остальное... }
В прерывании действительно получил что хотел, НО, пролог и эпилог исчезли из SPI_IRQ_Routine(). Соответственно входя в прерывание софт падает по причине порчи регистров и указателя стека. Что jmp, что call. CODE SPI_TransferComplete_ISR: { SPDR = SPI_NextDataByte; 000444 BCFE OUT SPDR,R15 asm("jmp SPI_IRQ_Routine"); 000448 940C 0227 JMP SPI_IRQ_Routine
00044C 9518 RETI }
void SPI_IRQ_Routine(void) { SPI_IRQ_Routine: 00044E 2F28 MOV R18,R24 TEST_PIN2_HIGH(); 000450 9A5C SBI 0xB,4 000452 9100 01B8 LDS R16,SPI_State 000456 2300 TST R16 000458 F029 BREQ 0x464 //..... и т.д. }
Цитата(KRS @ Nov 27 2012, 15:15)  Можно только вывод в SPDR на асме написать, а потом сделать jmp на С функцию, или разместить их последовательно (тогда и jmp) не нужен! Можно уточнить этот момент. Не понял. Цитата(KRS @ Nov 27 2012, 15:15)  Кроме того можно зарезервировать регистр под переменную, тогда в стеке ничего сохранять не надо достаточно будет одной команды out SPDR, R15 и после нее разместить обработчик прерывания. И тут не понял  . Если я команду выну из обработчика, то когда мне её исполнять.
|
|
|
|
|
Nov 27 2012, 14:04
|
Гуру
     
Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322

|
Может попробовать так: 1) Сделать свой обработчик прерывания на асме Код org SPI_STC_vect xjmp spi_stc_handler_asm
rseg CODE(1)
spi_stc_handler_asm: OUT SPDR, R15 call spi_stc_handler_C reti 1) Остальную часть сделать на Си Код extern "C" void spi_stc_handler_C() { // остальной алгоритм } Цитата(Sergey_Aleksandrovi4 @ Nov 27 2012, 14:46)  CODE #pragma vector=SPI_STC_vect __interrupt void SPI_TransferComplete_ISR(void) { SPDR = SPI_NextDataByte; asm("jmp SPI_IRQ_Routine"); }
Если программа JMP делает, то как туда вернуться чтобы RETI сделать?
|
|
|
|
|
Nov 27 2012, 14:12
|
Частый гость
 
Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764

|
Цитата(_Артём_ @ Nov 27 2012, 18:04)  Если программа JMP делает, то как туда вернуться чтобы RETI сделать? Да это просто вторую итерацию кода скопировал, изначально "call" был. Пробовал по мотивам совета KRS. Что ещё удивило, что вызывая из прерывания функцию (call то по сути и есть вызов), линкер не пытался сохранить все scratch регистры (а их в AVR дофига, если вспомнить соглашение о вызовах). За советы с асмом спасибо, попробую. Правда, пока решил до конца реализовать программу, а потом с быстродействием уже воевать. Может к тому времени и про прологи кто-нибудь идей подкинет.
|
|
|
|
|
Nov 27 2012, 18:28
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Sergey_Aleksandrovi4 @ Nov 27 2012, 16:46)  Сделал следующее. Выделил под мою SPI_NextDataByte РОН R15 (регистровая переменная) и сделал по Вашему совету jmp не так не получится! надо именно асм модуль! можно так Код COMMON INTVEC:CODE:ROOT(1) ORG SPI_STC_vect rjmp SpiAsmInt
RSEG ASMINTCODE:CODE:NOROOT(1) SpiAsmInt: out SPDR, R15
EXTERN SPI_TransferComplete_ISR REQUIRE SPI_TransferComplete_ISR
END а в С файле Код __regvar __no_init volatile uint8_t NextByte@15;
#pragma location="CINTCODE" __interrupt void SPI_TransferComplete_ISR(void) { ...... NextByte = ...; } а в XCL файле надо расположить сегменты последовательно -Z(CODE)....ASMINTCODE,CINTCODE если этого не делать, надо в asm файл вставлять rjmp
|
|
|
|
|
Nov 28 2012, 04:25
|
Частый гость
 
Группа: Участник
Сообщений: 130
Регистрация: 26-06-06
Из: Березовский
Пользователь №: 18 355

|
Когда-то делал так, оптимизация максимальная по размеру: Код void test() { asm("ST -Y, R30"); // здесь некритичный ко времени код, регистры нужные для сохранения зависят от кода asm("LD R30, Y+"); }
#pragma vector = TIMER0_COMPA_vect __interrupt void timer0_compa_isr(void) { OCR0A = 0x00; // делаем быстро и без пролога asm("call test"); } Ассемблер: Код test: ST -Y, R30 LD R30, Y+ RET
timer0_compa_isr: ST -Y, R16 LDI R16, 0 OUT 0x36, R16 call test LD R16, Y+ RETI
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|