|
Перенос кода из под ИАРа на WinAVR, возникают некоторые вопросы... |
|
|
|
Nov 23 2008, 17:23
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
Цитата(sonycman @ Nov 23 2008, 20:09)  А может кто подскажет, где поискать документацию по AVR GCC в виде PDF? А то не очень удобно пользоваться html версией...  как правило, она находится в папке WinAVR\doc\avr-libc - я ведь верно понял, что интересует документация на avr-libc?
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Nov 23 2008, 18:03
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(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 и С++ класс дали при компиляции тождественный код.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 23 2008, 19:09
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(ARV @ Nov 23 2008, 21:23)  как правило, она находится в папке WinAVR\doc\avr-libc - я ведь верно понял, что интересует документация на avr-libc? Нет, на библиотеку как раз pdf есть. Мне бы на компилятор, со всеми его ключевыми словами, аттрибутами и т.д... Она там только в формате html... в принципе, сойдёт, если нет лучшего Цитата(ReAl @ Nov 23 2008, 22:03)  Очень симпатично сделано... Учтено всё. Спасибо за разъяснение, стало понятнее! Сам только недавно начал осваивать С++, и имею небольшое представление о классах. Очень интересная штука У меня возникла очередная проблема в портировании проекта 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' Что-то я пока что в тупике. Описания такой функции нет в исходниках компилятора. Что делать? Почему так получилось?
|
|
|
|
|
Nov 23 2008, 20:38
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(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) И переносимо, и без хитрых атрибутов? Может я чего-то не понял. Или есть какой-то потаёный от меня смысл?
|
|
|
|
|
Nov 23 2008, 21:37
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(sonycman @ Nov 23 2008, 21:09)  то есть при создании объекта класса выдаётся ошибка: undefined reference to `__cxa_guard_release' Что-то я пока что в тупике. Описания такой функции нет в исходниках компилятора. Что делать? Почему так получилось?  Странно. К тому коду было добавлено Код 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 можно найти объяснения.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 23 2008, 21:37
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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
|
|
|
|
|
Nov 23 2008, 21:43
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(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
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 23 2008, 21:45
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(sonycman @ Nov 23 2008, 21:09)  то есть при создании объекта класса выдаётся ошибка: undefined reference to `__cxa_guard_release' Полный ответ здесь. Но я на это не нарывался, видимо потому, что использую --ffunction-section и --gc-sections. Я и узнал-то об этих функциях только из статьи по ссылке.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 23 2008, 21:51
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(sonycman @ Nov 23 2008, 23:37)  А если объявить tmrSpinup как static? Есть информация, что некоторые статические объекты вызывают такую ошибку в новых версиях компилятора, так как отсутствует какая-то библиотека... Решилось добавлением ключа -fno-threadsafe-statics Так у меня она static и объявлена, вероятно, у меня достаточно старые версии :-) За ключик спасибо, добавлю как упреждающий манёвр. Цитата(Сергей Борщ @ Nov 23 2008, 23:45)  У меня из дому какие-то проблемы именно с этим сайтом :-( По прямому линку заходит, но страшно медленно, а вот на вторую страницу какой-то темы зайти уже нереально. Поэтому я его практически не смотрю, на работе как правило не до интернета. При этом всё остальное дома "летает", как и положено adsl2+, многое быстрее, чем на работе. Так что гляну уже после "типоотпуска".
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Nov 23 2008, 21:54
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 23 2008, 22:09)  А может кто подскажет, где поискать документацию по AVR GCC в виде PDF? А то не очень удобно пользоваться html версией...  А на родной страничке http://gcc.gnu.org/onlinedocs/ ссылки "also in PDF" - это разве не оно?
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 24 2008, 07:35
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(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++)
|
|
|
|
|
Nov 24 2008, 11:05
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Портировал я свою программу с 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; жаль, не догадался компилер использовать указатель А зря. Итог - внушительный, в два раза больший, размер функции. Также в коде есть одна немаленькая inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия? За короткое время знакомства с GCC понравилось: удобная реализация атомарности через макросы ATOMIC_BLOCK(), удобная обёртка для обработчиков прерываний вида ISR(vector_name){}. Что не понравилось: гиморрой с обработкой данных, расположенных во флеш - для их чтения необходимо использовать специальный макрос pgm_read_...(). В ИАРе достаточно объявить указатель типа __flash и можно работать с ним совершенно обычным образом Также не очень удобно каждый раз пользоваться PSTR() для обозначения in-line строк вида: printText(PSTR("some")). ИАР тут опять на высоте со своим __flash, так как подобная функция в нём декларируется как printText(char const __flash __flash * pointer), и вызов делается просто: printText("that`s cool")
|
|
|
|
|
Nov 24 2008, 11:35
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 24 2008, 14:05)  Также в коде есть одна немаленькая inline функция, которая используется всего один раз (и то, понятно, инлайнится в тело вызывающей функции), но непонятно, почему остаётся её вторая копия? Добавте при обьявлении этой функции static. Анатолий.
|
|
|
|
|
Nov 24 2008, 11:56
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(sonycman @ Nov 24 2008, 14:05)  и вызов делается просто: printText("that`s cool")  Я уже описал как добиться того же результата и в 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
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Nov 24 2008, 11:57
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aesok @ Nov 24 2008, 15:35)  Добавте при обьявлении этой функции static.
Анатолий. У меня она так и объявлена: Код static inline void ReadButtons(void) __attribute__ ((always_inline));
static inline void ReadButtons(void) {} Результата нет - всё равно две копии
|
|
|
|
|
Nov 24 2008, 14:15
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Интересно, почему при делении на два не всегда используются оптимизации? Например: Код 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> Видно, что для деления вызывается подпрограмма библиотеки. Почему не используется простой сдвиг?
|
|
|
|
|
Nov 24 2008, 14:22
|

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

