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



參考資訊:
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"
#include "hw/misc/f1c100s_ccu.h"
#include "hw/intc/f1c100s.h"
#include "hw/char/f1c100s_uart.h"
#include "hw/timer/f1c100s.h"
#include "hw/ssi/f1c100s.h"

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

int f1c100s_debug_level = TRACE_LEVEL;

enum {
    SRAM_BASE,
    CCU_BASE,
    INTC_BASE,
    GPIO_BASE,
    TIMER_BASE,
    SDRAM_BASE,
    UART0_BASE,
    UART1_BASE,
    UART2_BASE,
    SPI0_BASE,
    SPI1_BASE,
    BOOTROM_BASE
};

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

    f1c100s_ccu_state ccu;
    f1c100s_spi_state spi[2];
    f1c100s_intc_state intc;
    f1c100s_gpio_state gpio;
    f1c100s_timer_state timer;
    f1c100s_uart_state uart[3];
};

static const hwaddr f1c100s_memmap[] = {
    [SRAM_BASE]    = 0x00000000,
    [CCU_BASE]     = 0x01c20000,
    [INTC_BASE]    = 0x01c20400,
    [GPIO_BASE]    = 0x01c20800,
    [TIMER_BASE]   = 0x01c20c00,
    [SDRAM_BASE]   = 0x80000000,
    [UART0_BASE]   = 0x01c25000,
    [UART1_BASE]   = 0x01c25400,
    [UART2_BASE]   = 0x01c25800,
    [SPI0_BASE]    = 0x01c05000,
    [SPI1_BASE]    = 0x01c06000,
    [BOOTROM_BASE] = 0xffff0000
};

enum {
    IRQ_TIMER0 = 13,
    IRQ_TIMER1 = 14,
    IRQ_TIMER2 = 15,
};

static struct arm_boot_info f1c100s_binfo = { 0 };
 
static void f1c100s_soc_realize(DeviceState *dev, Error **errp)
{
    int i = 0;
    f1c100s_soc_state *s = F1C100S(dev);

    trace("call %s()\n", __func__);
    qdev_realize(DEVICE(&s->cpu), NULL, errp);

    memory_region_init_ram(&s->sram, OBJECT(dev), "sram", 40 * KiB, &error_abort);
    memory_region_add_subregion(get_system_memory(), s->memmap[SRAM_BASE], &s->sram);

    sysbus_realize(SYS_BUS_DEVICE(&s->ccu), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccu), 0, s->memmap[CCU_BASE]);

    sysbus_realize(SYS_BUS_DEVICE(&s->intc), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->intc), 0, s->memmap[INTC_BASE]);
    sysbus_connect_irq(SYS_BUS_DEVICE(&s->intc), 0, qdev_get_gpio_in(DEVICE(&s->cpu), ARM_CPU_IRQ));

    sysbus_realize(SYS_BUS_DEVICE(&s->gpio), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, s->memmap[GPIO_BASE]);

    sysbus_realize(SYS_BUS_DEVICE(&s->timer), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer), 0, s->memmap[TIMER_BASE]);
    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 0, qdev_get_gpio_in(DEVICE(&s->intc), IRQ_TIMER0));
    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 1, qdev_get_gpio_in(DEVICE(&s->intc), IRQ_TIMER1));
    sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer), 2, qdev_get_gpio_in(DEVICE(&s->intc), IRQ_TIMER2));

    for (i = 0; i < 3; i++) {
        qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i));
        sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), &error_fatal);
        sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, s->memmap[UART0_BASE + i]);
    }

    sysbus_realize(SYS_BUS_DEVICE(&s->spi[0]), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[0]), 0, s->memmap[SPI0_BASE]);

    sysbus_realize(SYS_BUS_DEVICE(&s->spi[1]), &error_fatal);
    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[1]), 0, s->memmap[SPI1_BASE]);

    DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
    if (dinfo) {
        DeviceState *spi_flash;
        BusState *spi_bus;

        spi_flash = qdev_new("w25q64");
        qdev_prop_set_drive(spi_flash, "drive", blk_by_legacy_dinfo(dinfo));
        spi_bus = qdev_get_child_bus(DEVICE(&s->spi[0]), "ssi");
        qdev_realize_and_unref(spi_flash, spi_bus, &error_fatal);

        qemu_irq cs_line = qdev_get_gpio_in_named(spi_flash, SSI_GPIO_CS, 0);
        sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[0]), 0, cs_line);
    }
}
 
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, "ccu", &s->ccu, TYPE_F1C100S_CCU);
    object_initialize_child(obj, "intc", &s->intc, TYPE_F1C100S_INTC);
    object_initialize_child(obj, "gpio", &s->gpio, TYPE_F1C100S_GPIO);
    object_initialize_child(obj, "timer", &s->timer, TYPE_F1C100S_TIMER);
    object_initialize_child(obj, "uart0", &s->uart[0], TYPE_F1C100S_UART);
    object_initialize_child(obj, "uart1", &s->uart[1], TYPE_F1C100S_UART);
    object_initialize_child(obj, "uart2", &s->uart[2], TYPE_F1C100S_UART);
    object_initialize_child(obj, "spi[0]", &s->spi[0], TYPE_F1C100S_SPI);
    object_initialize_child(obj, "spi[1]", &s->spi[1], TYPE_F1C100S_SPI);
}
 
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_add_subregion(get_system_memory(), s->memmap[SDRAM_BASE], machine->ram);
    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/ssi/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/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/ssi/ssi.h"
