Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Перенос кода из под ИАРа на WinAVR
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
Страницы: 1, 2, 3
sonycman
Сейчас пытаюсь подогнать ИАРовский проект на меге88 под ВинАВР 4.3.2.
Чувствую, помучаться придётся немало smile3046.gif

Пока не могу разобраться, как красиво прочитать/записать 16-ти битный таймер, обеспечив атомарность операции.
Аналога иаровского __monitor не нашёл.
Существуют ли подобные макросы? Или надо писать самому?
demiurg_spb
Поможет WinAVR\avr\include\util\atomic.h
sonycman
Цитата(demiurg_spb @ Nov 23 2008, 00:04) *
Поможет WinAVR\avr\include\util\atomic.h

Уря, разобрался! a14.gif
С меня beer.gif

Я то пытался накропать код и заюзать что-то типа cli() и sei() своими корявенькими ручонками, а тут уже готовое решение, такое компактное и мощное одновременно!
Разрабы туда, наверное, тонну хитрости запихали! Учитывать все выходы из секции для восстановления прерываний... бла, как они это осуществили?

Всё, пошёл спать. С утра буду невыспавшийся - зато довольный laughing.gif
MrYuran
Насколько я помню, у WinAVR имеется отличная документация, включающая в том числе раздел "Porting Code From IAR"
sonycman
Цитата(MrYuran @ Nov 23 2008, 12:24) *
Насколько я помню, у WinAVR имеется отличная документация, включающая в том числе раздел "Porting Code From IAR"

Спасибо, теперь всё понятно и с прерываниями biggrin.gif
sonycman
Цитата(MrYuran @ Nov 23 2008, 12:24) *
Насколько я помню, у WinAVR имеется отличная документация, включающая в том числе раздел "Porting Code From IAR"

А может кто подскажет, где поискать документацию по AVR GCC в виде PDF?
А то не очень удобно пользоваться html версией... 07.gif
ARV
Цитата(sonycman @ Nov 23 2008, 20:09) *
А может кто подскажет, где поискать документацию по AVR GCC в виде PDF?
А то не очень удобно пользоваться html версией... 07.gif

как правило, она находится в папке WinAVR\doc\avr-libc - я ведь верно понял, что интересует документация на avr-libc?
ReAl
Цитата(sonycman @ Nov 22 2008, 23:17) *
Разрабы туда, наверное, тонну хитрости запихали! Учитывать все выходы из секции для восстановления прерываний... бла, как они это осуществили?
У gcc есть довольно мощная штука - атрибуты. Куча разных.
Например, атрибутом можно сказать, что результат, возвращаемый данной функцией, зависит только от передаваемых ей параметров и она сама вовне ничего не меняет, т.е. не имеет "побочных эффектов".
Вся математика типа sqrt() такая.
Тогда если её вызывать с одними и теми же неизменными аргументами (константами либо переменными, про которые компилятор знает, что они не менялись) несколько раз - компилятор вызовет одитн раз, запомнит значение и подставит где нужно сам.

Для переменных можно задать аттрибутами функции создания и очистки, при создании переменной будет вызвана одна функция, при уничтожении (при выходе из окружающего её блока { } ) - другая. Это что-то типа конструкторов-деструкторов в C++. Вот они и спрятали в макросах переменную, при создании которой в неё копируется SREG и прерывания запрещаются/разрешаются, в зависимости от типа блока, а при удалении она переписывается назад в SREG, таким образом состояние прерываний восстанавливается.
Чтобы созданная как бы до блока переменная была привязана к блоку, т.е. чтобы можно было писать
Код
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
   ...
}

А не
Код
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE);
   ...
}
макросы оформили в виде заголовка цикла for(;;) - переменная, которая объявлена в заголовке (for( unsigned char u; ...; ... ) ) имеет время жизни в пределах цикла, создаётся до первого входа в тело и уничтожается после выхода из цикла.

Очень симпатично сделано... Учтено всё.

Вот тут ещё на эту тему было, только "наоборот" - нужно на время разрешить прерывания:
http://electronix.ru/forum/index.php?showt...mp;#entry487831
в примере макрос из atomic.h и С++ класс дали при компиляции тождественный код.
sonycman
Цитата(ARV @ Nov 23 2008, 21:23) *
как правило, она находится в папке WinAVR\doc\avr-libc - я ведь верно понял, что интересует документация на avr-libc?

Нет, на библиотеку как раз pdf есть. Мне бы на компилятор, со всеми его ключевыми словами, аттрибутами и т.д...
Она там только в формате html... в принципе, сойдёт, если нет лучшего smile.gif

Цитата(ReAl @ Nov 23 2008, 22:03) *
Очень симпатично сделано... Учтено всё.

Спасибо за разъяснение, стало понятнее!
Сам только недавно начал осваивать С++, и имею небольшое представление о классах.
Очень интересная штука wink.gif

У меня возникла очередная проблема в портировании проекта IAR на WinAVR.
Есть класс таймеров:
Код
#include    <util/atomic.h>

typedef    unsigned long    dword;
typedef    unsigned short    word;
typedef    unsigned char    byte;

extern volatile    word    tcounter;       //hardware timer

class    CTimer    {
private:
    word    interval;
    word    counter;

public:
    CTimer (word timeout    =    0) {    Set(timeout);    }
    void    operator =    (word    timeout);
    void    Set (word timeout);
    bool    operator ! ();
    operator    bool ();
};

        CTimer::operator    bool()
{
    if (!interval) return TRUE;
    if ((tcounter - counter) >= interval)
    {
        interval    =    0;
        return    TRUE;
    }
    return    FALSE;
}

bool    CTimer::operator !    ()
{
    return    !operator bool();
}

void    CTimer::operator =    (word    timeout)
{
    Set(timeout);
}

void    CTimer::Set(word timeout)
{
    if (timeout)
    {
        interval    =    timeout    /    10;            //interrupts timer resolution is 10ms
        counter    =    tcounter;
    }
    else
    {
        interval    =    0;
    }
}

Этот файл компилируется нормально.
Но при компиляции таких строк:
Код
static CTimer tmrSpinup;

то есть при создании объекта класса выдаётся ошибка: undefined reference to `__cxa_guard_release'

Что-то я пока что в тупике.
Описания такой функции нет в исходниках компилятора.
Что делать? Почему так получилось? wacko.gif
Petka
Цитата(ReAl @ Nov 23 2008, 21:03) *
У gcc есть довольно мощная штука - атрибуты. Куча разных.
....
Для переменных можно задать аттрибутами функции создания и очистки, при создании переменной будет вызвана одна функция, при уничтожении (при выходе из окружающего её блока { } ) - другая. Это что-то типа конструкторов-деструкторов в C++. Вот они и спрятали в макросах переменную, при создании которой в неё копируется SREG и прерывания запрещаются/разрешаются, в зависимости от типа блока, а при удалении она переписывается назад в SREG, таким образом состояние прерываний восстанавливается.
Чтобы созданная как бы до блока переменная была привязана к блоку, т.е. чтобы можно было писать
Код
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
   ...
}

А не
Код
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE);
   ...
}
макросы оформили в виде заголовка цикла for(;;) - переменная, которая объявлена в заголовке (for( unsigned char u; ...; ... ) ) имеет время жизни в пределах цикла, создаётся до первого входа в тело и уничтожается после выхода из цикла.

Очень симпатично сделано... Учтено всё.
....

В принципе понятно как они сделали. но во зачем - непонятно. зачем такой хитрый выверт с непереносимыми атрибутами, когда аналогичную штуку можно проделать без них? например:
Код
#define ATOMIC_BLOCK()   for(U8 __temp=get_SREG_and_CLI(),iter=0;iter<1;i++,SREG=__temp)

И переносимо, и без хитрых атрибутов? Может я чего-то не понял. Или есть какой-то потаёный от меня смысл?
ReAl
Цитата(sonycman @ Nov 23 2008, 21:09) *
то есть при создании объекта класса выдаётся ошибка: undefined reference to `__cxa_guard_release'

