using_tools(3)

πŸ“¦ version 2.2.1, in fedora 43, fedora 44, opensuse-leap 16.0

🌸 NAME 🌸

using_tools β€” Using the GNU tools

okay so bestie, real talk πŸ’– this page is the short-and-sweet summary of the AVR-specific aspects of using the GNU tools. Normally the generic documentation for these tools is, like, enormous and it's maintained in texinfo files. Command-line options? Those get explained in detail in the manual page. So this is the AVR-flavored recap, from the avr-libc manual. πŸŽ€


πŸ”§ Options for the C compiler avr-gcc πŸ”§

πŸŽ€ Machine-specific options for the AVR πŸŽ€

The C compiler frontend recognizes all of the machine-specific options below. And on top of the preprocessor macros listed in the tables, the preprocessor will also define the macros __AVR and __AVR__ (both set to the value 1) whenever you're compiling for an AVR target. ✨

The macro AVR gets defined too when you use the standard levels gnu89 (that one's the default) and gnu99 β€” but not with c89 and c99. no cap. πŸ’…

πŸ’« -mmcu=architecture πŸ’«

Compile code for architecture. Currently known architectures are:

  • avr1 β€” macros: __AVR_ARCH__=1, __AVR_ASM_ONLY__, __AVR_2_BYTE_PC__ [2]. Simple CPU core, only assembler support. πŸ’€
  • avr2 β€” macros: __AVR_ARCH__=2, __AVR_2_BYTE_PC__ [2]. 'Classic' CPU core, up to 8 KB of ROM.
  • avr25 [1] β€” macros: __AVR_ARCH__=25, __AVR_HAVE_MOVW__ [1], __AVR_HAVE_LPMX__ [1], __AVR_2_BYTE_PC__ [2]. 'Classic' CPU core with 'MOVW' and 'LPM Rx, Z[+]' instruction, up to 8 KB of ROM.
  • avr3 β€” macros: __AVR_ARCH__=3, __AVR_MEGA__ [5], __AVR_HAVE_JMP_CALL__ [4], __AVR_2_BYTE_PC__ [2]. 'Classic' CPU core, 16 KB to 64 KB of ROM.
  • avr31 β€” macros: __AVR_ARCH__=31, __AVR_MEGA__ [5], __AVR_HAVE_JMP_CALL__ [4], __AVR_HAVE_RAMPZ__ [4], __AVR_HAVE_ELPM__ [4], __AVR_2_BYTE_PC__ [2]. 'Classic' CPU core, 128 KB of ROM.
  • avr35 [3] β€” macros: __AVR_ARCH__=35, __AVR_MEGA__ [5], __AVR_HAVE_JMP_CALL__ [4], __AVR_HAVE_MOVW__ [1], __AVR_HAVE_LPMX__ [1], __AVR_2_BYTE_PC__ [2]. 'Classic' CPU core with 'MOVW' and 'LPM Rx, Z[+]' instruction, 16 KB to 64 KB of ROM.
  • avr4 β€” macros: __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 core, up to 8 KB of ROM.
  • avr5 β€” macros: __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 core, 16 KB to 64 KB of ROM.
  • avr51 β€” macros: __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 core, 128 KB of ROM.
  • avr6 [2] β€” macros: __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 core, 256 KB of ROM.

Now the little footnote era, keep up 🫢:

  • [1] New in GCC 4.2
  • [2] Unofficial patch for GCC 4.1
  • [3] New in GCC 4.2.3
  • [4] New in GCC 4.3
  • [5] Obsolete.

By default, code is generated for the avr2 architecture. that's the default bestie, it's giving classic ✨

Note that when only using -mmcu=architecture but no -mmcu=MCU type, including the file <avr/io.h> cannot work since it cannot decide which device's definitions to select. 😭 it literally cannot pick a fave.

πŸ’« -mmcu=MCU type πŸ’«

The following MCU types are currently understood by avr-gcc. The table matches them against the corresponding avr-gcc architecture name, and shows the preprocessor symbol declared by the -mmcu option. πŸ‘‘

  • arch avr1 β€” at90s1200 β†’ __AVR_AT90S1200__, attiny11 β†’ __AVR_ATtiny11__, attiny12 β†’ __AVR_ATtiny12__, attiny15 β†’ __AVR_ATtiny15__, attiny28 β†’ __AVR_ATtiny28__
  • arch 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__
  • arch avr2/avr25 [1] β€” at86rf401 β†’ __AVR_AT86RF401__, ata5272 β†’ __AVR_ATA5272__, ata6616c β†’ __AVR_ATA6616C__, 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__, attiny441 β†’ __AVR_ATtiny441__, attiny45 β†’ __AVR_ATtiny45__, attiny461 β†’ __AVR_ATtiny461__, attiny461a β†’ __AVR_ATtiny461A__, attiny48 β†’ __AVR_ATtiny48__, attiny828 β†’ __AVR_ATtiny828__, attiny84 β†’ __AVR_ATtiny84__, attiny84a β†’ __AVR_ATtiny84A__, attiny841 β†’ __AVR_ATtiny841__, attiny85 β†’ __AVR_ATtiny85__, attiny861 β†’ __AVR_ATtiny861__, attiny861a β†’ __AVR_ATtiny861A__, attiny87 β†’ __AVR_ATtiny87__, attiny88 β†’ __AVR_ATtiny88__
  • arch avr3 β€” atmega603 β†’ __AVR_ATmega603__, at43usb355 β†’ __AVR_AT43USB355__
  • arch avr3/avr31 [3] β€” atmega103 β†’ __AVR_ATmega103__, at43usb320 β†’ __AVR_AT43USB320__
  • arch avr3/avr35 [2] β€” at90usb82 β†’ __AVR_AT90USB82__, at90usb162 β†’ __AVR_AT90USB162__, ata5505 β†’ __AVR_ATA5505__, ata6617c β†’ __AVR_ATA6617C__, ata664251 β†’ __AVR_ATA664251__, atmega8u2 β†’ __AVR_ATmega8U2__, atmega16u2 β†’ __AVR_ATmega16U2__, atmega32u2 β†’ __AVR_ATmega32U2__, attiny167 β†’ __AVR_ATtiny167__, attiny1634 β†’ __AVR_ATtiny1634__
  • arch avr3 β€” at76c711 β†’ __AVR_AT76C711__
  • arch avr4 β€” ata6285 β†’ __AVR_ATA6285__, ata6286 β†’ __AVR_ATA6286__, ata6289 β†’ __AVR_ATA6289__, ata6612c β†’ __AVR_ATA6612C__, atmega48 β†’ __AVR_ATmega48__, atmega48a β†’ __AVR_ATmega48A__, atmega48pa β†’ __AVR_ATmega48PA__, atmega48pb β†’ __AVR_ATmega48PB__, atmega48p β†’ __AVR_ATmega48P__, atmega8 β†’ __AVR_ATmega8__, atmega8a β†’ __AVR_ATmega8A__, atmega8515 β†’ __AVR_ATmega8515__, atmega8535 β†’ __AVR_ATmega8535__, atmega88 β†’ __AVR_ATmega88__, atmega88a β†’ __AVR_ATmega88A__, atmega88p β†’ __AVR_ATmega88P__, atmega88pa β†’ __AVR_ATmega88PA__, atmega88pb β†’ __AVR_ATmega88PB__, atmega8hva β†’ __AVR_ATmega8HVA__, at90pwm1 β†’ __AVR_AT90PWM1__, at90pwm2 β†’ __AVR_AT90PWM2__, at90pwm2b β†’ __AVR_AT90PWM2B__, at90pwm3 β†’ __AVR_AT90PWM3__, at90pwm3b β†’ __AVR_AT90PWM3B__, at90pwm81 β†’ __AVR_AT90PWM81__
  • arch avr5 β€” at90can32 β†’ __AVR_AT90CAN32__, at90can64 β†’ __AVR_AT90CAN64__, at90pwm161 β†’ __AVR_AT90PWM161__, at90pwm216 β†’ __AVR_AT90PWM216__, at90pwm316 β†’ __AVR_AT90PWM316__, at90scr100 β†’ __AVR_AT90SCR100__, at90usb646 β†’ __AVR_AT90USB646__, at90usb647 β†’ __AVR_AT90USB647__, at94k β†’ __AVR_AT94K__, atmega16 β†’ __AVR_ATmega16__, ata5702m322 β†’ __AVR_ATA5702M322__, ata5782 β†’ __AVR_ATA5782__, ata5790 β†’ __AVR_ATA5790__, ata5790n β†’ __AVR_ATA5790N__, ata5795 β†’ __AVR_ATA5795__, ata5831 β†’ __AVR_ATA5831__, ata6613c β†’ __AVR_ATA6613C__, ata6614q β†’ __AVR_ATA6614Q__, atmega161 β†’ __AVR_ATmega161__, atmega162 β†’ __AVR_ATmega162__, atmega163 β†’ __AVR_ATmega163__, atmega164a β†’ __AVR_ATmega164A__, atmega164p β†’ __AVR_ATmega164P__, atmega164pa β†’ __AVR_ATmega164PA__, atmega165 β†’ __AVR_ATmega165__, atmega165a β†’ __AVR_ATmega165A__, atmega165p β†’ __AVR_ATmega165P__, atmega165pa β†’ __AVR_ATmega165PA__, atmega168 β†’ __AVR_ATmega168__, atmega168a β†’ __AVR_ATmega168A__, atmega168p β†’ __AVR_ATmega168P__, atmega168pa β†’ __AVR_ATmega168PA__, 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__, atmega32a β†’ __AVR_ATmega32A__, atmega323 β†’ __AVR_ATmega323__, atmega324a β†’ __AVR_ATmega324A__, atmega324p β†’ __AVR_ATmega324P__, atmega324pa β†’ __AVR_ATmega324PA__, atmega325 β†’ __AVR_ATmega325__, atmega325a β†’ __AVR_ATmega325A__, atmega325p β†’ __AVR_ATmega325P__, atmega325pa β†’ __AVR_ATmega325PA__, atmega3250 β†’ __AVR_ATmega3250__, atmega3250a β†’ __AVR_ATmega3250A__, atmega3250p β†’ __AVR_ATmega3250P__, atmega3250pa β†’ __AVR_ATmega3250PA__, 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__, atmega3290pa β†’ __AVR_ATmega3290PA__, atmega32c1 β†’ __AVR_ATmega32C1__, atmega32hvb β†’ __AVR_ATmega32HVB__, atmega32hvbrevb β†’ __AVR_ATmega32HVBREVB__, atmega32m1 β†’ __AVR_ATmega32M1__, atmega32u4 β†’ __AVR_ATmega32U4__, atmega32u6 β†’ __AVR_ATmega32U6__, atmega406 β†’ __AVR_ATmega406__, atmega644rfr2 β†’ __AVR_ATmega644RFR2__, atmega64rfr2 β†’ __AVR_ATmega64RFR2__, atmega64 β†’ __AVR_ATmega64__, atmega64a β†’ __AVR_ATmega64A__, 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__, atmega64hve2 β†’ __AVR_ATmega64HVE2__, atmega64m1 β†’ __AVR_ATmega64M1__, m3000 β†’ __AVR_M3000__
  • arch avr5/avr51 [3] β€” at90can128 β†’ __AVR_AT90CAN128__, at90usb1286 β†’ __AVR_AT90USB1286__, at90usb1287 β†’ __AVR_AT90USB1287__, atmega128 β†’ __AVR_ATmega128__, atmega128a β†’ __AVR_ATmega128A__, atmega1280 β†’ __AVR_ATmega1280__, atmega1281 β†’ __AVR_ATmega1281__, atmega1284 β†’ __AVR_ATmega1284__, atmega1284p β†’ __AVR_ATmega1284P__, atmega1284rfr2 β†’ __AVR_ATmega1284RFR2__, atmega128rfr2 β†’ __AVR_ATmega128RFR2__
  • arch avr6 β€” atmega2560 β†’ __AVR_ATmega2560__, atmega2561 β†’ __AVR_ATmega2561__, atmega2564rfr2 β†’ __AVR_ATmega2564RFR2__, atmega256rfr2 β†’ __AVR_ATmega256RFR2__

okay so the device list is literally never-ending and I'm obsessed with it πŸ’… here's every single one, and yes you need all of them:

avrxmega2atxmega8e5__AVR_ATxmega8E5__
avrxmega2atxmega16a4__AVR_ATxmega16A4__
avrxmega2atxmega16a4u__AVR_ATxmega16A4U__
avrxmega2atxmega16c4__AVR_ATxmega16C4__
avrxmega2atxmega16d4__AVR_ATxmega16D4__
avrxmega2atxmega32a4__AVR_ATxmega32A4__
avrxmega2atxmega32a4u__AVR_ATxmega32A4U__
avrxmega2atxmega32c3__AVR_ATxmega32C3__
avrxmega2atxmega32c4__AVR_ATxmega32C4__
avrxmega2atxmega32d3__AVR_ATxmega32D3__
avrxmega2atxmega32d4__AVR_ATxmega32D4__
avrxmega2atxmega32e5__AVR_ATxmega32E5__
avrxmega4atxmega64a3__AVR_ATxmega64A3__
avrxmega4atxmega64a3u__AVR_ATxmega64A3U__
avrxmega4atxmega64a4u__AVR_ATxmega64A4U__
avrxmega4atxmega64b1__AVR_ATxmega64B1__
avrxmega4atxmega64b3__AVR_ATxmega64B3__
avrxmega4atxmega64c3__AVR_ATxmega64C3__
avrxmega4atxmega64d3__AVR_ATxmega64D3__
avrxmega4atxmega64d4__AVR_ATxmega64D4__
avrxmega5atxmega64a1__AVR_ATxmega64A1__
avrxmega5atxmega64a1u__AVR_ATxmega64A1U__
avrxmega6atxmega128a3__AVR_ATxmega128A3__
avrxmega6atxmega128a3u__AVR_ATxmega128A3U__
avrxmega6atxmega128b1__AVR_ATxmega128B1__
avrxmega6atxmega128b3__AVR_ATxmega128B3__
avrxmega6atxmega128c3__AVR_ATxmega128C3__
avrxmega6atxmega128d3__AVR_ATxmega128D3__
avrxmega6atxmega128d4__AVR_ATxmega128D4__
avrxmega6atxmega192a3__AVR_ATxmega192A3__
avrxmega6atxmega192a3u__AVR_ATxmega192A3U__
avrxmega6atxmega192c3__AVR_ATxmega192C3__
avrxmega6atxmega192d3__AVR_ATxmega192D3__
avrxmega6atxmega256a3__AVR_ATxmega256A3__
avrxmega6atxmega256a3u__AVR_ATxmega256A3U__
avrxmega6atxmega256a3b__AVR_ATxmega256A3B__
avrxmega6atxmega256a3bu__AVR_ATxmega256A3BU__
avrxmega6atxmega256c3__AVR_ATxmega256C3__
avrxmega6atxmega256d3__AVR_ATxmega256D3__
avrxmega6atxmega384c3__AVR_ATxmega384C3__
avrxmega6atxmega384d3__AVR_ATxmega384D3__
avrxmega7atxmega128a1__AVR_ATxmega128A1__
avrxmega7atxmega128a1u__AVR_ATxmega128A1U__
avrxmega7atxmega128a4u__AVR_ATxmega128A4U__
avrtiny10attiny4__AVR_ATtiny4__ avrtiny10attiny5__AVR_ATtiny5__
avrtiny10attiny9__AVR_ATtiny9__ avrtiny10attiny10__AVR_ATtiny10__
avrtiny10attiny20__AVR_ATtiny20__ avrtiny10attiny40__AVR_ATtiny40__

and the little footnotes that come with them, pls don't skip these bestie 🫢

  • [1] 'avr25' architecture is new in GCC 4.2
  • [2] 'avr35' architecture is new in GCC 4.2.3
  • [3] 'avr31' and 'avr51' architectures is new in GCC 4.3

  • -morder1
  • -morder2

okay so these two are the register-assignment-shuffle options ✨ they change the order of register assignment. The default is:

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

Order 1 uses:

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

Order 2 uses:

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 β€” assume int is an 8-bit integer. lowkey important warning: this is not really supported by avr-libc, so it should normally not be used, fr fr. The default is to use 16-bit integers.
  • -mno-interrupts β€” generates code that changes the stack pointer without disabling interrupts. Normally, the state of the status register SREG is saved in a temporary register, interrupts are disabled while changing the stack pointer, and SREG is restored. Specifying this option will define the preprocessor macro __NO_INTERRUPTS__ to the value 1.
  • -mcall-prologues β€” use subroutines for function prologue/epilogue. For complex functions that use a lot of registers (the ones that need to be saved/restored on function entry/exit), this saves some space at the cost of a slightly increased execution time. tradeoff queen πŸ’…
  • -mtiny-stack β€” change only the low 8 bits of the stack pointer.
  • -mno-tablejump β€” deprecated, use -fno-jump-tables instead.
  • -mshort-calls β€” use rjmp/rcall (limited range) on >8K devices. On avr2 and avr4 architectures (less than 8 KB of flash memory), this is always the case. On avr3 and avr5 architectures, calls and jumps to targets outside the current function will by default use jmp/call instructions that can cover the entire address range, but that require more flash ROM and execution time.
  • -mrtl β€” dump the internal compilation result called RTL into comments in the generated assembler code. Used for debugging avr-gcc πŸ›
  • -msize β€” dump the address, size, and relative cost of each statement into comments in the generated assembler code. Also used for debugging avr-gcc.
  • -mdeb β€” generate lots of debugging information to stderr. it's giving chatty ngl

✨ Selected general compiler options ✨

The following general gcc options might be of some interest to AVR users, bestie πŸ‘‘

  • -On β€” optimization level n. Increasing n is meant to optimize more, and an optimization level of 0 means no optimization at all, which is the default if no -O option is present. The special option -Os is meant to turn on all -O2 optimizations that are not expected to increase code size.

Note that at -O3, gcc attempts to inline all 'simple' functions. For the AVR target, this will normally constitute a large pessimization due to the code increasement 😭 The only other optimization turned on with -O3 is -frename-registers, which could rather be enabled manually instead.

A simple -O option is equivalent to -O1.

Note also that turning off all optimizations will prevent some warnings from being issued, since the generation of those warnings depends on code analysis steps that are only performed when optimizing (unreachable code, unused variables).

See also the appropriate FAQ entry for issues regarding debugging optimized code.

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

Pass the listed options to the assembler, or linker, respectively.

  • -g β€” generate debugging information that can be used by avr-gdb.
  • -ffreestanding β€” assume a 'freestanding' environment as per the C standard. This turns off automatic builtin functions (though they can still be reached by prepending __builtin_ to the actual function name). It also makes the compiler not complain when main() is declared with a void return type, which makes some sense in a microcontroller environment where the application cannot meaningfully provide a return value to its environment (in most cases, main() won't even return anyway). However, this also turns off all optimizations normally done by the compiler which assume that functions known by a certain name behave as described by the standard. E. g., applying the function strlen() to a literal string will normally cause the compiler to immediately replace that call by the actual length of the string, while with -ffreestanding, it will always call strlen() at run-time.
  • -funsigned-char β€” make any unqualified char type an unsigned char. Without this option, they default to a signed char.
  • -funsigned-bitfields β€” make any unqualified bitfield type unsigned. By default, they are signed.
  • -fshort-enums β€” allocate to an enum type only as many bytes as it needs for the declared range of possible values. Specifically, the enum type will be equivalent to the smallest integer type which has enough room.
  • -fpack-struct β€” pack all structure members together without holes.
  • -fno-jump-tables β€” do not generate tablejump instructions. By default, jump tables can be used to optimize switch statements. When turned off, sequences of compare statements are used instead. Jump tables are usually faster to execute on average, but in particular for switch statements, where most of the jumps would go to the default label, they might waste a bit of flash memory.
NOTE: the tablejump instructions use the LPM assembler instruction for access to jump tables. Always use the -fno-jump-tables switch if compiling a bootloader for devices with more than 64 KB of code memory. like, always always πŸ™

πŸ¦‹ Options for the assembler avr-as πŸ¦‹

πŸŽ€ Machine-specific assembler options πŸŽ€

  • -mmcu=architecture
  • -mmcu=MCU name

avr-as understands the same -mmcu= options as avr-gcc. By default, avr2 is assumed, but this can be altered by using the appropriate .arch pseudo-instruction inside the assembler source file.

  • -mall-opcodes β€” turns off opcode checking for the actual MCU type, and allows any possible AVR opcode to be assembled. chaos mode, slay πŸ’…
  • -mno-skip-bug β€” don't emit a warning when trying to skip a 2-word instruction with a CPSE/SBIC/SBIS/SBRC/SBRS instruction. Early AVR devices suffered from a hardware bug where these instructions could not be properly skipped πŸ›
  • -mno-wrap β€” for RJMP/RCALL instructions, don't allow the target address to wrap around for devices that have more than 8 KB of memory.
  • --gstabs β€” generate .stabs debugging symbols for assembler source lines. This enables avr-gdb to trace through assembler source files. This option must not be used when assembling sources that have been generated by the C compiler β€” those files already contain the appropriate line number information from the C source files.
  • -a[cdhlmns=file] β€” turn on the assembler listing. The sub-options are:
    • 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 set the name of the listing file

The various sub-options can be combined into a single -a option list; =file must be the last one in that case. no skipping the order bestie 😀

🌈 Examples for assembler options passed through the C compiler 🌈

Remember that assembler options can be passed from the C compiler frontend using -Wa (see above), so in order to include the C source code into the assembler listing in file foo.lst, when compiling foo.c, the following compiler command-line can be used:

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

In order to pass an assembler file through the C preprocessor first, and have the assembler generate line number debugging information for it, the following command can be used:

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

Note that on Unix systems that have case-distinguishing file systems, specifying a file name with the suffix .S (upper-case letter S) will make the compiler automatically assume -x assembler-with-cpp, while using .s would pass the file directly to the assembler (no preprocessing done). the case sensitivity jump scare is so real πŸ™ˆ

Controlling the linker avr-ld πŸ”—βœ¨

Selected linker options πŸŽ€

Okay so avr-ld doesn't have any machine-specific options, bestie β€” but a bunch of the standard linker options are still gonna be super relevant for AVR girlies. periodt.

  • -lname β€” This one goes looking for the archive library called libname.a, then uses it to clear up any currently unresolved symbols. It searches along a path made of builtin pathname entries that got baked in at compile time (e. g. /usr/local/avr/lib on Unix systems), and that path can also be extended by pathname entries you pass with -L options (which, important, have to come before the -l options on the command-line). πŸ”
  • -Lpath β€” Another place to look for archive libraries that -l options ask for. πŸ“š
  • --defsym symbol=expr β€” Defines a global symbol symbol and gives it expr as its value. ✨
  • -M β€” Prints a linker map to stdout. πŸ—ΊοΈ
  • -Map mapfile β€” Prints a linker map to mapfile. πŸ“
  • --cref β€” Outputs a cross reference table to the map file (if -Map is also there), or to stdout. πŸ”Ž
  • --section-start sectionname=org β€” Starts section sectionname at absolute address org. πŸ“
  • -Tbss org β€” Starts the bss section at org. 🫧
  • -Tdata org β€” Starts the data section at org. πŸ’Ύ
  • -Ttext org β€” Starts the text section at org. πŸ“œ
  • -T scriptfile β€” Uses scriptfile as the linker script and replaces the default linker script. Default linker scripts live in a system-specific location (e. g. under /usr/local/avr/lib/ldscripts on Unix systems), and their names are the AVR architecture name (avr2 through avr5) plus the suffix .x. They describe how all the different memory sections get linked together. 🧩

Passing linker options from the C compiler πŸš€

By default, every unknown non-option argument on the avr-gcc command-line gets passed straight to the linker β€” that means all filename arguments that don't have a suffix handled by avr-gcc. So all files ending in .o (object files) and .a (object libraries) get handed over to the linker. No cap.

System libraries usually aren't passed by their explicit filename; instead they use the -l option, which takes an abbreviated form of the archive filename (see above). avr-libc ships two system libraries, libc.a, and libm.a. The standard library libc.a will always get searched for unresolved references whenever the linker is started through the C compiler frontend (i. e., there's always at least one implied -lc option). The mathematics library libm.a, though, needs to be explicitly requested with -lm. See also the entry in the FAQ explaining this. 🧚

Makefiles conventionally use the make macro LDLIBS to keep track of -l (and possibly -L) options that should only be appended to the C compiler command-line when linking the final binary. Meanwhile, the macro LDFLAGS stores other command-line options for the C compiler that should be passed as options during the linking stage. The difference is placement: options go early on the command-line, while libraries go at the end, because they're there to resolve global symbols that are still unresolved at that point. 🧠

You can pass specific linker flags from the C compiler command-line with the -Wl compiler option, see above. This option needs there to be no spaces in the appended linker option, but some of the linker options above (like -Map or --defsym) would require a space. In those situations, you can replace the space with an equal sign instead. For example, this command-line compiles foo.c into an executable and also makes a link map with a cross-reference list in the file foo.map:

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

Alternatively, a comma works as a placeholder and will be replaced by a space before the option is passed to the linker. So for a device with external SRAM, this command-line would make the linker put the data segment at address 0x2000 in the SRAM:

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

See the explanation of the data section for why 0x800000 needs to be added to the actual value. Note that the stack will still stay in internal RAM, through the symbol __stack that the run-time startup code provides. That's probably a good idea anyway (internal RAM access is faster), and it's even required for some early devices that had hardware bugs preventing them from using a stack in external RAM. Also note that the heap for malloc() will still be placed after all the variables in the data section, so in this situation, no stack/heap collision can occur. 🌸

To relocate the stack from its default spot at the top of internal RAM, you can change the value of the symbol __stack on the linker command-line. Since the linker is typically called from the compiler frontend, you can do this with a compiler option like -Wl,--defsym=__stack=0x8003ff

The above makes the code use stack space from RAM address 0x3ff downwards. How much stack space is available then depends on the bottom address of internal RAM for a particular device. It is the responsibility of the application to ensure the stack does not grow out of bounds, and to arrange for the stack to not collide with variable allocations made by the compiler (sections .data and .bss). πŸ›‘


That's the whole vibe, bestie! Version 2.1.0, dated Sat Jan 29 2022, from using_tools(3) β€” slay. πŸ’–βœ¨

πŸ“¦ where this page comes from