#include "hw/ssi/f1c100s.h"

static uint64_t f1c100s_spi_read(void *opaque, hwaddr offset, unsigned size)
{
    f1c100s_spi_state *s = F1C100S_SPI(opaque);

    trace("call %s(offset=0x%lx, size=%d)\n", __func__, offset, size);

    switch (offset) {
    case RXD:
        while (!fifo8_is_empty(&s->rx_fifo)) {
            trace("spi rx data = 0x%x\n", fifo8_pop(&s->rx_fifo));
        }
        break;
    }

    return 0;
}

static void f1c100s_spi_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
{
    int i = 0;
    uint8_t tx = 0;
    uint8_t rx = 0;
    f1c100s_spi_state *s = F1C100S_SPI(opaque);

    trace("call %s(offset=0x%lx, val=0x%lx, size=%d)\n", __func__, offset, val, size);

    switch (offset) {
    case MBC:
        s->burst_len = val;
        trace("spi brust size = %d\n", s->burst_len);
        break;
    case TXD:
        if (!fifo8_is_full(&s->tx_fifo)) {
            fifo8_push(&s->tx_fifo, (uint8_t)val);
        }
        break;
    case TCR:
        if (val & (1ULL << 31)) {
            qemu_irq_lower(s->ss_line);
            for (i = 0; i < s->burst_len; i++) {
                tx = 0xff;
                if (!fifo8_is_empty(&s->tx_fifo)) {
                    tx = fifo8_pop(&s->tx_fifo);
                }
                trace("spi tx data = 0x%x\n", tx);
                rx = ssi_transfer(s->spi, tx);

                if (!fifo8_is_full(&s->rx_fifo)) {
                    fifo8_push(&s->rx_fifo, rx);
                }
            }
            qemu_irq_raise(s->ss_line);
        }
        break;
    }
}

static const MemoryRegionOps f1c100s_spi_ops = {
    .read = f1c100s_spi_read,
    .write = f1c100s_spi_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

static void f1c100s_spi_realize(DeviceState *dev, Error **errp)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
    f1c100s_spi_state *s = F1C100S_SPI(dev);

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

    sysbus_init_irq(sbd, &s->ss_line);
    memory_region_init_io(&s->iomem, OBJECT(s), &f1c100s_spi_ops, s, TYPE_F1C100S_SPI, 4 * KiB);
    sysbus_init_mmio(sbd, &s->iomem);
    s->spi = ssi_create_bus(dev, "ssi");
    fifo8_create(&s->tx_fifo, s->fifo_depth);
    fifo8_create(&s->rx_fifo, s->fifo_depth);
}

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