Что-то я пока что в тупике.
Описания такой функции нет в исходниках компилятора.
Что делать? Почему так получилось? wacko.gif
Странно. К тому коду было добавлено
Код
static CTimer tmrSpinup;

int main()
{
    tmrSpinup.Set(10);
    while( !tmrSpinup );
    for(;;);
}
и всё собралось-слинковалось несколькими версиями от 3.4.6 (WinAVR-20060421) до 4.4.0 (Klen-20080530), более свежих нет на компе.
Это функция, похоже, должна была бы быть в libstdc++, если бы она для AVR была. Там же __cxa_pure_virtual и т.д., т.е. поддержка для более "взрослого" применения С++ с исключениями и т.д.
Почему вдруг в данном случае компилятор решил, что нужна помощь зала библиотеки поддержки - неясно. Иногда такое бывает и иногда при этом помогает добавление ключей
Код
    CFLAGS += -ffunction-sections #-fdata-sections
    LDFLAGS += -Wl,--gc-sections
Это предназначено для итеративного выбрасывания кода и данных, которые никогда не вызывается/не используются.
За компанию стоит добавить
Код
    LDFLAGS += -Wl,--relax

поиском по разделу AVR можно найти объяснения.
sonycman
Цитата(Petka @ Nov 24 2008, 00:38) *
В принципе понятно как они сделали. но во зачем - непонятно. зачем такой хитрый выверт с непереносимыми атрибутами, когда аналогичную штуку можно проделать без них? например:
Код
#define ATOMIC_BLOCK()   for(U8 __temp=get_SREG_and_CLI(),iter=0;iter<1;i++,SREG=__temp)

И переносимо, и без хитрых атрибутов? Может я чего-то не понял. Или есть какой-то потаёный от меня смысл?

А если внутри блока будет break или return? Выполнится ли тогда SREG=__temp?

Цитата(ReAl @ Nov 24 2008, 01:37) *
Странно. К тому коду было добавлено
Код
static CTimer tmrSpinup;

int main()
{
    tmrSpinup.Set(10);
    while( !tmrSpinup );
    for(;;);
}
и всё собралось-слинковалось несколькими версиями от 3.4.6 (WinAVR-20060421) до 4.4.0 (Klen-20080530), более свежих нет на компе.

Есть информация, что некоторые статические объекты вызывают такую ошибку в новых версиях компилятора, так как отсутствует какая-то библиотека...
Решилось добавлением ключа -fno-threadsafe-statics
ReAl
Цитата(Petka @ Nov 23 2008, 22:38) *
В принципе понятно как они сделали. но во зачем - непонятно. зачем такой хитрый выверт с непереносимыми атрибутами, когда аналогичную штуку можно проделать без них? например:
Код
#define ATOMIC_BLOCK()   for(U8 __temp=get_SREG_and_CLI(),iter=0;iter<1;i++,SREG=__temp)
И переносимо, и без хитрых атрибутов? Может я чего-то не понял. Или есть какой-то потаёный от меня смысл?
Это НЕ аналогичная штука. По ссылке, там где я пример с классом приводил, достаточно полный пример зачем это надо (конструкторы-деструкторы, пусть даже в виде прицепленных атрибутами функций).
Может быть выход из середины блока, простой "переносимый" вариант при этом не отработает освобождение (у вcякой задачи есть два решения - простое и правильное ;-) )
Код
#include <avr/interrupt.h>
#include <util/atomic.h>

uint8_t get_SREG_and_CLI() { uint8_t s = SREG; cli(); return s; }

#define ATOM()   for(uint8_t __temp=get_SREG_and_CLI(),iter=0; iter<1; iter++,SREG=__temp)


volatile uint8_t v;

void foo(uint8_t z)
{
    ATOM() {
        ++v;
        if(z) return;
        ++v;
    }    
}


void moo(uint8_t z)
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        ++v;
        if(z) return;
        ++v;
    }    
}
Код
.global    foo
    .type    foo, @function
foo:
    in r18,95-0x20
/* #APP */
    cli
/* #NOAPP */
    lds r25,v
    subi r25,lo8(-(1))
    sts v,r25
    tst r24
    brne .L10
    lds r24,v
    subi r24,lo8(-(1))
    sts v,r24
    out 95-0x20,r18
.L10:                           ; <--- а кто прерывания взад вернёт???
    ret

.global    moo
    .type    moo, @function
moo:
    in r18,95-0x20
/* #APP */
    cli
/* #NOAPP */
    lds r25,v
    subi r25,lo8(-(1))
    sts v,r25
    tst r24
    brne .L4
    lds r24,v
    subi r24,lo8(-(1))
    sts v,r24
.L4:                            ; <--- вот это другое дело!
    out 95-0x20,r18
    ret
