Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: mspgcc + _even_in_range_
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
MrYuran
В ИАРе для МСП430 есть такая классная фича - квалификатор _even_in_range_(range) для аргументов switch().
То есть если аргумент чётный и распределён в ограниченном диапазоне, то свитч просто формирует сплошную таблицу переходов, а аргумент свича выступает индексом в этой таблице. Таким образом, осуществляется переход за одну команду независимо от количества кейсов в свиче.
Оптимизация жуткая, причём это особенно актуально при обработке прерываний от второго вектора таймера В (TB_CCR1_VECTOR), когда пишем switch(TBIV) и максимально быстро выруливаем на нужный обработчик.

Так вот, как бы такую штуку провернуть в GCC? На форуме mspgcc были какие-то наброски на асме, но это как-то некрасиво.
типа этого
Хотелось бы на чистом си, типа макроса чтоли...
ReAl
Цитата(MrYuran @ Apr 17 2009, 09:57) *
Хотелось бы на чистом си, типа макроса чтоли...
Просьба на чистом С просить только то, что и у IAR делается на чистом С rolleyes.gif

Так пойдёт?
Код
unsigned char foo(unsigned char sel)
{
    // !!! проедполагается, что sel  чётно И sizeof(void*)==2

    // для простоты изложения для AVR в ОЗУ,
    static const void *sw[] = { &&la, &&lb, &&lc };

    void *target = *(void**)((unsigned char*)sw + sel);
    
    goto *target;

    la:
        return 5;
    lb:
        return 8;
    lc:
        return 11;
}

msp-gcc под рукой нет, avr-gcc:
Код
foo:
    mov r30,r24
    ldi r31,lo8(0)
    subi r30,lo8(-(sw.1469))
    sbci r31,hi8(-(sw.1469))
    ld __tmp_reg__,Z+
    ld r31,Z
    mov r30,__tmp_reg__
    ijmp
.L2:
    ldi r24,lo8(5)
    ldi r25,hi8(5)
    ret
.L4:
    ldi r24,lo8(8)
    ldi r25,hi8(8)
    ret
.L5:
    ldi r24,lo8(11)
    ldi r25,hi8(11)
    ret

Для MSP, по идее, будет несколько хуже, чем совсем красивое добавление прямо к PC, но всё же лучше стандартного switch
MrYuran
Цитата(ReAl @ Apr 17 2009, 11:39) *
Так пойдёт?
Для MSP, по идее, будет несколько хуже, чем совсем красивое добавление прямо к PC, но всё же лучше стандартного switch

Да, примерно в этом роде.
Только бы вот ещё завернуть ещё как-то покрасивше...
Но даже в таком виде красивее, чем на асме.
MrYuran
Ну, короче, вот что получилось.
Код
#define switch_even_in_range(index, ... )   \
            static const unsigned int *sw[] = {__VA_ARGS__};\
            unsigned int *target = *(void**)((unsigned int*)sw + index);\
            goto *target;

index - переключатель, далее следует записать все метки кейсов.
Код
interrupt (TIMERB1_VECTOR) Timerb_ccr1(void)
{    
  switch_even_in_range(TBIV, &&def, &&l2, &&l4, &&l6, &&l8, &&l10, &&l12 )
  {
    l2:
        pxTimerB1ccr1expired();
    l4:
        pxTimerB1ccr2expired();
    l6:
        (void)pxMBPortCBTimerExpired();
    l8:
        pxTimerB1ccr4expired();
    l10:
        pxTimerB1ccr5expired();
    l12:
        pxTimerB1ccr6expired();

    def:
  }
}

Ничё так, гламурненько smile.gif

А вот выход:
CODE