|
Цитата(sonycman @ Nov 24 2008, 17:15)  Интересно, почему при делении на два не всегда используются оптимизации? Видно, что для деления вызывается подпрограмма библиотеки. Почему не используется простой сдвиг?  Я обычно ручками пишу <<1 (2,3,etc) или >>1, не надеясь на компилятор. А вообще от флагов оптимизации зависит.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Nov 24 2008, 14:46
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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[]^";
|
|
|
|
|
Nov 24 2008, 15:58
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(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";
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Nov 24 2008, 16:05
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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"; Спасибо, попробовал. Но всё осталось по прежнему...
|
|
|
|
|
Nov 24 2008, 19:07
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 24 2008, 14:57)  У меня она так и объявлена: Код static inline void ReadButtons(void) __attribute__ ((always_inline));
static inline void ReadButtons(void) {} Результата нет - всё равно две копии  Почитайте про static inline функции здесь: http://www.greenend.org.uk/rjk/2003/03/inline.htmlС ними есть какието тонкости, у меня сейчас нет времени вникать. Анатолий.
|
|
|
|
|
Nov 24 2008, 19:26
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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С ними есть какието тонкости, у меня сейчас нет времени вникать. Анатолий. Спасибо, ознакомлюсь.  ЗЫ: Хм, написано, что инлайн функция не должна иметь статических переменных или обращаться к таковым... а у меня внутри есть статическая переменная. Может, дело в этом?
|
|
|
|
|
Nov 24 2008, 20:00
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
Цитата(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
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Nov 24 2008, 20:23
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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
|
|
|
|
|
Nov 24 2008, 21:14
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(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
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 24 2008, 23:36
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(Сергей Борщ @ 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?
|
|
|
|
|
Nov 25 2008, 05:56
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
-ff-sections заставляет компилятор размещать код каждой функции в отдельной секции памяти. -gc-sections заставляет компоновщик удалять все секции кода, на которые нет ссылок из других секций. в итоге все функции, которые в тексте определены, но ни разу не вызваны, удаляются из результирующего кода, чем уменьшается его размер. -Wl, - это опция, которая указывает, что компоновщику надо передать список следующих опций (список разделяется запятыми). При этом в списке недопустимы пробелы, которые обозначают конец списка. Именно по этому лучше тупо писать -Wl,-gc-sections , чем добавлять опции к командной строке иными способами. для Eclipse Ganimed (которым я и сам пользуюсь) имеется плагин, специально предназначенный для работы с AVR, по-моему, он называется avreclipse. Этот плагин имеет "очеловеченный" интерфейс к avr-gcc, который позволяет большинство опций задавать "галочками", а для остальных есть поля ручного ввода. При ручном вводе опции просто добавляются, проблем не возникает. И никаких заморочек с make-файлами, все автоматически делается. Рекомендую. P.S. по непонятным причинам, результат компиляции одного и того же проекта с одинаковыми (во всяком случае, с моей точки зрения) опциями в AVR Studio и Eclipse получается (у меня) разным! Второй - меньшего размера  подозреваю, что дело в том, в каком порядке компилируются файлы проекта (порядок реально разный получается)...
Сообщение отредактировал ARV - Nov 25 2008, 06:02
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Nov 25 2008, 07:39
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(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?  Для того, что бы удалялись неиспользуемые функции -ffunction-sections надо передавать компилятору, а --gc-sections -- линкеру/компоновщику. А ошибка в том, что вы забыли "-" перед Wl,-gc-sections
|
|
|
|
|
Nov 25 2008, 09:41
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(gotty @ Nov 25 2008, 11:39)  Для того, что бы удалялись неиспользуемые функции -ffunction-sections надо передавать компилятору, а --gc-sections -- линкеру/компоновщику. А ошибка в том, что вы забыли "-" перед Wl,-gc-sections Спасибо. Просто мне посоветовали -ffunction-sections тоже передать линкеру В общем - сделал вот так: Компилятор: Код 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 байт! Надо проверить, что он там повыкидывал... ЗЫ: а ничего, что --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.
|
|
|
|
|
Nov 25 2008, 10:01
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(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 байт! Надо проверить, что он там повыкидывал... ЗЫ: а ничего, что --relax передаётся без запятой? Ничего страшного, по моим представлениям запись вида -Wl,--gc-sections вместо --gc-sections используется лиш в случае совмещения компиляции и скомпоновки, аналогично и --relax, и -Map и все остальные параметры компоновщика. Что бы проверить, какие секции были выкинуты сборщиком мусора, можно добавить параметр --print-gc-sections
|
|
|
|
|
Nov 25 2008, 10:42
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(ARV @ Nov 25 2008, 13:52)  практика показывает, что ничего полезного не выкидывает  Проверил - всё в порядке, выкинул только не используемые функции Прекрасно! Цитата(gotty @ Nov 25 2008, 14:01)  Ничего страшного, по моим представлениям запись вида -Wl,--gc-sections вместо --gc-sections используется лиш в случае совмещения компиляции и скомпоновки, аналогично и --relax, и -Map и все остальные параметры компоновщика. Что бы проверить, какие секции были выкинуты сборщиком мусора, можно добавить параметр --print-gc-sections А я уже листинги сравнил тотал коммандером Понятно, значит, --relax оставляю как есть.
|
|
|
|
|
Nov 25 2008, 21:58
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Привет, 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 же, судя по приведенному им примеру, левый операнд имеет знаковый тип.
Сообщение отредактировал alx2 - Nov 25 2008, 21:47
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 26 2008, 09:39
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(ARV @ Nov 26 2008, 10:22)  на сколько я смог понять их документации, --relax для платформы AVR смысла не имеет. экспериментальная проверка на своем проекте подтвердила - 0 байт изменений с и без --relax На каком контроллере Вы проверяли, размер программы? Анатолий.
|
|
|
|
|
Nov 26 2008, 09:50
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
Цитата(aesok @ Nov 26 2008, 12:39)  На каком контроллере Вы проверяли, размер программы?
Анатолий. проверял на контроллере AT90CAN128, программа в итоге получается 14606 байт. всего линкуется почти десяток объектников.
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Nov 26 2008, 10:17
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(alx2 @ Nov 26 2008, 01:58)  Привет, sonycman! Можно тестовый пример с версией и опциями компилятора? Привет Сейчас у меня вот такие опции: Компилятор: Код 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. Именно в этом, видимо, и вся проблема. Спасибо, теперь я понял
|
|
|
|
|
Nov 26 2008, 20:20
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(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 просил оптимизировать по размеру кода).
Сообщение отредактировал alx2 - Nov 26 2008, 21:00
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 26 2008, 20:45
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(alx2 @ Nov 26 2008, 22:20)  -2, -3 Однако, сдвиг действительно арифметический. Более подробно посмотрю этот момент, когда попаду за комп, потому как с трубы лень искать. Возможно, я и не прав. Цитата Попробуй привести код со сдвигами, выполняющий деление значения типа int (уже находящегося в регистрах) на 2, который был бы не длиннее трех машинных инструкций (напоминаю, что sonycman просил оптимизировать по размеру кода). Если уж быть точным, там тип signed char у sonycman, так что inc rx, asr rx вполне бы прошло.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Nov 26 2008, 21:35
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

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

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(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-ти бит  ... даже 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> } Одно хоть радует - при вызове функции есть хоть какая-то польза от восьмибитных параметров - их быстрее загружать А дальше всё равно бессмысленное "растягивание" до int... и впустую потраченное время и место во флэш.
|
|
|
|
|
Nov 27 2008, 04:14
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 27 2008, 01:02)  Но смысла в нём мало - всё равно всё тупо "растягивается" до 16-ти бит  ... даже switch: 6.8.4.2 The switch statement Constraints 1 The controlling expression of a switch statement shall have integer type. Цитата(sonycman @ Nov 27 2008, 01:02)  А дальше всё равно бессмысленное "растягивание" до int... и впустую потраченное время и место во флэш. x = (unsigned char )(LCD_WIDTH + x - lcdGetStringWidth(text)) / 2 Код 31 000a 4983 std Y+1,r20 32 .LVL1: 33 000c 0E94 0000 call lcdGetStringWidth 34 .LVL2: 35 0010 4981 ldd r20,Y+1 36 0012 405A subi r20,lo8(-(96)) 37 .LVL3: 38 0014 481B sub r20,r24 39 0016 4695 lsr r20 40 0018 4093 0000 sts xx,r20 Анатолий.
Сообщение отредактировал aesok - Nov 27 2008, 04:15
|
|
|
|
|
Nov 27 2008, 08:51
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(Rst7 @ Nov 27 2008, 09:56)  Аналогично есть вопросы к выражению. Все операнды и результат 8 бит, а в середине выражение вычисляется через 16 бит. Чтото не так. Это называется Integer promotion - приведение char к int в промежуточных вычислениях, прописано в стандарте.
|
|
|
|
|
Nov 27 2008, 09:24
|
Знающий
   
Группа: Свой
Сообщений: 526
Регистрация: 24-08-07
Из: Беларусь, Минск
Пользователь №: 30 045

|
Цитата(Rst7 @ Nov 27 2008, 11:08)  Ссылку. Чтото я совсем потерялся. ISO/IEC 9899:19995.1.2.3 пункт 10 Цитата EXAMPLE 2 In executing the fragment Код char c1, c2; /* ... */ c1 = c1 + c2; the ‘‘integer promotions’’ require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum. Provided the addition of two chars can be done without overflow, or with overflow wrapping silently to produce the correct result, the actual execution need only produce the same result, possibly omitting the promotions. 6.3.1.1 пункт 2 Цитата If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.
48) The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.
|
|
|
|
|
Nov 27 2008, 09:45
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(sonycman @ Nov 27 2008, 12:28)  Но неужели слепое следование стандартам больших машин - это абсолютно правильно и на AVR? Следование стандартам ВСЕГДА правильно, а вот то, что int на восьмибитовике 1бит - вот это уже НЕ ПРАВИЛЬНОЕ (хотя не противоречащее стандарту) решение принятое когда-то производителями восьмибитовых компиляторов  , полагаю для тупой совместимости с массовыми на тот момент 16bit-овиками, дабы _бездумно_  портировать исходники с 16bit интами. Если int действительно имел максимально естественую для 8bit контроллера разрядность 8bit, то и проблем c этитм не было-бы, как их нет не 16/32bit платформах. Цитата А может существует какая-то опция, позволяющая отключать эту фичу? Гипотетически изменить размерность int в хидерах на 8bit и пресобрать все, включая библиотеки. Если авторы компилятора все делали правильно, то должно получиться. А вообще пора завязывать с 8bit
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Nov 27 2008, 10:04
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(zltigo @ Nov 27 2008, 13:45)  Следование стандартам ВСЕГДА правильно, а вот то, что int на восьмибитовике 1бит - вот это уже НЕ ПРАВИЛЬНОЕ (хотя не противоречащее стандарту) решение принятое когда-то производителями восьмибитовых компиляторов  , полагаю для тупой совместимости с массовыми на тот момент 16bit-овиками, дабы _бездумно_  портировать исходники с 16bit интами. Если int действительно имел максимально естественую для 8bit контроллера разрядность 8bit, то и проблем c этитм не было-бы, как их нет не 16/32bit платформах. Гипотетически изменить размерность int в хидерах на 8bit и пресобрать все, включая библиотеки. Если авторы компилятора все делали правильно, то должно получиться. А вообще пора завязывать с 8bit  Понятно, спасибо Оставим это как дань стандартам и одновременно лень производителей Почти во всём при компиляции своего проекта уже разобрался. Ещё вот беспокоят такие ворнинги: Код only initialized variables can be placed into program memory area на стоки, подобные вот этим: Код if (statusRx.lock_err) usartSendString(PSTR("Receiver LOCKED!"));
const char PROGMEM fntable[] = "!\"%`()*+,-./0123456789:;<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^"; то есть ругается на все PSTR() и на все PROGMEM. Чего такого неправильного я там сделал?
|
|
|
|
|
Nov 27 2008, 11:29
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 27 2008, 03:02)  Код void lcdPrintText(PGM_P text, byte flags, signed char x, signed char y) switch(flags & 0xe0) { ... Но смысла в нём мало - всё равно всё тупо "растягивается" до 16-ти бит  ... даже 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 Как ты получил такой код??? =8-( ) Вот такой тестовый пример: Код int do_something(void);
void fff(char flags) { switch(flags & 0xe0) { case 0x80: do_something(); } } у меня компилируется вот в такой код: Код fff: /* prologue: function */ /* frame size = 0 */ andi r24,lo8(-32) cpi r24,lo8(-128) brne .L4 rcall do_something .L4: ret при любой -O отличной от -O0. gcc-4.3.1. Скажи, пожалуйста, версию своего компилятора, с какой оптимизацией компилировался код и как определено byte.
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 27 2008, 11:35
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(sonycman @ Nov 27 2008, 12:04)  Понятно, спасибо Оставим это как дань стандартам и одновременно лень производителей  Стандарт не запрещает оптимизатору не расширять char до int если результат останется одинаковым. Или расширить, но потом все лишнее выкинуть. Цитата(sonycman @ Nov 27 2008, 12:04)  то есть ругается на все PSTR() и на все PROGMEM. Чего такого неправильного я там сделал?  Это не вы, это они намудрили в компиляторе.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 27 2008, 11:50
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(aesok @ Nov 27 2008, 09:14)  The controlling expression of a switch statement shall have integer type. И что? Тип char - тоже integer type, что не мешает ему иметь размер 8 бит... Тут проблема совсем в другом: результат выражения (flags & 0xe0) всегда дает нули во всех байтах кроме младшего. Поэтому вменяемый оптимизатор должен выкинуть их вычисление из генерируемого кода. У меня так и происходит (см. выше). Почему этого не происходит у sonycman, непонятно... Цитата(sonycman @ Nov 27 2008, 14:28)  Но неужели слепое следование стандартам больших машин - это абсолютно правильно и на AVR? Неужели в вышеприведённом примере столь необходимо было следовать такому стандарту? не "стандартам больших машин", а правилам языка C. Цитата(sonycman @ Nov 27 2008, 14:28)  А может существует какая-то опция, позволяющая отключать эту фичу? Поверь, тебе это не нужно. Я слышал о компиляторах, у которых такая опция есть. Но писать программы с рассчетом на такую опцию - значит писать их не на языке C. То есть сразу теряется совместимость с любыми другими C компиляторами, у которых такой опции нет. Плюс периодически ломать голову над тем, почему результат выражения получился вот такой, а не такой, какой ожидался. Ты хочешь, чтобы у тебя (127 + 127) / 2 давало -1?  И все ради чего? Ради экономии пары процентов машинного кода?
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 27 2008, 13:35
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(alx2 @ Nov 27 2008, 15:29)  Как ты получил такой код??? =8-( ) Скажи, пожалуйста, версию своего компилятора, с какой оптимизацией компилировался код и как определено byte. byte определён как unsigned char. Версия компилятора - 4.3.2 - последний релиз кандидат. Оптимизациия Оs. Полностью командные строки компилятора и линкера я приводил немного ранее. Странно всё это...
|
|
|
|
|
Nov 27 2008, 16:52
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(Rst7 @ Nov 27 2008, 10:56)  вычисляется через 16 бит. Чтото не так. Расмотрим пример (x = 20): #define LCD_WIDTH 240 void lcdPrintText(PGM_P text, byte flags, unsigned char x, signed char y) { x = (LCD_WIDTH + x ) / 2; } Если расчеты выполняються в 16-битном виде то: x = (240 + 20) / 2 = 260 /2 = 130. Если в 8-битной то: x = (240 + 20) / 2 = 4 /2 = 2. Вы все еще хотите такую оптимизацию? Анатолий.
|
|
|
|
|
Nov 27 2008, 17:18
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 27 2008, 20:03)  2aesok Если у меня в программе операнд имеет тип char, то это значит, что ни при каких условиях не будет достигнуто переполнение. Я вам привел пример когда оба операнда имеют тип char, и происходит переполнение. Цитата(sonycman @ Nov 27 2008, 20:03)  Следуя вашей логике, можно и операнды типа word обрабатывать, расширяя до double word... Компилятор это за вас не сделает, но если Вы не хотите получит ошибку переполнения, то Вы проанализируете исходные данные и формулы, и возжможно, да будете выполнять промежуточные расчеты с 32-битными числами, даже если операнды и результат 16-битные. Анатолий. Цитата(Rst7 @ Nov 27 2008, 20:08)  Только не надо спрашивать, как это - "в другом стиле", на форуме выложенно достаточно, чтобы оценить кривость моих рук  Компилятор тоже не может этого оценить. Но он стараеться генерировать правильный код в не зависимости от кривости рук програмиста, при этом код иногда получаеться более длинным. Анатолий.
|
|
|
|
|
Nov 27 2008, 19:49
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата Этот пример не имеет ничего общего с моим приложением. Как не трудно было заметить из моего примера, константа LCD_WIDTH всегда складывается с отрицательным числом. Плюс операнды имеют значения, не допускающие переполнения. Это Ваш пример? Код #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; } (LCD_WIDTH + x - lcdGetStringWidth(text)) = 96 + [-128..-1] - [0..255] = [95..-287] Это чисто попещаеться в signed char? В unsigned char? Цитата Насчёт кривости рук - что вы скажете про то, откуда берутся странные предупреждения об обязательной инициализации и так инициализированных строковых массивов, расположенных в памяти программ? Исходники GCC находяться здесь http://gcc.gnu.org/viewcvs/ Вы можете убрать мешающие Вам предупреждения сами. Анатолий.
Сообщение отредактировал aesok - Nov 27 2008, 19:54
|
|
|
|
|
Nov 27 2008, 21:52
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aesok @ Nov 27 2008, 23:49)  Это Ваш пример? Код #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; } (LCD_WIDTH + x - lcdGetStringWidth(text)) = 96 + [-128..-1] - [0..255] = [95..-287] Это чисто попещаеться в signed char? В unsigned char? Нет. Но такие величины никогда не будут получены, так как операнды имеют значения, не допускающие переполнения. Не надо выдёргивать из контекста. Цитата Исходники GCC находяться здесь http://gcc.gnu.org/viewcvs/ Вы можете убрать мешающие Вам предупреждения сами. Как? Я, у которого в программе переполнения на каждом шагу, должен исправлять глюки "умного" компилятора, который стараеться генерировать правильный код в не зависимости от кривости рук програмиста? Что-то мне кажется, что мы ведём непродуктивный диалог. После того, как было установлено, что приведение операндов к 16-ти битам делается для того, чтобы просто удовлетворять стандарту, вы хотите сказать, что это не так? Что на самом деле этот шаг направлен на предотвращение переполнения? Цитата(alx2 @ Nov 27 2008, 15:29)  Как ты получил такой код??? =8-( ) Вот такой тестовый пример: Код int do_something(void);
void fff(char flags) { switch(flags & 0xe0) { case 0x80: do_something(); } } у меня компилируется вот в такой код: Код fff: /* prologue: function */ /* frame size = 0 */ andi r24,lo8(-32) cpi r24,lo8(-128) brne .L4 rcall do_something .L4: ret при любой -O отличной от -O0. gcc-4.3.1. Скажи, пожалуйста, версию своего компилятора, с какой оптимизацией компилировался код и как определено byte. Добрался до компа. На всякий случай повторю повнятнее: У меня WinAVR 20081118rc2 с компилятором GCC 4.3.2. Оптимизация Os плюс ещё несколько "фишек". Командная строка компилятора: Код avr-g++ -I"F:\Electronics\Projects\GNU\FanController\Headers" -Wall -g2 -gdwarf-2 -Os -fpack-struct -fshort-enums -mcall-prologues -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" Скомпилировал твой пример, вот что получилось: Код void do_something(char b) { volatile static char a = b; }
void fff(char flags) { switch(flags & 0xe0) { case 0x80: do_something(0); case 0xc0: do_something(1); } }
int main( void ) {
fff(128); ... и листинг: Код void fff(char flags) { switch(flags & 0xe0) 15be: 90 e0 ldi r25, 0x00; 0 15c0: 80 7e andi r24, 0xE0; 224 15c2: 90 70 andi r25, 0x00; 0 15c4: 80 38 cpi r24, 0x80; 128 15c6: 91 05 cpc r25, r1 15c8: 21 f0 breq .+8 ; 0x15d2 <fff(char)+0x14> 15ca: 80 3c cpi r24, 0xC0; 192 15cc: 91 05 cpc r25, r1 15ce: 29 f4 brne .+10 ; 0x15da <fff(char)+0x1c> 15d0: 02 c0 rjmp .+4 ; 0x15d6 <fff(char)+0x18> { case 0x80: do_something(0); 15d2: 80 e0 ldi r24, 0x00; 0 15d4: e9 df rcall .-46 ; 0x15a8 <do_something(char)> case 0xc0: do_something(1); 15d6: 81 e0 ldi r24, 0x01; 1 15d8: e7 df rcall .-50 ; 0x15a8 <do_something(char)> 15da: 08 95 ret
|
|
|
|
|
Nov 27 2008, 22:22
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 28 2008, 00:52)  Нет. Но такие величины никогда не будут получены, так как операнды имеют значения, не допускающие переполнения. Не надо выдёргивать из контекста. Причем тут я? Я человек я мог бы Вас и понять. Я Вам показываю как думает тупая железка, тоесть компилятор. Где Вы ему сказали что " операнды имеют значения, не допускающие переполнения"? Я Вам показывал вариант как это ему сказать: x = ( byte) (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2; И компилятор начал вычислять выражение как 8-битное. Цитата(sonycman @ Nov 28 2008, 00:52)  Как? Я, у которого в программе переполнения на каждом шагу, должен исправлять глюки "умного" компилятора, который стараеться генерировать правильный код в не зависимости от кривости рук програмиста?  Я не говорил что у Вас кривые руки, Я говорил что компилятор должен компилировать программу написанную даже кривыми руками, при этом программа должна работать в соответствии со стандартом, а в стандарте никаких указазий о скорости выполнения программы нет. Это оставленно на осмотрение разработчика компилятора. Цитата(sonycman @ Nov 28 2008, 00:52)  После того, как было установлено, что приведение операндов к 16-ти битам делается для того, чтобы просто удовлетворять стандарту, вы хотите сказать, что это не так? Что на самом деле этот шаг направлен на предотвращение переполнения? Я попытался на примере Вам показать почему так написан стандарт. Цитата(sonycman @ Nov 28 2008, 00:52)  Добрался до компа. На всякий случай повторю повнятнее: Пока не знаю. Анатолий.
Сообщение отредактировал aesok - Nov 27 2008, 22:28
|
|
|
|
|
Nov 27 2008, 22:46
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aesok @ Nov 28 2008, 02:22)  Я попытался на примере Вам показать почему так написан стандарт. Анатолий. Хм... то есть, разработчики стандарта таким образом подстраховали все вычисления с операндами типа char? Мне всё равно трудно понять рациональность этого хода. Потому, что ошибка с переполнением может возникнуть с любым по размеру операндом: 8, 16, 32, 64 и так далее бит. Почему для 8-ми бит сделали исключение? Цитата (LCD_WIDTH + x - lcdGetStringWidth(text)) = 96 + [-128..-1] - [0..255] = [95..-287] Зачем "правильно" считать результат на 16-ти битах, если в конце концов он будет разрушен, будучи "обрезанным" и "запиханным" в signed char? Если программист не продумал все варианты - ошибка обязательно вылезет всё равно. Да... Но всё-же, спасибо за попытку всё объяснить. Извиняюсь за некоторое раздражение в моём тоне. В любом случае, стандарты придумывают весьма умные дядьки, не чета таким, как я Порой трудно бывает понять их мотивы. Воистину, пути их неисповедимы
|
|
|
|
|
Nov 27 2008, 23:20
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 28 2008, 01:46)  Хм... то есть, разработчики стандарта таким образом подстраховали все вычисления с операндами типа char? Мне всё равно трудно понять рациональность этого хода. Потому, что ошибка с переполнением может возникнуть с любым по размеру операндом: 8, 16, 32, 64 и так далее бит. Почему для 8-ми бит сделали исключение? На самом деле исключение сделано для int. Это как бы тип по умолчанию, разрядность int обычно равна разрядности регистров общего назначения, что гарантирует максимальную производительность для этого типа. Для AVR разрядность int больше разрядности регистра. Сделать int 8-битным нельзя, в стандарте требуется - размерность указателя равна размерности int, а 8-битный указатель это слишком мало. Если быть совсем точным то avr-gcc имеет ключ: -mint8 Use an 8-bit 'int' type Его практическая ценность его мала, avr-libc не совместима с этим ключом, и никогда не будет. Но если будете писать что нибудь маленькое, можете с ним поиграться. Цитата Зачем "правильно" считать результат на 16-ти битах, если в конце концов он будет разрушен, будучи "обрезанным" и "запиханным" в signed char? Если программист не продумал все варианты - ошибка обязательно вылезет всё равно. Потому что в вашем примере обрезание рузультата до char произойдеи уже после деления, а для того чтобы результат деления на 2 влез в char делимое может иметь значение до char * 2, и для его хранения нужен int. Цитата Порой трудно бывает понять их мотивы. Воистину, пути их неисповедимы На самом деле все очень просто, отладив несколько своих проектов Вы начнете думать как они... Анатолий.
Сообщение отредактировал aesok - Nov 27 2008, 23:26
|
|
|
|
|
Nov 28 2008, 00:34
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aesok @ Nov 28 2008, 03:20)  Потому что в вашем примере обрезание рузультата до char произойдеи уже после деления, а для того чтобы результат деления на 2 влез в char делимое может иметь значение до char * 2, и для его хранения нужен int. Ну, не думаю, что компилер будет строить такие предположения. Исходное положение: Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2; e54: e0 df rcall .-64; 0xe16 <lcdGetStringWidth(char const*)> e56: 21 2f mov r18, r17 e58: 33 27 eor r19, r19 e5a: 27 fd sbrc r18, 7 e5c: 30 95 com r19 e5e: 20 5a subi r18, 0xA0; 160 e60: 3f 4f sbci r19, 0xFF; 255 e62: a9 01 movw r20, r18 e64: 48 1b sub r20, r24 e66: 51 09 sbc r21, r1 e68: ca 01 movw r24, r20 e6a: 62 e0 ldi r22, 0x02; 2 e6c: 70 e0 ldi r23, 0x00; 0 e6e: 17 d5 rcall .+2606; 0x189e <__divmodhi4> Смотрите, я убираю деление совсем, и вот что имеем: Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)); e54: e0 df rcall .-64; 0xe16 <lcdGetStringWidth(char const*)> e56: 0a c0 rjmp .+20; 0xe6c <lcdPrintText(char const*, unsigned char, signed char, ... e6c: 10 5a subi r17, 0xA0; 160 e6e: 18 1b sub r17, r24 то есть весь код сводится к простым операциям. Заметьте - результат выражения по-прежнему будет: Код (LCD_WIDTH + x - lcdGetStringWidth(text)) = 96 + [-128..-1] - [0..255] = [95..-287] "невлезающим" в char, и "простая железка" может это просчитать как два пальца... Но не хочет То же самое будет, если вывести деление во вторую строку: Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)); e54: e0 df rcall .-64; 0xe16 <lcdGetStringWidth(char const*)> e56: 10 5a subi r17, 0xA0; 160 x /= 2; e58: 18 1b sub r17, r24 e5a: 81 2f mov r24, r17 e5c: 62 e0 ldi r22, 0x02; 2 e5e: 03 d5 rcall .+2566; 0x1866 <__divmodqi4> Первое и последнее выражения абсолютно эквивалентны по результату! Зато какие разные по размеру кода? Тут явно что-то в этом делении...  Не первый раз замечаю, что, чем проще и короче строка программы в си, тем лучше получается у компилятора код. Лучше разбивать одно длинное выражение на несколько простых. А так хочется извратиться позаковыристее, так, чтобы самому потом полчаса глаза таращить Задумывались бы чаще над этой проблемой те умные дядьки, которые всякие хитрые стандарты придумывают. Уже 21 век на дворе, а мы всё плюсик на одной строке, а минусик - только на следующей, а то ещё у железки голова закружится... Цитата(aesok @ Nov 28 2008, 03:20)  На самом деле все очень просто, отладив несколько своих проектов Вы начнете думать как они... Ну дай-то бог!  ЗЫ: Всем спокойной ночи!
|
|
|
|
|
Nov 28 2008, 01:08
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(sonycman @ Nov 28 2008, 02:34)  То же самое будет, если вывести деление во вторую строку: Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)); тем самым в процессе присваивания неявно приводя результат типа int к типу x, т.е. signed char, что и советовал вам aesok: Цитата(aesok @ Nov 28 2008, 00:22)  x = (byte) (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2; Так что, как видите, никаких чудес. Цитата(sonycman @ Nov 28 2008, 02:34)  А тут деление signed char x на int 2 тоже с приведением результата. Tут компилятор смог сообразить и отбросить расширение.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Nov 29 2008, 11:51
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 28 2008, 02:52)  Код switch(flags & 0xe0) { case 0x80: do_something(0); case 0xc0: do_something(1); } как показали эксперименты, как только в switch появляется второй case, в ассемблерном коде появляется вычисление незначащего старшего байта. Склоняюсь к выводу о недостатке оптимизатора в данном случае. Цитата(sonycman @ Nov 28 2008, 03:46)  Мне всё равно трудно понять рациональность этого хода. Потому, что ошибка с переполнением может возникнуть с любым по размеру операндом: 8, 16, 32, 64 и так далее бит. Почему для 8-ми бит сделали исключение? Зачем "правильно" считать результат на 16-ти битах, если в конце концов он будет разрушен, будучи "обрезанным" и "запиханным" в signed char? Если программист не продумал все варианты - ошибка обязательно вылезет всё равно. Я не знаю точно, из каких соображений язык C сделан именно так, но выскажу свои соображения по этом поводу. Во-первых, с последним утверждением согласен. Исходим из предположения, что кодер подумал, и ошибок в коде нет. Во-вторых, я подозреваю, что вероятность выхода результата неких вычислений за границы диапазона (-128...127) намного выше чем диапазона (-32768...32767). Поэтому кодеру очень часто пришлось бы писать явное приведение к типу int, например писать (0 + a + b) / 2 вместо (a + b) / 2. Лично мне это бы не понравилось, т.к. во-первых, больше текста - больше вероятность ошибиться, а во-вторых, ухудшается читаемость кода. Почему тип приводится к int, а не к long или long long - я думаю, это компромисс между удобством и эффективностью. Случаи, когда диапазон значений int для вычислений недостаточен - достаточно редки чтобы явное приведение к long (long) не доставляло больших неудобств. Наконец, что результат "в конце концов будет разрушен, будучи "обрезанным" и "запиханным" в signed char" - просто неверно, об этом aesok уже сказал. Цитата(sonycman @ Nov 28 2008, 05:34)  Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)) / 2; Код x = (LCD_WIDTH + x - lcdGetStringWidth(text)); x /= 2; Первое и последнее выражения абсолютно эквивалентны по результату! Не эквивалентны. В первом случае преобразование к типу char производится перед делением, во втором - после. Например при LCD_WIDTH=96, x=32 и lcdGetStringWidth(text)=0 результат этих выражений будет разным.
Сообщение отредактировал alx2 - Nov 29 2008, 11:53
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 29 2008, 21:02
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(alx2 @ Nov 29 2008, 15:51)  Поэтому кодеру очень часто пришлось бы писать явное приведение к типу int, например писать (0 + a +  / 2 вместо (a +  / 2. Лично мне это бы не понравилось, т.к. во-первых, больше текста - больше вероятность ошибиться, а во-вторых, ухудшается читаемость кода. Зачем часто писать явное приведение к int? Разумнее сразу иметь хотя-бы одну переменную размерности int, если обработка данных типа char приводит к образованию переполнения В моём случае (применительно к моему проекту) ситуация совсем обратная. Мне частенько приходится делать явное приведение к char Почему? Потому что я резонно предполагал более эффективную работу восьмибитного ядра именно с "родным" типом данных - с char. Поэтому старался иметь максимальное количество переменных такого типа. Цитата Не эквивалентны. В первом случае преобразование к типу char производится перед делением, во втором - после. Например при LCD_WIDTH=96, x=32 и lcdGetStringWidth(text)=0 результат этих выражений будет разным. Ты говоришь про общий случай. Я - про строку кода конкретной функции lcdPrintText, в которой делимое всегда лежит в пределах 0 <= d < 96. Поэтому эти выражения совершенно эквивалентны. Ты можешь возразить - а откуда это может знать компилятор? Я соглашусь - не может он этого знать, он просто "железяка", которой внушили, что она много знает, и которой время от времени приходится "выпрямлять руки". Но продолжим о "выражениях". Благодаря стандарту, первое выражение имеет в два с половиной раза больший объём кода. А если в программе таких выражений десятки? Тогда мы получаем в коде сотни байт ненужного мусора, что для маленьких кристаллов является актуальной проблемой. Что мы делаем? Мы делаем явное приведение типов/разбиваем выражения на несколько частей и т.д. и т.п., что, как ты верно заметил, Цитата лично мне это бы не понравилось, т.к. во-первых, больше текста - больше вероятность ошибиться, а во-вторых, ухудшается читаемость кода в чём я полностью с тобой согласен! Так что это палка о двух концах.  Да бог с этими стандартами. Я попробую компилировать проект сразу в двух компиляторах - IAR и GCC. Хочу сравнить и отписаться об их "узких местах", так как меня это заинтересовало. Может, ещё кому-то это будет интересно...  ЗЫ: вся эта возня с битами снова навела меня на мысль "пощупать" 16-ти битные MSP430. Как-то годик назад я взглянул мельком на несколько мануалов, но показалось, что они послабее периферией универсальных "мег"...
|
|
|
|
|
Nov 29 2008, 21:43
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(AHTOXA @ Nov 29 2008, 21:31)  Но ведь 50% от 0xFFFF - это гораздо больше, чем 50% от 0xFF!  Плохо Вы знаете определение вероятности. Вероятность - это отношение количества интересующих состояний к общему количеству состояний. В рассматриваемом случае вероятность одинакова (мелочь не учитываем). А уж абсолютные количества состояний - это совсем другой вопрос. Если выбирается разрядность 8, значит кодер чтото знает о свойствах своих переменных. А вообще, истоки этого дела (расширение до int) в системе команд PDP-11. Так же, как и описание восьмиричных констант на символ меньше шестнадцатиричных (ребята из DEC'а очень любили восьмиричную систему) С другой стороны лично я выработал некий набор костылей, которые позволяют писать код, хорошо ложащийся как на AVR, так и на ARM (или на другой 32хбитный камень, да и 16тибитный тоже). В частности, это регулярное применение каста (UREG) и (REG), а сами эти типы определяют размер регистра (например, char для AVR, int для архитектур с более высокой разрядностью). Код, конечно, для неподготовленного человека содержит приличное количество странной писанины, но зато с весьма хорошим результатом после компиляции. Еще один финт - минимизация использования знаковых переменных, т.к. это часто приводит к лишним операциям типа расширения знака. Кроме того, лично я, например, против современного игнорирования ключевого слова register. Но это отдельный разговор.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Nov 29 2008, 21:56
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(Rst7 @ Nov 30 2008, 01:43)  С другой стороны лично я выработал некий набор костылей, которые позволяют писать код, хорошо ложащийся как на AVR, так и на ARM (или на другой 32хбитный камень, да и 16тибитный тоже). В частности, это регулярное применение каста (UREG) и (REG), а сами эти типы определяют размер регистра (например, char для AVR, int для архитектур с более высокой разрядностью). Код, конечно, для неподготовленного человека содержит приличное количество странной писанины, но зато с весьма хорошим результатом после компиляции. Еще один финт - минимизация использования знаковых переменных, т.к. это часто приводит к лишним операциям типа расширения знака.
Кроме того, лично я, например, против современного игнорирования ключевого слова register. Но это отдельный разговор. Спасибо за советы Вас, видимо, весьма интересует тема оптимального кода. Наверное, осталась привычка со времён спектрума? Мне кажется, сейчас не каждый заглядывает в "недра" бинарников, особенно программеры PC софта. За отсутствием необходимости?  Раскопал ещё один "пережиток стандарта". Имеем текст: Код typedef unsigned char byte; byte percent; byte maxRPM; byte minRPM;
percent = (maxRPM - minRPM) / 2; и результат: Код percent = (maxRPM - minRPM) / 2; 6c0: 80 85 ldd r24, Z+8; 0x08 6c2: 90 e0 ldi r25, 0x00; 0 6c4: 21 85 ldd r18, Z+9; 0x09 6c6: 82 1b sub r24, r18 6c8: 91 09 sbc r25, r1 6ca: 62 e0 ldi r22, 0x02; 2 6cc: 70 e0 ldi r23, 0x00; 0 6ce: ee d8 rcall .-3620; 0xfffff8ac <__eeprom_end+0xff7ef884> Здесь вроде нет знаковых переменных, и нет сложения. Почему не сработала оптимизация? Разве есть смысл в "запихивании" отрицательного значения в unsigned char? (впрочем, в ИАРе аналогичная фиговина) Также есть проблемы с утилитой создания листинга avr-objdump - как видно, неправильно вычисляется адрес подпрограммы деления... Цитата Цитата За короткое время знакомства с GCC понравилось: удобная реализация атомарности через макросы ATOMIC_BLOCK(), удобная обёртка для обработчиков прерываний вида ISR(vector_name){}. Так это как раз не компилятор, это просто библиотечные макросы. Аналогичные макросы можно сделать для любого минимально вменяемого компилятора. Но разве есть подобные макросы для IAR? Там ведь приходится пользоваться громоздкой конструкцией с pragma и примитивными __disable_interrupt() и __enable_interrupt()...
|
|
|
|
|
Nov 29 2008, 22:00
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(sonycman @ Nov 30 2008, 00:02)  Я попробую компилировать проект сразу в двух компиляторах - IAR и GCC. Хочу сравнить и отписаться об их "узких местах", так как меня это заинтересовало. Может, ещё кому-то это будет интересно...  Мне это будет интересно. Еще интереснее если Вы вооружитесь стандартом на язык С и выступите в качестве третьего компилятора. Еще, GCC в первую очередь компилятор стандартного языка С, во вторую компилятор для нескольких популярных архитектур x86, ARM, 68k и уже в третью - четвертую компилятор для AVR. Для AVR добавлено много оптимизаций, но всегда есть куда совершенствоваться. Да но это не значит что на каждое Ваше хочу ктото кудато побежит чегото делать, какие-то вещи невозможно сделать, какие-то очень трудно, на кикие-то просто нет времени, а какаето просто не интересно делать. Не забывайте, что Вам никто ничего не обязан. Любая конструктивная и обоснованная критика принимается. Не конструктивная, типа "мне ПОФИГ, что стандарт требует приведения выражения к int, я знаю что при моих входных параметрах выражение никогда не даст в результате больше 96, и я ХОЧЮ чтобы компилятор это угадал и использовав при расчетах тип char" меня не интересует. К сожалению в мои таланты не входит просто и доходчиво учить и разъяснять положения стандарта и занимаюсь я этим очень редко и неохотно. Это ссылка на стандарт: www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf С уважением, Анатолий.
|
|
|
|
|
Nov 29 2008, 22:24
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(aesok @ Nov 30 2008, 02:00)  Не забывайте, что Вам никто ничего не обязан. Я разве здесь хоть что-то от кого-то потребовал? Просто привожу вещи, которые мне непонятны/кажутся глупыми/достойными восхищения и т.д. и т.п. И премного благодарен всем, кто наставляет меня "на путь истинный" И, вероятнее всего, не только меня. Цитата мне ПОФИГ, что стандарт требует приведения выражения к int, я знаю что при моих входных параметрах выражение никогда не даст в результате больше 96, и я ХОЧЮ чтобы компилятор это угадал и использовав при расчетах тип char Если вы не поняли, то весь смысл про char был в одном - применительно к AVR это неудобно и неэффективно. С моей точки зрения. Никому её не навязываю и тем более никого не убеждаю изменять стандарты  А вот доводить до ума оптимизацию, думаю, нужно. В частности, по обработке switch?
|
|
|
|
|
Nov 29 2008, 22:36
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(Rst7 @ Nov 30 2008, 01:24)  Простите, а Вы участвуете в разработке GCC, в частности для AVR? Да. Цитата(Rst7 @ Nov 30 2008, 01:24)  Если да, то у меня есть конструктивная критика. Изложена она в ветке про сборки klen'а. Это Вас заинтересует? Или обычный ответ - "никто никому ничего не должен?"  К сожалению ответ тоже, да. Идей и багов намного больше чем времени на них. Анатолий.
|
|
|
|
|
Nov 29 2008, 22:53
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата(sonycman @ Nov 29 2008, 23:56)  Там ведь приходится пользоваться громоздкой конструкцией с pragma и примитивными __disable_interrupt() и __enable_interrupt()... Дык гдето в соседней ветке (кажется, "За что я не люблю Си") ктото привел макрос критической секции из цикла for, а я дальше выложил его вариант для IAR'а, с учетом его встроенных intrinsinc'ов. Кстати, есть еще в IAR'е всякие полезности типа __monitor, __save_interrupt и __restore_interrupt. Так что все не так плохо  Цитата(aesok @ Nov 30 2008, 00:36)  К сожалению ответ тоже, да. Я так и думал. Очень жаль. На самом деле внесенный баг весьма серьезен. Вынужден констатировать, что многие опенсорсные проекты в конце концов превращаются в "жрите что дают". Да еще и морально подпитываются говнопиаром класса "халява рулит"  За сим откланиваюсь. Было приятно освежить в памяти подзабытые вещи благодаря конструктивной беседе с Вами.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Nov 29 2008, 23:31
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(Rst7 @ Nov 30 2008, 01:53)  Вынужден констатировать, что многие опенсорсные проекты в конце концов превращаются в "жрите что дают". Во первых да, Вы правы: Код $ avr-gcc --version avr-gcc (GCC) 4.4.0 20081129 (experimental) Copyright (C) 2008 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Дословный перевод этого и есть: "жрите что дают". Во вторых, Вы ничего не платили и поэтому ничего не вправе требовать, только ждать пока сделают что Вам нужно или сделать сам. Я делаю только то что интересно и нужно мне. Цитата(Rst7 @ Nov 30 2008, 01:53)  Да еще и морально подпитываются говнопиаром класса "халява рулит"  Я могу ответить только за себя. Я НИКОГДА не занимался пиаром свободного софта. ВЫ не найдете ни одного моего призыва чем то пользаваться а чем то нет. Мой выбор очень прост, Я пытаюсь решить задачу наиболее простым и эффективным способом, если для этого нужен коммерческий софт, я ипользую его, если лучше подходит свободный софт, я его применяю. Анатолий. PS: Для чего я этим занимаюсь, Вас не касается.
Сообщение отредактировал aesok - Nov 29 2008, 23:59
|
|
|
|
|
Nov 30 2008, 10:23
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Неважнецки получается у GCC работа со структурой в нижеприведённой функции. Обработчик прерывания usart: Код typedef unsigned char byte;
struct usart_rx_status { byte counter; byte mode; volatile bool data_available; bool frame_err; bool ovrun_err; bool parity_err; bool length_err; bool lock_err; bool chksum_err; bool unknown_command; } statusRx;
ISR(USART_RX_vect) { static byte sign[] PROGMEM = {'S','N'}; static byte length; byte data, state;
do { state = UCSR0A; data = UDR0; if (statusRx.data_available) { statusRx.lock_err = TRUE; continue; } if (state & ((1<<FE0)|(1<<DOR0)|(1<<UPE0))) { if (state & (1<<FE0)) statusRx.frame_err = TRUE; if (state & (1<<DOR0)) statusRx.ovrun_err = TRUE; if (state & (1<<UPE0)) statusRx.parity_err = TRUE; statusRx.mode = RX_SEEK; statusRx.counter = 0; continue; } else { statusRx.frame_err = 0; statusRx.ovrun_err = 0; statusRx.parity_err = 0; statusRx.length_err = 0; statusRx.lock_err = 0; statusRx.chksum_err = 0; } switch(statusRx.mode) { case RX_SEEK: if (statusRx.counter < sizeof(sign)) { if (data == pgm_read_byte(sign[statusRx.counter])) { statusRx.counter++; } else { statusRx.counter = 0; } } else { statusRx.counter = 0; if (data >= sizeof(rx_buffer) || data == 0) { statusRx.length_err = TRUE; break; } rx_buffer[statusRx.counter++] = data; length = ++data; statusRx.mode = RX_RUNNING; } break; case RX_RUNNING: rx_buffer[statusRx.counter++] = data; length--; if (!length) { statusRx.data_available = TRUE; statusRx.mode = RX_SEEK; statusRx.counter = 0; } break; }
}while(UCSR0A & (1<<RXC0)); } Вот результат: Код ISR(USART_RX_vect) b10: 1f 92 push r1 b12: 0f 92 push r0 b14: 0f b6 in r0, 0x3f; 63 b16: 0f 92 push r0 b18: 11 24 eor r1, r1 b1a: 0f 93 push r16 b1c: 1f 93 push r17 b1e: 2f 93 push r18 b20: 3f 93 push r19 b22: 4f 93 push r20 b24: 5f 93 push r21 b26: 6f 93 push r22 b28: 7f 93 push r23 b2a: 8f 93 push r24 b2c: 9f 93 push r25 b2e: af 93 push r26 b30: bf 93 push r27 b32: ef 93 push r30 b34: ff 93 push r31 b36: 00 91 8d 01 lds r16, 0x018D b3a: b0 91 9b 01 lds r27, 0x019B b3e: f0 91 99 01 lds r31, 0x0199 b42: e0 91 98 01 lds r30, 0x0198 b46: 70 91 97 01 lds r23, 0x0197 b4a: 60 91 96 01 lds r22, 0x0196 b4e: 50 91 93 01 lds r21, 0x0193 b52: 10 91 94 01 lds r17, 0x0194 b56: 40 91 9a 01 lds r20, 0x019A // errorRx.allbits = 0; byte data, state;
do { state = UCSR0A; b5a: 90 91 c0 00 lds r25, 0x00C0 data = UDR0; b5e: a0 91 c6 00 lds r26, 0x00C6 if (statusRx.data_available) b62: 80 91 95 01 lds r24, 0x0195 b66: 88 23 and r24, r24 b68: 11 f0 breq .+4; 0xb6e <__vector_18+0x5e> b6a: 41 e0 ldi r20, 0x01; 1 b6c: 54 c0 rjmp .+168; 0xc16 <__vector_18+0x106> { statusRx.lock_err = TRUE; continue; } if (state & ((1<<FE0)|(1<<DOR0)|(1<<UPE0))) b6e: 29 2f mov r18, r25 b70: 30 e0 ldi r19, 0x00; 0 b72: c9 01 movw r24, r18 b74: 8c 71 andi r24, 0x1C; 28 b76: 90 70 andi r25, 0x00; 0 b78: 89 2b or r24, r25 b7a: 49 f0 breq .+18; 0xb8e <__vector_18+0x7e> { if (state & (1<<FE0)) statusRx.frame_err = TRUE; b7c: 24 fd sbrc r18, 4 b7e: 61 e0 ldi r22, 0x01; 1 if (state & (1<<DOR0)) statusRx.ovrun_err = TRUE; b80: 23 fd sbrc r18, 3 b82: 71 e0 ldi r23, 0x01; 1 if (state & (1<<UPE0)) statusRx.parity_err = TRUE; b84: 22 fd sbrc r18, 2 b86: e1 e0 ldi r30, 0x01; 1 b88: 50 e0 ldi r21, 0x00; 0 b8a: 10 e0 ldi r17, 0x00; 0 b8c: 44 c0 rjmp .+136; 0xc16 <__vector_18+0x106> statusRx.length_err = 0; statusRx.lock_err = 0; statusRx.chksum_err = 0; } // lcdPrintByte(data, 0, 0); switch(statusRx.mode) b8e: 11 23 and r17, r17 b90: 19 f0 breq .+6; 0xb98 <__vector_18+0x88> b92: 11 30 cpi r17, 0x01; 1 b94: d1 f5 brne .+116; 0xc0a <__vector_18+0xfa> b96: 27 c0 rjmp .+78; 0xbe6 <__vector_18+0xd6> { case RX_SEEK: if (statusRx.counter < sizeof(sign)) b98: 52 30 cpi r21, 0x02; 2 b9a: 70 f4 brcc .+28; 0xbb8 <__vector_18+0xa8> { if (data == pgm_read_byte(sign[statusRx.counter])) b9c: e5 2f mov r30, r21 b9e: f0 e0 ldi r31, 0x00; 0 ba0: ec 5c subi r30, 0xCC; 204 ba2: ff 4f sbci r31, 0xFF; 255 ba4: e0 81 ld r30, Z ba6: f0 e0 ldi r31, 0x00; 0 ba8: e4 91 lpm r30, Z+ baa: ae 17 cp r26, r30 bac: 19 f0 breq .+6; 0xbb4 <__vector_18+0xa4> bae: b0 e0 ldi r27, 0x00; 0 bb0: f0 e0 ldi r31, 0x00; 0 bb2: 08 c0 rjmp .+16; 0xbc4 <__vector_18+0xb4> { statusRx.counter++; bb4: 5f 5f subi r21, 0xFF; 255 bb6: 29 c0 rjmp .+82; 0xc0a <__vector_18+0xfa> } } else { statusRx.counter = 0; if (data >= sizeof(rx_buffer) || data == 0) bb8: 8a 2f mov r24, r26 bba: 81 50 subi r24, 0x01; 1 bbc: 8f 31 cpi r24, 0x1F; 31 bbe: 38 f0 brcs .+14; 0xbce <__vector_18+0xbe> bc0: b0 e0 ldi r27, 0x00; 0 bc2: f1 e0 ldi r31, 0x01; 1 bc4: e0 e0 ldi r30, 0x00; 0 bc6: 70 e0 ldi r23, 0x00; 0 bc8: 60 e0 ldi r22, 0x00; 0 bca: 50 e0 ldi r21, 0x00; 0 bcc: 23 c0 rjmp .+70; 0xc14 <__vector_18+0x104> { statusRx.length_err = TRUE; break; } rx_buffer[statusRx.counter++] = data; bce: a0 93 a1 01 sts 0x01A1, r26 length = ++data; bd2: 0a 2f mov r16, r26 bd4: 0f 5f subi r16, 0xFF; 255 bd6: b0 e0 ldi r27, 0x00; 0 bd8: f0 e0 ldi r31, 0x00; 0 bda: e0 e0 ldi r30, 0x00; 0 bdc: 70 e0 ldi r23, 0x00; 0 bde: 60 e0 ldi r22, 0x00; 0 be0: 51 e0 ldi r21, 0x01; 1 be2: 11 e0 ldi r17, 0x01; 1 be4: 17 c0 rjmp .+46; 0xc14 <__vector_18+0x104> // lcdPrintByte(data, 0, 1); statusRx.mode = RX_RUNNING; } break; case RX_RUNNING: rx_buffer[statusRx.counter++] = data; be6: e5 2f mov r30, r21 be8: f0 e0 ldi r31, 0x00; 0 bea: ef 55 subi r30, 0x5F; 95 bec: fe 4f sbci r31, 0xFE; 254 bee: a0 83 st Z, r26 bf0: 5f 5f subi r21, 0xFF; 255 length--; bf2: 01 50 subi r16, 0x01; 1 if (!length) bf4: 51 f4 brne .+20; 0xc0a <__vector_18+0xfa> { statusRx.data_available = TRUE; bf6: 10 93 95 01 sts 0x0195, r17 bfa: b0 e0 ldi r27, 0x00; 0 bfc: f0 e0 ldi r31, 0x00; 0 bfe: e0 e0 ldi r30, 0x00; 0 c00: 70 e0 ldi r23, 0x00; 0 c02: 60 e0 ldi r22, 0x00; 0 c04: 50 e0 ldi r21, 0x00; 0 c06: 10 e0 ldi r17, 0x00; 0 c08: 05 c0 rjmp .+10; 0xc14 <__vector_18+0x104> c0a: b0 e0 ldi r27, 0x00; 0 c0c: f0 e0 ldi r31, 0x00; 0 c0e: e0 e0 ldi r30, 0x00; 0 c10: 70 e0 ldi r23, 0x00; 0 c12: 60 e0 ldi r22, 0x00; 0 c14: 40 e0 ldi r20, 0x00; 0 // statusRx.mode = RX_SEEK; // statusRx.data_available = 0; // errorRx.allbits = 0; byte data, state;
do c16: 80 91 c0 00 lds r24, 0x00C0 c1a: 87 fd sbrc r24, 7 c1c: 9e cf rjmp .-196; 0xb5a <__vector_18+0x4a> c1e: 00 93 8d 01 sts 0x018D, r16 c22: b0 93 9b 01 sts 0x019B, r27 c26: f0 93 99 01 sts 0x0199, r31 c2a: e0 93 98 01 sts 0x0198, r30 c2e: 70 93 97 01 sts 0x0197, r23 c32: 60 93 96 01 sts 0x0196, r22 c36: 50 93 93 01 sts 0x0193, r21 c3a: 10 93 94 01 sts 0x0194, r17 c3e: 40 93 9a 01 sts 0x019A, r20 } break; }
}while(UCSR0A & (1<<RXC0)); } c42: ff 91 pop r31 c44: ef 91 pop r30 c46: bf 91 pop r27 c48: af 91 pop r26 c4a: 9f 91 pop r25 c4c: 8f 91 pop r24 c4e: 7f 91 pop r23 c50: 6f 91 pop r22 c52: 5f 91 pop r21 c54: 4f 91 pop r20 c56: 3f 91 pop r19 c58: 2f 91 pop r18 c5a: 1f 91 pop r17 c5c: 0f 91 pop r16 c5e: 0f 90 pop r0 c60: 0f be out 0x3f, r0; 63 c62: 0f 90 pop r0 c64: 1f 90 pop r1 c66: 18 95 reti 344 байта. У ИАРа тот-же код занимает 278 байт...
|
|
|
|
|
Nov 30 2008, 11:09
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Хоть я и откланялся, но господину sonycman'у отвечу, потому как тема имеет тенденцию сдвигаться в немного другую область, нежели IAR vs GCC. Цитата Неважнецки получается у GCC работа со структурой в нижеприведённой функции. Вообщем-то философия оптимизации данной функции гнусем понятна - загрузить в регистры все что можно и на выходе выгрузить. Подход правильный, основанный на общей идеологии RISC и Load-Store. Но в данном случае он немного не оправдывает себя. IAR подходит к проблеме утилизации Load-Store с более сдержанных позиций, он выполняет эти операции в более локальной окрестности действий с полями структуры. С другой стороны, это позволяет кодеру помочь компилятору в ручном указании, какие переменные стоит загрузить в самом начале (и сохранить в самом конце), какие - нет. Я пришел к следующей эмпирической оценке - оцениваю количество обращений к переменной. Если их больше 3, то имеет смысл переносить переменную в регистр. Причем, чем больше обращений (с учетом циклов), тем выше приоритет переменной на перенос в регистр. Ну и не забывать, что операция a=b++ - суть 2 обращения, а иногда и 3. Можно ли так подсказать гнусю - я затрудняюсь ответить, необходимо проверять. Кроме того, я бы флаги ошибок собрал бы в битовой структуре. Так проще. В результате, получился бы примерно следующий код (IAR) Код typedef struct { byte frame_err:1,ovrun_err:1,parity_err:1,length_err:1,lock_err:1,chksum_err:1,unknown_command:1; } USART_ERRORS;
struct usart_rx_status { byte counter; byte mode; volatile byte data_available; USART_ERRORS err; } statusRx;
#pragma vector=USART_RX_vect __interrupt void ISR_USART_RX(void) { static __flash byte sign[] = {'S','N'}; static byte save_length; byte data, state; USART_ERRORS err=statusRx.err; byte length=save_length; byte counter=statusRx.counter; do { state = UCSR0A; data = UDR0; if (statusRx.data_available) { err.lock_err = TRUE; continue; } if (state & ((1<<FE0)|(1<<DOR0)|(1<<UPE0))) { if (state & (1<<FE0)) err.frame_err = TRUE; if (state & (1<<DOR0)) err.ovrun_err = TRUE; if (state & (1<<UPE0)) err.parity_err = TRUE; statusRx.mode = RX_SEEK; counter = 0; continue; } else { err.frame_err = 0; err.ovrun_err = 0; err.parity_err = 0; err.length_err = 0; err.lock_err = 0; err.chksum_err = 0; } switch(statusRx.mode) { case RX_SEEK: if (counter < sizeof(sign)) { if (data == sign[counter]) { counter++; } else { counter = 0; } } else { counter = 0; if (data >= sizeof(rx_buffer) || data == 0) { err.length_err = TRUE; break; } rx_buffer[counter++] = data; length = ++data; statusRx.mode = RX_RUNNING; } break; case RX_RUNNING: rx_buffer[counter++] = data; length--; if (!length) { statusRx.data_available = TRUE; statusRx.mode = RX_SEEK; counter = 0; } break; } }while(UCSR0A & (1<<RXC0)); save_length=length; statusRx.err=err; statusRx.counter=counter; } и результат Код // 27 __interrupt void ISR_USART_RX(void) ISR_USART_RX: // 28 { ST -Y, R27 ST -Y, R26 ST -Y, R31 ST -Y, R30 ST -Y, R22 ST -Y, R21 ST -Y, R20 ST -Y, R19 ST -Y, R18 ST -Y, R17 ST -Y, R16 IN R22, 0x3F // 29 static __flash byte sign[] = {'S','N'}; // 30 static byte save_length; // 31 byte data, state; // 32 USART_ERRORS err=statusRx.err; LDI R26, LOW(statusRx) LDI R27, (statusRx) >> 8 MOVW R31:R30, R27:R26 LDD R16, Z+3 // 33 byte length=save_length; LDD R19, Z+4 // 34 byte counter=statusRx.counter; LD R18, X // 35 // 36 do // 37 { // 38 state = UCSR0A; ??ISR_USART_RX_0: LDS R21, 192 // 39 data = UDR0; LDS R17, 198 // 40 if (statusRx.data_available) MOVW R31:R30, R27:R26 LDD R20, Z+2 TST R20 BREQ ??ISR_USART_RX_1 // 41 { // 42 err.lock_err = TRUE; ORI R16, 0x10 // 43 continue; RJMP ??ISR_USART_RX_2 // 44 } // 45 if (state & ((1<<FE0)|(1<<DOR0)|(1<<UPE0))) ??ISR_USART_RX_1: MOV R20, R21 ANDI R20, 0x1C BREQ ??ISR_USART_RX_3 // 46 { // 47 if (state & (1<<FE0)) err.frame_err = TRUE; BST R21, 4 BRTC ??ISR_USART_RX_4 ORI R16, 0x01 // 48 if (state & (1<<DOR0)) err.ovrun_err = TRUE; ??ISR_USART_RX_4: BST R21, 3 BRTC ??ISR_USART_RX_5 ORI R16, 0x02 // 49 if (state & (1<<UPE0)) err.parity_err = TRUE; ??ISR_USART_RX_5: BST R21, 2 BRTC ??ISR_USART_RX_6 ORI R16, 0x04 // 50 statusRx.mode = RX_SEEK; RJMP ??ISR_USART_RX_6 // 51 counter = 0; // 52 continue; // 53 } // 54 else // 55 { // 56 err.frame_err = 0; // 57 err.ovrun_err = 0; // 58 err.parity_err = 0; // 59 err.length_err = 0; // 60 err.lock_err = 0; // 61 err.chksum_err = 0; ??ISR_USART_RX_3: ANDI R16, 0xC0 // 62 } // 63 switch(statusRx.mode) LDD R20, Z+1 SUBI R20, 0 BREQ ??ISR_USART_RX_7 DEC R20 BREQ ??ISR_USART_RX_8 RJMP ??ISR_USART_RX_2 // 64 { // 65 case RX_SEEK: // 66 if (counter < sizeof(sign)) ??ISR_USART_RX_7: CPI R18, 2 BRCC ??ISR_USART_RX_9 // 67 { // 68 if (data == sign[counter]) LDI R31, 0 MOV R30, R18 SUBI R30, LOW((-(??sign) & 0xFFFF)) SBCI R31, (-(??sign) & 0xFFFF) >> 8 LPM R20, Z CP R17, R20 BRNE ??ISR_USART_RX_10 // 69 { // 70 counter++; INC R18 RJMP ??ISR_USART_RX_2 // 71 } // 72 else // 73 { // 74 counter = 0; // 75 } // 76 } // 77 else // 78 { // 79 counter = 0; ??ISR_USART_RX_9: LDI R18, 0 // 80 if (data >= sizeof(rx_buffer) || data == 0) CPI R17, 100 BRCC ??ISR_USART_RX_11 TST R17 BRNE ??ISR_USART_RX_12 // 81 { // 82 err.length_err = TRUE; ??ISR_USART_RX_11: ORI R16, 0x08 // 83 break; RJMP ??ISR_USART_RX_2 // 84 } // 85 rx_buffer[counter++] = data; ??ISR_USART_RX_12: STS rx_buffer, R17 LDI R18, 1 // 86 length = ++data; MOV R19, R17 INC R19 // 87 statusRx.mode = RX_RUNNING; STD Z+1, R18 RJMP ??ISR_USART_RX_2 // 88 } // 89 break; // 90 case RX_RUNNING: // 91 rx_buffer[counter++] = data; ??ISR_USART_RX_8: LDI R31, 0 MOV R30, R18 SUBI R30, LOW((-(rx_buffer) & 0xFFFF)) SBCI R31, (-(rx_buffer) & 0xFFFF) >> 8 ST Z, R17 INC R18 // 92 length--; DEC R19 // 93 if (!length) TST R19 BRNE ??ISR_USART_RX_2 // 94 { // 95 statusRx.data_available = TRUE; LDI R17, 1 MOVW R31:R30, R27:R26 STD Z+2, R17 // 96 statusRx.mode = RX_SEEK; ??ISR_USART_RX_6: LDI R17, 0 STD Z+1, R17 // 97 counter = 0; ??ISR_USART_RX_10: LDI R18, 0 // 98 } // 99 break; // 100 } // 101 // 102 }while(UCSR0A & (1<<RXC0)); ??ISR_USART_RX_2: LDS R17, 192 SBRC R17, 7 RJMP ??ISR_USART_RX_0 // 103 save_length=length; MOVW R31:R30, R27:R26 STD Z+4, R19 // 104 statusRx.err=err; STD Z+3, R16 // 105 statusRx.counter=counter; ST X, R18 // 106 } OUT 0x3F, R22 LD R16, Y+ LD R17, Y+ LD R18, Y+ LD R19, Y+ LD R20, Y+ LD R21, Y+ LD R22, Y+ LD R30, Y+ LD R31, Y+ LD R26, Y+ LD R27, Y+ RETI
.... // 218 bytes in segment CODE Попробуйте сделать аналогичный финт с гнусем, посмотрим, как он себя поведет.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Nov 30 2008, 13:13
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Привет, Rst7! Цитата(Rst7 @ Nov 29 2008, 17:53)  Да ну? Рассмотрим операцию сложения. Независимо от размера - char или int - половина возможных операндов (немного меньше, без 2n, где n - количество разрядов) приведет к переполнению. А значит - вероятность почти 50% и не зависит от размерности  Честно говоря, я ничего не понял.  Я ведь говорю не о теоретической математике, а о сугубо прикладной. Математические величины, над которыми производятся вычисления в программе, как правило, отражают реальные сущности окружающего мира. Поэтому значения этих величин первичны по отношению к программе. Исходя из них программист выбирает, в частности, разрядность, необходимую для проведения вычислений. Я имел в виду, что для представления некой величины 8-ми бит может оказаться недостаточно с большей вероятностью, чем 16, и, тем более, 32-х. К примеру, количество людей, прошедших через турникет за час - оно вполне может измеряться несколькими сотнями. А вот превысить 65 тысяч, я думаю, не сможет.
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 30 2008, 13:22
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
RST7Не получается у меня научить гнуса правильно обращаться с битовым полем. Даже с Вашей помощью... Вот что получилось: Код typedef struct { byte frame_err: 1, ovrun_err: 1, parity_err: 1, length_err: 1, lock_err: 1, chksum_err: 1, unknown_command: 1; } USART_ERROR;
struct usart_rx_status { byte counter; byte mode; volatile bool data_available; USART_ERROR error; };
ISR(USART_RX_vect) { static byte sign[] PROGMEM = {'S','N'}; static byte length; byte data, state; USART_ERROR error; error = statusRx.error;
do { state = UCSR0A; data = UDR0; if (statusRx.data_available) { error.lock_err = TRUE; continue; } if (state & ((1<<FE0)|(1<<DOR0)|(1<<UPE0))) { if (state & (1<<FE0)) error.frame_err = TRUE; if (state & (1<<DOR0)) error.ovrun_err = TRUE; if (state & (1<<UPE0)) error.parity_err = TRUE; statusRx.mode = RX_SEEK; statusRx.counter = 0; continue; } else { error.frame_err = 0; error.ovrun_err = 0; error.parity_err = 0; error.length_err = 0; error.lock_err = 0; error.chksum_err = 0; } switch(statusRx.mode) { case RX_SEEK: if (statusRx.counter < sizeof(sign)) { if (data == pgm_read_byte(sign[statusRx.counter])) { statusRx.counter++; } else { statusRx.counter = 0; } } else { statusRx.counter = 0; if (data >= sizeof(rx_buffer) || data == 0) { error.length_err = TRUE; break; } rx_buffer[statusRx.counter++] = data; length = ++data; statusRx.mode = RX_RUNNING; } break; case RX_RUNNING: rx_buffer[statusRx.counter++] = data; length--; if (!length) { statusRx.data_available = TRUE; statusRx.mode = RX_SEEK; statusRx.counter = 0; } break; }
}while(UCSR0A & (1<<RXC0)); statusRx.error = error; } и результат компиляции первой строчки: Код error = statusRx.error; b38: 80 91 ae 01 lds r24, 0x01AE b3c: 58 2f mov r21, r24 b3e: 52 95 swap r21 b40: 51 70 andi r21, 0x01; 1 b42: b8 2f mov r27, r24 b44: b1 70 andi r27, 0x01; 1 b46: 68 2f mov r22, r24 b48: 66 95 lsr r22 b4a: 61 70 andi r22, 0x01; 1 b4c: 78 2f mov r23, r24 b4e: 76 95 lsr r23 b50: 76 95 lsr r23 b52: 71 70 andi r23, 0x01; 1 b54: f8 2f mov r31, r24 b56: f6 95 lsr r31 b58: f6 95 lsr r31 b5a: f6 95 lsr r31 b5c: f1 70 andi r31, 0x01; 1 b5e: e8 2f mov r30, r24 b60: e2 95 swap r30 b62: e6 95 lsr r30 b64: e1 70 andi r30, 0x01; 1 b66: 08 2f mov r16, r24 b68: 02 95 swap r16 b6a: 06 95 lsr r16 b6c: 06 95 lsr r16 b6e: 01 70 andi r16, 0x01; 1 дальше в коде в основном всё осталось по-прежнему...
|
|
|
|
|
Nov 30 2008, 13:41
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 30 2008, 02:02)  Зачем часто писать явное приведение к int? Разумнее сразу иметь хотя-бы одну переменную размерности int, если обработка данных типа char приводит к образованию переполнения  Это можно. Но тогда в памяти будут храниться незначащие байты. Цитата(sonycman @ Nov 30 2008, 02:02)  Ты говоришь про общий случай. Я - про строку кода конкретной функции lcdPrintText, в которой делимое всегда лежит в пределах 0 <= d < 96. Поэтому эти выражения совершенно эквивалентны. Я говорил о правилах языка, а они пишутся именно для общего случая. Я вполне допускаю, что изменение правил дало бы для данной конкретной программы выигрыш в объеме. Вопрос в том, стоит ли этот выигрыш потери совместимости. В конце концов, если уж так хочется, можно ИМХО создать свой класс mychar, и определить для него такую математику, какую хочется. В том числе приведение к char каждого промежуточного результата. И другой язык для этого придумывать не обязательно.
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 30 2008, 13:46
|