Сергей Борщ
Цитата(sonycman @ Nov 23 2008, 21:09) *
то есть при создании объекта класса выдаётся ошибка: undefined reference to `__cxa_guard_release'
Полный ответ здесь. Но я на это не нарывался, видимо потому, что использую --ffunction-section и --gc-sections. Я и узнал-то об этих функциях только из статьи по ссылке.
ReAl
Цитата(sonycman @ Nov 23 2008, 23:37) *
А если объявить tmrSpinup как static?
Есть информация, что некоторые статические объекты вызывают такую ошибку в новых версиях компилятора, так как отсутствует какая-то библиотека...
Решилось добавлением ключа -fno-threadsafe-statics
Так у меня она static и объявлена, вероятно, у меня достаточно старые версии :-)
За ключик спасибо, добавлю как упреждающий манёвр.



Цитата(Сергей Борщ @ Nov 23 2008, 23:45) *
Полный ответ здесь.

У меня из дому какие-то проблемы именно с этим сайтом :-(
По прямому линку заходит, но страшно медленно, а вот на вторую страницу какой-то темы зайти уже нереально.
Поэтому я его практически не смотрю, на работе как правило не до интернета.
При этом всё остальное дома "летает", как и положено adsl2+, многое быстрее, чем на работе.
Так что гляну уже после "типоотпуска".
alx2
Цитата(sonycman @ Nov 23 2008, 22:09) *
А может кто подскажет, где поискать документацию по AVR GCC в виде PDF?
А то не очень удобно пользоваться html версией... 07.gif
А на родной страничке http://gcc.gnu.org/onlinedocs/ ссылки "also in PDF" - это разве не оно?
Petka
Цитата(ReAl @ Nov 24 2008, 00:43) *
Это НЕ аналогичная штука. По ссылке, там где я пример с классом приводил, достаточно полный пример зачем это надо (конструкторы-деструкторы, пусть даже в виде прицепленных атрибутами функций).
Может быть выход из середины блока, простой "переносимый" вариант при этом не отработает освобождение (у вcякой задачи есть два решения - простое и правильное ;-) )
Код
#include <avr/interrupt.h>
#include <util/atomic.h>

uint8_t get_SREG_and_CLI() { uint8_t s = SREG; cli(); return s; }

#define ATOM()   for(uint8_t __temp=get_SREG_and_CLI(),iter=0; iter<1; iter++,SREG=__temp)
volatile uint8_t v;

Мысль понял.
а если так:
Код
define ATOM()   for(uint8_t __temp=get_SREG_and_CLI(),iter=0; iter<1; iter++,SREG=__temp) for(int iter2=0; iter2 < 1; iter2++)
sonycman
Портировал я свою программу с IARа на GCC.
Оптимизация максимальная по скорости у первого, и Os с ключами -mcall-prologues -fno-threadsafe-statics -ffunction-sections --gc-sections --relax у второго.
В результате код IARа - 6 090 bytes of CODE memory + 493 bytes of DATA memory.
Код GCC - Program: 7462 bytes + Data: 363 bytes.

Код иара компактнее почти на 1,5 килобайта. Правда, некоторые функции "тюнингованы" специальными ключами (типа __z_x и т.д.) для оптимальной генерации.
Добавление некоторых аттрибутов для GCC в аналогичных целях, к сожалению, не дало абсолютно никакого результата...

На первый взгляд, скомпилированный GCC код весьма красив и аккуратен, за исключением излишней любви к LDS и STS, например:
исходный текст:
Код
    case    USART_SEND_DATA_EX:
        length    =    4 + 4 + 4 + 20;
        *pb++    =    length + 1;
        *pb++    =    command;
        *pb++    =    temperature.cpu;
        *pb++    =    temperature.gpu;
        *pb++    =    temperature.amb;
        *pb++    =    temperature.hdd;
        *pb++   =   fan_speed[0];
        *pb++   =   fan_speed[1];
        *pb++   =   fan_speed[2];
        *pb++   =   fan_speed[3];
        *pb++    =    cpuFan.GetCurrentSpeed();
        *pb++    =    rearFan.GetCurrentSpeed();
        *pb++    =    sideFan.GetCurrentSpeed();
        *pb++    =    frontFan.GetCurrentSpeed();
    *pb++    =    cpuFan.RPMval[RPM_MIN];
    *pb++    =    cpuFan.RPMval[RPM_MED];
    *pb++    =    cpuFan.RPMval[RPM_MAX];
    *pb++    =    cpuFan.minTEMPval;
    *pb++    =    cpuFan.maxTEMPval;
    *pb++    =    rearFan.RPMval[RPM_MIN];
    *pb++    =    rearFan.RPMval[RPM_MED];
    *pb++    =    rearFan.RPMval[RPM_MAX];
    *pb++    =    rearFan.minTEMPval;
    *pb++    =    rearFan.maxTEMPval;
    *pb++    =    sideFan.RPMval[RPM_MIN];
    *pb++    =    sideFan.RPMval[RPM_MED];
    *pb++    =    sideFan.RPMval[RPM_MAX];
    *pb++    =    sideFan.minTEMPval;
    *pb++    =    sideFan.maxTEMPval;
    *pb++    =    frontFan.RPMval[RPM_MIN];
    *pb++    =    frontFan.RPMval[RPM_MED];
    *pb++    =    frontFan.RPMval[RPM_MAX];
    *pb++    =    frontFan.minTEMPval;
    *pb++    =    frontFan.maxTEMPval;
    break;

результат IARа:
Код
    105              case    USART_SEND_DATA_EX:
    106                  length    =    4 + 4 + 4 + 20;
   \                     ??usartSendCommand_4:
   \   000000C4   E280               LDI     R24, 32
    107                  *pb++    =    length + 1;
   \   000000C6   E201               LDI     R16, 33
   \   000000C8   930D               ST      X+, R16
    108                  *pb++    =    command;
   \   000000CA   934D               ST      X+, R20
    109                  *pb++    =    temperature.cpu;
   \   000000CC   9100....           LDS     R16, temperature
   \   000000D0   930D               ST      X+, R16
    110                  *pb++    =    temperature.gpu;
   \   000000D2   9100....           LDS     R16, (temperature + 1)
   \   000000D6   930D               ST      X+, R16
    111                  *pb++    =    temperature.amb;        
   \   000000D8   9100....           LDS     R16, (temperature + 2)
   \   000000DC   930D               ST      X+, R16
    112                  *pb++    =    temperature.hdd;        
   \   000000DE   9100....           LDS     R16, (temperature + 3)
   \   000000E2   930D               ST      X+, R16
    113                  *pb++   =   fan_speed[0];
   \   000000E4   9100....           LDS     R16, fan_speed
   \   000000E8   930D               ST      X+, R16
    114                  *pb++   =   fan_speed[1];
   \   000000EA   9100....           LDS     R16, (fan_speed + 1)
   \   000000EE   930D               ST      X+, R16
    115                  *pb++   =   fan_speed[2];
   \   000000F0   9100....           LDS     R16, (fan_speed + 2)
   \   000000F4   930D               ST      X+, R16
    116                  *pb++   =   fan_speed[3];
   \   000000F6   9100....           LDS     R16, (fan_speed + 3)
   \   000000FA   930D               ST      X+, R16
    117                  *pb++    =    cpuFan.GetCurrentSpeed();
   \   000000FC   ....               LDI     R16, LOW(cpuFan)
   \   000000FE   ....               LDI     R17, (cpuFan) >> 8
   \   00000100   ....               RCALL   ??GetCurrentSpeed
   \   00000102   930D               ST      X+, R16
    118                  *pb++    =    rearFan.GetCurrentSpeed();
   \   00000104   ....               LDI     R16, LOW(rearFan)
   \   00000106   ....               LDI     R17, (rearFan) >> 8
   \   00000108   ....               RCALL   ??GetCurrentSpeed
   \   0000010A   930D               ST      X+, R16
    119                  *pb++    =    sideFan.GetCurrentSpeed();
   \   0000010C   ....               LDI     R16, LOW(sideFan)
   \   0000010E   ....               LDI     R17, (sideFan) >> 8
   \   00000110   ....               RCALL   ??GetCurrentSpeed
   \   00000112   930D               ST      X+, R16
    120                  *pb++    =    frontFan.GetCurrentSpeed();
   \   00000114   ....               LDI     R16, LOW(frontFan)
   \   00000116   ....               LDI     R17, (frontFan) >> 8
   \   00000118   ....               RCALL   ??GetCurrentSpeed
   \   0000011A   930D               ST      X+, R16
    121                  *pb++    =    cpuFan.RPMval[RPM_MIN];
   \   0000011C   9100....           LDS     R16, (cpuFan + 5)
   \   00000120   930D               ST      X+, R16
    122                  *pb++    =    cpuFan.RPMval[RPM_MED];
   \   00000122   9100....           LDS     R16, (cpuFan + 6)
   \   00000126   930D               ST      X+, R16
    123                  *pb++    =    cpuFan.RPMval[RPM_MAX];
   \   00000128   9100....           LDS     R16, (cpuFan + 7)
   \   0000012C   930D               ST      X+, R16
    124                  *pb++    =    cpuFan.minTEMPval;
   \   0000012E   9100....           LDS     R16, (cpuFan + 8)
   \   00000132   930D               ST      X+, R16
    125                  *pb++    =    cpuFan.maxTEMPval;
   \   00000134   9100....           LDS     R16, (cpuFan + 9)
   \   00000138   930D               ST      X+, R16
    126                  *pb++    =    rearFan.RPMval[RPM_MIN];
   \   0000013A   9100....           LDS     R16, (rearFan + 5)
   \   0000013E   930D               ST      X+, R16
    127                  *pb++    =    rearFan.RPMval[RPM_MED];
   \   00000140   9100....           LDS     R16, (rearFan + 6)
   \   00000144   930D               ST      X+, R16
    128                  *pb++    =    rearFan.RPMval[RPM_MAX];
   \   00000146   9100....           LDS     R16, (rearFan + 7)
   \   0000014A   930D               ST      X+, R16
    129                  *pb++    =    rearFan.minTEMPval;
   \   0000014C   9100....           LDS     R16, (rearFan + 8)
   \   00000150   930D               ST      X+, R16
    130                  *pb++    =    rearFan.maxTEMPval;
   \   00000152   9100....           LDS     R16, (rearFan + 9)
   \   00000156   930D               ST      X+, R16
    131                  *pb++    =    sideFan.RPMval[RPM_MIN];
   \   00000158   9100....           LDS     R16, (sideFan + 5)
   \   0000015C   930D               ST      X+, R16
    132                  *pb++    =    sideFan.RPMval[RPM_MED];
   \   0000015E   9100....           LDS     R16, (sideFan + 6)
   \   00000162   930D               ST      X+, R16
    133                  *pb++    =    sideFan.RPMval[RPM_MAX];
   \   00000164   9100....           LDS     R16, (sideFan + 7)
   \   00000168   930D               ST      X+, R16
    134                  *pb++    =    sideFan.minTEMPval;
   \   0000016A   9100....           LDS     R16, (sideFan + 8)
   \   0000016E   930D               ST      X+, R16
    135                  *pb++    =    sideFan.maxTEMPval;
   \   00000170   9100....           LDS     R16, (sideFan + 9)
   \   00000174   930D               ST      X+, R16
    136                  *pb++    =    frontFan.RPMval[RPM_MIN];
   \   00000176   9100....           LDS     R16, (frontFan + 5)
   \   0000017A   930D               ST      X+, R16
    137                  *pb++    =    frontFan.RPMval[RPM_MED];
   \   0000017C   9100....           LDS     R16, (frontFan + 6)
   \   00000180   930D               ST      X+, R16
    138                  *pb++    =    frontFan.RPMval[RPM_MAX];
   \   00000182   9100....           LDS     R16, (frontFan + 7)
   \   00000186   930D               ST      X+, R16
    139                  *pb++    =    frontFan.minTEMPval;
   \   00000188   9100....           LDS     R16, (frontFan + 8)
   \   0000018C   930D               ST      X+, R16
    140                  *pb++    =    frontFan.maxTEMPval;
   \   0000018E   9100....           LDS     R16, (frontFan + 9)
   \   00000192   CF96               RJMP    ??usartSendCommand_12
    141                  break;

вполне прилично.
А вот у GCC:
Код
    case    USART_SEND_DATA_EX:
        length    =    4 + 4 + 4 + 20;
        *pb++    =    length + 1;
    153c:    81 e2           ldi    r24, 0x21; 33
    153e:    80 93 ed 01     sts    0x01ED, r24
        *pb++    =    command;
    1542:    90 93 ee 01     sts    0x01EE, r25
        *pb++    =    temperature.cpu;
    1546:    80 91 34 01     lds    r24, 0x0134
    154a:    80 93 ef 01     sts    0x01EF, r24
        *pb++    =    temperature.gpu;
    154e:    80 91 35 01     lds    r24, 0x0135
    1552:    80 93 f0 01     sts    0x01F0, r24
        *pb++    =    temperature.amb;
    1556:    80 91 36 01     lds    r24, 0x0136
    155a:    80 93 f1 01     sts    0x01F1, r24
        *pb++    =    temperature.hdd;
    155e:    80 91 37 01     lds    r24, 0x0137
    1562:    80 93 f2 01     sts    0x01F2, r24
        *pb++   =   fan_speed[0];
    1566:    80 91 6f 01     lds    r24, 0x016F
    156a:    80 93 f3 01     sts    0x01F3, r24
        *pb++   =   fan_speed[1];
    156e:    80 91 70 01     lds    r24, 0x0170
    1572:    80 93 f4 01     sts    0x01F4, r24
        *pb++   =   fan_speed[2];
    1576:    80 91 71 01     lds    r24, 0x0171
    157a:    80 93 f5 01     sts    0x01F5, r24
        *pb++   =   fan_speed[3];
    157e:    80 91 72 01     lds    r24, 0x0172
    1582:    80 93 f6 01     sts    0x01F6, r24
        *pb++    =    cpuFan.GetCurrentSpeed();
    1586:    87 e3           ldi    r24, 0x37; 55
    1588:    92 e0           ldi    r25, 0x02; 2
    158a:    4c d7           rcall    .+3736   ; 0x2424 <__data_load_end+0x6fe>
    158c:    80 93 f7 01     sts    0x01F7, r24
        *pb++    =    rearFan.GetCurrentSpeed();
    1590:    81 e4           ldi    r24, 0x41; 65
    1592:    92 e0           ldi    r25, 0x02; 2
    1594:    47 d7           rcall    .+3726   ; 0x2424 <__data_load_end+0x6fe>
    1596:    80 93 f8 01     sts    0x01F8, r24
        *pb++    =    sideFan.GetCurrentSpeed();
    159a:    8b e4           ldi    r24, 0x4B; 75
    159c:    92 e0           ldi    r25, 0x02; 2
    159e:    42 d7           rcall    .+3716   ; 0x2424 <__data_load_end+0x6fe>
    15a0:    80 93 f9 01     sts    0x01F9, r24
        *pb++    =    frontFan.GetCurrentSpeed();
    15a4:    85 e5           ldi    r24, 0x55; 85
    15a6:    92 e0           ldi    r25, 0x02; 2
    15a8:    3d d7           rcall    .+3706   ; 0x2424 <__data_load_end+0x6fe>
    15aa:    80 93 fa 01     sts    0x01FA, r24
        *pb++    =    cpuFan.RPMval[RPM_MIN];
    15ae:    80 91 3c 02     lds    r24, 0x023C
    15b2:    80 93 fb 01     sts    0x01FB, r24
        *pb++    =    cpuFan.RPMval[RPM_MED];
    15b6:    80 91 3d 02     lds    r24, 0x023D
    15ba:    80 93 fc 01     sts    0x01FC, r24
        *pb++    =    cpuFan.RPMval[RPM_MAX];
    15be:    80 91 3e 02     lds    r24, 0x023E
    15c2:    80 93 fd 01     sts    0x01FD, r24
        *pb++    =    cpuFan.minTEMPval;
    15c6:    80 91 3f 02     lds    r24, 0x023F
    15ca:    80 93 fe 01     sts    0x01FE, r24
        *pb++    =    cpuFan.maxTEMPval;
    15ce:    80 91 40 02     lds    r24, 0x0240
    15d2:    80 93 ff 01     sts    0x01FF, r24
        *pb++    =    rearFan.RPMval[RPM_MIN];
    15d6:    80 91 46 02     lds    r24, 0x0246
    15da:    80 93 00 02     sts    0x0200, r24
        *pb++    =    rearFan.RPMval[RPM_MED];
    15de:    80 91 47 02     lds    r24, 0x0247
    15e2:    80 93 01 02     sts    0x0201, r24
        *pb++    =    rearFan.RPMval[RPM_MAX];
    15e6:    80 91 48 02     lds    r24, 0x0248
    15ea:    80 93 02 02     sts    0x0202, r24
        *pb++    =    rearFan.minTEMPval;
    15ee:    80 91 49 02     lds    r24, 0x0249
    15f2:    80 93 03 02     sts    0x0203, r24
        *pb++    =    rearFan.maxTEMPval;
    15f6:    80 91 4a 02     lds    r24, 0x024A
    15fa:    80 93 04 02     sts    0x0204, r24
        *pb++    =    sideFan.RPMval[RPM_MIN];
    15fe:    80 91 50 02     lds    r24, 0x0250
    1602:    80 93 05 02     sts    0x0205, r24
        *pb++    =    sideFan.RPMval[RPM_MED];
    1606:    80 91 51 02     lds    r24, 0x0251
    160a:    80 93 06 02     sts    0x0206, r24
        *pb++    =    sideFan.RPMval[RPM_MAX];
    160e:    80 91 52 02     lds    r24, 0x0252
    1612:    80 93 07 02     sts    0x0207, r24
        *pb++    =    sideFan.minTEMPval;
    1616:    80 91 53 02     lds    r24, 0x0253
    161a:    80 93 08 02     sts    0x0208, r24
        *pb++    =    sideFan.maxTEMPval;
    161e:    80 91 54 02     lds    r24, 0x0254
    1622:    80 93 09 02     sts    0x0209, r24
        *pb++    =    frontFan.RPMval[RPM_MIN];
    1626:    80 91 5a 02     lds    r24, 0x025A
    162a:    80 93 0a 02     sts    0x020A, r24
        *pb++    =    frontFan.RPMval[RPM_MED];
    162e:    80 91 5b 02     lds    r24, 0x025B
    1632:    80 93 0b 02     sts    0x020B, r24
        *pb++    =    frontFan.RPMval[RPM_MAX];
    1636:    80 91 5c 02     lds    r24, 0x025C
    163a:    80 93 0c 02     sts    0x020C, r24
        *pb++    =    frontFan.minTEMPval;
    163e:    80 91 5d 02     lds    r24, 0x025D
    1642:    80 93 0d 02     sts    0x020D, r24
        *pb++    =    frontFan.maxTEMPval;
    1646:    80 91 5e 02     lds    r24, 0x025E
    164a:    80 93 0e 02     sts    0x020E, r24
    164e:    10 e2           ldi    r17, 0x20; 32
    1650:    cf e0           ldi    r28, 0x0F; 15
    1652:    d2 e0           ldi    r29, 0x02; 2
    1654:    08 c0           rjmp    .+16     ; 0x1666 <_Z16usartSendCommandhPKvh+0x24e>
        break;

жаль, не догадался компилер использовать указатель 05.gif
А зря. Итог - внушительный, в два раза больший, размер функции.

Также в коде есть одна немаленькая inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия?

За короткое время знакомства с GCC понравилось: удобная реализация атомарности через макросы ATOMIC_BLOCK(), удобная обёртка для обработчиков прерываний вида ISR(vector_name){}.

Что не понравилось: гиморрой с обработкой данных, расположенных во флеш - для их чтения необходимо использовать специальный макрос pgm_read_...(). В ИАРе достаточно объявить указатель типа __flash и можно работать с ним совершенно обычным образом smile.gif
Также не очень удобно каждый раз пользоваться PSTR() для обозначения in-line строк вида: printText(PSTR("some")).
ИАР тут опять на высоте со своим __flash, так как подобная функция в нём декларируется как printText(char const __flash __flash * pointer), и вызов делается просто: printText("that`s cool") smile.gif
aesok
Цитата(sonycman @ Nov 24 2008, 14:05) *
Также в коде есть одна немаленькая inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия?


