avr-gcc деструктивная оптимизация. Avr gcc оптимизация кода


c - avr-gcc деструктивная оптимизация

Я знаю, что мой ответ наступает через ~ 2 года после того, как этот вопрос был задан, но я верю, что до сих пор нет правильного, углубленного ответа.

Начнем с немного теории:

Когда вы вызываете GCC, он обычно выполняет предварительную обработку, компиляцию, сборку и компоновку. "Общие параметры" позволяют остановить этот процесс на промежуточной стадии. Например, опция -c говорит, что нельзя запускать компоновщик. Затем вывод состоит из объектных файлов, выводимых ассемблером.

Другие варианты передаются на один этап обработки. Некоторые параметры управляют препроцессором, а другие - самим компилятором. Однако другие опции управляют ассемблером и компоновщиком; большинство из них не описаны здесь, поскольку вам редко приходится использовать любой из них.

источник: GCC Online Docs

LDFLAGS

Дополнительные флаги для компиляторов, когда они должны ссылаться на компоновщик, ld, например -L. Библиотеки (-L foo) должны быть добавлены к переменной LDLIBS.

источник: GNU make Manual

Как вы можете видеть, это зависит от GCC (я буду называть это таким образом, чтобы отличать от реального компилятора, вы можете найти его как называемый C-компилятор или просто компилятор), какие параметры будут переданы каким инструментам, и кажется, что -On не передается компоновщику (вы можете проверить его, предоставив параметр -v для GCC). Таким образом, вызывать GCC без этой опции, когда предполагается только соединение, в порядке.

Реальная проблема заключается в том, что вы не предоставляете -mmcu=dev для GCC при связывании. Поэтому он не может найти правильный файл crt*.o (C RunTime) и сообщить компоновщику ссылку на него; ваше приложение заканчивается без кода инициализации.

Поэтому учтите, что вы должны включить -mmcu=dev в LDFLAGS или передать его в GCC независимо от того, что он предназначен (предварительная обработка/компиляция/сборка/привязка). Я уже видел пару make файлов без этой опции в LDFLAGS в Интернете, так что будьте осторожны.

Теперь пришло время для некоторой практики - предположив, что ваш источник находится в файле test.c, выполните следующие команды (в linux):

avr-gcc -mmcu=attiny13a -DF_CPU=1200000 -Wall -O1 -c -o testO1.o test.c avr-gcc -mmcu=attiny13a -DF_CPU=1200000 -Wall -Os -c -o testOs.o test.c avr-gcc -o testO1_nodev.elf testO1.o avr-gcc -v -o testOs_nodev.elf testOs.o > testOs_nodev.log 2>&1 avr-gcc -v -mmcu=attiny13a -o testOs_correct.elf testOs.o > testOs_correct.log 2>&1

Я оставил только необходимые опции + -Wall, для ATtiny13a вам нужно -mmcu=attiny13a вместо -mmcu=attiny13.

Давайте взглянем на testOs_nodev.log и testOs_correct.log. Выполните следующую команду:

diff testOs_nodev.log testOs_correct.log

и вы увидите что-то вроде:

2c2 < Reading specs from /usr/lib/gcc/avr/5.2.0/device-specs/specs-avr2 --- > Reading specs from /usr/lib/gcc/avr/5.2.0/device-specs/specs-attiny13a 10,12c10,12 < LIBRARY_PATH=/usr/lib/gcc/avr/5.2.0/:/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/ < COLLECT_GCC_OPTIONS='-v' '-o' 'testOs_nodev.elf' '-specs=device-specs/specs-avr2' < /usr/lib/gcc/avr/5.2.0/collect2 -plugin /usr/lib/gcc/avr/5.2.0/liblto_plugin.so \ -plugin-opt=/usr/lib/gcc/avr/5.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccqBjM6T.res \ -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc \ -o testOs_nodev.elf -L/usr/lib/gcc/avr/5.2.0 -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib \ testOs.o --start-group -lgcc -lm -lc --end-group --- > LIBRARY_PATH=/usr/lib/gcc/avr/5.2.0/avr25/tiny-stack/:\ /usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack/:\ /usr/lib/gcc/avr/5.2.0/:/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/ > COLLECT_GCC_OPTIONS='-v' '-o' 'testOs_correct.elf' '-specs=device-specs/specs-attiny13a' \ '-mmcu=avr25' '-msp8' > /usr/lib/gcc/avr/5.2.0/collect2 -plugin /usr/lib/gcc/avr/5.2.0/liblto_plugin.so \ -plugin-opt=/usr/lib/gcc/avr/5.2.0/lto-wrapper -plugin-opt=-fresolution=/tmp/ccV919rY.res \ -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lm -plugin-opt=-pass-through=-lc \ -plugin-opt=-pass-through=-lattiny13a -mavr25 -o testOs_correct.elf \ /usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack/crtattiny13a.o \ -L/usr/lib/gcc/avr/5.2.0/avr25/tiny-stack -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib/avr25/tiny-stack \ -L/usr/lib/gcc/avr/5.2.0 -L/usr/lib/gcc/avr/5.2.0/../../../../avr/lib testOs.o \ --start-group -lgcc -lm -lc -lattiny13a --end-group

(Я сломал несколько строк, чтобы сделать их доступными для чтения)

Разница в том, что без -mmcu=dev GCC по умолчанию использует файл avr2 specs и не связывает какой-либо файл CRT.

Изучите объектные файлы (*.o) и выходные файлы (*.elf) с помощью avr-objdump:

avr-objdump -xd testOs_nodev.elf

Вы заметите, что файлы *_nodev.elf не содержат правильной информации об архитектуре (avr вместо avr: 25) и никакого кода запуска (сравните testOs_correct.elf с testOs_nodev.elf). Секция кода представляет собой дословную копию того, что было поставлено в объектном файле.

Если какая-либо часть моей разработки кажется неясной или нуждается в дополнительном объяснении, не стесняйтесь спрашивать (комментировать).

qaru.site

c - Оптимизация этого кода (AVR)

У меня есть обработчик прерываний, который просто не работает достаточно быстро для того, что я хочу делать. В основном я использую его для генерации синусоидальных волн, выводя значение из таблицы поиска в ПОРТ на микроконтроллере AVR, но, к сожалению, этого не происходит достаточно быстро, чтобы я мог получить частоту волны, которую я хочу. Мне сказали, что я должен рассмотреть возможность его реализации в сборке, поскольку сборка, сгенерированная компилятором, может быть немного неэффективной и может быть оптимизирована, но после просмотра кода сборки я действительно не вижу, что я могу сделать лучше.

Это код C:

const uint8_t amplitudes60[60] = {127, 140, 153, 166, 176, 191, 202, 212, 221, 230, 237, 243, 248, 251, 253, 254, 253, 251, 248, 243, 237, 230, 221, 212, 202, 191, 179, 166, 153, 140, 127, 114, 101, 88, 75, 63, 52, 42, 33, 24, 17, 11, 6, 3, 1, 0, 1, 3, 6, 11, 17, 24, 33, 42, 52, 63, 75, 88, 101, 114}; const uint8_t amplitudes13[13] = {127, 176, 221, 248, 202, 153, 101, 52, 17, 1, 6, 33, 75}; const uint8_t amplitudes10[10] = {127, 176, 248, 202, 101, 52, 17, 1, 33, 75}; volatile uint8_t numOfAmps = 60; volatile uint8_t *amplitudes = amplitudes60; volatile uint8_t amplitudePlace = 0; ISR(TIMER1_COMPA_vect) { PORTD = amplitudes[amplitudePlace]; amplitudePlace++; if(amplitudePlace == numOfAmps) { amplitudePlace = 0; } }

амплитуды и numOfAmps оба изменены другой подпрограммы обработки прерывания, которая работает гораздо медленнее, чем этот (он в основном выполняется для изменения частоты, которые играются). В конце дня я не буду использовать эти точные массивы, но это будет очень похоже на настройку. Скорее всего, у меня будет массив с 60 значениями, а другой - всего 30. Это потому, что я строю частотную подметальную машину, а на более низких частотах я могу позволить себе больше образцов, поскольку у меня больше часов, чтобы играть, но на более высоких частотах я очень привязан к времени.

Я действительно понимаю, что я могу заставить его работать с более низкой частотой дискретизации, но я не хочу проходить менее 30 выборок за период. Я не думаю, что указатель на массив делает его медленнее, поскольку сборка получает значение из массива, а сборка для получения значения из указателя на массив кажется одинаковым (что имеет смысл).

На самой высокой частоте, которую мне приходится производить, мне сказали, что я смогу заставить ее работать примерно с 30 образцами за период синусоидальной волны. На данный момент с 30 образцами, самый быстрый из них будет работать примерно на половину необходимой максимальной частоты, что, я думаю, означает, что мое прерывание должно работать в два раза быстрее.

Для этого кода при имитации требуется 65 циклов. Опять же, мне сказали, что я смогу довести его до 30 циклов в лучшем случае.

Это ASM код, созданный, с моим мышлением о том, что каждая строка делает рядом с ней:

ISR(TIMER1_COMPA_vect) { push r1 push r0 in r0, 0x3f ; save status reg push r0 eor r1, r1 ; generates a 0 in r1, used much later push r24 push r25 push r30 push r31 ; all regs saved PORTD = amplitudes[amplitudePlace]; lds r24, 0x00C8 ; r24 <- amplitudePlace I’m pretty sure lds r30, 0x00B4 ; these two lines load in the address of the lds r31, 0x00B5 ; array which would explain why it’d a 16 bit number ; if the atmega8 uses 16 bit addresses add r30, r24 ; aha, this must be getting the ADDRESS OF THE element adc r31, r1 ; at amplitudePlace in the array. ld r24, Z ; Z low is r30, makes sense. I think this is loading ; the memory located at the address in r30/r31 and ; putting it into r24 out 0x12, r24 ; fairly sure this is putting the amplitude into PORTD amplitudePlace++; lds r24, 0x011C ; r24 <- amplitudePlace subi r24, 0xFF ; subi is subtract imediate.. 0xFF = 255 so I’m ; thinking with an 8 bit value x, x+1 = x - 255; ; I might just trust that the compiler knows what it’s ; doing here rather than try to change it to an ADDI sts 0x011C, r24 ; puts the new value back to the address of the ; variable if(amplitudePlace == numOfAmps) lds r25, 0x00C8 ; r24 <- amplitudePlace lds r24, 0x00B3 ; r25 <- numOfAmps cp r24, r24 ; compares them brne .+4 ; 0xdc <__vector_6+0x54> { amplitudePlace = 0; sts 0x011C, r1 ; oh, this is why r1 was set to 0 earlier } } pop r31 ; restores the registers pop r30 pop r25 pop r24 pop r19 pop r18 pop r0 out 0x3f, r0 ; 63 pop r0 pop r1 reti

Помимо возможно с использованием меньшего количества регистров прерываний, так что у меня меньше PUSH/хлопков я действительно могу Посмотрите, где этот код сборки неэффективен.

Мое другое мнение - возможно, утверждение if можно было бы избавиться, если бы я мог решить, как получить n-битовый int-тип данных в C, чтобы число обернулось, когда оно достигнет конца? Под этим я имею в виду, что у меня было бы 2^n - 1 выборки, а затем переменная амплитуды Place просто продолжала бы подсчитывать, так что, когда она достигнет 2^n, она переполнится и будет сброшена до нуля.

Я попытался смоделировать код без бит if, и в то время как он улучшил скорость, потребовалось всего около 10 циклов, так что это было около 55 циклов для одного исполнения, которое все еще недостаточно быстро, к сожалению поэтому мне нужно оптимизировать код еще больше, что сложно рассмотреть, так как это всего лишь 2 строки!

Моя единственная реальная мысль - посмотреть, могу ли я хранить статические таблицы поиска где-нибудь, для чего требуется меньше тактовых циклов? Инструкции LDS, которые он использует для доступа к массиву, я думаю, что все это занимает 2 цикла, поэтому я, вероятно, не собираюсь экономить много времени, но на этом этапе я готов попробовать что угодно.

Я полностью потерял место, куда идти. Я не вижу, как я мог бы сделать свой C-код более эффективным, но я только новичок в этом, поэтому я мог что-то упустить.Я бы хотел, чтобы какая-то помощь .. Я понимаю, что это довольно специфическая и связанная с этим проблема, и обычно я стараюсь избегать задавать такие вопросы здесь, но я долгое время работал над этим, Я действительно сделаю любую помощь, которую я смогу получить.

stackoverrun.com

avr-gcc: опции компилятора C для микроконтроллеров AVR | avr

В этой статье рассматриваются специфичные для AVR особенности использования инструментов GNU (перевод документации по компилятору C avr-gcc, ассемблеру avr-as, линкеру/компоновщику avr-ld). Обычно документация для этих инструментов довольно объемная, и поставляется в виде файлов texinfo. Опции командной строки более детально описаны в соответствующей документации.

[Опции компилятора C avr-gcc]

Опции, специфичные для разновидности микроконтроллера AVR (Machine-specific options)

Интерфейс компилятора C avr-gcc распознает опции, относящиеся к микроконтроллеру. В дополнение к макросам препроцессора, показанных в таблице далее, препроцессор также определяет макросы __AVR и __AVR__ (со значением 1), когда компилируется цель AVR (AVR target). Макросы AVR будут определены в стандартном режиме gnu89 (по умолчанию) и в режиме gnu99, но не будут определены в режимах c89 и c99.

-mmcu=architecture

В поле architecture опции закодирован тип микроконтроллера. Сейчас известны следующие архитектуры:

Архитектура Макросы Описание
avr1 __AVR_ARCH__=1__AVR_ASM_ONLY____AVR_2_BYTE_PC__ (2) Простое ядро CPU, поддерживается только ассемблер
avr2 __AVR_ARCH__=2__AVR_2_BYTE_PC__ (2) "Классическое" ядро CPU, до 8 килобайт ROM
avr25 (1) __AVR_ARCH__=25__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_2_BYTE_PC__ (2) "Классическое" ядро CPU с поддержкой инструкций 'MOVW' и 'LPM Rx, Z[+]', до 8 килобайт ROM
avr3 __AVR_ARCH__=3__AVR_MEGA__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_2_BYTE_PC__ (2) "Классическое" ядро CPU, от 16 до 64 килобайт ROM
avr31 __AVR_ARCH__=31__AVR_MEGA__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_HAVE_RAMPZ__ (4)__AVR_HAVE_ELPM__ (4)__AVR_2_BYTE_PC__ (2) "Классическое" ядро CPU, 128 килобайт ROM
avr35 (3) __AVR_ARCH__=35__AVR_MEGA__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_2_BYTE_PC__ (2) "Классическое" ядро CPU с поддержкой инструкций 'MOVW' и 'LPM Rx, Z[+]', от 18 до 64 килобайт ROM
avr4 __AVR_ARCH__=4__AVR_ENHANCED__ (5)__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_HAVE_MUL__ (1)__AVR_2_BYTE_PC__ (2) "Усиленное (enhanced)" ядро CPU, до 8 килобайт ROM
avr5 __AVR_ARCH__=5__AVR_MEGA__ (5)__AVR_ENHANCED__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_HAVE_MUL__ (1)__AVR_2_BYTE_PC__ (2) "Усиленное (enhanced)" ядро CPU, от 16 до 64 килобайт ROM
avr51 __AVR_ARCH__=51__AVR_MEGA__ (5)__AVR_ENHANCED__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_HAVE_MUL__ (1)__AVR_HAVE_RAMPZ__ (4)__AVR_HAVE_ELPM__ (4)__AVR_HAVE_ELPMX__ (4)__AVR_2_BYTE_PC__ (2) "Усиленное (enhanced)" ядро CPU, 128 килобайт ROM
avr6 (2) __AVR_ARCH__=6__AVR_MEGA__ (5)__AVR_ENHANCED__ (5)__AVR_HAVE_JMP_CALL__ (4)__AVR_HAVE_MOVW__ (1)__AVR_HAVE_LPMX__ (1)__AVR_HAVE_MUL__ (1)__AVR_HAVE_RAMPZ__ (4)__AVR_HAVE_ELPM__ (4)__AVR_HAVE_ELPMX__ (4)__AVR_3_BYTE_PC__ (2) "Усиленное (enhanced)" ядро CPU, 256 килобайт ROM

Примечания:(1) Появилось в GCC 4.2(2) Неофициальный патч для GCC 4.1(3) Появилось в GCC 4.2.3(4) Появилось в GCC 4.3(5) Устарело.

По умолчанию генерируется код для архитектуры avr2.

Внимание: когда используется опция -mmcu=architecture, но не опция -mmcu=MCU, подключаемый файл <avr/io.h> работать не будет, поскольку он не сможет выбрать определения (definitions) для конкретного типа микроконтроллера AVR.

-mmcu=MCU type

Сейчас avr-gcc понимает следующие типы микроконтроллеров (MCU types), перечисленные в таблице. В таблице также перечислены соответствующие типы архитектур для указанных микроконтроллеров, и показаны макросы препроцессора, которые будут заданы при использовании опции -mmcu.

Внимание, очень важно: тип микроконтроллера нужно указывать в нижнем регистре, т. е. маленькими буквами! Не надо умничать, и задавать что-то типа -mmcu=ATmega32U4, компилятор avr-gcc Вас не поймет и выдаст ошибку (надо было задать -mmcu=atmega32u4):

myfile.c:1:0: error: unrecognized argument to -mmcu= option: 'ATmega32U4' myfile.c:1:0: note: See --target-help for supported MCUs

Итак, поддерживаемые имена MCU для опции -mmcu:

Архитектура Имя MCU

Макрос

