qemu/hw/avr/atmega.c
Gihun Nam 235948bf53 hw/avr/atmega: Fix wrong initial value of stack pointer
The current implementation initializes the stack pointer of AVR devices
to 0. Although older AVR devices used to be like that, newer ones set
it to RAMEND.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1525
Signed-off-by: Gihun Nam <gihun.nam@outlook.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <PH0P222MB0010877445B594724D40C924DEBDA@PH0P222MB0010.NAMP222.PROD.OUTLOOK.COM>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
2023-11-28 14:27:12 +01:00

462 lines
16 KiB
C

/*
* QEMU ATmega MCU
*
* Copyright (c) 2019-2020 Philippe Mathieu-Daudé
*
* This work is licensed under the terms of the GNU GPLv2 or later.
* See the COPYING file in the top-level directory.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/units.h"
#include "qapi/error.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
#include "qom/object.h"
#include "hw/misc/unimp.h"
#include "atmega.h"
enum AtmegaPeripheral {
POWER0, POWER1,
GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF,
GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL,
USART0, USART1, USART2, USART3,
TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5,
PERIFMAX
};
#define GPIO(n) (n + GPIOA)
#define USART(n) (n + USART0)
#define TIMER(n) (n + TIMER0)
#define POWER(n) (n + POWER0)
typedef struct {
uint16_t addr;
enum AtmegaPeripheral power_index;
uint8_t power_bit;
/* timer specific */
uint16_t intmask_addr;
uint16_t intflag_addr;
bool is_timer16;
} peripheral_cfg;
struct AtmegaMcuClass {
/*< private >*/
SysBusDeviceClass parent_class;
/*< public >*/
const char *uc_name;
const char *cpu_type;
size_t flash_size;
size_t eeprom_size;
size_t sram_size;
size_t io_size;
size_t gpio_count;
size_t adc_count;
const uint8_t *irq;
const peripheral_cfg *dev;
};
typedef struct AtmegaMcuClass AtmegaMcuClass;
DECLARE_CLASS_CHECKERS(AtmegaMcuClass, ATMEGA_MCU,
TYPE_ATMEGA_MCU)
static const peripheral_cfg dev168_328[PERIFMAX] = {
[USART0] = { 0xc0, POWER0, 1 },
[TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false },
[TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
[POWER0] = { 0x64 },
[TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
[GPIOD] = { 0x29 },
[GPIOC] = { 0x26 },
[GPIOB] = { 0x23 },
}, dev1280_2560[PERIFMAX] = {
[USART3] = { 0x130, POWER1, 2 },
[TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true },
[GPIOL] = { 0x109 },
[GPIOK] = { 0x106 },
[GPIOJ] = { 0x103 },
[GPIOH] = { 0x100 },
[USART2] = { 0xd0, POWER1, 1 },
[USART1] = { 0xc8, POWER1, 0 },
[USART0] = { 0xc0, POWER0, 1 },
[TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, /* TODO async */
[TIMER4] = { 0xa0, POWER1, 4, 0x72, 0x39, true },
[TIMER3] = { 0x90, POWER1, 3, 0x71, 0x38, true },
[TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
[POWER1] = { 0x65 },
[POWER0] = { 0x64 },
[TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
[GPIOG] = { 0x32 },
[GPIOF] = { 0x2f },
[GPIOE] = { 0x2c },
[GPIOD] = { 0x29 },
[GPIOC] = { 0x26 },
[GPIOB] = { 0x23 },
[GPIOA] = { 0x20 },
};
enum AtmegaIrq {
USART0_RXC_IRQ, USART0_DRE_IRQ, USART0_TXC_IRQ,
USART1_RXC_IRQ, USART1_DRE_IRQ, USART1_TXC_IRQ,
USART2_RXC_IRQ, USART2_DRE_IRQ, USART2_TXC_IRQ,
USART3_RXC_IRQ, USART3_DRE_IRQ, USART3_TXC_IRQ,
TIMER0_CAPT_IRQ, TIMER0_COMPA_IRQ, TIMER0_COMPB_IRQ,
TIMER0_COMPC_IRQ, TIMER0_OVF_IRQ,
TIMER1_CAPT_IRQ, TIMER1_COMPA_IRQ, TIMER1_COMPB_IRQ,
TIMER1_COMPC_IRQ, TIMER1_OVF_IRQ,
TIMER2_CAPT_IRQ, TIMER2_COMPA_IRQ, TIMER2_COMPB_IRQ,
TIMER2_COMPC_IRQ, TIMER2_OVF_IRQ,
TIMER3_CAPT_IRQ, TIMER3_COMPA_IRQ, TIMER3_COMPB_IRQ,
TIMER3_COMPC_IRQ, TIMER3_OVF_IRQ,
TIMER4_CAPT_IRQ, TIMER4_COMPA_IRQ, TIMER4_COMPB_IRQ,
TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ,
TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ,
TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ,
IRQ_COUNT
};
#define USART_IRQ_COUNT 3
#define USART_RXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_RXC_IRQ)
#define USART_DRE_IRQ(n) (n * USART_IRQ_COUNT + USART0_DRE_IRQ)
#define USART_TXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_TXC_IRQ)
#define TIMER_IRQ_COUNT 5
#define TIMER_CAPT_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_CAPT_IRQ)
#define TIMER_COMPA_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPA_IRQ)
#define TIMER_COMPB_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPB_IRQ)
#define TIMER_COMPC_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPC_IRQ)
#define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ)
static const uint8_t irq168_328[IRQ_COUNT] = {
[TIMER2_COMPA_IRQ] = 8,
[TIMER2_COMPB_IRQ] = 9,
[TIMER2_OVF_IRQ] = 10,
[TIMER1_CAPT_IRQ] = 11,
[TIMER1_COMPA_IRQ] = 12,
[TIMER1_COMPB_IRQ] = 13,
[TIMER1_OVF_IRQ] = 14,
[TIMER0_COMPA_IRQ] = 15,
[TIMER0_COMPB_IRQ] = 16,
[TIMER0_OVF_IRQ] = 17,
[USART0_RXC_IRQ] = 19,
[USART0_DRE_IRQ] = 20,
[USART0_TXC_IRQ] = 21,
}, irq1280_2560[IRQ_COUNT] = {
[TIMER2_COMPA_IRQ] = 14,
[TIMER2_COMPB_IRQ] = 15,
[TIMER2_OVF_IRQ] = 16,
[TIMER1_CAPT_IRQ] = 17,
[TIMER1_COMPA_IRQ] = 18,
[TIMER1_COMPB_IRQ] = 19,
[TIMER1_COMPC_IRQ] = 20,
[TIMER1_OVF_IRQ] = 21,
[TIMER0_COMPA_IRQ] = 22,
[TIMER0_COMPB_IRQ] = 23,
[TIMER0_OVF_IRQ] = 24,
[USART0_RXC_IRQ] = 26,
[USART0_DRE_IRQ] = 27,
[USART0_TXC_IRQ] = 28,
[TIMER3_CAPT_IRQ] = 32,
[TIMER3_COMPA_IRQ] = 33,
[TIMER3_COMPB_IRQ] = 34,
[TIMER3_COMPC_IRQ] = 35,
[TIMER3_OVF_IRQ] = 36,
[USART1_RXC_IRQ] = 37,
[USART1_DRE_IRQ] = 38,
[USART1_TXC_IRQ] = 39,
[TIMER4_CAPT_IRQ] = 42,
[TIMER4_COMPA_IRQ] = 43,
[TIMER4_COMPB_IRQ] = 44,
[TIMER4_COMPC_IRQ] = 45,
[TIMER4_OVF_IRQ] = 46,
[TIMER5_CAPT_IRQ] = 47,
[TIMER5_COMPA_IRQ] = 48,
[TIMER5_COMPB_IRQ] = 49,
[TIMER5_COMPC_IRQ] = 50,
[TIMER5_OVF_IRQ] = 51,
[USART2_RXC_IRQ] = 52,
[USART2_DRE_IRQ] = 53,
[USART2_TXC_IRQ] = 54,
[USART3_RXC_IRQ] = 55,
[USART3_DRE_IRQ] = 56,
[USART3_TXC_IRQ] = 57,
};
static void connect_peripheral_irq(const AtmegaMcuClass *k,
SysBusDevice *dev, int dev_irqn,
DeviceState *cpu,
unsigned peripheral_index)
{
int cpu_irq = k->irq[peripheral_index];
if (!cpu_irq) {
return;
}
/* FIXME move that to avr_cpu_set_int() once 'sample' board is removed */
assert(cpu_irq >= 2);
cpu_irq -= 2;
sysbus_connect_irq(dev, dev_irqn, qdev_get_gpio_in(cpu, cpu_irq));
}
static void connect_power_reduction_gpio(AtmegaMcuState *s,
const AtmegaMcuClass *k,
DeviceState *cpu,
unsigned peripheral_index)
{
unsigned power_index = k->dev[peripheral_index].power_index;
assert(k->dev[power_index].addr);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwr[power_index - POWER0]),
k->dev[peripheral_index].power_bit,
qdev_get_gpio_in(cpu, 0));
}
static void atmega_realize(DeviceState *dev, Error **errp)
{
AtmegaMcuState *s = ATMEGA_MCU(dev);
const AtmegaMcuClass *mc = ATMEGA_MCU_GET_CLASS(dev);
DeviceState *cpudev;
SysBusDevice *sbd;
char *devname;
size_t i;
assert(mc->io_size <= 0x200);
if (!s->xtal_freq_hz) {
error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
return;
}
/* CPU */
object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type);
object_property_set_uint(OBJECT(&s->cpu), "init-sp",
mc->io_size + mc->sram_size - 1, &error_abort);
qdev_realize(DEVICE(&s->cpu), NULL, &error_abort);
cpudev = DEVICE(&s->cpu);
/* SRAM */
memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size,
&error_abort);
memory_region_add_subregion(get_system_memory(),
OFFSET_DATA + mc->io_size, &s->sram);
/* Flash */
memory_region_init_rom(&s->flash, OBJECT(dev),
"flash", mc->flash_size, &error_fatal);
memory_region_add_subregion(get_system_memory(), OFFSET_CODE, &s->flash);
/*
* I/O
*
* 0x00 - 0x1f: Registers
* 0x20 - 0x5f: I/O memory
* 0x60 - 0xff: Extended I/O
*/
s->io = qdev_new(TYPE_UNIMPLEMENTED_DEVICE);
qdev_prop_set_string(s->io, "name", "I/O");
qdev_prop_set_uint64(s->io, "size", mc->io_size);
sysbus_realize_and_unref(SYS_BUS_DEVICE(s->io), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->io), 0, OFFSET_DATA, -1234);
/* Power Reduction */
for (i = 0; i < POWER_MAX; i++) {
int idx = POWER(i);
if (!mc->dev[idx].addr) {
continue;
}
devname = g_strdup_printf("power%zu", i);
object_initialize_child(OBJECT(dev), devname, &s->pwr[i],
TYPE_AVR_MASK);
sysbus_realize(SYS_BUS_DEVICE(&s->pwr[i]), &error_abort);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwr[i]), 0,
OFFSET_DATA + mc->dev[idx].addr);
g_free(devname);
}
/* GPIO */
for (i = 0; i < GPIO_MAX; i++) {
int idx = GPIO(i);
if (!mc->dev[idx].addr) {
continue;
}
devname = g_strdup_printf("atmega-gpio-%c", 'a' + (char)i);
create_unimplemented_device(devname,
OFFSET_DATA + mc->dev[idx].addr, 3);
g_free(devname);
}
/* USART */
for (i = 0; i < USART_MAX; i++) {
int idx = USART(i);
if (!mc->dev[idx].addr) {
continue;
}
devname = g_strdup_printf("usart%zu", i);
object_initialize_child(OBJECT(dev), devname, &s->usart[i],
TYPE_AVR_USART);
qdev_prop_set_chr(DEVICE(&s->usart[i]), "chardev", serial_hd(i));
sbd = SYS_BUS_DEVICE(&s->usart[i]);
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[USART(i)].addr);
connect_peripheral_irq(mc, sbd, 0, cpudev, USART_RXC_IRQ(i));
connect_peripheral_irq(mc, sbd, 1, cpudev, USART_DRE_IRQ(i));
connect_peripheral_irq(mc, sbd, 2, cpudev, USART_TXC_IRQ(i));
connect_power_reduction_gpio(s, mc, DEVICE(&s->usart[i]), idx);
g_free(devname);
}
/* Timer */
for (i = 0; i < TIMER_MAX; i++) {
int idx = TIMER(i);
if (!mc->dev[idx].addr) {
continue;
}
if (!mc->dev[idx].is_timer16) {
create_unimplemented_device("avr-timer8",
OFFSET_DATA + mc->dev[idx].addr, 5);
create_unimplemented_device("avr-timer8-intmask",
OFFSET_DATA
+ mc->dev[idx].intmask_addr, 1);
create_unimplemented_device("avr-timer8-intflag",
OFFSET_DATA
+ mc->dev[idx].intflag_addr, 1);
continue;
}
devname = g_strdup_printf("timer%zu", i);
object_initialize_child(OBJECT(dev), devname, &s->timer[i],
TYPE_AVR_TIMER16);
object_property_set_uint(OBJECT(&s->timer[i]), "cpu-frequency-hz",
s->xtal_freq_hz, &error_abort);
sbd = SYS_BUS_DEVICE(&s->timer[i]);
sysbus_realize(sbd, &error_abort);
sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[idx].addr);
sysbus_mmio_map(sbd, 1, OFFSET_DATA + mc->dev[idx].intmask_addr);
sysbus_mmio_map(sbd, 2, OFFSET_DATA + mc->dev[idx].intflag_addr);
connect_peripheral_irq(mc, sbd, 0, cpudev, TIMER_CAPT_IRQ(i));
connect_peripheral_irq(mc, sbd, 1, cpudev, TIMER_COMPA_IRQ(i));
connect_peripheral_irq(mc, sbd, 2, cpudev, TIMER_COMPB_IRQ(i));
connect_peripheral_irq(mc, sbd, 3, cpudev, TIMER_COMPC_IRQ(i));
connect_peripheral_irq(mc, sbd, 4, cpudev, TIMER_OVF_IRQ(i));
connect_power_reduction_gpio(s, mc, DEVICE(&s->timer[i]), idx);
g_free(devname);
}
create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6);
create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8);
create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2);
create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1);
create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3);
create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3);
}
static Property atmega_props[] = {
DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState,
xtal_freq_hz, 0),
DEFINE_PROP_END_OF_LIST()
};
static void atmega_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = atmega_realize;
device_class_set_props(dc, atmega_props);
/* Reason: Mapped at fixed location on the system bus */
dc->user_creatable = false;
}
static void atmega168_class_init(ObjectClass *oc, void *data)
{
AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
amc->flash_size = 16 * KiB;
amc->eeprom_size = 512;
amc->sram_size = 1 * KiB;
amc->io_size = 256;
amc->gpio_count = 23;
amc->adc_count = 6;
amc->irq = irq168_328;
amc->dev = dev168_328;
};
static void atmega328_class_init(ObjectClass *oc, void *data)
{
AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
amc->flash_size = 32 * KiB;
amc->eeprom_size = 1 * KiB;
amc->sram_size = 2 * KiB;
amc->io_size = 256;
amc->gpio_count = 23;
amc->adc_count = 6;
amc->irq = irq168_328;
amc->dev = dev168_328;
};
static void atmega1280_class_init(ObjectClass *oc, void *data)
{
AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
amc->cpu_type = AVR_CPU_TYPE_NAME("avr51");
amc->flash_size = 128 * KiB;
amc->eeprom_size = 4 * KiB;
amc->sram_size = 8 * KiB;
amc->io_size = 512;
amc->gpio_count = 86;
amc->adc_count = 16;
amc->irq = irq1280_2560;
amc->dev = dev1280_2560;
};
static void atmega2560_class_init(ObjectClass *oc, void *data)
{
AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
amc->cpu_type = AVR_CPU_TYPE_NAME("avr6");
amc->flash_size = 256 * KiB;
amc->eeprom_size = 4 * KiB;
amc->sram_size = 8 * KiB;
amc->io_size = 512;
amc->gpio_count = 54;
amc->adc_count = 16;
amc->irq = irq1280_2560;
amc->dev = dev1280_2560;
};
static const TypeInfo atmega_mcu_types[] = {
{
.name = TYPE_ATMEGA168_MCU,
.parent = TYPE_ATMEGA_MCU,
.class_init = atmega168_class_init,
}, {
.name = TYPE_ATMEGA328_MCU,
.parent = TYPE_ATMEGA_MCU,
.class_init = atmega328_class_init,
}, {
.name = TYPE_ATMEGA1280_MCU,
.parent = TYPE_ATMEGA_MCU,
.class_init = atmega1280_class_init,
}, {
.name = TYPE_ATMEGA2560_MCU,
.parent = TYPE_ATMEGA_MCU,
.class_init = atmega2560_class_init,
}, {
.name = TYPE_ATMEGA_MCU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AtmegaMcuState),
.class_size = sizeof(AtmegaMcuClass),
.class_init = atmega_class_init,
.abstract = true,
}
};
DEFINE_TYPES(atmega_mcu_types)