Добавте при обьявлении этой функции static.

Анатолий.
demiurg_spb
Цитата(sonycman @ Nov 24 2008, 14:05) *
и вызов делается просто: printText("that`s cool") smile.gif
Я уже описал как добиться того же результата и в GCC (правда через макрос)...
Повторяю:
Код
void lcd_print_str  (unsigned char x, unsigned char y, char* p);          // print RAM str
void lcd_print_cstr (unsigned char x, unsigned char y, const char* p);    // print FLASH str

#define  lcd_print_PSTR(X,Y,LCDPSTR)   lcd_print_cstr(X,Y,PSTR(LCDPSTR))  // put str in to FLASH and then print
sonycman
Цитата(aesok @ Nov 24 2008, 15:35) *
Добавте при обьявлении этой функции static.

Анатолий.

У меня она так и объявлена:
Код
static inline void    ReadButtons(void) __attribute__ ((always_inline));

static inline void    ReadButtons(void)
{}


Результата нет - всё равно две копии sad.gif
sonycman
Интересно, почему при делении на два не всегда используются оптимизации?
Например:
Код
else if    (percent == MED_AVAIL) percent = (RPMval[RPM_MAX] - RPMval[RPM_MIN]) / 2;
     68c:    61 38           cpi    r22, 0x81; 129
     68e:    49 f4           brne    .+18     ; 0x6a2 <_ZN13CFanRegulator8SetSpeedEhh+0x44>
     690:    87 81           ldd    r24, Z+7; 0x07
     692:    90 e0           ldi    r25, 0x00; 0
     694:    25 81           ldd    r18, Z+5; 0x05
     696:    82 1b           sub    r24, r18
     698:    91 09           sbc    r25, r1
     69a:    62 e0           ldi    r22, 0x02; 2
     69c:    70 e0           ldi    r23, 0x00; 0
     69e:    52 da           rcall    .-2908   ; 0xfffffb44 <__eeprom_end+0xff7efb1c>