avr1 at90s1200 __AVR_AT90S1200__
attiny11 __AVR_ATtiny11__
attiny12 __AVR_ATtiny12__
attiny15 __AVR_ATtiny15__
attiny28 __AVR_ATtiny28__
avr2 at90s2313 __AVR_AT90S2313__
at90s2323 __AVR_AT90S2323__
at90s2333 __AVR_AT90S2333__
at90s2343 __AVR_AT90S2343__
attiny22 __AVR_ATtiny22__
attiny26 __AVR_ATtiny26__
at90s4414 __AVR_AT90S4414__
at90s4433 __AVR_AT90S4433__
at90s4434 __AVR_AT90S4434__
at90s8515 __AVR_AT90S8515__
at90c8534 __AVR_AT90C8534__
at90s8535 __AVR_AT90S8535__
avr2/avr25 (1) at86rf401 __AVR_AT86RF401__
ata6289 __AVR_ATA6289__
attiny13 __AVR_ATtiny13__
attiny13a __AVR_ATtiny13A__
attiny2313 __AVR_ATtiny2313__
attiny2313a __AVR_ATtiny2313A__
attiny24 __AVR_ATtiny24__
attiny24a __AVR_ATtiny24A__
attiny25 __AVR_ATtiny25__
attiny261 __AVR_ATtiny261__
attiny261a __AVR_ATtiny261A__
attiny4313 __AVR_ATtiny4313__
attiny43u __AVR_ATtiny43U__
attiny44 __AVR_ATtiny44__
attiny44a __AVR_ATtiny44A__
attiny45 __AVR_ATtiny45__
attiny461 __AVR_ATtiny461__
attiny461a __AVR_ATtiny461A__
attiny48 __AVR_ATtiny48__
attiny84 __AVR_ATtiny84__
attiny84a __AVR_ATtiny84A__
attiny85 __AVR_ATtiny85__
attiny861 __AVR_ATtiny861__
attiny861a __AVR_ATtiny861A__
attiny87 __AVR_ATtiny87__
attiny88 __AVR_ATtiny88__
avr3 atmega603 __AVR_ATmega603__
at43usb355 __AVR_AT43USB355__
avr3/avr31 (3) atmega103 __AVR_ATmega103__
at43usb320 __AVR_AT43USB320__
avr3/avr35 (2) at90usb82 __AVR_AT90USB82__
at90usb162 __AVR_AT90USB162__
atmega8u2 __AVR_ATmega8U2__
atmega16u2 __AVR_ATmega16U2__
atmega32u2 __AVR_ATmega32U2__
attiny167 __AVR_ATtiny167__
avr3 at76c711 __AVR_AT76C711__
avr4 atmega48 __AVR_ATmega48__
atmega48a __AVR_ATmega48A__
atmega48p __AVR_ATmega48P__
atmega8 __AVR_ATmega8__
atmega8515 __AVR_ATmega8515__
atmega8535 __AVR_ATmega8535__
atmega88 __AVR_ATmega88__
atmega88a __AVR_ATmega88A__
atmega88p __AVR_ATmega88P__
atmega88pa __AVR_ATmega88PA__
atmega8hva __AVR_ATmega8HVA__
at90pwm1 __AVR_AT90PWM1__
at90pwm2 __AVR_AT90PWM2__
at90pwm2b __AVR_AT90PWM2B__
at90pwm3 __AVR_AT90PWM3__
at90pwm3b __AVR_AT90PWM3B__
at90pwm81 __AVR_AT90PWM81__
avr5 at90can32 __AVR_AT90CAN32__
at90can64 __AVR_AT90CAN64__
at90pwm216 __AVR_AT90PWM216__
at90pwm316 __AVR_AT90PWM316__
at90scr100 __AVR_AT90SCR100__
at90usb646 __AVR_AT90USB646__
at90usb647 __AVR_AT90USB647__
at94k __AVR_AT94K__
atmega16 __AVR_ATmega16__
atmega161 __AVR_ATmega161__
atmega162 __AVR_ATmega162__
atmega163 __AVR_ATmega163__
atmega164a __AVR_ATmega164A__
atmega164p __AVR_ATmega164P__
atmega165 __AVR_ATmega165__
atmega165a __AVR_ATmega165A__
atmega165p __AVR_ATmega165P__
atmega168 __AVR_ATmega168__
atmega168a __AVR_ATmega168A__
atmega168p __AVR_ATmega168P__
atmega169 __AVR_ATmega169__
atmega169a __AVR_ATmega169A__
atmega169p __AVR_ATmega169P__
atmega169pa __AVR_ATmega169PA__
atmega16a __AVR_ATmega16A__
atmega16hva __AVR_ATmega16HVA__
atmega16hva2 __AVR_ATmega16HVA2__
atmega16hvb __AVR_ATmega16HVB__
atmega16hvbrevb __AVR_ATmega16HVBREVB__
atmega16m1 __AVR_ATmega16M1__
atmega16u4 __AVR_ATmega16U4__
atmega32 __AVR_ATmega32__
atmega323 __AVR_ATmega323__
atmega324a __AVR_ATmega324A__
atmega324p __AVR_ATmega324P__
atmega324pa __AVR_ATmega324PA__
atmega325 __AVR_ATmega325__
atmega325a __AVR_ATmega325A__
atmega325p __AVR_ATmega325P__
atmega3250 __AVR_ATmega3250__
atmega3250a __AVR_ATmega3250A__
atmega3250p __AVR_ATmega3250P__
atmega328 __AVR_ATmega328__
atmega328p __AVR_ATmega328P__
atmega329 __AVR_ATmega329__
atmega329a __AVR_ATmega329A__
atmega329p __AVR_ATmega329P__
atmega329pa __AVR_ATmega329PA__
atmega3290 __AVR_ATmega3290__
atmega3290a __AVR_ATmega3290A__
atmega3290p __AVR_ATmega3290P__
atmega32c1 __AVR_ATmega32C1__
atmega32hvb __AVR_ATmega32HVB__
atmega32hvbrevb __AVR_ATmega32HVBREVB__
atmega32m1 __AVR_ATmega32M1__
atmega32u4 __AVR_ATmega32U4__
atmega32u6 __AVR_ATmega32U6__
atmega406 __AVR_ATmega406__
atmega64 __AVR_ATmega64__
atmega640 __AVR_ATmega640__
atmega644 __AVR_ATmega644__
atmega644a __AVR_ATmega644A__
atmega644p __AVR_ATmega644P__
atmega644pa __AVR_ATmega644PA__
atmega645 __AVR_ATmega645__
atmega645a __AVR_ATmega645A__
atmega645p __AVR_ATmega645P__
atmega6450 __AVR_ATmega6450__
atmega6450a __AVR_ATmega6450A__
atmega6450p __AVR_ATmega6450P__
atmega649 __AVR_ATmega649__
atmega649a __AVR_ATmega649A__
atmega6490 __AVR_ATmega6490__
atmega6490a __AVR_ATmega6490A__
atmega6490p __AVR_ATmega6490P__
atmega649p __AVR_ATmega649P__
atmega64c1 __AVR_ATmega64C1__
atmega64hve __AVR_ATmega64HVE__
atmega64m1 __AVR_ATmega64M1__
m3000 __AVR_M3000__
avr5/avr51 (3) at90can128 __AVR_AT90CAN128__
at90usb1286 __AVR_AT90USB1286__
at90usb1287 __AVR_AT90USB1287__
atmega128 __AVR_ATmega128__
atmega1280 __AVR_ATmega1280__
atmega1281 __AVR_ATmega1281__
atmega1284p __AVR_ATmega1284P__
avr6 atmega2560 __AVR_ATmega2560__
atmega2561 __AVR_ATmega2561__
avrxmega2 atxmega16a4 __AVR_ATxmega16A4__
atxmega16d4 __AVR_ATxmega16D4__
atxmega32a4 __AVR_ATxmega32A4__
atxmega32d4 __AVR_ATxmega32D4__
avrxmega4 atxmega64a3 __AVR_ATxmega64A3__
atxmega64d3 __AVR_ATxmega64D3__
avrxmega5 atxmega64a1 __AVR_ATxmega64A1__
atxmega64a1u __AVR_ATxmega64A1U__
avrxmega6 atxmega128a3 __AVR_ATxmega128A3__
atxmega128d3 __AVR_ATxmega128D3__
atxmega192a3 __AVR_ATxmega192A3__
atxmega192d3 __AVR_ATxmega192D3__
atxmega256a3 __AVR_ATxmega256A3__
atxmega256a3b __AVR_ATxmega256A3B__
atxmega256d3 __AVR_ATxmega256D3__
avrxmega7 atxmega128a1 __AVR_ATxmega128A1__
atxmega128a1u __AVR_ATxmega128A1U__
avrtiny10 attiny4 __AVR_ATtiny4__
attiny5 __AVR_ATtiny5__
attiny9 __AVR_ATtiny9__
attiny10 __AVR_ATtiny10__
attiny20 __AVR_ATtiny20__
attiny40 __AVR_ATtiny40__

Примечания:(1) Архитектура 'avr25' появилась в GCC 4.2(2) Архитектура 'avr35' появилась в GCC 4.2.3(3) Архитектуры 'avr31' и 'avr51' появились в in GCC 4.3

-morder1-morder2

Изменение порядка назначения регистров. По умолчанию порядок следующий:

r24, r25, r18, r19, r20, r21, r22, r23, r30, r31, r26, r27, r28, r29, r17, r16, r15, r14, r13, r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r0, r1

order1 использует следующий порядок:

r18, r19, r20, r21, r22, r23, r24, r25, r30, r31, r26, r27, r28, r29, r17, r16, r15, r14, r13, r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r0, r1

order2 использует следующий порядок:

r25, r24, r23, r22, r21, r20, r19, r18, r30, r31, r26, r27, r28, r29, r17, r16, r15, r14, r13, r12, r11, r10, r9, r8, r7, r6, r5, r4, r3, r2, r1, r0

-mint8

Подразумевает, что тип int соответствует 8-битному целому (8-bit integer). Имейте в виду, что библиотекой avr-libc реально не поддерживается такой режим, так что он обычно не должен использоваться. По умолчанию в качестве int используются 16-битные целые числа (16-bit integers).

-mno-interrupts

Генерирует код, который меняет указатель стека (stack pointer) без запрета прерываний. Обычно состояние регистра статуса SREG (status register SREG) сохраняется во временном регистре, прерывания запрещаются в во время изменения указателя стека, и значение регистра SREG восстанавливается. Если указать эту опцию, также будет определен макрос препроцессора __NO_INTERRUPTS__ в значении 1.

-mcall-prologues

Для функции пролога/эпилога (function prologue/epilogue) будут использоваться подпрограммы (subroutines). Для сложных функций, которые используют много регистров (состояние которых нужно сохранить/восстановить на входе/выходе функции), эта опция будет экономить некоторое количество памяти кода программ взамен на некоторое снижение быстродействия (increase execution time).

-mtiny-stack

"Маленький стек". В указателе стека изменяются только младшие 8 бит.

-mno-tablejump

Эта опция устарела, используйте вместо неё опцию -fno-jump-tables.

-mshort-calls

Для микроконтроллеров с памятью размером >8K будут использованы команды перехода rjmp/rcall (имеющие ограниченный адресный диапазон). На архитектурах avr2 и avr4 (у которых размер памяти flash меньше 8 кбайт) это всегда имеет место. На архитектурах avr3 и avr5 вызовы подпрограмм и переходы по адресу, которые выходят за этот диапазон, будут по умолчанию использовать инструкции jmp/call, которые могут закрыть все адресное пространство, однако это потребует большего размера flash ROM и увеличивает время выполнения.

