In which I PEBKAC so you don’t have to
Say you have a simple bit of code:
#include <avr/io.h>
#include <util/delay.h>
#define LED_BUILTIN _BV(PORTB5)
int main(void)
{
DDRB |= LED_BUILTIN;
while (1)
{
PORTB |= LED_BUILTIN; // turn on led
_delay_ms(1000); // delay 1s
PORTB &= ~LED_BUILTIN; // turn off led
_delay_ms(1000); // delay 1s
}
}
You have a Makefile that compiles that into an object (.o
) file like this:
avr-gcc -mmcu=atmega328p -DF_CPU=16000000 -Os -c blink.c
If you were to forget to set the device type when compiling your .c
file into an object file (.o
), you would get a warning:
$ avr-gcc -DF_CPU=16000000 -Os -c blink.c
In file included from blink.c:1:0:
/usr/avr/include/avr/io.h:623:6: warning: #warning "device type not defined" [-Wcpp]
# warning "device type not defined"
^~~~~~~
But if you were to forget to set the device type when linking the final ELF binary, you would not be so lucky:
$ avr-gcc -o blink.elf blink.o
<...silence...>
So you would, perhaps, be surprised when you flash this to your device and it doesn’t work. If you take a look at the assembly generated for the above command, it looks like this:
$ avr-objdump -d blink.elf
blink.elf: file format elf32-avr
Disassembly of section .text:
00000000 <main>:
0: 25 9a sbi 0x04, 5 ; 4
2: 2d 9a sbi 0x05, 5 ; 5
4: 2f e3 ldi r18, 0x3F ; 63
6: 8d e0 ldi r24, 0x0D ; 13
8: 93 e0 ldi r25, 0x03 ; 3
a: 21 50 subi r18, 0x01 ; 1
c: 80 40 sbci r24, 0x00 ; 0
e: 90 40 sbci r25, 0x00 ; 0
10: e1 f7 brne .-8 ; 0xa <__zero_reg__+0x9>
12: 00 c0 rjmp .+0 ; 0x14 <__zero_reg__+0x13>
14: 00 00 nop
16: 2d 98 cbi 0x05, 5 ; 5
18: 2f e3 ldi r18, 0x3F ; 63
1a: 8d e0 ldi r24, 0x0D ; 13
1c: 93 e0 ldi r25, 0x03 ; 3
1e: 21 50 subi r18, 0x01 ; 1
20: 80 40 sbci r24, 0x00 ; 0
22: 90 40 sbci r25, 0x00 ; 0
24: e1 f7 brne .-8 ; 0x1e <__zero_reg__+0x1d>
26: 00 c0 rjmp .+0 ; 0x28 <__zero_reg__+0x27>
28: 00 00 nop
2a: eb cf rjmp .-42 ; 0x2 <__zero_reg__+0x1>
If you remember to include the device type:
$ avr-gcc -mmcu=atmega328p -o blink.elf blink.o
You instead get:
$ avr-objdump -d blink.elf
blink.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
74: 0e 94 40 00 call 0x80 ; 0x80 <main>
78: 0c 94 56 00 jmp 0xac ; 0xac <_exit>
0000007c <__bad_interrupt>:
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000080 <main>:
80: 25 9a sbi 0x04, 5 ; 4
82: 2d 9a sbi 0x05, 5 ; 5
84: 2f e3 ldi r18, 0x3F ; 63
86: 8d e0 ldi r24, 0x0D ; 13
88: 93 e0 ldi r25, 0x03 ; 3
8a: 21 50 subi r18, 0x01 ; 1
8c: 80 40 sbci r24, 0x00 ; 0
8e: 90 40 sbci r25, 0x00 ; 0
90: e1 f7 brne .-8 ; 0x8a <main+0xa>
92: 00 c0 rjmp .+0 ; 0x94 <main+0x14>
94: 00 00 nop
96: 2d 98 cbi 0x05, 5 ; 5
98: 2f e3 ldi r18, 0x3F ; 63
9a: 8d e0 ldi r24, 0x0D ; 13
9c: 93 e0 ldi r25, 0x03 ; 3
9e: 21 50 subi r18, 0x01 ; 1
a0: 80 40 sbci r24, 0x00 ; 0
a2: 90 40 sbci r25, 0x00 ; 0
a4: e1 f7 brne .-8 ; 0x9e <main+0x1e>
a6: 00 c0 rjmp .+0 ; 0xa8 <main+0x28>
a8: 00 00 nop
aa: eb cf rjmp .-42 ; 0x82 <main+0x2>
000000ac <_exit>:
ac: f8 94 cli
000000ae <__stop_program>:
ae: ff cf rjmp .-2 ; 0xae <__stop_program>
You can see that this code includes things like the jump table, without which your code won’t run.
The moral of this story is: don’t forget to set the device type.