Видно, что для деления вызывается подпрограмма библиотеки.
Почему не используется простой сдвиг? 07.gif
MrYuran
Цитата(sonycman @ Nov 24 2008, 17:15) *
Интересно, почему при делении на два не всегда используются оптимизации?
Видно, что для деления вызывается подпрограмма библиотеки.
Почему не используется простой сдвиг? 07.gif

Я обычно ручками пишу <<1 (2,3,etc) или >>1, не надеясь на компилятор.
А вообще от флагов оптимизации зависит.
sonycman
Цитата(MrYuran @ Nov 24 2008, 18:22) *
Я обычно ручками пишу <<1 (2,3,etc) или >>1, не надеясь на компилятор.
А вообще от флагов оптимизации зависит.

Точно, надо попробовать.
С другой стороны, приятнее читать программу с обычными символами /, чем сдвигами.

А где можно почитать про флаги для оптимизации?
Я уже кучу флагов надобавлял (в сети понаходил).
Вот они:
-ffunction-sections -fno-inline-small-functions -fno-tree-scev-cprop
-mcall-prologues

и ключ -fno-inline-small-functions помог уменьшить размер кода на 400 байт (итого получилось 7110 байт).

Ещё непонятно, почему компилер выдаёт предупреждения типа:
only initialized variables can be placed into program memory area
на все строки, подобные:
Код
static prog_char  fntable[]    PROGMEM    = "!%`()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^";
demiurg_spb
Цитата(sonycman @ Nov 24 2008, 17:46) *
Ещё непонятно, почему компилер выдаёт предупреждения типа:
only initialized variables can be placed into program memory area
на все строки, подобные:
Код
static prog_char  fntable[]    PROGMEM    = "!%`()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^";