static Property f1c100s_spi_properties[] = {
    DEFINE_PROP_UINT8("fifo_depth", f1c100s_spi_state, fifo_depth, 64),
    DEFINE_PROP_END_OF_LIST()
};

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

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

static const TypeInfo f1c100s_spi_info = {
    .name = TYPE_F1C100S_SPI,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_init = f1c100s_spi_init,
    .instance_size = sizeof(f1c100s_spi_state),
    .class_init = f1c100s_spi_class_init,
};

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

    type_register_static(&f1c100s_spi_info);
}

type_init(f1c100s_spi_register)

include/hw/ssi/f1c100s.h

#ifndef __SPI_F1C100S_H__
#define __SPI_F1C100S_H__

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

#define TYPE_F1C100S_SPI "f1c100s-spi"
OBJECT_DECLARE_SIMPLE_TYPE(f1c100s_spi_state, F1C100S_SPI)

#define GCR  0x04
#define TCR  0x08
#define ISR  0x14
#define FCR  0x18
#define FSR  0x1c
#define WCR  0x20
#define CCR  0x24
#define MBC  0x30
#define MTC  0x34
#define BCC  0x38
#define TXD  0x200
#define RXD  0x300

struct f1c100s_spi_state {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    SSIBus *spi;
    qemu_irq ss_line;

    Fifo8 tx_fifo;
    Fifo8 rx_fifo;
    uint8_t fifo_depth;
    uint8_t burst_len;
};

#endif

hw/ssi/meson.build

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

測試程式(main.s)

    .global _start
 
    .equ SPI0_BASE,  0x01c05000
    .equ CCU_BASE,   0x01c20000
    .equ GPIO_BASE,  0x01c20800
    .equ UART0_BASE, 0x01c25000
 
    .equ PLL_PERIPH_CTRL_REG,   0x0028
    .equ AHB_APB_HCLKC_CFG_REG, 0x0054
    .equ BUS_CLK_GATING_REG0,   0x0060
    .equ BUS_CLK_GATING_REG2,   0x0068
    .equ BUS_SOFT_RST_REG0,     0x02c0
    .equ BUS_SOFT_RST_REG2,     0x02d0
 
    .equ PC,   (0x24 * 2)
    .equ PE,   (0x24 * 4)
    .equ CFG0, 0x00
    .equ DATA, 0x10
    .equ RBR,  0x0000
    .equ DLL,  0x0000
    .equ DLH,  0x0004
    .equ IER,  0x0004
    .equ IIR,  0x0008
    .equ LCR,  0x000c
    .equ MCR,  0x0010
    .equ USR,  0x007c
 
    .equ GCR,  0x04
    .equ TCR,  0x08
    .equ IER,  0x10
    .equ ISR,  0x14
    .equ FCR,  0x18
    .equ FSR,  0x1c
    .equ WCR,  0x20
    .equ CCR,  0x24
    .equ MBC,  0x30
    .equ MTC,  0x34
    .equ BCC,  0x38
    .equ TXD,  0x200
    .equ RXD,  0x300
 
    .arm
    .text
_start:
    .long 0xea000016
    .byte 'e', 'G', 'O', 'N', '.', 'B', 'T', '0'
    .long 0, __spl_size
    .byte 'S', 'P', 'L', 2
    .long 0, 0
    .long 0, 0, 0, 0, 0, 0, 0, 0
    .long 0, 0, 0, 0, 0, 0, 0, 0
     
_vector:
    b reset
    b .
    b .
    b .
    b .
    b .
    b .
    b .
     
