Skip to content

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.

Steps to reproduce

  1. 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
  2. Convert it to bin file: avr-objcopy --input-target=ihex --output-target=binary test.hex test.bin

  3. 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?

    //...
}
Edited by Gihun Nam | 남기훈
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information