prog_char ведь это уже:
Код
typedef char PROGMEM prog_char;
для prog_char уже PROGMEM не требуется.
Тут c С++ и prog_char какие-то нюансы всплывали...
Попробуйте так:
Код
const char PROGMEM Vasja[] = "Vasja";
sonycman
Цитата(demiurg_spb @ Nov 24 2008, 19:58) *
prog_char ведь это уже:
Код
typedef char PROGMEM prog_char;
второй раз после prog_char PROGMEM уже не требуется.
Тут c С++ и prog_char какие-то нюансы всплывали...
Попробуйте так:
Код
const char PROGMEM Vasja[] = "Vasja";


Спасибо, попробовал. Но всё осталось по прежнему...
ARV
смутил ключик --gc-sections я всегда использую -Wl,-gc-sections возможно, это одно и то же, но на всякий случай обращу ваше внимание...

кстати, результат оптимизации сильно зависит от не сразу заметных моментов, например, локальная static переменная и глобальная static переменная для тех же целей может дать разный размер кода. та же песня и с указателями - далеко не всегда они приводят к уменьшению размера... строгих закономерностей я не обнаружил, но сказанное имеет место...
aesok
Цитата(sonycman @ Nov 24 2008, 14:57) *
У меня она так и объявлена:
Код
static inline void    ReadButtons(void) __attribute__ ((always_inline));

static inline void    ReadButtons(void)
{}


Результата нет - всё равно две копии sad.gif


Почитайте про static inline функции здесь:
http://www.greenend.org.uk/rjk/2003/03/inline.html

С ними есть какието тонкости, у меня сейчас нет времени вникать.

Анатолий.
sonycman
Цитата(ARV @ Nov 24 2008, 20:40) *
смутил ключик --gc-sections я всегда использую -Wl,-gc-sections возможно, это одно и то же, но на всякий случай обращу ваше внимание...

Попробовал и с -gc-sections. Вроде без разницы.
Полный текст командной строки линкера:
Код
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref --gc-sections --relax -mmcu=atmega88


Цитата(aesok @ Nov 24 2008, 23:07) *
Почитайте про static inline функции здесь:
http://www.greenend.org.uk/rjk/2003/03/inline.html

С ними есть какието тонкости, у меня сейчас нет времени вникать.

Анатолий.

Спасибо, ознакомлюсь. smile.gif

ЗЫ: Хм, написано, что инлайн функция не должна иметь статических переменных или обращаться к таковым... а у меня внутри есть статическая переменная.
Может, дело в этом?
ARV
Цитата(sonycman @ Nov 24 2008, 22:26) *
Попробовал и с -gc-sections. Вроде без разницы.
Полный текст командной строки линкера:
Код
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref --gc-sections --relax -mmcu=atmega88
если уж вы передаете разные параметры компоновщику, то указывайте тогда их все через запятую
Код
avr-g++ -Wl,-Map,FanController.map,--cref,-gc-sections --relax -mmcu=atmega88

и снова: почему у вас 2 минуса перед gc-sections? разве 2 или 1 - это все равно? и, при всем при том, где -ffunction-sections? они же в паре должны быть. я так понимаю:
Код
avr-g++ -ffunction-sections -Wl,-gc-sections,-Map,FanController.map,--cref --relax -mmcu=atmega88
sonycman
Цитата(ARV @ Nov 25 2008, 00:00) *
Код
avr-g++ -ffunction-sections -Wl,-gc-sections,-Map,FanController.map,--cref --relax -mmcu=atmega88

Дело в том, что я компилирую через Эклипс Ганимед.
И там доп. опции куда хочешь не добавить - только в специально отведённую для этого строку.

Есть там вот что:
Код
Expert settings.
Command line pattern:
${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}

Может, это и есть очерёдность ввода параметров?

Вот что у меня теперь:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -ffunction-sections -gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
Finished building target: FanController.elf
Сергей Борщ
Цитата(sonycman @ Nov 24 2008, 22:23) *
Вот что у меня теперь:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -ffunction-sections -gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
Finished building target: FanController.elf
Добавьте -Wl, перед -gc-sections. Странно, что для линковки вызывается avr-g++ а не avr-gcc или avr-ld
sonycman
Цитата(Сергей Борщ @ Nov 25 2008, 01:14) *
Добавьте -Wl, перед -gc-sections. Странно, что для линковки вызывается avr-g++ а не avr-gcc или avr-ld

Так выдаёт ошибку:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -ffunction-sections Wl,-gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
avr-g++.exe: Wl,-gc-sections: No such file or directory
make: *** [FanController.elf] Error 1

А что вообще означают опции Wl и -gc-sections? rolleyes.gif
ARV
-ff-sections заставляет компилятор размещать код каждой функции в отдельной секции памяти.
-gc-sections заставляет компоновщик удалять все секции кода, на которые нет ссылок из других секций.
в итоге все функции, которые в тексте определены, но ни разу не вызваны, удаляются из результирующего кода, чем уменьшается его размер.
-Wl, - это опция, которая указывает, что компоновщику надо передать список следующих опций (список разделяется запятыми). При этом в списке недопустимы пробелы, которые обозначают конец списка. Именно по этому лучше тупо писать -Wl,-gc-sections , чем добавлять опции к командной строке иными способами.

для Eclipse Ganimed (которым я и сам пользуюсь) имеется плагин, специально предназначенный для работы с AVR, по-моему, он называется avreclipse. Этот плагин имеет "очеловеченный" интерфейс к avr-gcc, который позволяет большинство опций задавать "галочками", а для остальных есть поля ручного ввода. При ручном вводе опции просто добавляются, проблем не возникает. И никаких заморочек с make-файлами, все автоматически делается. Рекомендую.

P.S. по непонятным причинам, результат компиляции одного и того же проекта с одинаковыми (во всяком случае, с моей точки зрения) опциями в AVR Studio и Eclipse получается (у меня) разным! Второй - меньшего размера smile.gif подозреваю, что дело в том, в каком порядке компилируются файлы проекта (порядок реально разный получается)...
msalov
Цитата(sonycman @ Nov 25 2008, 01:36) *
Так выдаёт ошибку:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -ffunction-sections Wl,-gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
avr-g++.exe: Wl,-gc-sections: No such file or directory
make: *** [FanController.elf] Error 1

А что вообще означают опции Wl и -gc-sections? rolleyes.gif

Для того, что бы удалялись неиспользуемые функции -ffunction-sections надо передавать компилятору, а --gc-sections -- линкеру/компоновщику.
А ошибка в том, что вы забыли "-" перед Wl,-gc-sections
sonycman
Цитата(gotty @ Nov 25 2008, 11:39) *
Для того, что бы удалялись неиспользуемые функции -ffunction-sections надо передавать компилятору, а --gc-sections -- линкеру/компоновщику.
А ошибка в том, что вы забыли "-" перед Wl,-gc-sections


Спасибо. Просто мне посоветовали -ffunction-sections тоже передать линкеру smile.gif

В общем - сделал вот так:
Компилятор:
Код
Invoking: AVR C++ Compiler
avr-g++ -I"F:\Electronics\Projects\GNU\FanController\Headers" -Wall -g2 -gdwarf-2 -Os -fpack-struct -fshort-enums -mcall-prologues -std=gnu++98 -funsigned-char -funsigned-bitfields -fno-exceptions -fno-threadsafe-statics -fno-inline-small-functions -ffunction-sections -mmcu=atmega88 -DF_CPU=10000000UL -MMD -MP -MF"Sources/pwm.d" -MT"Sources/pwm.d" -c -o"Sources/pwm.o" "../Sources/pwm.cpp"
Finished building: ../Sources/pwm.cpp

