I was going to post what I’ll be writing here on avr freaks forum,
but, unfortunately, I was not able to register there:
Access Denied You don't have permission to access "http://login.microchip.com/ssologin/connect/authorize?" on this server. Reference #18.21f01502.1701763696.382a727f
Ok, I will post some of my observations here.
Let me start from very simple example.
export fn main () noreturn {
const ddrb: *u8 = @ptrFromInt(0x24);
const portb: *u8 = @ptrFromInt(0x25);
const led_pin: u3 = 5;
ddrb.* |= (1 << led_pin); // as output
portb.* |= (1 << led_pin); // LED on
//portb.* &= ~(@as(u8,1) << led_pin); // LED off
while (true) {
}
}
This program just turn the LED on or off and then starts spinning in the loop.
I am using this simple bash script to build:
rm -f prog.*
rm -f main.o
rm -f main.o.o
#ZC=/opt/zig-0.10/zig
#ZC=/opt/zig-0.11/zig
ZC=/opt/zig-0.12/zig
export LANG=C
$ZC build-obj -O ReleaseSmall -target avr-freestanding-none -mcpu atmega328p main.zig &&
avr-ld -o prog.elf main.o &&
avr-objcopy -j .text -j .data -O ihex prog.elf prog.hex &&
avr-objdump -D -h prog.elf > prog.dmp
Let’s look at disassembly (prog.dmp
):
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000074 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00800060 00800060 0000007a 2**0
CONTENTS, ALLOC, LOAD, DATA
Disassembly of section .text:
00000000 <main>:
0: 25 9a sbi 0x04, 5 ; 4
2: 2d 9a sbi 0x05, 5 ; 5
4: ff cf rjmp .-2 ; 0x4 <__zero_reg__+0x3>
Looks ok. Note sbi
, btw.
Now, let’s add some more stuff:
var x: u16 = 0;
export fn main () noreturn {
const ddrb: *u8 = @ptrFromInt(0x24);
const portb: *u8 = @ptrFromInt(0x25);
const led_pin: u3 = 5;
ddrb.* |= (1 << led_pin); // as output
//portb.* |= (1 << led_pin); // LED on
portb.* &= ~(@as(u8,1) << led_pin); // LED off
x = 7;
while (true) {
x += 1;
if (0x01 == (x & 0x01)) {
portb.* |= (1 << led_pin);
} else {
portb.* &= ~(@as(u8,1) << led_pin);
}
}
}
And the the disassembly of this is:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000006 00000000 00000000 00000074 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00800060 00800060 0000007a 2**0
CONTENTS, ALLOC, LOAD, DATA
Disassembly of section .text:
00000000 <main>:
0: 25 9a sbi 0x04, 5 ; 4
2: 2d 98 cbi 0x05, 5 ; 5
4: ff cf rjmp .-2 ; 0x4 <__zero_reg__+0x3>
Surprise! Same code as in the first example, compiler has thrown away my nifty loop!
Ok, let’s add one little word:
export var x: u16 = 0; // added export keyword
export fn main () noreturn {
const ddrb: *u8 = @ptrFromInt(0x24);
const portb: *u8 = @ptrFromInt(0x25);
const led_pin: u3 = 5;
ddrb.* |= (1 << led_pin); // output
//portb.* |= (1 << led_pin); // LED on
portb.* &= ~(@as(u8,1) << led_pin); // LED off
x = 7;
while (true) {
x += 1;
if (0x01 == (x & 0x01)) {
portb.* |= (1 << led_pin);
} else {
portb.* &= ~(@as(u8,1) << led_pin);
}
}
}
Now the disassembly is more or less is what was intented:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000002c 00000000 00000000 00000094 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00800060 00800060 000000c0 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000002 00800060 00800060 000000c0 2**0
ALLOC
Disassembly of section .text:
00000000 <main>:
0: 25 9a sbi 0x04, 5 ; 4
2: 2d 98 cbi 0x05, 5 ; 5
4: 87 e0 ldi r24, 0x07 ; 7
6: 90 e0 ldi r25, 0x00 ; 0
8: 01 96 adiw r24, 0x01 ; 1
a: 90 93 61 00 sts 0x0061, r25 ; 0x800061 <__DATA_REGION_ORIGIN__+0x1>
e: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <__DATA_REGION_ORIGIN__>
12: 95 b1 in r25, 0x05 ; 5
14: 9f 7d andi r25, 0xDF ; 223
16: 82 95 swap r24
18: 80 7f andi r24, 0xF0 ; 240
1a: 88 0f add r24, r24
1c: 80 72 andi r24, 0x20 ; 32
1e: 89 2b or r24, r25
20: 85 b9 out 0x05, r24 ; 5
22: 80 91 60 00 lds r24, 0x0060 ; 0x800060 <__DATA_REGION_ORIGIN__>
26: 90 91 61 00 lds r25, 0x0061 ; 0x800061 <__DATA_REGION_ORIGIN__+0x1>
2a: ee cf rjmp .-36 ; 0x8 <__zero_reg__+0x7>
Disassembly of section .bss:
00800060 <x>:
...
Now we have bss
. Note sts/lds
instructions.
Closer to the topic; after I uploaded this to the device, I was not able to upload any other code anymore!!!. avrdude
was saying “programmer is not responding” permanently. But after some thinking I discovered a procedure to deal with it, here it is:
- disconnect USB cable
- press and hold down reset button
- connect USB cable
- now tricky part - release reset button and run a programmer (
avrdude
) immediately (it must start communicating with the board before a program has started execution).
At this point I started to think, that Zig/LLVM is doing something wrong (sts 0x0060
…?).
If I understand it right, STS/LDS
instructions require “absolute” address; for example, to work with port B we must use 0x23 (PINB), 0x24 (DDRB) and 0x25 (PORTB) (unlike IN/OUT
and SBI/CBI
where we have to substruct 0x20)
If so STS 0x0060 / 0x0060
is not what I want, because it’s WDTCSR
and CLKPR
, respectively and I was not going to write to these I/O registers.
Ok let’s do the same things in C:
#include <avr/io.h>
unsigned short x = 0;
void main(void) {
DDRB |= _BV(DDB5); /* output */
PORTB &= ~_BV(PORTB5); /* turn LED off */
x = 7;
while (1) {
x++;
if (x& 0x0001) {
PORTB |= _BV(PORTB5); /* turn LED on */
} else {
PORTB &= ~_BV(PORTB5); /* turn LED off */
}
}
}
Hmm… there was no such an amazing effect (programmer lock up) when I uploaded this.
What is in disassembly?
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000003e 00000000 00000000 00000094 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000000 00800060 0000003e 000000d2 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000002 00800060 00800060 000000d2 2**0
ALLOC
3 .comment 00000011 00000000 00000000 000000d2 2**0
CONTENTS, READONLY
Disassembly of section .text:
00000000 <__ctors_end>:
0: 20 e0 ldi r18, 0x00 ; 0
2: a0 e6 ldi r26, 0x60 ; 96
4: b0 e0 ldi r27, 0x00 ; 0
6: 01 c0 rjmp .+2 ; 0xa <.do_clear_bss_start>
00000008 <.do_clear_bss_loop>:
8: 1d 92 st X+, r1
0000000a <.do_clear_bss_start>:
a: a2 36 cpi r26, 0x62 ; 98
c: b2 07 cpc r27, r18
e: e1 f7 brne .-8 ; 0x8 <.do_clear_bss_loop>
00000010 <main>:
10: 25 9a sbi 0x04, 5 ; 4
12: 2d 98 cbi 0x05, 5 ; 5
14: 87 e0 ldi r24, 0x07 ; 7
16: 90 e0 ldi r25, 0x00 ; 0
18: 90 93 61 00 sts 0x0061, r25 ; 0x800061 <__DATA_REGION_ORIGIN__+0x1>
1c: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <__DATA_REGION_ORIGIN__>
20: 80 91 60 00 lds r24, 0x0060 ; 0x800060 <__DATA_REGION_ORIGIN__>
24: 90 91 61 00 lds r25, 0x0061 ; 0x800061 <__DATA_REGION_ORIGIN__+0x1>
28: 01 96 adiw r24, 0x01 ; 1
2a: 90 93 61 00 sts 0x0061, r25 ; 0x800061 <__DATA_REGION_ORIGIN__+0x1>
2e: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <__DATA_REGION_ORIGIN__>
32: 80 ff sbrs r24, 0
34: 02 c0 rjmp .+4 ; 0x3a <main+0x2a>
36: 2d 9a sbi 0x05, 5 ; 5
38: f3 cf rjmp .-26 ; 0x20 <main+0x10>
3a: 2d 98 cbi 0x05, 5 ; 5
3c: f1 cf rjmp .-30 ; 0x20 <main+0x10>
STS 0x0060
is there.
And then when I added second global variable:
#include <avr/io.h>
unsigned short x = 0;
unsigned short y = 0;
void main(void) {
DDRB |= _BV(DDB5); /* output */
PORTB &= ~_BV(PORTB5); /* turn LED off */
x = 7;
y = 9;
while (1) {
x++;
y++;
if ((x + y) & 0x0001) {
PORTB |= _BV(PORTB5); /* turn LED on */
} else {
PORTB &= ~_BV(PORTB5); /* turn LED off */
}
}
}
I got exactly the same behavior - programmer stopped responding to requests from avrdude
.
Now my brain is broken. Nevertheless, I think these miracles are somehow related with allocating global vars at addresses which belong to “extended I/O registers”.