00002eb0 <Timerb_ccr1>:
2eb0: 0f 12 push r15 ;
2eb2: 0e 12 push r14 ;
2eb4: 0d 12 push r13 ;
2eb6: 0c 12 push r12 ;
2eb8: 05 12 push r5 ;
2eba: 04 12 push r4 ;
2ebc: 05 41 mov r1, r5 ;
2ebe: 35 50 0e 00 add #14, r5 ;#0x000e
2ec2: 21 83 decd r1 ;
2ec4: 04 41 mov r1, r4 ;
2ec6: 1f 42 1e 01 mov &0x011e,r15 ;0x011e
2eca: 0f 5f rla r15 ;
2ecc: 3f 50 04 02 add #516, r15 ;#0x0204
2ed0: a4 4f 00 00 mov @r15, 0(r4) ;
2ed4: 20 44 br @r4 ;
2ed6: 1f 42 ca 03 mov &0x03ca,r15 ;0x03ca
2eda: 8f 12 call r15 ;
2edc: 1f 42 a8 03 mov &0x03a8,r15 ;0x03a8
2ee0: 8f 12 call r15 ;
2ee2: 1f 42 aa 03 mov &0x03aa,r15 ;0x03aa
2ee6: 8f 12 call r15 ;
2ee8: 1f 42 c6 03 mov &0x03c6,r15 ;0x03c6
2eec: 8f 12 call r15 ;
2eee: 1f 42 c4 03 mov &0x03c4,r15 ;0x03c4
2ef2: 8f 12 call r15 ;
2ef4: 1f 42 c2 03 mov &0x03c2,r15 ;0x03c2
2ef8: 8f 12 call r15 ;
2efa: 21 53 incd r1 ;
2efc: 34 41 pop r4 ;
2efe: 35 41 pop r5 ;
2f00: 3c 41 pop r12 ;
2f02: 3d 41 pop r13 ;
2f04: 3e 41 pop r14 ;
2f06: 3f 41 pop r15 ;
2f08: 00 13 reti

ссылка по указателю на функцию в прерывании это конечно изврат, но речь сейчас не об этом.
В целом вроде бы всё удалось.
ReAl
Цитата(MrYuran @ Apr 17 2009, 12:19) *
Ну, короче, вот что получилось.
Код
#define switch_even_in_range(index, ... )   \
            static const unsigned int *sw[] = {__VA_ARGS__};\
            unsigned int *target = *(void**)((unsigned int*)sw + index);\
            goto *target;
Симпатишненько.
Только вроде бы ж надо
Цитата
= *(void**)((unsigned char*)sw + index);
, на входе чётное число, смещение в таблице в байтах.
ReAl
"раз пошла такая пьянка"
(Cлабонервным не смотреть, тут, как и в protothreads, не просто используется goto, а ещё и внутрь блока оператора цикла. Правда, "одноразового", как в ATOMIC_BLOCK или привычного в макросах do { } while(0) )
Код
#define switch_even_in_range(index, ... )   \
        static const void *sw[] = {__VA_ARGS__};\
        void *target = *(void**)((unsigned char*)sw + index);\
        goto *target; \
        for(int s_e_i_r_I = 0; s_e_i_r_I == 0; s_e_i_r_I = 1)