-mrtl

Будет выведен дамп внутреннего результата компиляции (internal compilation result, так называемый RTL) в комментариях к генерируемому коду ассемблера. Эта опция используется для отладки avr-gcc.

-msize

Будет выведен дамп адреса, размера, и относительной стоимости каждого оператора в комментариях к генерируемому коду ассемблера. Эта опция используется для отладки avr-gcc.

-mdeb

Генерирует вывод некоторой отладочной информации в поток stderr (обычно консоль).

Выборочное описание некоторых главных опций компилятора (general compiler options)

Следующие общие опции компилятора gcc (general gcc options) могут представлять некоторый интерес для пользователей AVR.

-On

Уровень оптимизации n. Увеличение числа n означает увеличение степени оптимизации, при этом уровень 0 полностью отключает оптимизацию (это является поведением по умолчанию, когда опция -O отсутствует). Специальная опция -Os означает включение всех оптимизаций -O2, которые не должны привести к увеличению размера кода.

Имейте в виду, что при использовании -O3 компилятор gcc будет пытаться перевести во встраиваемый код (inline) все "простые" функции. Для микроконтроллеров AVR (AVR target) это приведет к пагубному росту используемого пространства под код. Единственно полезная оптимизация, которая также включается в -O3, является -frename-registers, однако эту опцию лучше активировать отдельно вручную.

Если просто указать опцию -O, то это будет эквивалентно указанию опции -O1.

Имейте в виду, что полное выключение оптимизации предотвратит выдачу некоторых предупреждений, так как генерация их зависит от шагов анализа кода, которые будут выполняться только при оптимизации (нерабочий код unreachable code, неиспользуемые переменные unused variables). См. также соответствующий вопрос в FAQ [1], который относится к отладке оптимизированного кода.

-Wa,assembler-options-Wl,linker-options

Передать список опций ассемблеру и линкеру соответственно.

-g

Сгенерировать отладочную информацию (debugging information), которая может быть использована отладчиком avr-gdb.

-ffreestanding

Предполагается использование "freestanding" environment (независимое окружение), что соответствует стандарту C. Это отключит все автоматически генерируемые встроенные функции (которые все еще можно вызвать с префиксом __builtin_, предварительно добавленным к действительному имени функции). Это также заставляет компилятор не жаловаться, когда функция main() декларируется как возвращающая void, что больше соответствует среде выполнения микроконтроллера, где приложение не может предоставить обоснованный код возврата для среды выполнения (в большинстве случаев из функции main() никогда не происходит возврат). Однако это также отключит все оптимизации, обычно выполняемые компилятором, который предполагает, что функции, известные под определенным именем, ведут себя как описано в стандарте. Например, E. g., применение функции strlen() к литеральной строке обычно заставляет компилятор просто заменить вызов функции действующей длиной строки, тогда как с опцией -ffreestanding, во время выполнения всегда будет вызвана strlen().

-funsigned-char

Заменяет любой неквалифицированный специально тип char на unsigned char. Без этой опции по умолчанию char соответствует signed char.

-funsigned-bitfields

Заменяет любой неквалифицированный специально тип битовых полей (unqualified bitfield type) на беззнаковый (unsigned). По умолчанию он со знаком (signed).

-fshort-enums

Выделение перечислимому типу (enum) только такое количество байтов, сколько требуется для объявленного диапазона возможных значений. В частности, перечислимый тип (enum type) будет эквивалентен самому маленькому целочисленному типу, который имеет достаточный размер.

-fpack-struct

Плотная упаковка всех полей структуры (structure members) без пустого выравнивающего байтового пространства.

-fno-jump-tables

Не генерировать инструкций табличного перехода (tablejump instructions). По умолчанию таблицы переходов могут быть использованы для оптимизации операторов switch. Когда эта возможность выключена, вместо таблицы переходов будет использована последовательность команд сравнения. Таблицы переходов (Jump tables) обычно в среднем работают быстрее, однако в частности для операторов switch, которые должны делать переход по метке default, это может привести к некоторой бесполезной потере памяти кода (flash memory).

Внимание: инструкции табличного перехода tablejump используют команду ассемблера LPM для доступа к таблицам перехода. Всегда используйте -fno-jump-tables, если Вы компилируете бутлоадер (bootloader) для устройств, которые имеют больше 64 килобайта памяти кода.

[Опции для ассемблера avr-as]

Опции, специфичные для разновидности микроконтроллера AVR (Machine-specific assembler options)

-mmcu=architecture-mmcu=MCU name

Ассемблер avr-as понимает те же самые опции -mmcu=, используемые для avr-gcc. По умолчанию подразумевается архитектура avr2, однако это может быть отменено использованием подходящей псевдоинструкции .arch внутри исходного кода программы ассемблера.

-mall-opcodes

Выключает проверку кода операций (opcode checking) для действующего типа микроконтроллера (actual MCU type) и позволяет ассемблировать любой возможный для AVR код операции (opcode).

-mno-skip-bug

Отключает выдачу предупреждения, когда происходит попытка пропуска команды/инструкции, состоящей из 2 слов (2-word instruction) с командой CPSE/SBIC/SBIS/SBRC/SBRS. Раннее выпущенные микроконтроллеры AVR пострадали от аппаратной ошибки, из-за которой эти инструкции не могли быть должным образом пропущены.

-mno-wrap

Запрет использования повторения инструкций RJMP/RCALL для перехода по целевому адресу для микроконтроллеров, у которых память кода больше 8 килобайт.

--gstabs

Генерировать символы отладки .stabs для строк исходного кода ассемблера. Это позволит отладчику avr-gdb выполнять трассировку по исходному коду ассемблера. Эта опция не должна использоваться, когда ассемблируются исходные коды, которые были сгенерированы компилятором C; эти файлы уже содержат соответствующую информацию о номерах строк файлов исходного кода C.

-a[cdhlmns=file]

Включить листинг ассемблера. Подопции следующие:

c пропустить ложные условные выражения (omit false conditionals)d пропустить директивы отладки (omit debugging directives)h включить код высокого уровня (include high-level source)l включить код ассемблера (include assembly)m включить развертывание кода макросов (include macro expansions)n пропустить обработку форм (omit forms processing)s включить символы (include symbols)=file установить имя для файла листинга

Различные подопции можно комбинировать в один список опций -a; в этом случае =file должна быть последней в этом списке.

Примеры опций ассемблера, передаваемых через компилятор C

Помните, что опции ассемблера могут быть переданы через интерфейс (командную строку) компилятора C опцией -Wa (см. выше). Так, чтобы включить исходный код C в файл листинга foo.lst, когда компилируется foo.c, должна использоваться следующая командная строка:

$ avr-gcc -c -O foo.c -o foo.o -Wa,-ahls=foo.lst

Чтобы сначала передать ассемблерный файл через препроцессор C и дать команду ассемблеру сгенерировать отладочную информацию с номерами строк для него, нужно использовать следующую командную строку:

$ avr-gcc -c -x assembler-with-cpp -o foo.o foo.S -Wa,--gstabs

Имейте в виду, что системы Unix имеют чувствительную к регистру символов файловые системы (case-distinguishing file systems), так что указание имени файла с суффиксом (расширением) .S (буква S в верхнем регистре, upper-case) сделает автоматическое подразумевание компилятором -x assembler-with-cpp, в то время как использование .s передало бы файл напрямую ассемблеру (не будет производится препроцессинг). При переносе проектов с Linux на Windows (для компиляции через avr-gcc пакета WinAVR) регистр имен файлов служит частым источником неприятных ошибок, требующих ручной корректировки makefile.

[Управление линковщиком avr-ld]

Выбранные опции линкера

Так как для линкера avr-ld нет специфичных для микроконтроллера AVR опций (machine-specific options), то пользователя AVR может заинтересовать некоторое количество стандартных опций.

-lname

Указание на использование архива библиотеки с именем libname.a, и использования его для разрешения нераспознанных в настоящий момент символов. Файл библиотеки будет искаться по пути, состоящем из встроенных записей, указанных во время компиляции (например, /usr/local/avr/lib на системах Unix), возможно расширенных записями пути, указанными опциями -L (которые в командной строке должны предшествовать опциям -l).

-Lpath

Дополнительный путь для поиска архивных библиотек, запрашиваемых опциями -l.

--defsym symbol=expr

Определение глобального символа symbol, используя для его значения выражение expr.

-M

Напечатать карту линковки (linker map) в stdout (обычно консоль).

-Map mapfile

Вывести карту линковки (linker map) в файл mapfile.

--cref

Вывести таблицу перекрестных ссылок (cross reference table) в файл карты линковки map file (в случае наличия опции -Map), или в stdout (обычно консоль).

--section-start sectionname=org

Начать секцию с именем sectionname по абсолютному адресу org.

-Tbss org-Tdata org-Ttext org

Начать соответственно секцию bss, data, или text по адресу org.

-T scriptfile

Использование файла scriptfile в качестве скрипта линковки (linker script), который заменит скрипт линковки по умолчанию (default linker script). Скрипты линковки по умолчанию размещены в местах, которые зависят от системы (например /usr/local/avr/lib/ldscripts на системах Unix), и состоит из имени архитектуры AVR (от avr2 до avr5) с добавленным суффиксом .x. Скрипты линковки описывают, как различные секции памяти должны быть слинкованы друг с другом.

Передача опций линкера (компоновщика) через компилятор C

По умолчанию все неизвестные аргументы командной строки (unknown non-option arguments) компилятора avr-gcc (например, все аргументы имени файла, которые не имеют суффикса, поддерживаемого avr-gcc) передаются напрямую линкеру. Таким образом все файлы, оканчивающиеся на .o (объектные файлы) и на .a (объектные библиотеки) передаются линкеру.

