Wrong initial value of stack pointer on AVR devices
Host environment
- Operating system: Windows 11 22H2
- OS/kernel version: 22521.1265
- Architecture: AMD64
- QEMU flavor: qemu-system-avr
- QEMU version: v7.2.0-11948-ge6523b71fc-dirty
- QEMU command line: qemu-system-avr.exe -machine uno -bios (some valid AVR binary)
Emulated/Virtualized environment
- Operating system: None
- OS/kernel version: None
- Architecture: AVR
Description of problem
The initial value of stack pointer of AVR MCUs should be RAMEND (address of the end of their RAM), but QEMU initialize them to 0.
qemu-system-avr -machine help
lists 4 flavors of MCUs which are ATmega168, ATmega2560, ATmega1280, ATmega328P. According to their datasheets, the stack pointer should be initialized as follows on reset.
- ATmega168: RAMEND (which is 0x04FF)
- ATmega2560 and ATmega1280: RAMEND (which is 0x21FF)
- ATmega328P: RAMEND (which is 0x08FF)
Steps to reproduce
-
Assemble the assembly code below:
avrasm2 -fI test.asm
;; test.asm .INCLUDE "m328Pdef.inc" .EQU F_CPU = 16000000 .EQU BAUD_RATE = 9600 .EQU PRESCALE = (F_CPU / (16 * BAUD_RATE)) - 1 .CSEG start: ;; initialize USART (serial port) LDI R16, LOW(PRESCALE) LDI R17, HIGH(PRESCALE) STS UBRR0L, R16 STS UBRR0H, R17 LDI R16, (1 << RXEN0) | (1 << TXEN0) STS UCSR0B, R16 ;; Get stack pointer low byte and print it in ASCII IN R16, SPL LDI R17, 0x30 ADD R16, R17 print1: LDS r17, UCSR0A SBRS r17, UDRE0 RJMP print1 STS UDR0, r16 ;; Get stack pointer high byte and print it in ASCII IN R16, SPH LDI R17, 0x30 ADD R16, R17 print2: LDS r17, UCSR0A SBRS r17, UDRE0 RJMP print2 STS UDR0, r16 end: RJMP end
-
Convert it to bin file:
avr-objcopy --input-target=ihex --output-target=binary test.hex test.bin
-
Run it with QEMU:
qemu-system-avr -machine uno -bios test.bin -serial stdio
This should print 00 which means that the stack pointer is initialized to 0.
Additional information
I examined the source code and I think that editing the function avr_cpu_reset_hold
in /target/avr/cpu.c
might fix this issue. This is my first time seeing QEMU source code, so I might be wrong, though.
// in /target/avr/cpu.c line 70
static void avr_cpu_reset_hold(Object *obj)
{
// ...
env->rampD = 0;
env->rampX = 0;
env->rampY = 0;
env->rampZ = 0;
env->eind = 0;
env->sp = 0; // <-- change this value in accordance with board type?
//...
}