Йа моск ;)
     
Группа: Модераторы
Сообщений: 4 345
Регистрация: 7-07-05
Из: Kharkiv-city
Пользователь №: 6 610

|
Цитата и результат компиляции первой строчки: Жесть. Все биты превратил в char'ы и разложил в регистрах. В конце функции, кстати, должен был собрать вместе  Выбросьте Вы, наверное, нафиг эту опенсорсную какашку. Потому как на данном этапе получить из него более лучший чем у IAR'а код не получается. И врядли когда-либо в обозримом будущем получится. Даже с учетом того, что у IAR'а иногда наблюдаются психозы с указателями. А в общем, я бы рекомнедовал купить IAR и клевать мозг его разработчикам. Потому как тогда будут заплачены денежки и есть за что бороться. Сам я в ближайшее время собираюсь по этому пути пойти. А с гнусем - Вы же сами видите, любая просьба (даже о рассмотрении серьезного бага в оптимизаторе) посылается куда подальше - ведь "никто никому не должен". Аргумент о том, что багов и пожеланий больше чем времени, не принимается - нет времени пилить напильником гнуся - не надо за него вообще браться. Как там, в StarWars - "не надо пытаться, делай - или не делай"  Пардон, если зацепил кого. Ничего личного, просто констатация факта.
--------------------
"Практика выше (теоретического) познания, ибо она имеет не только достоинство всеобщности, но и непосредственной действительности." - В.И. Ленин
|
|
|
|
|
Nov 30 2008, 13:57
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 30 2008, 02:56)  Имеем текст: Код typedef unsigned char byte; byte percent; byte maxRPM; byte minRPM;
percent = (maxRPM - minRPM) / 2; и результат: Код percent = (maxRPM - minRPM) / 2; 6c0: 80 85 ldd r24, Z+8; 0x08 6c2: 90 e0 ldi r25, 0x00; 0 6c4: 21 85 ldd r18, Z+9; 0x09 6c6: 82 1b sub r24, r18 6c8: 91 09 sbc r25, r1 6ca: 62 e0 ldi r22, 0x02; 2 6cc: 70 e0 ldi r23, 0x00; 0 6ce: ee d8 rcall .-3620; 0xfffff8ac <__eeprom_end+0xff7ef884> Здесь вроде нет знаковых переменных, и нет сложения. Почему не сработала оптимизация? Что именно тут можно было сделать более оптимально? Цитата(sonycman @ Nov 30 2008, 02:56)  Также есть проблемы с утилитой создания листинга avr-objdump - как видно, неправильно вычисляется адрес подпрограммы деления... А зачем вообще в данном случае использовать дизассемблирование? Почему не смотреть сразу вывод компилятора? Там хотя бы все имена символов на своих местах: Код lds r24,maxRPM ldi r25,lo8(0) lds r18,minRPM sub r24,r18 sbc r25,__zero_reg__ ldi r22,lo8(2) ldi r23,hi8(2) rcall __divmodhi4 sts percent,r22 ret А узнать адрес функции удобнее из map-файла... Цитата(sonycman @ Nov 30 2008, 02:56)  Так это как раз не компилятор, это просто библиотечные макросы. Аналогичные макросы можно сделать для любого минимально вменяемого компилятора.
Но разве есть подобные макросы для IAR? Там ведь приходится пользоваться громоздкой конструкцией с pragma и примитивными __disable_interrupt() и __enable_interrupt()... Не знаю, я IAR'ом никогда не пользовался. Даже если нет, что мешает написать? Вот в GCC тоже нет таких макросов - так сделали же...
Сообщение отредактировал alx2 - Nov 30 2008, 14:50
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 30 2008, 14:19
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(Rst7 @ Nov 30 2008, 17:46)  Жесть. Все биты превратил в char'ы и разложил в регистрах. В конце функции, кстати, должен был собрать вместе  Выбросьте Вы, наверное, нафиг эту опенсорсную какашку. Наверное, так и придётся поступить в конце концов. Но почему-то жаль... Цитата(alx2 @ Nov 30 2008, 17:41)  В конце концов, если уж так хочется, можно ИМХО создать свой класс mychar, и определить для него такую математику, какую хочется. А вот это очень интересно! Надо будет попробовать на досуге! Цитата(alx2 @ Nov 30 2008, 17:57)  А зачем вообще в данном случае использовать дизассемблирование? Почему не смотреть сразу вывод компилятора? Там хотя бы все имена символов на своих местах: А как помотреть? Я в эклипсе не вижу больше никаких файлов после компиляции, кроме .lss Цитата Даже если нет, что мешает написать? Мне самому просто знаний не хватит написать "извращение", подобное ATOMIC_BLOCK(). По крайней мере, на данных порах.
|
|
|
|
|
Nov 30 2008, 14:21
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 30 2008, 04:14)  В ИАРе очень удобно сделана работа с указателями на память программ и eeprom. Она совершенно не отличается от указателей на оперативку Хотелось бы видеть такое когда-нибудь и в GCC. Можно в двух словах, как это сделано в IAR? Предполагаю, что там сделаны "универсальные указатели", которые включают не только адрес, но и идентификатор адресного пространства (код/данные). Кстати, опять же, IMHO ничто не мешает определить какой-нибудь класс u_pointer, хранящий такой указатель, и все необходимые для него операции. И не надо ждать, когда авторы компилятора что-то там сделают...
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
|
Nov 30 2008, 14:34
|