Системные библиотеки обычно передаются не по их явном имени файла, а через применение опции -l, которая использует аббревиатурную форму имени файла архива (см. выше). Линкер avr-libc поставляется с двумя системными библиотеками libc.a и libm.a. В то время как стандартная библиотека libc.a будет всегда использоваться для поиска нераспознанных ссылок (unresolved references), когда линкер был запущен через командную строку компилятора C (например, всегда подразумевается неявная опция -lc), библиотека математики libm.a нуждается в явном запросе с использованием -lm. См. также запись в FAQ [2], в котором это объясняется подробнее.

По сложившейся традиции Makefiles использует макрос типа LDLIBS для отслеживания -l (и возможно -L) опций, которые должны быть добавлены только к командной строке компилятора C только тогда, когда линкуется конечный бинарный файл. В отличие от него макрос LDFLAGS используется для хранения других опций командной строки для компилятора C, которые должны быть переданы как опции во время стадии линковки. Разница состоит в том, какие опции будут помещены раньше в командную строку, в то время как библиотеки должны быть помещены в конец, так как они должны использоваться только для того, чтобы разрешить глобальные символы, которые все еще не разрешены в этой точке сборки.

Специфические флаги линкера можно передать компилятору C через командную строку с использованием опции -Wl (см. выше). Эта опция требует, чтобы не было никаких добавленных пробелов в этой опции линкера, в то время как некоторые опции линкера выше (наподобие -Map или --defsym) требуют пробелов. В этих ситуациях пробелы можно заменить эквивалентными знаками минуса. Например, следующая командная строка может использоваться для компиляции foo.c в исполняемый файл, и для получения карты линковки (link map), которая содержит список перекрестных ссылок (cross-reference list) в файле foo.map:

$ avr-gcc -O -o foo.out -Wl,-Map=foo.map -Wl,--cref foo.c

Альтернативно запятая как заполнитель будет заменена пробелом перед передачей прежде, чем будет передана компоновщику. Например, для устройства с внешней SRAM должна использоваться следующая командная строка, которая укажет компоновщику поместить сегмент данных по адресу 0x2000 в SRAM:

$ avr-gcc -mmcu=atmega128 -o foo.out -Wl,-Tdata,0x802000

См. документацию по секции данных (data section [3]) чтобы стало ясно, для чего нужно 0x800000 добавить к действительному значению адреса. Имейте в виду, что стек все еще останется во внутреннем RAM благодаря символу __stack, который предоставлен кодом запуска среды выполнения. Вероятно, что это все равно хорошая идея (так как доступ к внутренней RAM быстрее), и даже требуемый для некоторых ранних микроконтроллеров, которые имели аппаратные ошибки, из-за которых нельзя было использовать стек во внешнем RAM. Обратите внимание также, что куча heap для malloc() будет все еще размещена после всех переменных в секции данных (data section), так что в этой ситуации не будет коллизии stack/heap.

Чтобы поменять место размещения стека по умолчанию (в верхней части внутренней RAM), можно поменять значение символа __stack в командной строке компоновщика. Поскольку компоновщик обычно вызывается через командную строку компилятора (compiler frontend), этого можно добиться использованием опции компилятора наподобие

-Wl,--defsym=__stack=0x8003ff

Эта опция сделает код, который использует пространство для стека начиная с адреса RAM 0x3ff, вниз. Тогда доступный размер области стека зависит от нижней границы адреса внутреннего RAM для конкретной модели микроконтроллера AVR. Приложение (программа firmware AVR) обязана удостовериться, что стек не вырос за пределы границы, а также должна принять меры, чтобы стек не столкнулся с памятью, выделенной под переменные компилятором (секции .data и .bss).

[Ссылки]

1. Why does the PC randomly jump around when single-stepping through my program in avr-gdb? site:nongnu.org - почему счетчик команд PC случайно перепрыгивает по коду, когда выполняется пошаговое выполнение моей программы в avr-gdb?2. I get "undefined reference to..." for functions like "sin()" site:nongnu.org - я получил сообщение "undefined reference to..." для функции наподобие "sin()". 3. The .data Section site:nongnu.org - описание секции .data.

microsin.net

Портирование кода IAR на AVR GCC | avr

Язык C был изначально разработан с целью портирования кода. Имеется два вида портирования - перенос приложения на другую платформу (другая операционная система и/или процессор), и перенос приложения на другой компилятор. Портирование на другой компилятор может быть усложнено, когда программа относится ко встраиваемым системам (embedded system - приборы и автоматика, построенные на микроконтроллере). Например, стандарт языка C странным образом не определяет правила для декларирования и определения обработчиков прерывания (Interrupt Service Routine, ISR). Разные компиляторы это делают по-разному, и используют разные регистры микроконтроллера, а также используют нестандартные структуры языка. В этой статье описаны некоторые методы и указания по портированию программ для AVR (код firmware) с проприетарного компилятора IAR (IDE проектирования IAR Embedded Workbench for AVR) на тулчейн GNU (AVR GCC, на платформе Windows это пакет WinAVR). Имейте в виду, что статья может оказаться неполным набором указаний по такому портированию. Здесь и далее перевод руководства "Porting From IAR to AVR GCC" с сайта www.nongnu.org.

[Регистры AVR]

Заголовочные файлы, описывающие адресное пространство регистров ввода/вывода (IO header files), содержат мнемонические идентификаторы для всех имен регистров и имен бит для каждой конкретной модели микроконтроллера. IAR имеет индивидуальные файлы заголовков для каждого процессора, и они должны быть подключены к коду, когда в нем используются имена регистров. Название подключаемого заголовочного файла часто связано с моделью применяемого микроконтроллера. Например:

#include < iom169.h >

Примечание: IAR не всегда использует те же самые имена регистров или бит, которые определены в даташите Atmel для микроконтроллеров AVR.

AVR GCC также имеет индивидуальные заголовочные файлы для каждого процессора. Однако реальный тип процессора указывается как опция командной строки компилятора (опция -mmcu=тип_микроконтроллера). Это часто делается через переменные makefile, как например MCU=atmega32. Такой вариант реализации позволяет Вам подключать всегда один и тот же заголовочный файл для любого типа процессора:

#include < avr/io.h >

Примечание: прямой слеш в имени файла avr/io.h для разделения подпапок может использоваться и в дистрибутивах тулчейна для Windows, и это является рекомендуемым методом для подключения файла заголовка IO (несмотря на то, что в именах путей Windows изначально использовался обратный слеш \). Использование прямого слеша / предпочтительнее, так как обратный слеш используется для указания символов типа \0, \n, \r и т. п.

Компилятор (из опции командной строки -mcu) знает тип процессора, и может с помощью одного вышеуказанного файла подключить тот индивидуальный заголовочный файл IO, который действительно необходим. Достоинство такого метода в том, что Вам нужно указать только один стандартный заголовочный файл avr/io.h, и можете проще портировать Ваше приложение на другой тип процессора без необходимости корректировки всех модулей для подключения нового файла заголовка IO.

Тулчейн AVR (WinAVR + библиотека avr-libc) пытается точно придерживаться оригинальных названий регистров и бит, которые можно найти в даташите AVR. Может быть некоторое несоответствие между именами регистров в IO заголовках IAR по сравнению с IO заголовками AVR GCC.

[Обработчики прерывания ISR (Interrupt Service Routines)]

Как уже упоминалось, язык C не имеет собственного стандарта для определения ISR. Так что у каждого компилятора может оказаться свой собственный способ, как это нужно делать. IAR декларирует ISR так:

#pragma vector=TIMER0_OVF_vect __interrupt void MotorPWMBottom() { // код обработчика прерывания }

В AVR GCC Вы декларируете ISR так:

