tinygo/targets/avr.S
Ayke van Laethem bb5050a50d avr: fix .data initialization for binaries over 64kB
I found that .data is not correctly initialized with firmware images
that use over 64kB of flash. The problem is that the data in .data
(which is stored in flash, and copied to RAM at reset) is beyond the
64kB limit and must therefore be loaded using the elpm instruction
instead of the lpm instruction.

I encountered this issue while getting testdata/math.go to work for AVR.
The following command mostly works with this patch, while it prints
garbage withtout it:

    tinygo run -target=simavr -size=short -scheduler=none ./testdata/math.go

(This also requires a patch to picolibc to work, see
https://github.com/picolibc/picolibc/pull/371)

It still doesn't work entirely with this patch: some of the math
operations have an incorrect result. But at least it's an improvement as
it won't print garbage anymore.
2022-11-14 22:22:26 +01:00

84 строки
2,2 КиБ
ArmAsm

; This file provides common code across AVRs that cannot be implemented directly
; in Go.
; The reset vector is device-specific and is generated by tools/gen-device-avr.py.
; These definitions are necessary because LLVM does not yet know these register
; aliases. See: https://reviews.llvm.org/D96492
#define xl r26
#define xh r27
#define yl r28
#define yh r29
#define zl r30
#define zh r31
; Ugly hack until https://reviews.llvm.org/D137572 is merged.
#if !defined(__AVR_HAVE_ELPM__) && defined(__flash1)
#define __AVR_HAVE_ELPM__
#endif
; Startup code
.section .text.__vector_RESET
.global __vector_RESET
__vector_RESET:
clr r1 ; r1 is expected to be 0 by the C calling convention
; Set up the stack pointer.
ldi xl, lo8(_stack_top)
ldi xh, hi8(_stack_top)
out 0x3d, xl; SPL
out 0x3e, xh; SPH
; Subtract one from the stack pointer, so it doesn't point in the .data section.
push r0
; Initialize .data
init_data:
ldi xl, lo8(_sdata)
ldi xh, hi8(_sdata)
ldi yl, lo8(_edata)
ldi yh, hi8(_edata)
ldi zl, lo8(_sidata)
ldi zh, hi8(_sidata)
#ifdef __AVR_HAVE_ELPM__
ldi r16, hh8(_sidata) ; RAMPZ = hh8(_sidata)
out 0x3b, r16
#endif
init_data_loop:
cp xl, yl ; if x == y
cpc xh, yh
breq init_data_end ; goto main
#ifdef __AVR_HAVE_ELPM__
elpm r0, Z+ ; r0 = *(z++)
#else
lpm r0, Z+ ; r0 = *(z++)
#endif
st X+, r0 ; *(x++) = r0
rjmp init_data_loop ; goto init_data_loop
init_data_end:
; main will be placed right after here by the linker script so there's no
; need to jump.
; The only thing this WDT handler really does is disable itself, to get out of
; sleep mode.
.section .text.__vector_WDT
.global __vector_WDT
__vector_WDT:
push r16
clr r16
wdr ; Reset watchdog
out 0x34, r16 ; Clear reset reason (MCUSR)
; part 1: set WDCE and WDE to enable editing WDTCSR
lds r16, 0x60 ; r16 = WDTCSR
ori r16, 0x18 ; r16 |= WDCE | WDE
sts 0x60, r16 ; WDTCSR = r16
; part 2: within 4 clock cycles, set the new value for WDTCSR
clr r16
sts 0x60, r16 ; WDTCSR = 0
pop r16
reti