reset:
    ldr r0, =CCU_BASE
    ldr r1, =0x80041800
    str r1, [r0, #PLL_PERIPH_CTRL_REG]
    ldr r1, =0x00003180
    str r1, [r0, #AHB_APB_HCLKC_CFG_REG]
    ldr r1, =(1 << 20)
    str r1, [r0, #BUS_CLK_GATING_REG0]
    str r1, [r0, #BUS_CLK_GATING_REG2]
    str r1, [r0, #BUS_SOFT_RST_REG0]
    str r1, [r0, #BUS_SOFT_RST_REG2]
  
    ldr r0, =GPIO_BASE
    ldr r1, =0x55
    str r1, [r0, #(PE + CFG0)]
    ldr r1, =0x2222
    str r1, [r0, #(PC + CFG0)]
  
    ldr r0, =UART0_BASE
    ldr r1, =0x00
    str r1, [r0, #IER]
    ldr r1, =0xf7
    str r1, [r0, #IIR]
    ldr r1, =0x00
    str r1, [r0, #MCR]
    ldr r1, [r0, #LCR]
    orr r1, #(1 << 7)
    str r1, [r0, #LCR]
    ldr r1, =54
    str r1, [r0, #DLL]
    ldr r1, =0x00
    str r1, [r0, #DLH]
    ldr r1, [r0, #LCR]
    bic r1, #(1 << 7)
    str r1, [r0, #LCR]
    ldr r1, [r0, #LCR]
    bic r1, #0x1f
    orr r1, #0x03
    str r1, [r0, #LCR]
 
    ldr r4, =SPI0_BASE
    ldr r1, =(1 << 1) | (1 << 0)
    str r1, [r4, #GCR]
    ldr r1, =(1 << 2) | (1 << 8)
    str r1, [r4, #TCR]
    ldr r1, =(1 << 8)
    str r1, [r4, #CCR]
    ldr r2, =(1 << 31) | (1 << 15)
    str r2, [r4, #FCR]
1:
    ldr r1, [r4, #FCR]
    tst r1, r2
    bne 1b
    ldr r1, =4
    str r1, [r4, #MBC]
    ldr r1, =1
    str r1, [r4, #MTC]
    ldr r1, =1
    str r1, [r4, #BCC]
    ldr r1, =0x9f
    strb r1, [r4, #TXD]
    ldr r1, [r4, #TCR]
    orr r1, #(1 << 31)
    str r1, [r4, #TCR]
1:
    ldr r1, [r4, #TCR]
    tst r1, #(1 << 31)
    bne 1b
 
    ldr r0, [r4, #RXD] 
main:
    b main
    .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
$ dd if=/dev/zero of=spi.bin bs=1M count=8
$ ./build/qemu-system-arm -M f1c100s -bios main.bin -drive if=mtd,file=spi.bin,format=raw
    [TRACE] call f1c100s_uart_register_types()
    [TRACE] call f1c100s_gpio_register()
    [TRACE] call f1c100s_intc_register()
    [TRACE] call f1c100s_ccu_register()
    [TRACE] call f1c100s_spi_register()
    [TRACE] call f1c100s_timer_register()
    [TRACE] call f1c100s_soc_register_types()
    [TRACE] call f1c100s_soc_init()
    [TRACE] call f1c100s_ccu_class_init()
    [TRACE] call f1c100s_uart_class_init()
    [TRACE] call f1c100s_soc_class_init()
    [TRACE] call f1c100s_intc_class_init()
    [TRACE] call f1c100s_spi_class_init()
    [TRACE] call f1c100s_gpio_class_init()
    [TRACE] call f1c100s_timer_class_init()
    [TRACE] call f1c100s_soc_board_init()
    [TRACE] call f1c100s_soc_instance_init()
    [TRACE] call f1c100s_ccu_init()
    [TRACE] call f1c100s_intc_init()
    [TRACE] call f1c100s_gpio_init()
    [TRACE] call f1c100s_timer_init()
    [TRACE] call f1c100s_uart_init()
    [TRACE] call f1c100s_uart_init()
    [TRACE] call f1c100s_uart_init()
    [TRACE] call f1c100s_spi_init()
    [TRACE] call f1c100s_spi_init()
    [TRACE] call f1c100s_soc_realize()
    [TRACE] call f1c100s_ccu_realize()
    [TRACE] call f1c100s_intc_realize()
    [TRACE] call f1c100s_gpio_realize()
    [TRACE] call f1c100s_timer_realize()
    [TRACE] call f1c100s_uart_realize()
    [TRACE] call f1c100s_uart_realize()
    [TRACE] call f1c100s_uart_realize()
    [TRACE] call f1c100s_spi_realize()
    [TRACE] call f1c100s_spi_realize()
    [TRACE] loading... "main.bin"
    [TRACE] call f1c100s_uart_reset()
    [TRACE] call f1c100s_uart_reset()
    [TRACE] call f1c100s_uart_reset()
    [TRACE] call f1c100s_intc_reset()
    [TRACE] call f1c100s_ccu_write(offset=0x28, val=0x80041800, size=4)
    [TRACE] call f1c100s_ccu_write(offset=0x54, val=0x3180, size=4)
    [TRACE] call f1c100s_ccu_write(offset=0x60, val=0x100000, size=4)
    [TRACE] call f1c100s_ccu_write(offset=0x68, val=0x100000, size=4)
    [TRACE] call f1c100s_ccu_write(offset=0x2c0, val=0x100000, size=4)
    [TRACE] call f1c100s_ccu_write(offset=0x2d0, val=0x100000, size=4)
    [TRACE] call f1c100s_gpio_write(offset=0x90, val=0x55, size=4)
    [TRACE] call f1c100s_gpio_write(offset=0x48, val=0x2222, size=4)
    [TRACE] call f1c100s_uart_write(addr=0x10, val=0x0, size=4)
    [TRACE] call f1c100s_uart_write(addr=0x8, val=0xf7, size=4)
    [TRACE] call f1c100s_uart_write(addr=0x10, val=0x0, size=4)
    [TRACE] call f1c100s_uart_read()
    [TRACE] call f1c100s_uart_write(addr=0xc, val=0x80, size=4)
    [TRACE] call f1c100s_uart_write(addr=0x0, val=0x36, size=4)
    [TRACE] call f1c100s_uart_write(addr=0x4, val=0x0, size=4)
    [TRACE] call f1c100s_uart_read()
    [TRACE] call f1c100s_uart_write(addr=0xc, val=0x0, size=4)
    [TRACE] call f1c100s_uart_read()
    [TRACE] call f1c100s_uart_write(addr=0xc, val=0x3, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x4, val=0x3, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x8, val=0x104, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x24, val=0x100, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x18, val=0x80008000, size=4)
    [TRACE] call f1c100s_spi_read(offset=0x18, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x30, val=0x4, size=4)
    [TRACE] spi brust size = 4
    [TRACE] call f1c100s_spi_write(offset=0x34, val=0x1, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x38, val=0x1, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x200, val=0x9f, size=1)
    [TRACE] call f1c100s_spi_read(offset=0x8, size=4)
    [TRACE] call f1c100s_spi_write(offset=0x8, val=0x80000000, size=4)
    [TRACE] spi tx data = 0x9f
    [TRACE] spi tx data = 0xff
    [TRACE] spi tx data = 0xff
    [TRACE] spi tx data = 0xff
    [TRACE] call f1c100s_spi_read(offset=0x8, size=4)
    [TRACE] call f1c100s_spi_read(offset=0x300, size=4)
    [TRACE] spi rx data = 0x0
    [TRACE] spi rx data = 0xef
    [TRACE] spi rx data = 0x40
    [TRACE] spi rx data = 0x17