Линкер:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -Wl,-gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
Finished building target: FanController.elf

Код проекта сразу уменьшился на 600 байт!
Надо проверить, что он там повыкидывал... yeah.gif

ЗЫ: а ничего, что --relax передаётся без запятой?

Цитата(ARV @ Nov 25 2008, 09:56) *
-ff-sections заставляет компилятор размещать код каждой функции в отдельной секции памяти.
-gc-sections заставляет компоновщик удалять все секции кода, на которые нет ссылок из других секций.
в итоге все функции, которые в тексте определены, но ни разу не вызваны, удаляются из результирующего кода, чем уменьшается его размер.
-Wl, - это опция, которая указывает, что компоновщику надо передать список следующих опций (список разделяется запятыми). При этом в списке недопустимы пробелы, которые обозначают конец списка. Именно по этому лучше тупо писать -Wl,-gc-sections , чем добавлять опции к командной строке иными способами.

для Eclipse Ganimed (которым я и сам пользуюсь) имеется плагин, специально предназначенный для работы с AVR, по-моему, он называется avreclipse. Этот плагин имеет "очеловеченный" интерфейс к avr-gcc, который позволяет большинство опций задавать "галочками", а для остальных есть поля ручного ввода. При ручном вводе опции просто добавляются, проблем не возникает. И никаких заморочек с make-файлами, все автоматически делается. Рекомендую.


Спасибо за разъяснение.
У меня установлен именно avreclipse плагин, версии 2.3.0.
ARV
Цитата(sonycman @ Nov 25 2008, 12:41) *
Надо проверить, что он там повыкидывал... yeah.gif
практика показывает, что ничего полезного не выкидывает smile.gif
msalov
Цитата(sonycman @ Nov 25 2008, 11:41) *
Линкер:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -Wl,-gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
Finished building target: FanController.elf

Код проекта сразу уменьшился на 600 байт!
Надо проверить, что он там повыкидывал... yeah.gif

ЗЫ: а ничего, что --relax передаётся без запятой?

Ничего страшного, по моим представлениям запись вида -Wl,--gc-sections вместо --gc-sections используется лиш в случае совмещения компиляции и скомпоновки, аналогично и --relax, и -Map и все остальные параметры компоновщика.
Что бы проверить, какие секции были выкинуты сборщиком мусора, можно добавить параметр --print-gc-sections
sonycman
Цитата(ARV @ Nov 25 2008, 13:52) *
практика показывает, что ничего полезного не выкидывает smile.gif

Проверил - всё в порядке, выкинул только не используемые функции smile.gif
Прекрасно!

Цитата(gotty @ Nov 25 2008, 14:01) *
Ничего страшного, по моим представлениям запись вида -Wl,--gc-sections вместо --gc-sections используется лиш в случае совмещения компиляции и скомпоновки, аналогично и --relax, и -Map и все остальные параметры компоновщика.
Что бы проверить, какие секции были выкинуты сборщиком мусора, можно добавить параметр --print-gc-sections

А я уже листинги сравнил тотал коммандером laughing.gif
Понятно, значит, --relax оставляю как есть.
alx2
Привет, sonycman!
Цитата(sonycman @ Nov 24 2008, 16:05) *
Также в коде есть одна немаленькая inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия?
Можно тестовый пример с версией и опциями компилятора?

Цитата(sonycman @ Nov 24 2008, 16:05) *
За короткое время знакомства с GCC понравилось: удобная реализация атомарности через макросы ATOMIC_BLOCK(), удобная обёртка для обработчиков прерываний вида ISR(vector_name){}.
Так это как раз не компилятор, это просто библиотечные макросы. Аналогичные макросы можно сделать для любого минимально вменяемого компилятора.

Цитата(sonycman @ Nov 24 2008, 16:05) *
Видно, что для деления вызывается подпрограмма библиотеки.
Почему не используется простой сдвиг?
Видимо, потому что простой сдвиг не умещается в три процессорные инструкции. При отличной от -Os оптимизации генерится именно сдвиг.

Цитата(sonycman @ Nov 24 2008, 16:05) *
А где можно почитать про флаги для оптимизации?
В документации, естественно: http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gc...ptimize-Options

Цитата(sonycman @ Nov 24 2008, 16:05) *
А что вообще означают опции Wl и -gc-sections?
Все описано в мануалах. Про эти две тебе уже ответили, но в мануалах есть еще не один десяток опций, о которых полезно знать...

Цитата(MrYuran @ Nov 24 2008, 19:22) *
Я обычно ручками пишу <<1 (2,3,etc) или >>1, не надеясь на компилятор.
И зря. "x / 2" и "x >> 1" - не эквивалентные операции. Они дают гарантированно одинаковый результат только для положительного x. Но в этом случае (если x имеет тип unsigned) gcc и код генерит одинаковый (со сдвигом), и нет никакого смысла писать одно вместо другого. У sonycman же, судя по приведенному им примеру, левый операнд имеет знаковый тип.
ARV
Цитата(sonycman @ Nov 25 2008, 13:42) *
Понятно, значит, --relax оставляю как есть.
на сколько я смог понять их документации, --relax для платформы AVR смысла не имеет. экспериментальная проверка на своем проекте подтвердила - 0 байт изменений с и без --relax
aesok
Цитата(ARV @ Nov 26 2008, 10:22) *
на сколько я смог понять их документации, --relax для платформы AVR смысла не имеет. экспериментальная проверка на своем проекте подтвердила - 0 байт изменений с и без --relax


На каком контроллере Вы проверяли, размер программы?

Анатолий.
ARV
Цитата(aesok @ Nov 26 2008, 12:39) *
На каком контроллере Вы проверяли, размер программы?

Анатолий.

проверял на контроллере AT90CAN128, программа в итоге получается 14606 байт. всего линкуется почти десяток объектников.
sonycman
Цитата(alx2 @ Nov 26 2008, 01:58) *
Привет, sonycman!
Можно тестовый пример с версией и опциями компилятора?

Привет smile.gif
Сейчас у меня вот такие опции:
Компилятор:
Код
Building file: ../Sources/main.cpp
Invoking: AVR C++ Compiler
avr-g++ -I"F:\Electronics\Projects\GNU\FanController\Headers" -Wall -g2 -gdwarf-2 -Os -fpack-struct -fshort-enums -mcall-prologues -std=gnu++98 -funsigned-char -funsigned-bitfields -fno-exceptions -fno-threadsafe-statics -fno-inline-small-functions -ffunction-sections -mmcu=atmega88 -DF_CPU=10000000UL -MMD -MP -MF"Sources/main.d" -MT"Sources/main.d" -c -o"Sources/main.o" "../Sources/main.cpp"

и линкер:
Код
Building target: FanController.elf
Invoking: AVR C++ Linker
avr-g++ -Wl,-Map,FanController.map,--cref -Wl,-gc-sections --relax -mmcu=atmega88 -o"FanController.elf"  ./Sources/CFanRegulator.o ./Sources/CTimer.o ./Sources/DS18S20.o ./Sources/Interrupts.o ./Sources/LCD.o ./Sources/USART.o ./Sources/debug.o ./Sources/generic.o ./Sources/main.o ./Sources/pwm.o  
Finished building target: FanController.elf

С включением опции -Wl,-gc-sections неиспользуемая копия встраиваемой функции исчезла.
Всё заработало правильно!

Цитата
Видимо, потому что простой сдвиг не умещается в три процессорные инструкции. При отличной от -Os оптимизации генерится именно сдвиг.