ISR(PCINT1_vect) { // код обработчика прерывания }

AVR GCC использует макрос для определения ISR. Этот макрос требует подключения файла заголовка:

#include < avr/interrupt.h >

Имена разных векторов прерываний можно найти в индивидуальном файле заголовка IO, который должен подключаться через файл avr/io.h.

Примечание: имена векторов прерываний в AVR GCC были изменены для соответствия именам векторов в IAR. Это значительно упрощает портирование приложений с IAR на AVR GCC.

[Встроенные в язык подпрограммы, соответствующие командам ассемблера (Intrinsic Routines)]

IAR имеет некоторые подпрограммы intrinsic, такие как __enable_interrupts(), __disable_interrupts(), __watchdog_reset().

Эти функции intrinsic компилируются в специальные коды команд ассемблера AVR (SEI, CLI, WDR).

Имеются эквивалентные макросы в AVR GCC, однако все они не размещены в одном заголовочном файле. AVR GCC имеет sei() для замены __enable_interrupts() и cli() для __disable_interrupts(). Оба эти макроса размещены в файле заголовка avr/interrupts.h.

Также AVR GCC имеет макрос wdt_reset(), который заменяет __watchdog_reset(). Однако весь программный интерфейс сторожевого таймера (Watchdog Timer API) в AVR GCC можно найти в заголовочном файле avr/wdt.h.

[Переменные, размещенные в памяти Flash]

Язык C не был разработан для Гарвардской архитектуры процессоров, в которой заданы разные адресные пространства памяти (отдельно для кода и отдельно для переменных). Это означает, что нужны некоторые нестандартные пути для определения переменных, которые размещены в области памяти программ (для AVR это память Flash).

IAR использует нестандартное ключевое слово для декларирования переменной в памяти программ:

__flash int mydata[] = ....

AVR GCC использует атрибуты переменной для достижения того же самого эффекта:

int mydata[] __attribute__((progmem))

Примечание: см. руководство пользователя GCC (GCC User Manual) для дополнительной информации по атрибутам переменной.

Библиотека avr-libc предоставляет удобный макрос для атрибута переменной, размещаемой во Flash:

#include < avr/pgmspace.h > . . . int mydata[] PROGMEM = ....

Примечание: макрос PROGMEM раскрывается в атрибут переменной progmem. Этот макрос требует подключения заголовочного файла avr/pgmspace.h. Это канонический метод для определения переменной в области памяти программ.

Чтобы прочитать данные из flash, используются макросы pgm_read_*(), также заданные в файле avr/pgmspace.h. В этом файле определены все макросы, которые нужны для работы с памятью программ (Flash).

Есть также метод для определения переменных в памяти программ, который является общим для двух компиляторов (IAR и AVR GCC). Создайте заголовочный файл, который имеет следующие определения:

#if defined(__ICCAVR__) // IAR C Compiler #define FLASH_DECLARE(x) __flash x #endif #if defined(__GNUC__) // GNU Compiler #define FLASH_DECLARE(x) x __attribute__((__progmem__)) #endif

Этот кусок кода проверяет версию компилятора (какой компилятор используется - IAR или GCC) и определяет макрос FLASH_DECLARE(x), который будет декларировать переменную в памяти программ, используя подходящий метод для каждого компилятора. Вы можете использовать его так:

FLASH_DECLARE(int mydata[] = ...);

[Функция main() без возврата значения (Non-Returning main)]

Декларирование main() без кода возврата в IAR делается так:

__C_task void main(void) { // код программы }

Чтобы выполнить то же самое в AVR GCC, сделайте следующее:

void main(void) __attribute__((noreturn));   void main(void) { // код программы }

Примечание: см. руководство пользователя GCC (GCC User Manual) для дополнительной информации по атрибутам функции.

В AVR GCC нужно задать прототип для main(), где будет указано, что функция main() имеет тип "noreturn". Затем определите main() как обычно. Имейте в виду, что возвращаемый тип для функции main() теперь будет void.

[Резервирование регистров]

Компилятор IAR позволяет пользователю зарезервировать регистры общего назначения от r15 и ниже путем использования опций компилятора и следующих ключевых слов синтаксиса:

__regvar __no_init volatile unsigned int filteredTimeSinceCommutation @14;

Эта строка блокирует r14 для исключительного использования в Вашем коде под именем переменной "filteredTimeSinceCommutation". Это означает, что компилятор не может использовать этот регистр для генерации общего кода.

Чтобы выполнить то же самое в AVR GCC, сделайте следующее:

register unsigned char counter asm("r3");

Обычно таким способом можно использовать регистры от r2 до r15.

Примечание: не резервируйте r0 или r1, которые используются внутри компилятора для временного хранения данных и для нулевой величины. Резервирование регистров не рекомендуется в AVR GCC, так как это выводит эти регистры из-под контроля компилятора, что может ухудшить качество генерируемого объектного кода (который, кстати говоря, и так хуже кода, генерируемого компилятором IAR). Так что используйте резервирование регистров на собственный страх и риск.

microsin.net

Missed Optimization / AVR / Сообщество EasyElectronics.ru

Missed Optimization
Часто бывает удобно упаковать несколько логически связанных атрибутов в одну POD структуру и работать с ними, как с единым целым. К томе-же, если структура не большая и целиком помещается в регистры, то это должно быть еще и эффективно. Должно, но… Примеров таких структур много, это может быть точка:struct Point { int x; int y; }; Цвет пикселя:struct Color { uint8_t r, g, b; }; Или мы захотим сделать 3-х байтный указатель для AVR:struct Ptr24 { uint16_t low; uint8_t high; }; Давайте разберёмся как компиляторы работают с такими структурам. Разбираться будем на примерах для платформы AVR, так как она хорошо многим знакома и имеет относительно простой и понятный ассемблер, к тому-же выравнивание структур на этой платформе равно 1 и по идее не должно оказывать никакого влияния. Хотя всё описанное в равной степени относится и к другим платформам. Сравнивать будем традиционно компиляторы avr-gcc и IAR.

Структура, которую будем использовать для наших тестов:

enum{ Size = 3}; typedef struct { uint8_t bytes[Size]; } Bar; Есть такой хитрый вид оптимизации SRA — scalar replacement of aggregates, при его использовании компилятор начинает рассматривать структуру не как просто кусок памяти определённого размера, а как сумму полей его составляющую. Это даёт компилятору возможность применить другие виды оптимизации, например, хранить структуру в регистрах, избавится от неиспользуемых полей, эффективно её копировать, и передавать как параметр в функции и т. д. Посмотрим как с этим обстоят дела у наших подопытных на практике.Тест 1. Глобальная переменная:Bar bar; void Foo1() { PORTA = bar.bytes[0]++; PORTB = bar.bytes[1]++; PORTC = bar.bytes[2]++; } Ассемблерный листинг: avr-gcc:void Foo1() { PORTA = bar.bytes[0]++; 92: lds r24, 0x0064 96: out 0x1b, r24 98: subi r24, 0xFF 9a: sts 0x0064, r24 PORTB = bar.bytes[1]++; 9e: lds r24, 0x0065 a2: out 0x18, r24 a4: subi r24, 0xFF a6: sts 0x0065, r24 PORTC = bar.bytes[2]++; aa: lds r24, 0x0066 ae: out 0x15, r24 b0: subi r24, 0xFF b2: sts 0x0066, r24 } b6: 08 95 ret

IAR for AVR:

void Foo1() { PORTA = bar.bytes[0]++; 00000000 LDI R30, LOW(bar) 00000002 LDI R31, (bar) >> 8 00000004 LD R16, Z 00000006 OUT 0x1B, R16 00000008 LD R16, Z 0000000A INC R16 0000000C ST Z, R16 PORTB = bar.bytes[1]++; 0000000E LDD R16, Z+1 00000010 OUT 0x18, R16 00000012 LDD R16, Z+1 00000014 INC R16 00000016 STD Z+1, R16 PORTC = bar.bytes[2]++; 00000018 LDD R16, Z+2 0000001A OUT 0x15, R16 0000001C LDD R16, Z+2 0000001E INC R16 00000020 STD Z+2, R16 } 00000022 RET Здесь всё впорядке, между компиляторями практически паритет — gcc проигрывает 2 байта памяти, но выигрывает 3 такта по скорости.Тест 2. Локальная переменная.void Foo2() { Bar bar = {{1, 2, 3}}; PORTA = bar.bytes[0]; PORTB = bar.bytes[1]; PORTC = bar.bytes[2]; } avr-gcc:void Foo2() { b8: ldi r24, 0x01 ba: out 0x1b, r24 PORTB = bar.bytes[1]; bc: ldi r24, 0x02 be: out 0x18, r24 PORTC = bar.bytes[2]; c0: ldi r24, 0x03 c2: out 0x15, r24 } c4: ret IAR:void Foo2() { 00000000 LDI R30, LOW(`?<Constant {{(uint8_t)'\\001', (uint8_t)'\\002',`) 00000002 LDI R31, (`?<Constant {{(uint8_t)'\\001', (uint8_t)'\\002',`) >> 8 00000004 LPM R16, Z+ 00000006 LPM R17, Z+ 00000008 LPM R18, Z PORTA = bar.bytes[0]; PORTB = bar.bytes[1]; PORTC = bar.bytes[2]; 00000000 OUT 0x1B, R16 00000002 OUT 0x18, R17 00000004 OUT 0x15, R18 00000006 RET } Оптимизатор GCC сработал как надо, а вот IAR перемудрил сам себя, сохранив инициализатор структуры в памяти программ, в данном случае это было не уместно.Тест 3. Передача структуры как параметр функции по значению.void Foo3(Bar i) { PORTA = i.bytes[0]; PORTB = i.bytes[1]; PORTC = i.bytes[2]; } Структура в данном случае должна передоваться в регистрах, что в принципе и происходит. Но вот тут у нас начинаются первые чудеса с avr-gcc:void Foo3(Bar i) { c6: push r29 c8: push r28 ca: r28, 0x3d ; 61 cc: in r29, 0x3e ; 62 ce: subi r28, 0x03 ; 3 d0: out 0x3d, r28 ; 61 PORTA = i.bytes[0]; d2: out 0x1b, r22 ; 27 PORTB = i.bytes[1]; d4: out 0x18, r23 ; 24 PORTC = i.bytes[2]; d6: out 0x15, r24 ; 21 } d8: adiw r28, 0x03 ; 3 da: out 0x3d, r28 ; 61 dc: pop r28 de: pop r29 e0: ret Как видно он совершенно непонятно зачем создал кадр стека и выделил в нем место на 3 байта — как раз размер нашей структуры, но так им и не воспользовался. IAR в дела обстоят гораздо лучше и он генерирует вполне годный код:void Foo3(Bar i) { PORTA = i.bytes[0]; 00000000 OUT 0x1B, R16 PORTB = i.bytes[1]; 00000002 OUT 0x18, R17 PORTC = i.bytes[2]; 00000004 OUT 0x15, R18 } 00000006 RET Тест 4. Передача структуры как параметр функции по значению и ее возврат по значению.Bar Foo4(Bar i) { i.bytes[2] += 10; return i; } Чем дальше в лес, тем интереснее… avr-gcc чудит еще больше, генерируя совсем непотребный (хотя рабочий) код: Bar Foo4(Bar i) { e2: push r29 e4: push r28 e6: in r28, 0x3d ; 61 e8: in r29, 0x3e ; 62 ea: subi r28, 0x06 ; 6 ec: out 0x3d, r28 ; 61 ee: std Y+4, r22 ; 0x04 f0: std Y+5, r23 ; 0x05 i.bytes[2] += 10; return i; f2: subi r24, 0xF6 ; 246 f4: std Y+6, r24 ; 0x06 f6: movw r26, r28 f8: adiw r26, 0x01 ; 1 fa: movw r30, r28 fc: adiw r30, 0x04 ; 4 fe: ldi r24, 0x03 ; 3 100: ld r0, Z+ 102: st X+, r0 104: subi r24, 0x01 ; 1 106: brne .-8 ; 0x100 <Foo4+0x1e> 108: ldd r22, Y+1 ; 0x01 10a: ldd r23, Y+2 ; 0x02 } 10c: ldd r24, Y+3 ; 0x03 10e: ldi r25, 0x00 ; 0 110: adiw r28, 0x06 ; 6 112: out 0x3d, r28 ; 61 114: pop r28 116: pop r29 118: ret Здесь GCC инициализировал кадр стека выделил место под две структуры. Скопировал в первый буфер значения параметра из регистров, модифицировав один нужный байт, потом скопировал первый буфер во второй. После чего извлёк значение из стека опять в регистры, да, да, в те-же в которых оно и было, при вызове функции, вернул стек на место и вышел из функции. Жесть! IAR тут повел себя лучше, но не на много. Он скопировал параметры в стек только один раз :) Ну и за счёт того, что в IAR отдельный стек данных и он уже заранее подготовлен, код получается покомпактней. Но всё равно, зачем копировать параметр в стек и тутже извлекать его обратно?!!!Bar Foo4(Bar i) { 00000000 SBIW R29:R28, 3 00000002 ST Y, R16 00000004 STD Y+1, R17 00000006 STD Y+2, R18 i.bytes[2] += 10; 00000008 LDD R16, Y+2 0000000A SUBI R16, 246 0000000C STD Y+2, R16 return i; 0000000E LD R16, Y 00000010 LDD R18, Y+2 00000012 ADIW R29:R28, 3 00000014 RET } Это явный баг в оптимизаторах обоих компиляторах, только у IAR он несколько нивелирован отдельным стеком данных. В GCC этот баг проявляется на всех платформах, начиная на x86 и заканчивая ARM и AVR и отмечен в баг-трекере более десяти раз под разными названиями. Присутствует начиная с GCC 4.2. и по сей день в GCC 4.6. Причем на платформах чувствительных к выравниванию данных, например на ARM, помимо игрищь со стеком начинаются еще и пляски с выравниванием данных.

И самое интересное, размер структуры в самом начале, я выбрал равным 3 байтам не случайно, если его сделать кратным слову, или двойному слову, например, 2, 4 или 8 байт, то все эти извращения волшебным образом исчезают и GCC начинает генерировать совершенно идеальный код, вида:

Bar Foo4(Bar i) { b8: subi r24, 0xF6 ; 246 ba: ret } В IAR-е размер структуры и выравнивание никак качественно не влияют на генерируемый код, и он упорно продолжает складывать структуру в стек.
Выводы:
1) Не доверяйте компиляторам, всегда проверяйте, что он там нагенерировал. 2) Выравнивайте структуры по размеру слова или двойного слова, даже на архитектурах, теоретически не чувствительных к выравниванию.

