模擬器 - QEMU - Allwinner F1C100S - 移植教學 - 添加GPIO



參考資訊:
https://gitlab.com/qemu-project/qemu

hw/arm/f1c100s.c

#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/datadir.h"
#include "qemu/units.h"
#include "qemu/f1c100s_log.h"
#include "hw/sysbus.h"
#include "hw/arm/boot.h"
#include "hw/ssi/ssi.h"
#include "hw/misc/unimp.h"
#include "hw/boards.h"
#include "hw/usb/hcd-ohci.h"
#include "hw/loader.h"
#include "hw/firmware/smbios.h"
#include "qapi/error.h"
#include "sysemu/sysemu.h"
#include "sysemu/runstate.h"
#include "target/arm/cpu.h"
#include "hw/gpio/f1c100s.h"

#define TYPE_F1C100S "f1c100s"
OBJECT_DECLARE_SIMPLE_TYPE(f1c100s_soc_state, F1C100S)

int f1c100s_debug_level = TRACE_LEVEL;

enum {
    GPIO_BASE,
    BOOTROM_BASE
};

struct f1c100s_soc_state {
    DeviceState parent_obj;
    ARMCPU cpu;
    const hwaddr *memmap;
    MemoryRegion bootrom;

    f1c100s_gpio_state gpio;
};

static const hwaddr f1c100s_memmap[] = {
    [GPIO_BASE]    = 0x01c20800,
    [BOOTROM_BASE] = 0xffff0000
};
 
static struct arm_boot_info f1c100s_binfo = { 0 };
 
static void f1c100s_soc_realize(DeviceState *dev, Error **errp)
{
    f1c100s_soc_state *s = F1C100S(dev);

    trace("call %s()\n", __func__);
    qdev_realize(DEVICE(&s->cpu), NULL, errp);
    sysbus_realize(SYS_BUS_DEVICE(&s->gpio), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, s->memmap[GPIO_BASE]);
}
 
static void f1c100s_soc_instance_init(Object *obj)
{
    f1c100s_soc_state *s = F1C100S(obj);

    trace("call %s()\n", __func__);
    s->memmap = f1c100s_memmap;
    object_initialize_child(obj, "cpu", &s->cpu, ARM_CPU_TYPE_NAME("f1c100s"));
    object_initialize_child(obj, "gpio", &s->gpio, TYPE_F1C100S_GPIO);
}
 
static void f1c100s_soc_class_init(ObjectClass *oc, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(oc);
 
    trace("call %s()\n", __func__);
    dc->realize = f1c100s_soc_realize;
}
 
static const TypeInfo f1c100s_soc_type_info = {
    .name = "f1c100s",
    .parent = TYPE_DEVICE,
    .instance_size = sizeof(f1c100s_soc_state),
    .instance_init = f1c100s_soc_instance_init,
    .class_init = f1c100s_soc_class_init,
};
 
static void f1c100s_soc_register_types(void)
{
    trace("call %s()\n", __func__);
    type_register_static(&f1c100s_soc_type_info);
}
 
type_init(f1c100s_soc_register_types)
 
static void f1c100s_soc_board_init(MachineState *machine)
{
    f1c100s_soc_state *s = NULL;

    trace("call %s()\n", __func__);
    s = F1C100S(object_new(TYPE_F1C100S));
    object_property_add_child(OBJECT(machine), "soc", OBJECT(s));
    object_unref(OBJECT(s));
 
    qdev_realize(DEVICE(s), NULL, &error_abort);
    memory_region_init_rom(&s->bootrom, NULL, "f1c100s.bootrom", 64 * KiB, &error_fatal);
    memory_region_add_subregion(get_system_memory(), s->memmap[BOOTROM_BASE], &s->bootrom);
 
    char *fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
    if (fname) {
        trace("loading... \"%s\"\n", fname);
        load_image_targphys(fname, s->memmap[BOOTROM_BASE], 64 * KiB);
        g_free(fname);
 
        f1c100s_binfo.entry = s->memmap[BOOTROM_BASE];
    }
 
    f1c100s_binfo.ram_size = machine->ram_size;
    CPUARMState *env = &s->cpu.env;
    env->boot_info = &f1c100s_binfo;
    arm_load_kernel(&s->cpu, machine, &f1c100s_binfo);
};
 
static void f1c100s_soc_init(MachineClass *mc)
{
    trace("call %s()\n", __func__);
 
    mc->desc = "Allwinner F1C100S (ARM926EJ-S)";
    mc->init = f1c100s_soc_board_init;
    mc->min_cpus = 1;
    mc->max_cpus = 1;
    mc->default_cpus = 1;
    mc->default_cpu_type = ARM_CPU_TYPE_NAME("f1c100s");
    mc->default_ram_size = 32 * MiB;
    mc->default_ram_id = "f1c100s.ram";
};
 
