реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Разместить часть кода в прологе функции
Sergey_Aleksandr...
сообщение Nov 27 2012, 07:52
Сообщение #1


Частый гость
**

Группа: Свой
Сообщений: 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 да, добавлю, собирал как без оптимизации, так и с максимальной оптимизацией по скорости, размер пролога не уменьшается.
Go to the top of the page
 
+Quote Post
MrYuran
сообщение Nov 27 2012, 08:05
Сообщение #2


Беспросветный оптимист
******

Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646



А может, выкинуть лишнюю "рутину" из прерывания, чтобы не пришлось так много туда-сюда пересохранять?


--------------------
Программирование делится на системное и бессистемное. ©Моё :)
— а для кого-то БГ — это Bill Gilbert =)
Go to the top of the page
 
+Quote Post
Sergey_Aleksandr...
сообщение Nov 27 2012, 08:18
Сообщение #3


Частый гость
**

Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764



Да там этой рутины - кот наплакал. Анализ состояния SPI (приём команды, передача команды) и работа с кольцевым буфером. Функции из прерывания никакие не вызываю, даже inline. Единственно, много volatile переменных, надо их число посокращать бы. А выбрасывать из прерывания не могу, подготовка очередного байта данных критична к скорости, не так, как его загрузка в регистр ВВ, но тем не менее. Эх, был бы DMA или кварц пожирнее.
Ещё в голову пришло, объявить мою SPI_NextDataByte как регистровую, но боюсь, что оптимизатор с ума сойдёт тогда.
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 27 2012, 10:40
Сообщение #4


Профессионал
*****

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



полностью на асме обработчик написать.
Go to the top of the page
 
+Quote Post
Sergey_Aleksandr...
сообщение Nov 27 2012, 11:06
Сообщение #5


Частый гость
**

Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764



Вы будете смеяться, но я переписываю свой проект 3-х годичной давности с асма на Си, т.к. поддерживать асм-код стало хм... трудновато. Но да, если решение с прологом не будет найдено, на конечном этапе перепишу обработчик - деваться некуда -(
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 27 2012, 11:15
Сообщение #6


Профессионал
*****

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



Можно только вывод в SPDR на асме написать, а потом сделать jmp на С функцию, или разместить их последовательно (тогда и jmp) не нужен!
Кроме того можно зарезервировать регистр под переменную, тогда в стеке ничего сохранять не надо достаточно будет одной команды
out SPDR, R15
и после нее разместить обработчик прерывания.


Go to the top of the page
 
+Quote Post
Sergey_Aleksandr...
сообщение Nov 27 2012, 12:46
Сообщение #7


Частый гость
**

Группа: Свой
Сообщений: 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 и после нее разместить обработчик прерывания.

И тут не понял sad.gif . Если я команду выну из обработчика, то когда мне её исполнять.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение Nov 27 2012, 14:04
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 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 сделать?
Go to the top of the page
 
+Quote Post
Sergey_Aleksandr...
сообщение Nov 27 2012, 14:12
Сообщение #9


Частый гость
**

Группа: Свой
Сообщений: 168
Регистрация: 8-10-08
Из: РФ Смоленск
Пользователь №: 40 764



Цитата(_Артём_ @ Nov 27 2012, 18:04) *
Если программа JMP делает, то как туда вернуться чтобы RETI сделать?

Да это просто вторую итерацию кода скопировал, изначально "call" был. Пробовал по мотивам совета KRS. Что ещё удивило, что вызывая из прерывания функцию (call то по сути и есть вызов), линкер не пытался сохранить все scratch регистры (а их в AVR дофига, если вспомнить соглашение о вызовах).
За советы с асмом спасибо, попробую. Правда, пока решил до конца реализовать программу, а потом с быстродействием уже воевать. Может к тому времени и про прологи кто-нибудь идей подкинет.
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 27 2012, 18:28
Сообщение #10


Профессионал
*****

Группа: Модераторы
Сообщений: 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
Go to the top of the page
 
+Quote Post
a9d
сообщение Nov 27 2012, 21:11
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 312
Регистрация: 9-04-10
Пользователь №: 56 532



Недавно была схожая проблема. Нужно на асме писать. Но не попорти регистры в обработчике прерывания.
Go to the top of the page
 
+Quote Post
alag57
сообщение Nov 28 2012, 04:25
Сообщение #12


Частый гость
**

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 4th August 2025 - 18:24
Рейтинг@Mail.ru


Страница сгенерированна за 0.01476 секунд с 7
ELECTRONIX ©2004-2016