we.easyelectronics.ru

WinAVR. Использование инструментов GNU #01. Опции C-компилятора AVR-GCC

Сохранить или поделиться

Это краткая сводка характерных для AVR аспектов использования инструментов GNU. Обычно общая документация этих инструментов довольно велика. В этом разделе руководства детально раскрыты опции командной строки.

Опции C-компилятора avr-gcc

Опции для платформы AVR

Следующие характерные для платформы опции распознаются препроцессором C-компилятора. В дополнение к макросам препроцессора, показанным в таблице ниже, препроцессор будет определять макросы __AVR и __AVR__ (в значение 1), когда компилирует для AVR. Макрос AVR будет также определятся, когда используется стандарт уровней gnu89 (по умолчанию) и gnu99, но не c89 и c99.

-mmcu=архитектураКомпилирует код для архитектуры.

В настоящее время известны следующие архитектуры:

АрхитектураМакросыОписание
avr1__AVR_ARCH__=1__AVR_ASM_ONLY____AVR_2_BYTE_PC__ [2]“простое” ядро, поддерживает только ассемблер
avr2__AVR_ARCH__=2__AVR_2_BYTE_PC__ [2]“классическое” ядро с размером программной области памяти до 8 кбайт
avr25 [1]__AVR_ARCH__=25__AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_2_BYTE_PC__ [2]“классическое” ядро с инструкциями ‘MOVW’ и ‘LPM Rx, Z[+]’ и размером программной области памяти до 8 кбайт
avr3__AVR_ARCH__=3__AVR_MEGA__ [5]__AVR_HAVE_JMP_CALL__ [4]__AVR_2_BYTE_PC__ [2]“классическое” ядро с размером программной области памяти от 16 до 64 кбайт
avr31__AVR_ARCH__=31__AVR_MEGA____AVR_HAVE_RAMPZ__ [4]__AVR_HAVE_ELPM__ [4]__AVR_2_BYTE_PC__ [2]“классическое” ядро с размером программной области памяти 128 кбайт
avr35 [3]__AVR_ARCH__=35__AVR_MEGA__ [5]__AVR_HAVE_JMP_CALL__ [4]__AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_2_BYTE_PC__ [2]“классическое” ядро с инструкциями ‘MOVW’ и ‘LPM Rx, Z[+]’ и размером программной области памяти от 16 до 64 кбайт
avr4__AVR_ARCH__=4__AVR_ENHANCED__ [5]__AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_HAVE_MUL__ [1]__AVR_2_BYTE_PC__ [2]“расширенное” ядро с размером программной области памяти до 8 кбайт
avr5__AVR_ARCH__=5__AVR_MEGA__ [5]__AVR_ENHANCED__ [5]__AVR_HAVE_JMP_CALL__ [4]__AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_HAVE_MUL__ [1]__AVR_2_BYTE_PC__ [2]“расширенное” ядро с размером программной области памяти от 16 до 64 кбайт
avr51__AVR_ARCH__=51__AVR_MEGA____AVR_ENHANCED____AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_HAVE_MUL__ [1]__AVR_HAVE_RAMPZ__ [4]__AVR_HAVE_ELPM__ [4]__AVR_HAVE_ELPMX__ [4]__AVR_2_BYTE_PC__ [2]“расширенное” ядро с размером программной области памяти 128 кбайт
avr6 [2]__AVR_ARCH__=6__AVR_MEGA__ [5]__AVR_ENHANCED__ [5]__AVR_HAVE_JMP_CALL__ [4]__AVR_HAVE_MOVW__ [1]__AVR_HAVE_LPMX__ [1]__AVR_HAVE_MUL__ [1]__AVR_HAVE_RAMPZ__ [4]__AVR_HAVE_ELPM__ [4]__AVR_HAVE_ELPMX__ [4]__AVR_3_BYTE_PC__ [2]“расширенное” ядро с размером программной области памяти 256 кбайт

По умолчанию код генерируется для архитектуры avr2.

Обратите внимание, что, когда используется -mmcu=архитектура, а не -mmcu=тип МК, присоединяемый файл <avr/io.h> не сможет работать до тех пор, пока не сможет решить, описание какого устройства выбрать.

-mmcu=тип МКЗадает тип МК.

В настоящее время avr-gcc понимает следующие типы микроконтроллеров. Таблица сопоставляет их именам архитектур, передаваемых avr-gcc, и показывает обозначение препроцессора объявляемое опцией -mmcu.