DEFINE_MACHINE("f1c100s", f1c100s_soc_init)

hw/gpio/f1c100s.c

#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/f1c100s_log.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/gpio/f1c100s.h"

static uint64_t f1c100s_gpio_read(void *opaque, hwaddr offset, unsigned size)
{
    trace("call %s(offset=0x%lx, size=%d)\n", __func__, offset, size);

    return 0;
}

static void f1c100s_gpio_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    trace("call %s(offset=0x%lx, val=0x%lx, size=%d)\n", __func__, offset, val, size);
}

static const MemoryRegionOps f1c100s_gpio_ops = {
    .read = f1c100s_gpio_read,
    .write = f1c100s_gpio_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

static void f1c100s_gpio_realize(DeviceState *dev, Error **errp)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    f1c100s_gpio_state *s = F1C100S_GPIO(dev);

    trace("call %s()\n", __func__);

    memory_region_init_io(&s->iomem, OBJECT(s), &f1c100s_gpio_ops, s, TYPE_F1C100S_GPIO, 0x400);
    sysbus_init_mmio(sbd, &s->iomem);
}

static void f1c100s_gpio_init(Object *obj)
{
    trace("call %s()\n", __func__);
}

static void f1c100s_gpio_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    trace("call %s()\n", __func__);
    dc->realize = f1c100s_gpio_realize;
}

static const TypeInfo f1c100s_gpio_info = {
    .name = TYPE_F1C100S_GPIO,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_init = f1c100s_gpio_init,
    .instance_size = sizeof(f1c100s_gpio_state),
    .class_init = f1c100s_gpio_class_init,
};

static void f1c100s_gpio_register(void)
{
    trace("call %s()\n", __func__);

    type_register_static(&f1c100s_gpio_info);
}

type_init(f1c100s_gpio_register)

include/hw/gpio/f1c100s.h

#ifndef __GPIO_F1C100S_H__
#define __GPIO_F1C100S_H__

#include "qom/object.h"
#include "hw/sysbus.h"

#define TYPE_F1C100S_GPIO "f1c100s-gpio"
OBJECT_DECLARE_SIMPLE_TYPE(f1c100s_gpio_state, F1C100S_GPIO)

struct f1c100s_gpio_state {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
};

#endif

hw/gpio/meson.build

softmmu_ss.add(when: 'CONFIG_F1C100S', if_true: files('f1c100s.c'))

測試程式(main.s)

.global _start
  
    .equ GPIO_BASE, 0x01c20800 
    .equ PE_CFG0,   (GPIO_BASE + (4 * 0x24) + 0x00)
    .equ PE_DATA,   (GPIO_BASE + (4 * 0x24) + 0x10)
   
    .arm
    .text
_start:
    b reset
    b .
    b .
    b .
    b .
    b .
    b .
    b .
     
reset:
    ldr r0, =PE_CFG0
    ldr r1, =0x11223344
    str r1, [r0]
 
    ldr r0, =PE_DATA
    ldr r1, =0x55667788
    str r1, [r0]
0:
    b 0b
    .end

main.ld

MEMORY {
    RAM : ORIGIN = 0xffff0000, LENGTH = 32K
}

SECTIONS {
    text : {
        PROVIDE(__spl_start = .);
        *(.text*)
        PROVIDE(__spl_end = .);
    } > RAM
    PROVIDE(__spl_size = __spl_end - __spl_start);
}

編譯、測試

$ arm-none-eabi-as -mcpu=arm9 -o main.o main.s
$ arm-none-eabi-ld -T main.ld -o main.elf main.o
$ arm-none-eabi-objcopy -O binary main.elf main.bin

$ make -j4
$ ./build/qemu-system-arm -M f1c100s -bios main.bin
    [TRACE] call f1c100s_gpio_register()
    [TRACE] call f1c100s_soc_register_types()
    [TRACE] call f1c100s_soc_init()
    [TRACE] call f1c100s_soc_class_init()
    [TRACE] call f1c100s_gpio_class_init()
    [TRACE] call f1c100s_soc_board_init()
    [TRACE] call f1c100s_soc_instance_init()
    [TRACE] call f1c100s_gpio_init()
    [TRACE] call f1c100s_soc_realize()
    [TRACE] call f1c100s_gpio_realize()
    [TRACE] loading... "main.bin"
    [TRACE] call f1c100s_gpio_write(offset=0x90, val=0x11223344, size=4)
    [TRACE] call f1c100s_gpio_write(offset=0xa0, val=0x55667788, size=4)