unsigned moo(unsigned sel)
{
    unsigned result;
    switch_even_in_range(sel, &&la, &&lb, &&lc) {
    la:
        result = 2;
        break;
    lb:
        result = 1;
        break;
    lc:
        result = 11;
    }
    return result;
}
MrYuran
Цитата(ReAl @ Apr 17 2009, 15:43) *
"раз пошла такая пьянка"
(Cлабонервным не смотреть, тут, как и в protothreads, не просто используется goto, а ещё и внутрь блока оператора цикла.

Чё-то я не всосал...
Размер увеличился, смысл - неясен...
alx2
Немного отвлеченный вопрос: а откуда берется этот самый index? Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...
ReAl
Цитата(MrYuran @ Apr 17 2009, 15:10) *
Чё-то я не всосал...
Размер увеличился, смысл - неясен...
Где размер увеличился? Ни на грамм, переменная s_e_i_r_I не создаётся вообще, этот for - это "обёртка" в духе do { } while(0);, не генерирующая кода (при уровне оптимизации, отличающемся от полного отсутствия)
Код
moo:
    mov r31,r25
    mov r30,r24
    subi r30,lo8(-(sw.1367))
    sbci r31,hi8(-(sw.1367))
    ld __tmp_reg__,Z+
    ld r31,Z
    mov r30,__tmp_reg__
    ijmp
.L8:
    ldi r24,lo8(2)
    ldi r25,hi8(2)
    ret
.L10:
    ldi r24,lo8(1)
    ldi r25,hi8(1)
    ret
.L11:
    ldi r24,lo8(11)
    ldi r25,hi8(11)
    ret

Зато можно писать break;

Цитата(alx2 @ Apr 17 2009, 15:12) *
Немного отвлеченный вопрос: а откуда берется этот самый index?
Ну, например, из аппаратного регистра TBIV (TimerBInterruptVector, если я правильно расшифровал). Там именно индекс.

Цитата(alx2 @ Apr 17 2009, 15:12) *
Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...
Конечно проще!
Например, так http://electronix.ru/forum/index.php?showt...mp;#entry520130
Но тут другой случай.
MrYuran
Цитата(alx2 @ Apr 17 2009, 16:12) *
Немного отвлеченный вопрос: а откуда берется этот самый index? Не проще ли вместо индекса сразу работать с указателем на нужный код? Не нужно было бы получать указатель из таблицы...

index - это переключатель свитча
Данный подход выгоден, когда есть переключатель на большое количество индексируемых состояний.
Например, стэйт машин или обработчик кодов команд (параметров, etc)
То есть пишем switch(comand_code<<1,[список меток]) и максимально быстро перескакиваем на обработчик нужной команды

Цитата(ReAl @ Apr 17 2009, 16:27) *
Где размер увеличился? Ни на грамм, переменная s_e_i_r_I не создаётся вообще, этот for - это "обёртка" в духе do { } while(0);, не генерирующая кода (при уровне оптимизации, отличающемся от полного отсутствия)

Зато можно писать break;

А, теперь понятно...
У меня почему-то вставляет цикл.
Причём объявление for(int s_e_i_r_I = 0; s_e_i_r_I == 0; s_e_i_r_I = 1) не прокатывает,
пишет "TimerB\TimerB.c|168|`for' loop initial declaration used outside C99 mode|"
Приходится объявление int s_e_i_r_I вытаскивать наружу.
Как бы эту гадость победить, давно хочу.
ReAl
Цитата(MrYuran @ Apr 17 2009, 15:37) *
`for' loop initial declaration used outside C99 mode|"
Дык -std=c99
Или я чего-то не понял?
klen
typedef enum { func0=0 , func1 , .... , func_count } func_index ;

typedef void (*FPRT)(void) ;

inline void Func0(void)
{
/* код 0 */
}

inline void Func1(void)
{
/* код 1 */
}



FPRT func_table[fuc_count ] = {Func0,Func1,.....} ; // таблица адресов функий

далее в коде просто в нужном месте вместо
switch (arg)
{}
вызвать
(func_table[arg]) () ;

я всегда так делаю - добавлять обработчики и их индексы удобно и быстро. работает это тоже быстро
вопрос тока как это для MSP сгенерится, я армами пользуюсь, про MSP ничего сказать не могу
MrYuran
Цитата(ReAl @ Apr 17 2009, 16:58) *
Дык -std=c99
Или я чего-то не понял?

Я так понимаю, что данная инициализация FOR не соответствует (по мнению компилятора) стандарту С99.
Щас вот как раз качаю С99, надо ж когда-то начинать обращаться к первоисточникам...

Вот, нарыл. Что и требовалось доказать.
Цитата
6.8.5 Iteration statements
Syntax
iteration-statement:
while ( expression ) statement
do statement while ( expression );
for ( expressionopt ; expressionopt ; expressionopt ) statement
for ( declaration expressionopt ; expressionopt ) statement

Чё-то компилер глюкавит...
Сергей Борщ
Цитата(MrYuran @ Apr 20 2009, 08:10) *
Я так понимаю, что данная инициализация FOR не соответствует (по мнению компилятора) стандарту С99.
Как раз соответствует. Просто у вас поддержка C99 не включена.
MrYuran
Цитата(Сергей Борщ @ Apr 20 2009, 09:12) *
Как раз соответствует. Просто у вас поддержка C99 не включена.

CFLAGS = -mmcu=$(MCU) -c $(OPT) $(DEBUG) -std=c99
Вроде подключил, или нет?

Секундочку, чё-то я сам не пойму, куда чего подключил...
Ёжкин кот! У меня галка стояла на автомэйке!
А я тут чего-то исследую, оптимизацию типа меняю...

Ну вот, таки подключил...
Вообще чудеса пошли - ругается на всё из io.h

Особенно невзлюбил sfrb и sfrw
Точнее вот что:
#if defined(__MSP430_HAS_PORT1__) || defined(__MSP430_HAS_PORT1_R__)
__MSP430_EXTERN__ struct port_full_t port1 asm("0x0020");
#endif
Пишет "c:\mspgcc\msp430\include\msp430\iostructures.h|136|syntax error before "asm"|"

maniac.gif
И дался мне этот долбаный for с евоным с99... есть же ещё do{}while...
Хотя нет, в макрос не подставишь...
Сергей Борщ
Цитата(MrYuran @ Apr 20 2009, 08:27) *
Пишет "c:\mspgcc\msp430\include\msp430\iostructures.h|136|syntax error before "asm"|"
Подозреваю, что надо включить с99 с гнутыми расширениями: -std=gnu99
MrYuran
Итак, свершилось чудо!

Было:
Код
switch_even_in_range(TBIV, &&def, &&l2, &&l4, &&l6, &&l8, &&l10, &&l12 )    
    {

    l2:
        pxTimerB1ccr1expired();
        break;
    l4:
        pxTimerB1ccr2expired();
        break;
    l6:
        (void)pxMBPortCBTimerExpired();
        //break;
    l8:
        //pxTimerB1ccr4expired();
    l10:
        //pxTimerB1ccr5expired();
    l12:
        //pxTimerB1ccr6expired();

    def:
    }


Стало:
Код
00002b36 <Timerb_ccr1>:
    2b36:    0f 12           push    r15;
    2b38:    0e 12           push    r14;
    2b3a:    0d 12           push    r13;
    2b3c:    0c 12           push    r12;
    2b3e:    1f 42 1e 01     mov    &0x011e,r15;0x011e
    2b42:    3f 50 04 02     add    #516,    r15;#0x0204
    2b46:    20 4f           br    @r15;
    2b48:    92 12 42 04     call    &0x0442;
    2b4c:    06 3c           jmp    $+14    ;abs 0x2b5a
    2b4e:    05 3c           jmp    $+12    ;abs 0x2b5a
    2b50:    92 12 20 04     call    &0x0420;
    2b54:    02 3c           jmp    $+6     ;abs 0x2b5a
    2b56:    92 12 22 04     call    &0x0422;
    2b5a:    3c 41           pop    r12;
    2b5c:    3d 41           pop    r13;
    2b5e:    3e 41           pop    r14;
    2b60:    3f 41           pop    r15;
    2b62:    00 13           reti

свитч выродился в 2 команды (а нельзя ли в одну?)
Непонятно, почему в середине 2 джампа подряд

Цитата(Сергей Борщ @ Apr 20 2009, 11:29) *
Подозреваю, что надо включить с99 с гнутыми расширениями: -std=gnu99

Спасибо, так прокатило.

Счас ещё попробую вариант klen'а с таблицей указателей на функции.

Код
00002b44 <Timerb_ccr1>:
    2b44:    0f 12           push    r15    ;
    2b46:    0e 12           push    r14    ;
    2b48:    0d 12           push    r13    ;
    2b4a:    0c 12           push    r12    ;
    2b4c:    1f 42 1e 01     mov    &0x011e,r15;0x011e
    2b50:    0f 5f           rla    r15    ;
    2b52:    0f 5f           rla    r15    ;
    2b54:    9f 12 04 02     call    516(r15)    ;
    2b58:    3c 41           pop    r12    ;
    2b5a:    3d 41           pop    r13    ;
    2b5c:    3e 41           pop    r14    ;
    2b5e:    3f 41           pop    r15    ;
    2b60:    00 13           reti

Вот это я понимаю, оптимизация!

Спасибо всем за участие, почерпнул для себя много нового
MrYuran
Прочитал - таки в книжке про опции компилятора, многое встало на свои места.
Поколдовал немного с -fdata-sections и -ffunctions-sections и на -O3 получил код, по размеру не уступающий ИАРовскому.
Так что GCC - rules!
Где бы вот ещё Гриффитса в бумаге достать, а то с экрана читать неудобно
MrYuran
Мда...
Вчера полдня боролся непонятно с чем, пока не поменял свой супероптимизированный свич на стандартный...
И ведь вроде всё правильно делает, но программу уносит в неведомые дали, всё виснет на ходу...
Будем искать...

Кстати, большим открытием для меня было, что static const sw[] формируется не в флеше, как было бы логично, а в ОЗУ.
Пришлось его насильно запихнуть в флешь ( __attribute__(( section(".text") )) )

Ещё один фокус:
static inline void PulseRecharge() не инлайнится внутрь обработчика прерывания,
пока его принудительно туда не запихнёшь
static inline void PulseRecharge()__attribute__((always_inline));
Это если они в одном модуле, а если в разных, то не лезет ни в какую!

#######

Глянул diff-ом, разницы никакой не заметил, за исключением того что все адреса немного сползли из-за изменения размеров функции и добавления таблицы переходов...
В общем, полный абзац.
MrYuran
нашёл у Гриффитса такой абзац:
Нажмите для просмотра прикрепленного файла
Похоже, действительно всё перекашивается...
Пробовал -lno-gcse, не помогло...

Эх, хорошая была идея...
Сергей Борщ
Цитата(MrYuran @ Apr 22 2009, 07:44) *
Ещё один фокус:
static inline void PulseRecharge() не инлайнится внутрь обработчика прерывания,
пока его принудительно туда не запихнёшь
static inline void PulseRecharge()__attribute__((always_inline));
Это если они в одном модуле, а если в разных, то не лезет ни в какую!
Логично. Компилятор ведь не видит тела функции и не знает, какой же код вставлять в точку вызова. Поэтому инлайн-функции можно размещать в заголовочных файлах. По поводу атрибута - не могу сказать, почему компилятор без него решил не встраивать - у него свои критерии. Эта функция используется только в одном месте? Можно поиграться с -finline-functions-called-once и --param max-inline-insns-single. А что мешает сделать #define inline inline __attribute__((always_inline))?
alx2
Цитата(MrYuran @ Apr 22 2009, 09:44) *
Кстати, большим открытием для меня было, что static const sw[] формируется не в флеше, как было бы логично, а в ОЗУ.
Пришлось его насильно запихнуть в флешь ( __attribute__(( section(".text") )) )
Подозреваю, что у msp не фон-неймановская архитектура, и для доступа к ОЗУ и ПЗУ должен использоваться разный код. А gcc не поддерживает "универсальных" указателей (типа трехбайтных указателей в IAR'овском компиляторе для MCS51). Поэтмоу все данные размещаются в ОЗУ. При явном размещении данных в .text необходимо так же явно указывать компилятору, что читать объект требуется особым образом.
MrYuran
Цитата(alx2 @ Apr 22 2009, 15:22) *
Подозреваю, что у msp не фон-неймановская архитектура, и для доступа к ОЗУ и ПЗУ должен использоваться разный код.

Да нет, как раз-таки с этим всё нормально. Для чтения совершенно безразлично, флешь это, ОЗУ или регистры периферии.
Всё расположено линейно в едином пространстве адресов.
Вот AVR - там да, без пузыря во флешь не залезешь... biggrin.gif
alx2
Цитата(MrYuran @ Apr 22 2009, 18:14) *
Да нет, как раз-таки с этим всё нормально. Для чтения совершенно безразлично, флешь это, ОЗУ или регистры периферии.
Всё расположено линейно в едином пространстве адресов.
Тогда странно. Для ARM gcc размещает константные объекты, как и положено, в .rodata.
vvp532
для gcc-4.9.x (последний msp430-gcc на ti.com v4.9.3):

CODE

int asm_goto_example(int x) {
// switch (__even_in_range(x, 10))
asm goto ("add.w %0, PC\n\t"
"jmp %l[case_0]\n\t"
"jmp %l[case_2]\n\t"
"jmp %l[case_4]\n\t"
"jmp %l[case_6]\n\t"
"jmp %l[case_8]\n\t"
:
: "r"(x)
:
:case_0,case_2,case_4,case_6,case_8);

// case 10:
x ^= __LINE__;
goto case_break;

case_0: // case 0:
x ^= __LINE__;
goto case_break;

case_2: // case 0:
x ^= __LINE__;
goto case_break;

case_4: // case 0:
x ^= __LINE__;
goto case_break;

case_6: // case 0:
x ^= __LINE__;
goto case_break;

case_8: // case 0:
x ^= __LINE__;

case_break: // switch end
return ~x;
}


asm получается такой же как и у техасского cl430:

CODE

add.w R12, PC
jmp .L38
jmp .L39
jmp .L40
jmp .L41
jmp .L42

XOR.W #146, R12
.LVL52:
BR #.L43
.L38:
XOR.W #150, R12
.LVL53:
BR #.L43
.L39:
XOR.W #154, R12
.LVL54:
BR #.L43
.L40:
XOR.W #158, R12
.LVL55:
BR #.L43
.L41:
XOR.W #162, R12
.LVL56:
BR #.L43
.L42:
XOR.W #166, R12
.LVL57:
.L43:
INV.W R12
.LVL58:
RET
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2018 Invision Power Services, Inc.