АрхитектураТип МКМакрос
avr1at90s1200__AVR_AT90S1200__
avr1attiny11__AVR_ATtiny11__
avr1attiny12__AVR_ATtiny12__
avr1attiny15__AVR_ATtiny15__
avr1attiny28__AVR_ATtiny28__
avr2at90s2313__AVR_AT90S2313__
avr2at90s2323__AVR_AT90S2323__
avr2at90s2333__AVR_AT90S2333__
avr2at90s2343__AVR_AT90S2343__
avr2attiny22__AVR_ATtiny22__
avr2attiny26__AVR_ATtiny26__
avr2at90s4414__AVR_AT90S4414__
avr2at90s4433__AVR_AT90S4433__
avr2at90s4434__AVR_AT90S4434__
avr2at90s8515__AVR_AT90S8515__
avr2at90c8534__AVR_AT90C8534__
avr2at90s8535__AVR_AT90S8535__
avr2/avr25 [1]at86rf401__AVR_AT86RF401__
avr2/avr25 [1]ata6289__AVR_ATA6289__
avr2/avr25 [1]attiny13__AVR_ATtiny13__
avr2/avr25 [1]attiny13a__AVR_ATtiny13A__
avr2/avr25 [1]attiny2313__AVR_ATtiny2313__
avr2/avr25 [1]attiny24__AVR_ATtiny24__
avr2/avr25 [1]attiny25__AVR_ATtiny25__
avr2/avr25 [1]attiny261__AVR_ATtiny261__
avr2/avr25 [1]attiny43u__AVR_ATtiny43U__
avr2/avr25 [1]attiny44__AVR_ATtiny44__
avr2/avr25 [1]attiny45__AVR_ATtiny45__
avr2/avr25 [1]attiny461__AVR_ATtiny461__
avr2/avr25 [1]attiny48__AVR_ATtiny48__
avr2/avr25 [1]attiny84__AVR_ATtiny84__
avr2/avr25 [1]attiny85__AVR_ATtiny85__
avr2/avr25 [1]attiny861__AVR_ATtiny861__
avr2/avr25 [1]attiny87__AVR_ATtiny87__
avr2/avr25 [1]attiny88__AVR_ATtiny88__
avr3atmega603__AVR_ATmega603__
avr3at43usb355__AVR_AT43USB355__
avr3/avr31 [3]atmega103__AVR_ATmega103__
avr3/avr31 [3]at43usb320__AVR_AT43USB320__
avr3/avr35 [2]at90usb82__AVR_AT90USB82__
avr3/avr35 [2]at90usb162__AVR_AT90USB162__
avr3/avr35 [2]attiny167__AVR_ATtiny167__
avr3at76c711__AVR_AT76C711__
avr4atmega48__AVR_ATmega48__
avr4atmega48p__AVR_ATmega48P__
avr4atmega8__AVR_ATmega8__
avr4atmega8515__AVR_ATmega8515__
avr4atmega8535__AVR_ATmega8535__
avr4atmega88__AVR_ATmega88__
avr4atmega88p__AVR_ATmega88P__
avr4atmega8hva__AVR_ATmega8HVA__
avr4at90pwm1__AVR_AT90PWM1__
avr4at90pwm2__AVR_AT90PWM2__
avr4at90pwm2b__AVR_AT90PWM2B__
avr4at90pwm3__AVR_AT90PWM3__
avr4at90pwm3b__AVR_AT90PWM3B__
avr4at90pwm81__AVR_AT90PWM81__
avr5at90pwm216__AVR_AT90PWM216__
avr5at90pwm316__AVR_AT90PWM316__
avr5at90can32__AVR_AT90CAN32__
avr5at90can64__AVR_AT90CAN64__
avr5at90usb646__AVR_AT90USB646__
avr5at90usb647__AVR_AT90USB647__
avr5atmega16__AVR_ATmega16__
avr5atmega161__AVR_ATmega161__
avr5atmega162__AVR_ATmega162__
avr5atmega163__AVR_ATmega163__
avr5atmega164p__AVR_ATmega164P__
avr5atmega165__AVR_ATmega165__
avr5atmega165p__AVR_ATmega165P__
avr5atmega168__AVR_ATmega168__
avr5atmega168p__AVR_ATmega168P__
avr5atmega169__AVR_ATmega169__
avr5atmega169p__AVR_ATmega169P__
avr5atmega16hva__AVR_ATmega16HVA__
avr5atmega16m1__AVR_ATmega16M1__
avr5atmega16u4__AVR_ATmega16U4__
avr5atmega32__AVR_ATmega32__
avr5atmega323__AVR_ATmega323__
avr5atmega324p__AVR_ATmega324P__
avr5atmega325__AVR_ATmega325__
avr5atmega325p__AVR_ATmega325P__
avr5atmega3250__AVR_ATmega3250__
avr5atmega3250p__AVR_ATmega3250P__
avr5atmega328p__AVR_ATmega328P__
avr5atmega329__AVR_ATmega329__
avr5atmega329p__AVR_ATmega329P__
avr5atmega3290__AVR_ATmega3290__
avr5atmega3290p__AVR_ATmega3290P__
avr5atmega32c1__AVR_ATmega32C1__
avr5atmega32hvb__AVR_ATmega32HVB__
avr5atmega32m1__AVR_ATmega32M1__
avr5atmega32u4__AVR_ATmega32U4__
avr5atmega32u6__AVR_ATmega32U6__
avr5atmega406__AVR_ATmega406__
avr5atmega64__AVR_ATmega64__
avr5atmega640__AVR_ATmega640__
avr5atmega644__AVR_ATmega644__
avr5atmega644p__AVR_ATmega644P__
avr5atmega645__AVR_ATmega645__
avr5atmega6450__AVR_ATmega6450__
avr5atmega649__AVR_ATmega649__
avr5atmega6490__AVR_ATmega6490__
avr5atmega64c1__AVR_ATmega64C1__
avr5atmega64m1__AVR_ATmega64M1__
avr5at94k__AVR_AT94K__
avr5at90scr100__AVR_AT90SCR100__
avr5/avr51 [3]atmega128__AVR_ATmega128__
avr5/avr51 [3]atmega1280__AVR_ATmega1280__
avr5/avr51 [3]atmega1281__AVR_ATmega1281__
avr5/avr51 [3]atmega1284p__AVR_ATmega1284P__
avr5/avr51 [3]at90can128__AVR_AT90CAN128__
avr5/avr51 [3]at90usb1286__AVR_AT90USB1286__
avr5/avr51 [3]at90usb1287__AVR_AT90USB1287__
avr6atmega2560__AVR_ATmega2560__
avr6atmega2561__AVR_ATmega2561__
avrxmega2atxmega16a4__AVR_ATxmega16A4__
avrxmega2atxmega16d4__AVR_ATxmega16D4__
avrxmega2atxmega32d4__AVR_ATxmega32D4__
avrxmega3atxmega32a4__AVR_ATxmega32A4__
avrxmega4atxmega64a3__AVR_ATxmega64A3__
avrxmega5atxmega64a1__AVR_ATxmega64A1__
avrxmega6atxmega128a3__AVR_ATxmega128A3__
avrxmega6atxmega256a3__AVR_ATxmega256A3__
avrxmega6atxmega256a3b__AVR_ATxmega256A3B__
avrxmega7atxmega128a1__AVR_ATxmega128A1__
-morder1-morder2Меняет порядок распределения регистров. -mint8Принимает int за 8-разрядное целое. Обратите внимание, что это на самом деле не поддерживается avr-libc и поэтому обычно не используется. По умолчанию используются 16-разрядные целые.-mno-interruptsГенерирует код, который изменяет указатель стека без отключения прерываний. Обычно состояние регистра статуса SREG сохраняется во временном регистре, прерывания отключаются на время изменения указателя стека, после чего SREG восстанавливается. Таким образом, данная опция уменьшает размер кода за счет генерации кода, несовместимого с аппаратными прерываниями. Установка этой опции определяет макрос препроцессора __NO_INTERRUPTS__ в значение 1.-mcall-prologuesИспользует подпрограммы пролога и эпилога функций. Для сложных функций, которые используют много регистров (которые нуждаются в сохранении/восстановлении при входе/выходе из функции), это уменьшает размер генерируемого кода за счёт незначительного увеличения времени выполнения.-mtiny-stackИзменение только восьми младших разрядов указателя стека.-mno-tablejumpНе приветствуется, используйте вместо неё -fno-jump-tables.-mshort-callsИспользование rjmp/rcall (в ограниченном диапазоне) в устройствах более 8 кбайт. Это всегда используется в случае работы с архитектурами avr2 и avr4 (меньше 8 кбайт или flash-память). В архитектурах avr3 и avr5 вызовы и переходы по умолчанию будут использовать инструкции jmp/call, которые могут охватить весь диапазон адресов, но которые требуют большей памяти программ и большего времени выполнения.-mrtlВыводит внутренний результат компиляции вызова “RTL” в комментарии генерируемого ассемблерного кода. Используется для отладки avr-gcc.-msizeВыводит адрес, размер и относительные затраты для каждого операторав комментарии генерируемого ассемблерного кода. Используется для отладки avr-gcc.-mdebГенерирует всю отладочную информацию для stderr.

Некоторые основные опции компилятора

Следующие основные опции компилятора могут быть интересными пользователям AVR.

-OnУровень оптимизации.

Увеличение n означает увеличение оптимизации. Уровень оптимизации 0 означает, что оптимизации нет совсем (это значение по умолчанию, когда опция -O не выставлена). Специальная опция -Os похожа на -O2, но при этом будет пропускаться несколько шагов, увеличивающих размер кода.

Обратите внимание, что при -O3, gcc пытается сделать инлайновыми все “простые” функции. Для AVR это обычно очень ухудшает программу из-за увеличения размера кода. Поэтому вместе с оптимизацией -O3 включаются и другие опции, например, -frename-registers, которые лучше включать вручную.

Простая опция -O эквивалентна -O1.

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

Смотрите также соответствующий раздел FAQ для получения желаемого отлаженного и оптимизированного кода.

-Wa,опции ассемблера-Wl,опции компоновщикаПередача перечисленных опций ассемблеру и компоновщику соответственно.-gФормирование отладочной информации, которая может быть использована avr-gdb.-ffreestandingПредполагает “автономную” среду согласно стандарту Си. Это отключает автоматически встроенные функции (хотя они всё же могут быть переданы присоединением __builtin_ к реальному имени функции). Это также заставляет компилятор жаловаться, когда main() объявляется с пустым возвращаемым типом, что вполне логично в микроконтролерной среде, где приложение не может обеспечить значимую возвращаемую величину этой среде (в большинстве случаев из main() всё равно никак не возвращается). Однако, это также выключает все оптимизаци, выполняемые компилятором, который предполагает, что функции известны под определенными именами, описанными в стандарте. Например, применяя функцию strlen() к строковой константе обычно заствляет компилятор сразу заменить её вызов фактической длиной строки, хотя с -ffreestanding это всегда будет вызывать strlen() в реальном времени.-funsigned-charПриводит тип char без каких-либо условий к unsigned char. Без этой опции он по умолчанию signed char.-funsigned-bitfieldsПриводит тип bitfield без каких-либо условий к unsigned. По умолчанию он signed.-fshort-enumsНазначает тип enum как набор байтов, так как он нужен для объявленного диапазона возможных значений. В частности, тип enum эквивалентен наименьшему целочисленному типу, который имеет достаточную размерность.-fpack-structУпаковывает вместе все члены структуры без пустот.-fno-jump-tablesВыключает формирование инструкций табличных переходов. По умолчанию таблицы переходов могут использоваться для оптимизации операторов switch. При выключении же вместо этого используется последовательность операторов сравнения. Таблица переходов обычно в среднем быстрее, но, в частности, для операторов switch, где большинство переходов обращаются к метке default, они излишне расточительны к flash-памяти.

ВНИМАНИЕ: Инструкции табличных переходов используют ассемблерную инструкцию LPM для доступа к таблицам переходов. Всегда используйте переключатель -fno-jump-tables, если компилируете загрузчик для устройств с памятью программ более 64 килобайт.

Перевод раздела Using the GNU tools из AVR Libc v.1.6.6

Дополнительные источники по теме:

Сохранить или поделиться

radioprog.ru


Prostoy-Site | Все права защищены © 2018 | Карта сайта