參考資訊:
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