И зря. "x / 2" и "x >> 1" - не эквивалентные операции. Они дают гарантированно одинаковый результат только для положительного x. Но в этом случае (если x имеет тип unsigned) gcc и код генерит одинаковый (со сдвигом), и нет никакого смысла писать одно вместо другого. У sonycman же, судя по приведенному им примеру, левый операнд имеет знаковый тип.

Да. Операнд имеет тип signed char. Именно в этом, видимо, и вся проблема. Спасибо, теперь я понял smile.gif
Rst7
Цитата(alx2 @ Nov 25 2008, 23:58) *
И зря. "x / 2" и "x >> 1" - не эквивалентные операции. Они дают гарантированно одинаковый результат только для положительного x.

Да ну? Где Вы это вычитали? Сдвиг в Си для знаковых операндов всю жизнь был арифметический, с учетом знака.

А вот то, что гнусь не поставил команду ASR, а позвал деление - это непонятно.
alx2
Цитата(sonycman @ Nov 26 2008, 15:17) *
Сейчас у меня вот такие опции:
Хм. Странно. Ничего подозрительного не вижу, и на простых примерах воспроизвести не могу. Не генерится у меня код для таких функций...


Цитата(Rst7 @ Nov 26 2008, 17:46) *
Да ну? Где Вы это вычитали? Сдвиг в Си для знаковых операндов всю жизнь был арифметический, с учетом знака.
Читаем первоисточник:
Цитата
The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has
an unsigned type or if E1 has a signed type and a nonnegative value, the value of the
result is the integral part of the quotient of E1 / 2^E2. If E1 has a signed type and a
negative value, the resulting value is implementation-defined.

Цитата
When integers are divided, the result of the / operator is the
algebraic quotient with any fractional part discarded. 87)
.....
87) This is often called ``truncation toward zero''.
Обрати внимание, что результат оператора / - не есть целая часть арифметического частного.
И в качестве упражнения тестовый пример:
Код
alx2% cat test.c
#include <stdio.h>
int main(void)
{
    int x = -5;
    return printf("%d, %d\n", x / 2, x >> 1);
}
alx2% gcc -O2 -o t test.c
alx2% ./t
-2, -3


Цитата(Rst7 @ Nov 26 2008, 17:46) *
А вот то, что гнусь не поставил команду ASR, а позвал деление - это непонятно.
Попробуй привести код со сдвигами, выполняющий деление значения типа int (уже находящегося в регистрах) на 2, который был бы не длиннее трех машинных инструкций (напоминаю, что sonycman просил оптимизировать по размеру кода).
Rst7
Цитата(alx2 @ Nov 26 2008, 22:20) *
-2, -3

Однако, сдвиг действительно арифметический. Более подробно посмотрю этот момент, когда попаду за комп, потому как с трубы лень искать. Возможно, я и не прав.

Цитата
Попробуй привести код со сдвигами, выполняющий деление значения типа int (уже находящегося в регистрах) на 2, который был бы не длиннее трех машинных инструкций (напоминаю, что sonycman просил оптимизировать по размеру кода).

Если уж быть точным, там тип signed char у sonycman, так что inc rx, asr rx вполне бы прошло.
alx2
Цитата(Rst7 @ Nov 27 2008, 01:45) *
Если уж быть точным, там тип signed char у sonycman, так что inc rx, asr rx вполне бы прошло.
Если быть совсем точным, smile.gif во-первых, sonycman, к сожалению, не привел деклараций переменных. Во-вторых, там на два делилась разность двух целых значений, которая уже в один байт не помещается. В-третьих, даже если бы не было разности, простого inc rx, asr rx недостаточно: оно дает неверные значения для нечетных положительных чисел. Например, 1/2 даст 1, тогда как должно быть 0. Как минимум, требуется еще sbrs rx,7 в начале, что дает все те же три инструкции...
Rst7
Цитата(alx2 @ Nov 26 2008, 23:35) *
оно дает неверные значения для нечетных положительных чисел. Например, 1/2 даст 1, тогда как должно быть 0. Как минимум, требуется еще sbrs rx,7 в начале, что дает все те же три инструкции...

Да. Согласен. Чтото я погорячился.

Правда про signed char упомянуто. На предыдущей странице.
sonycman
Цитата(Rst7 @ Nov 27 2008, 00:45) *
Однако, сдвиг действительно арифметический. Более подробно посмотрю этот момент, когда попаду за комп, потому как с трубы лень искать. Возможно, я и не прав.
Если уж быть точным, там тип signed char у sonycman, так что inc rx, asr rx вполне бы прошло.

Да, там у меня char:
Код
#define    LCD_WIDTH 96
byte  lcdGetStringWidth(PGM_P text);

void  lcdPrintText(PGM_P text, byte flags, signed char x, signed char y)
  switch(flags & 0xe0)
  {
  case  TXT_CENTERED:
    if (x < 0)
    {
      x = (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2;
    }
...

Но смысла в нём мало - всё равно всё тупо "растягивается" до 16-ти бит crying.gif ... даже switch:
Код
{
  switch(flags & 0xe0)
     e3a:    70 e0           ldi    r23, 0x00; 0
     e3c:    60 7e           andi    r22, 0xE0; 224
     e3e:    70 70           andi    r23, 0x00; 0
     e40:    60 38           cpi    r22, 0x80; 128
     e42:    71 05           cpc    r23, r1
     e44:    21 f0           breq    .+8      ; 0xe4e <lcdPrintText(char const*, unsigned char, signed char, signed char)+0x26>
     e46:    60 3c           cpi    r22, 0xC0; 192
     e48:    71 05           cpc    r23, r1
     e4a:    11 f5           brne    .+68     ; 0xe90 <lcdPrintText(char const*, unsigned char, signed char, signed char)+0x68>
     e4c:    1d c0           rjmp    .+58     ; 0xe88 <lcdPrintText(char const*, unsigned char, signed char, signed char)+0x60>
     e4e:    c4 2f           mov    r28, r20
     e50:    dd 27           eor    r29, r29
     e52:    c7 fd           sbrc    r28, 7
     e54:    d0 95           com    r29
  {
  case  TXT_CENTERED:
    if (x < 0)
     e56:    47 ff           sbrs    r20, 7
     e58:    0c c0           rjmp    .+24     ; 0xe72 <lcdPrintText(char const*, unsigned char, signed char, signed char)+0x4a>
    {
      x = (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2;
     e5a:    da df           rcall    .-76     ; 0xe10 <lcdGetStringWidth(char const*)>
     e5c:    c0 5a           subi    r28, 0xA0; 160
     e5e:    df 4f           sbci    r29, 0xFF; 255
     e60:    9e 01           movw    r18, r28
     e62:    28 1b           sub    r18, r24
     e64:    31 09           sbc    r19, r1
     e66:    c9 01           movw    r24, r18
     e68:    62 e0           ldi    r22, 0x02; 2
     e6a:    70 e0           ldi    r23, 0x00; 0
     e6c:    17 d5           rcall    .+2606   ; 0x189c <__divmodhi4>
     e6e:    16 2f           mov    r17, r22
     e70:    0f c0           rjmp    .+30     ; 0xe90 <lcdPrintText(char const*, unsigned char, signed char, signed char)+0x68>
    }

Одно хоть радует - при вызове функции есть хоть какая-то польза от восьмибитных параметров - их быстрее загружать smile.gif
А дальше всё равно бессмысленное "растягивание" до int... и впустую потраченное время и место во флэш.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.