Любитель
    
Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695

|
Цитата(alx2 @ Nov 30 2008, 18:21)  Можно в двух словах, как это сделано в IAR? Предполагаю, что там сделаны "универсальные указатели", которые включают не только адрес, но и идентификатор адресного пространства (код/данные). Кстати, опять же, IMHO ничто не мешает определить какой-нибудь класс u_pointer, хранящий такой указатель, и все необходимые для него операции. И не надо ждать, когда авторы компилятора что-то там сделают... Я без понятия, как там это сделано. Но выглядит вот так: Код __no_init __eeprom byte ds1820romcodes[4][10] @ 0x0; static unsigned char ds18rom[4][10];
void ds18Init(void) { for(byte a=0;a<40;a++) { ds18rom[0][a] = ds1820romcodes[0][a]; } } Также и с обращением на чтение памяти программ: Код lcdPrintText("CPU");
void lcdPrintText(char const __flash __flash *text) { while(*text) lcdPutChar(*text++); }
|
|
|
|
|
Nov 30 2008, 14:40
|

Местный
  
Группа: Участник
Сообщений: 340
Регистрация: 25-10-05
Из: Пермь, Россия
Пользователь №: 10 091

|
Цитата(sonycman @ Nov 30 2008, 03:24)  А вот доводить до ума оптимизацию, думаю, нужно. В частности, по обработке switch? Можно немного подсказать gcc, поместив промежуточный результат во временную переменную: Код void do_something(); void do_something_else(); void fff(char flags) { char tmp = flags & 0xe0; switch(tmp) { case 0x40: do_something(); break; case 0x80: do_something_else(); } } Результат компиляции: Код _Z3fffc: andi r24,lo8(-32) cpi r24,lo8(64) breq .L3 cpi r24,lo8(-128) brne .L5 rjmp .L6 .L3: rcall _Z12do_somethingv ret .L6: rcall _Z17do_something_elsev .L5: ret
--------------------
Всего наилучшего, Alex Mogilnikov
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|