target-arm queue:

* Fix validation of 32-bit address spaces for aa32 (fixes an assert introduced in ba97be9f4a)
  * v8m: Ensure IDAU is respected if SAU is disabled
  * gdbstub: fix gdb_get_cpu(s, pid, tid) when pid and/or tid are 0
  * exec.c: Use correct attrs in cpu_memory_rw_debug()
  * accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write
  * target/arm: Don't clear supported PMU events when initializing PMCEID1
  * memory: add memory_region_flush_rom_device()
  * microbit: Add stub NRF51 TWI magnetometer/accelerometer detection
  * tests/microbit-test: extend testing of microbit devices
  * checkpatch: Don't emit spurious warnings about block comments
  * aspeed/smc: misc bug fixes
  * xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs
  * xlnx-zynqmp: Realize cluster after putting RPUs in it
  * accel/tcg: Add cluster number to TCG TB hash so differently configured
    CPUs don't pick up cached TBs for the wrong kind of CPU
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAlxQQA4ZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3rFzD/4mOedFLCEubFGFgpwEQf3P
 g/oEIOPm6xNFfk/cZPUwBqoANBIxb5sTZCJuNyEWEpziNLtxc+BRzq21UwazsaGV
 fxBJr3XHZxeX/XeQD8SioPeMeW9SGoOmj4Wigu3dMcQ8wqeOUb0/lGVdqCOJoPmV
 h7CCW0OufmX9PpLYR/IgFFfCeIGBxCqE+Kld0ZUlXs70ax8JxJK9JsbamJhEpDtC
 A888r2xbVlj9Pa9eu8MBKqaLF3KS8qWrgrBssPsX3I28p3MykjQAHgnVlNP1X0uo
 h03+9DiCbCPq/+hr763VaIrbugYeFL1zTo7B7C2cCgwPso2zsiExYR96/0hkZ/6b
 Yov759LeidcmxoelrJU5AUBgLonWAV4S0lbTN54pxPJcs4l9cvF65U8mgEs7ONAs
 mbCrLT5plzJrEK/Qj2pVJLY8OggBBg2/jNTW/CRda8Xdh9fNgKeTK2vLDsFygexk
 ARSOZ4yEzCP6HlSJ7z3s3IEEg1VUv41SY5mWhkhJU5s0WOe/UD5b3KLj7PMkD9bu
 +ITHKQ+/2OO46klhNWQ3Ipcjz/jHruAFCQQFtHeiK9pFQGgcAbEHzZrmVVoXmk0N
 cyNk9+e/hiRm/x6IWzJidV8x3iEEtRoNiukwd7jEgkUg99SiGevPrLYD6GVYnZsd
 R9PYmo6/X2Cjr/SFcOY9Fg==
 =SzmU
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190129' into staging

target-arm queue:
 * Fix validation of 32-bit address spaces for aa32 (fixes an assert introduced in ba97be9f4a)
 * v8m: Ensure IDAU is respected if SAU is disabled
 * gdbstub: fix gdb_get_cpu(s, pid, tid) when pid and/or tid are 0
 * exec.c: Use correct attrs in cpu_memory_rw_debug()
 * accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write
 * target/arm: Don't clear supported PMU events when initializing PMCEID1
 * memory: add memory_region_flush_rom_device()
 * microbit: Add stub NRF51 TWI magnetometer/accelerometer detection
 * tests/microbit-test: extend testing of microbit devices
 * checkpatch: Don't emit spurious warnings about block comments
 * aspeed/smc: misc bug fixes
 * xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs
 * xlnx-zynqmp: Realize cluster after putting RPUs in it
 * accel/tcg: Add cluster number to TCG TB hash so differently configured
   CPUs don't pick up cached TBs for the wrong kind of CPU

# gpg: Signature made Tue 29 Jan 2019 11:59:10 GMT
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20190129: (23 commits)
  gdbstub: Simplify gdb_get_cpu_pid() to use cpu->cluster_index
  accel/tcg: Add cluster number to TCG TB hash
  qom/cpu: Add cluster_index to CPUState
  hw/arm/xlnx-zynqmp: Realize cluster after putting RPUs in it
  aspeed/smc: snoop SPI transfers to fake dummy cycles
  aspeed/smc: Add dummy data register
  aspeed/smc: define registers for all possible CS
  aspeed/smc: fix default read value
  xlnx-zynqmp: Don't create rpu-cluster if there are no RPUs
  checkpatch: Don't emit spurious warnings about block comments
  tests/microbit-test: Check nRF51 UART functionality
  tests/microbit-test: Make test independent of global_qtest
  tests/libqtest: Introduce qtest_init_with_serial()
  memory: add memory_region_flush_rom_device()
  target/arm: Don't clear supported PMU events when initializing PMCEID1
  MAINTAINERS: update microbit ARM board files
  accel/tcg/user-exec: Don't parse aarch64 insns to test for read vs write
  exec.c: Use correct attrs in cpu_memory_rw_debug()
  tests/microbit-test: add TWI stub device test
  arm: Stub out NRF51 TWI magnetometer/accelerometer detection
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-01-29 12:00:19 +00:00
commit b4fbe1f65a
28 changed files with 874 additions and 243 deletions

View File

@ -829,9 +829,11 @@ M: Joel Stanley <joel@jms.id.au>
M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/arm/nrf51_soc.c
F: hw/arm/microbit.c
F: include/hw/arm/nrf51_soc.h
F: hw/*/nrf51*.c
F: hw/*/microbit*.c
F: include/hw/*/nrf51*.h
F: include/hw/*/microbit*.h
F: tests/microbit-test.c
CRIS Machines
-------------

View File

@ -325,6 +325,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
struct tb_desc desc;
uint32_t h;
cf_mask &= ~CF_CLUSTER_MASK;
cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT;
desc.env = (CPUArchState *)cpu->env_ptr;
desc.cs_base = cs_base;
desc.flags = flags;

View File

@ -1688,6 +1688,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
cflags |= CF_NOCACHE | 1;
}
cflags &= ~CF_CLUSTER_MASK;
cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT;
buffer_overflow:
tb = tb_alloc(pc);
if (unlikely(!tb)) {

View File

@ -479,28 +479,66 @@ int cpu_signal_handler(int host_signum, void *pinfo,
#elif defined(__aarch64__)
#ifndef ESR_MAGIC
/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
#define ESR_MAGIC 0x45535201
struct esr_context {
struct _aarch64_ctx head;
uint64_t esr;
};
#endif
static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
{
return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
}
static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
{
return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
}
int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
{
siginfo_t *info = pinfo;
ucontext_t *uc = puc;
uintptr_t pc = uc->uc_mcontext.pc;
uint32_t insn = *(uint32_t *)pc;
bool is_write;
struct _aarch64_ctx *hdr;
struct esr_context const *esrctx = NULL;
/* XXX: need kernel patch to get write flag faster. */
is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
/* Ingore bits 10, 11 & 21, controlling indexing. */
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
/* Ignore bits 23 & 24, controlling indexing. */
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
/* Find the esr_context, which has the WnR bit in it */
for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
if (hdr->magic == ESR_MAGIC) {
esrctx = (struct esr_context const *)hdr;
break;
}
}
if (esrctx) {
/* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
uint64_t esr = esrctx->esr;
is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
} else {
/*
* Fall back to parsing instructions; will only be needed
* for really ancient (pre-3.16) kernels.
*/
uint32_t insn = *(uint32_t *)pc;
is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
|| (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
|| (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
|| (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
|| (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
|| (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
|| (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
/* Ignore bits 10, 11 & 21, controlling indexing. */
|| (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
|| (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
/* Ignore bits 23 & 24, controlling indexing. */
|| (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */
}
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
}

19
exec.c
View File

@ -3162,6 +3162,19 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr,
cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask);
}
void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size)
{
/*
* In principle this function would work on other memory region types too,
* but the ROM device use case is the only one where this operation is
* necessary. Other memory regions should use the
* address_space_read/write() APIs.
*/
assert(memory_region_is_romd(mr));
invalidate_and_set_dirty(mr, addr, size);
}
static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr)
{
unsigned access_size_max = mr->ops->valid.max_access_size;
@ -3882,12 +3895,10 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr,
phys_addr += (addr & ~TARGET_PAGE_MASK);
if (is_write) {
address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr,
MEMTXATTRS_UNSPECIFIED,
buf, l);
attrs, buf, l);
} else {
address_space_rw(cpu->cpu_ases[asidx].as, phys_addr,
MEMTXATTRS_UNSPECIFIED,
buf, l, 0);
attrs, buf, l, 0);
}
len -= l;
buf += l;

120
gdbstub.c
View File

@ -644,50 +644,12 @@ static int memtox(char *buf, const char *mem, int len)
static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu)
{
#ifndef CONFIG_USER_ONLY
gchar *path, *name = NULL;
Object *obj;
CPUClusterState *cluster;
uint32_t ret;
path = object_get_canonical_path(OBJECT(cpu));
if (path == NULL) {
/* Return the default process' PID */
ret = s->processes[s->process_num - 1].pid;
goto out;
}
name = object_get_canonical_path_component(OBJECT(cpu));
assert(name != NULL);
/*
* Retrieve the CPU parent path by removing the last '/' and the CPU name
* from the CPU canonical path.
*/
path[strlen(path) - strlen(name) - 1] = '\0';
obj = object_resolve_path_type(path, TYPE_CPU_CLUSTER, NULL);
if (obj == NULL) {
/* Return the default process' PID */
ret = s->processes[s->process_num - 1].pid;
goto out;
}
cluster = CPU_CLUSTER(obj);
ret = cluster->cluster_id + 1;
out:
g_free(name);
g_free(path);
return ret;
#else
/* TODO: In user mode, we should use the task state PID */
return s->processes[s->process_num - 1].pid;
#endif
if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
/* Return the default process' PID */
return s->processes[s->process_num - 1].pid;
}
return cpu->cluster_index + 1;
}
static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid)
@ -756,35 +718,6 @@ static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu)
return cpu;
}
static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid)
{
GDBProcess *process;
CPUState *cpu;
if (!tid) {
/* 0 means any thread, we take the first one */
tid = 1;
}
cpu = find_cpu(tid);
if (cpu == NULL) {
return NULL;
}
process = gdb_get_cpu_process(s, cpu);
if (process->pid != pid) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return cpu;
}
/* Return the cpu following @cpu, while ignoring unattached processes. */
static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu)
{
@ -814,6 +747,49 @@ static CPUState *gdb_first_attached_cpu(const GDBState *s)
return cpu;
}
static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid)
{
GDBProcess *process;
CPUState *cpu;
if (!pid && !tid) {
/* 0 means any process/thread, we take the first attached one */
return gdb_first_attached_cpu(s);
} else if (pid && !tid) {
/* any thread in a specific process */
process = gdb_get_process(s, pid);
if (process == NULL) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return get_first_cpu_in_process(s, process);
} else {
/* a specific thread */
cpu = find_cpu(tid);
if (cpu == NULL) {
return NULL;
}
process = gdb_get_cpu_process(s, cpu);
if (pid && process->pid != pid) {
return NULL;
}
if (!process->attached) {
return NULL;
}
return cpu;
}
}
static const char *get_feature_xml(const GDBState *s, const char *p,
const char **newp, GDBProcess *process)
{

View File

@ -16,11 +16,13 @@
#include "exec/address-spaces.h"
#include "hw/arm/nrf51_soc.h"
#include "hw/i2c/microbit_i2c.h"
typedef struct {
MachineState parent;
NRF51State nrf51;
MicrobitI2CState i2c;
} MicrobitMachineState;
#define TYPE_MICROBIT_MACHINE MACHINE_TYPE_NAME("microbit")
@ -32,7 +34,9 @@ static void microbit_init(MachineState *machine)
{
MicrobitMachineState *s = MICROBIT_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *mr;
Object *soc = OBJECT(&s->nrf51);
Object *i2c = OBJECT(&s->i2c);
sysbus_init_child_obj(OBJECT(machine), "nrf51", soc, sizeof(s->nrf51),
TYPE_NRF51_SOC);
@ -41,6 +45,18 @@ static void microbit_init(MachineState *machine)
&error_fatal);
object_property_set_bool(soc, true, "realized", &error_fatal);
/*
* Overlap the TWI stub device into the SoC. This is a microbit-specific
* hack until we implement the nRF51 TWI controller properly and the
* magnetometer/accelerometer devices.
*/
sysbus_init_child_obj(OBJECT(machine), "microbit.twi", i2c,
sizeof(s->i2c), TYPE_MICROBIT_I2C);
object_property_set_bool(i2c, true, "realized", &error_fatal);
mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(i2c), 0);
memory_region_add_subregion_overlap(&s->nrf51.container, NRF51_TWI_BASE,
mr, -1);
armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
NRF51_SOC(soc)->flash_size);
}

View File

@ -178,13 +178,16 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
int i;
int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS);
if (num_rpus <= 0) {
/* Don't create rpu-cluster object if there's nothing to put in it */
return;
}
object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster,
sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER,
&error_abort, NULL);
qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1);
qdev_init_nofail(DEVICE(&s->rpu_cluster));
for (i = 0; i < num_rpus; i++) {
char *name;
@ -212,6 +215,8 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu,
return;
}
}
qdev_init_nofail(DEVICE(&s->rpu_cluster));
}
static void xlnx_zynqmp_init(Object *obj)

View File

@ -20,19 +20,65 @@
#include "qemu/osdep.h"
#include "hw/cpu/cluster.h"
#include "qom/cpu.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/cutils.h"
static Property cpu_cluster_properties[] = {
DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0),
DEFINE_PROP_END_OF_LIST()
};
typedef struct CallbackData {
CPUClusterState *cluster;
int cpu_count;
} CallbackData;
static int add_cpu_to_cluster(Object *obj, void *opaque)
{
CallbackData *cbdata = opaque;
CPUState *cpu = (CPUState *)object_dynamic_cast(obj, TYPE_CPU);
if (cpu) {
cpu->cluster_index = cbdata->cluster->cluster_id;
cbdata->cpu_count++;
}
return 0;
}
static void cpu_cluster_realize(DeviceState *dev, Error **errp)
{
/* Iterate through all our CPU children and set their cluster_index */
CPUClusterState *cluster = CPU_CLUSTER(dev);
Object *cluster_obj = OBJECT(dev);
CallbackData cbdata = {
.cluster = cluster,
.cpu_count = 0,
};
if (cluster->cluster_id >= MAX_CLUSTERS) {
error_setg(errp, "cluster-id must be less than %d", MAX_CLUSTERS);
return;
}
object_child_foreach_recursive(cluster_obj, add_cpu_to_cluster, &cbdata);
/*
* A cluster with no CPUs is a bug in the board/SoC code that created it;
* if you hit this during development of new code, check that you have
* created the CPUs and parented them into the cluster object before
* realizing the cluster object.
*/
assert(cbdata.cpu_count > 0);
}
static void cpu_cluster_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->props = cpu_cluster_properties;
dc->realize = cpu_cluster_realize;
}
static const TypeInfo cpu_cluster_type_info = {

View File

@ -7,5 +7,6 @@ common-obj-$(CONFIG_BITBANG_I2C) += bitbang_i2c.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_i2c.o
common-obj-$(CONFIG_IMX_I2C) += imx_i2c.o
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o
common-obj-$(CONFIG_NRF51_SOC) += microbit_i2c.o
obj-$(CONFIG_OMAP) += omap_i2c.o
obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o

127
hw/i2c/microbit_i2c.c Normal file
View File

@ -0,0 +1,127 @@
/*
* Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface
* http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
*
* This is a microbit-specific stub for the TWI controller on the nRF51 SoC.
* We don't emulate I2C devices but the firmware probes the
* accelerometer/magnetometer on startup and panics if they are not found.
* Therefore we stub out the probing.
*
* In the future this file could evolve into a full nRF51 TWI controller
* device.
*
* Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de>
* Copyright 2019 Red Hat, Inc.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/i2c/microbit_i2c.h"
static const uint32_t twi_read_sequence[] = {0x5A, 0x5A, 0x40};
static uint64_t microbit_i2c_read(void *opaque, hwaddr addr, unsigned int size)
{
MicrobitI2CState *s = opaque;
uint64_t data = 0x00;
switch (addr) {
case NRF51_TWI_EVENT_STOPPED:
data = 0x01;
break;
case NRF51_TWI_EVENT_RXDREADY:
data = 0x01;
break;
case NRF51_TWI_EVENT_TXDSENT:
data = 0x01;
break;
case NRF51_TWI_REG_RXD:
data = twi_read_sequence[s->read_idx];
if (s->read_idx < G_N_ELEMENTS(twi_read_sequence)) {
s->read_idx++;
}
break;
default:
data = s->regs[addr / sizeof(s->regs[0])];
break;
}
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u] = %" PRIx32 "\n",
__func__, addr, size, (uint32_t)data);
return data;
}
static void microbit_i2c_write(void *opaque, hwaddr addr, uint64_t data,
unsigned int size)
{
MicrobitI2CState *s = opaque;
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
__func__, addr, data, size);
s->regs[addr / sizeof(s->regs[0])] = data;
}
static const MemoryRegionOps microbit_i2c_ops = {
.read = microbit_i2c_read,
.write = microbit_i2c_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4,
};
static const VMStateDescription microbit_i2c_vmstate = {
.name = TYPE_MICROBIT_I2C,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, MicrobitI2CState, MICROBIT_I2C_NREGS),
VMSTATE_UINT32(read_idx, MicrobitI2CState),
},
};
static void microbit_i2c_reset(DeviceState *dev)
{
MicrobitI2CState *s = MICROBIT_I2C(dev);
memset(s->regs, 0, sizeof(s->regs));
s->read_idx = 0;
}
static void microbit_i2c_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
MicrobitI2CState *s = MICROBIT_I2C(dev);
memory_region_init_io(&s->iomem, OBJECT(s), &microbit_i2c_ops, s,
"microbit.twi", NRF51_TWI_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
}
static void microbit_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &microbit_i2c_vmstate;
dc->reset = microbit_i2c_reset;
dc->realize = microbit_i2c_realize;
dc->desc = "Microbit I2C controller";
}
static const TypeInfo microbit_i2c_info = {
.name = TYPE_MICROBIT_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MicrobitI2CState),
.class_init = microbit_i2c_class_init,
};
static void microbit_i2c_register_types(void)
{
type_register_static(&microbit_i2c_info);
}
type_init(microbit_i2c_register_types)

View File

@ -98,8 +98,8 @@
/* Misc Control Register #1 */
#define R_MISC_CTRL1 (0x50 / 4)
/* Misc Control Register #2 */
#define R_MISC_CTRL2 (0x54 / 4)
/* SPI dummy cycle data */
#define R_DUMMY_DATA (0x54 / 4)
/* DMA Control/Status Register */
#define R_DMA_CTRL (0x80 / 4)
@ -145,6 +145,9 @@
/* Flash opcodes. */
#define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SNOOP_OFF 0xFF
#define SNOOP_START 0x0
/*
* Default segments mapping addresses and size for each slave per
* controller. These can be changed when board is initialized with the
@ -529,7 +532,7 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr)
*/
if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) {
for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) {
ssi_transfer(fl->controller->spi, 0xFF);
ssi_transfer(fl->controller->spi, s->regs[R_DUMMY_DATA] & 0xff);
}
}
}
@ -566,6 +569,101 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
return ret;
}
/*
* TODO (clg@kaod.org): stolen from xilinx_spips.c. Should move to a
* common include header.
*/
typedef enum {
READ = 0x3, READ_4 = 0x13,
FAST_READ = 0xb, FAST_READ_4 = 0x0c,
DOR = 0x3b, DOR_4 = 0x3c,
QOR = 0x6b, QOR_4 = 0x6c,
DIOR = 0xbb, DIOR_4 = 0xbc,
QIOR = 0xeb, QIOR_4 = 0xec,
PP = 0x2, PP_4 = 0x12,
DPP = 0xa2,
QPP = 0x32, QPP_4 = 0x34,
} FlashCMD;
static int aspeed_smc_num_dummies(uint8_t command)
{
switch (command) { /* check for dummies */
case READ: /* no dummy bytes/cycles */
case PP:
case DPP:
case QPP:
case READ_4:
case PP_4:
case QPP_4:
return 0;
case FAST_READ:
case DOR:
case QOR:
case DOR_4:
case QOR_4:
return 1;
case DIOR:
case FAST_READ_4:
case DIOR_4:
return 2;
case QIOR:
case QIOR_4:
return 4;
default:
return -1;
}
}
static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data,
unsigned size)
{
AspeedSMCState *s = fl->controller;
uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3;
if (s->snoop_index == SNOOP_OFF) {
return false; /* Do nothing */
} else if (s->snoop_index == SNOOP_START) {
uint8_t cmd = data & 0xff;
int ndummies = aspeed_smc_num_dummies(cmd);
/*
* No dummy cycles are expected with the current command. Turn
* off snooping and let the transfer proceed normally.
*/
if (ndummies <= 0) {
s->snoop_index = SNOOP_OFF;
return false;
}
s->snoop_dummies = ndummies * 8;
} else if (s->snoop_index >= addr_width + 1) {
/* The SPI transfer has reached the dummy cycles sequence */
for (; s->snoop_dummies; s->snoop_dummies--) {
ssi_transfer(s->spi, s->regs[R_DUMMY_DATA] & 0xff);
}
/* If no more dummy cycles are expected, turn off snooping */
if (!s->snoop_dummies) {
s->snoop_index = SNOOP_OFF;
} else {
s->snoop_index += size;
}
/*
* Dummy cycles have been faked already. Ignore the current
* SPI transfer
*/
return true;
}
s->snoop_index += size;
return false;
}
static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
unsigned size)
{
@ -581,6 +679,10 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
switch (aspeed_smc_flash_mode(fl)) {
case CTRL_USERMODE:
if (aspeed_smc_do_snoop(fl, data, size)) {
break;
}
for (i = 0; i < size; i++) {
ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
}
@ -613,7 +715,9 @@ static const MemoryRegionOps aspeed_smc_flash_ops = {
static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl)
{
const AspeedSMCState *s = fl->controller;
AspeedSMCState *s = fl->controller;
s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START;
qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl));
}
@ -652,6 +756,9 @@ static void aspeed_smc_reset(DeviceState *d)
if (s->ctrl->segments == aspeed_segments_fmc) {
s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0);
}
s->snoop_index = SNOOP_OFF;
s->snoop_dummies = 0;
}
static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
@ -664,13 +771,14 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
addr == s->r_timings ||
addr == s->r_ce_ctrl ||
addr == R_INTR_CTRL ||
addr == R_DUMMY_DATA ||
(addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) ||
(addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)) {
(addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) {
return s->regs[addr];
} else {
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
__func__, addr);
return 0;
return -1;
}
}
@ -697,6 +805,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
if (value != s->regs[R_SEG_ADDR0 + cs]) {
aspeed_smc_flash_set_segment(s, cs, value);
}
} else if (addr == R_DUMMY_DATA) {
s->regs[addr] = value & 0xff;
} else {
qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
__func__, addr);
@ -790,10 +900,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
static const VMStateDescription vmstate_aspeed_smc = {
.name = "aspeed.smc",
.version_id = 1,
.minimum_version_id = 1,
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
VMSTATE_UINT8(snoop_index, AspeedSMCState),
VMSTATE_UINT8(snoop_dummies, AspeedSMCState),
VMSTATE_END_OF_LIST()
}
};

View File

@ -351,9 +351,11 @@ struct TranslationBlock {
#define CF_USE_ICOUNT 0x00020000
#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */
#define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */
#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */
#define CF_CLUSTER_SHIFT 24
/* cflags' mask for hashing/comparison */
#define CF_HASH_MASK \
(CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL)
(CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL | CF_CLUSTER_MASK)
/* Per-vCPU dynamic tracing state used to generate this TB */
uint32_t trace_vcpu_dstate;

View File

@ -1344,6 +1344,24 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr,
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client);
/**
* memory_region_flush_rom_device: Mark a range of pages dirty and invalidate
* TBs (for self-modifying code).
*
* The MemoryRegionOps->write() callback of a ROM device must use this function
* to mark byte ranges that have been modified internally, such as by directly
* accessing the memory returned by memory_region_get_ram_ptr().
*
* This function marks the range dirty and invalidates TBs so that TCG can
* detect self-modifying code.
*
* @mr: the region being flushed.
* @addr: the start, relative to the start of the region, of the range being
* flushed.
* @size: the size, in bytes, of the range being flushed.
*/
void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size);
/**
* memory_region_set_readonly: Turn a memory region read-only (or read-write)
*

View File

@ -25,6 +25,8 @@
#define NRF51_IOMEM_SIZE 0x20000000
#define NRF51_UART_BASE 0x40002000
#define NRF51_TWI_BASE 0x40003000
#define NRF51_TWI_SIZE 0x00001000
#define NRF51_TIMER_BASE 0x40008000
#define NRF51_TIMER_SIZE 0x00001000
#define NRF51_RNG_BASE 0x4000D000

View File

@ -39,6 +39,7 @@ typedef struct NRF51State {
MemoryRegion sram;
MemoryRegion flash;
MemoryRegion clock;
MemoryRegion twi;
uint32_t sram_size;
uint32_t flash_size;

View File

@ -34,12 +34,36 @@
* Arm big.LITTLE system) they should be in different clusters. If the CPUs do
* not have the same view of memory (for example the main CPU and a management
* controller processor) they should be in different clusters.
*
* A cluster is created by creating an object of TYPE_CPU_CLUSTER, and then
* adding the CPUs to it as QOM child objects (e.g. using the
* object_initialize_child() or object_property_add_child() functions).
* The CPUs may be either direct children of the cluster object, or indirect
* children (e.g. children of children of the cluster object).
*
* All CPUs must be added as children before the cluster is realized.
* (Regrettably QOM provides no way to prevent adding children to a realized
* object and no way for the parent to be notified when a new child is added
* to it, so this restriction is not checked for, but the system will not
* behave correctly if it is not adhered to. The cluster will assert that
* it contains at least one CPU, which should catch most inadvertent
* violations of this constraint.)
*
* A CPU which is not put into any cluster will be considered implicitly
* to be in a cluster with all the other "loose" CPUs, so all CPUs that are
* not assigned to clusters must be identical.
*/
#define TYPE_CPU_CLUSTER "cpu-cluster"
#define CPU_CLUSTER(obj) \
OBJECT_CHECK(CPUClusterState, (obj), TYPE_CPU_CLUSTER)
/*
* This limit is imposed by TCG, which puts the cluster ID into an
* 8 bit field (and uses all-1s for the default "not in any cluster").
*/
#define MAX_CLUSTERS 255
/**
* CPUClusterState:
* @cluster_id: The cluster ID. This value is for internal use only and should

View File

@ -0,0 +1,42 @@
/*
* Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface
* http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf
*
* Copyright 2019 Red Hat, Inc.
*
* This code is licensed under the GPL version 2 or later. See
* the COPYING file in the top-level directory.
*/
#ifndef MICROBIT_I2C_H
#define MICROBIT_I2C_H
#include "hw/sysbus.h"
#include "hw/arm/nrf51.h"
#define NRF51_TWI_TASK_STARTRX 0x000
#define NRF51_TWI_TASK_STARTTX 0x008
#define NRF51_TWI_TASK_STOP 0x014
#define NRF51_TWI_EVENT_STOPPED 0x104
#define NRF51_TWI_EVENT_RXDREADY 0x108
#define NRF51_TWI_EVENT_TXDSENT 0x11c
#define NRF51_TWI_REG_ENABLE 0x500
#define NRF51_TWI_REG_RXD 0x518
#define NRF51_TWI_REG_TXD 0x51c
#define NRF51_TWI_REG_ADDRESS 0x588
#define TYPE_MICROBIT_I2C "microbit.i2c"
#define MICROBIT_I2C(obj) \
OBJECT_CHECK(MicrobitI2CState, (obj), TYPE_MICROBIT_I2C)
#define MICROBIT_I2C_NREGS (NRF51_TWI_SIZE / sizeof(uint32_t))
typedef struct {
SysBusDevice parent_obj;
MemoryRegion iomem;
uint32_t regs[MICROBIT_I2C_NREGS];
uint32_t read_idx;
} MicrobitI2CState;
#endif /* MICROBIT_I2C_H */

View File

@ -98,6 +98,9 @@ typedef struct AspeedSMCState {
uint8_t conf_enable_w0;
AspeedSMCFlash *flashes;
uint8_t snoop_index;
uint8_t snoop_dummies;
} AspeedSMCState;
#endif /* ASPEED_SMC_H */

View File

@ -280,6 +280,11 @@ struct qemu_work_item;
/**
* CPUState:
* @cpu_index: CPU index (informative).
* @cluster_index: Identifies which cluster this CPU is in.
* For boards which don't define clusters or for "loose" CPUs not assigned
* to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will
* be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER
* QOM parent.
* @nr_cores: Number of cores within this CPU package.
* @nr_threads: Number of threads within this CPU.
* @running: #true if CPU is currently running (lockless).
@ -405,6 +410,7 @@ struct CPUState {
/* TODO Move common fields from CPUArchState here. */
int cpu_index;
int cluster_index;
uint32_t halted;
uint32_t can_do_io;
int32_t exception_index;
@ -1111,5 +1117,6 @@ extern const struct VMStateDescription vmstate_cpu_common;
#endif /* NEED_CPU_H */
#define UNASSIGNED_CPU_INDEX -1
#define UNASSIGNED_CLUSTER_INDEX -1
#endif

View File

@ -364,6 +364,7 @@ static void cpu_common_initfn(Object *obj)
CPUClass *cc = CPU_GET_CLASS(obj);
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
/* *-user doesn't have configurable SMP topology */
/* the default value is changed by qemu_init_vcpu() for softmmu */

View File

@ -1624,7 +1624,7 @@ sub process {
# Block comments use /* on a line of its own
if ($rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/
$rawline =~ m@^\+.*/\*\*?[ \t]*.+[ \t]*$@) { # /* or /** non-blank
$rawline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank
WARN("Block comments use a leading /* on a separate line\n" . $herecurr);
}

View File

@ -1039,8 +1039,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
unset_feature(env, ARM_FEATURE_PMU);
}
if (arm_feature(env, ARM_FEATURE_PMU)) {
cpu->pmceid0 = get_pmceid(&cpu->env, 0);
cpu->pmceid1 = get_pmceid(&cpu->env, 1);
pmu_init(cpu);
if (!kvm_enabled()) {
arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0);

View File

@ -1012,14 +1012,13 @@ void pmu_pre_el_change(ARMCPU *cpu, void *ignored);
void pmu_post_el_change(ARMCPU *cpu, void *ignored);
/*
* get_pmceid
* @env: CPUARMState
* @which: which PMCEID register to return (0 or 1)
* pmu_init
* @cpu: ARMCPU
*
* Return the PMCEID[01]_EL0 register values corresponding to the counters
* which are supported given the current configuration
* Initialize the CPU's PMCEID[01]_EL0 registers and associated internal state
* for the current configuration
*/
uint64_t get_pmceid(CPUARMState *env, unsigned which);
void pmu_init(ARMCPU *cpu);
/* SCTLR bit meanings. Several bits have been reused in newer
* versions of the architecture; in that case we define constants

View File

@ -1090,22 +1090,24 @@ static const pm_event pm_events[] = {
static uint16_t supported_event_map[MAX_EVENT_ID + 1];
/*
* Called upon initialization to build PMCEID0_EL0 or PMCEID1_EL0 (indicated by
* 'which'). We also use it to build a map of ARM event numbers to indices in
* our pm_events array.
* Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map
* of ARM event numbers to indices in our pm_events array.
*
* Note: Events in the 0x40XX range are not currently supported.
*/
uint64_t get_pmceid(CPUARMState *env, unsigned which)
void pmu_init(ARMCPU *cpu)
{
uint64_t pmceid = 0;
unsigned int i;
assert(which <= 1);
/*
* Empty supported_event_map and cpu->pmceid[01] before adding supported
* events to them
*/
for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) {
supported_event_map[i] = UNSUPPORTED_EVENT;
}
cpu->pmceid0 = 0;
cpu->pmceid1 = 0;
for (i = 0; i < ARRAY_SIZE(pm_events); i++) {
const pm_event *cnt = &pm_events[i];
@ -1113,13 +1115,16 @@ uint64_t get_pmceid(CPUARMState *env, unsigned which)
/* We do not currently support events in the 0x40xx range */
assert(cnt->number <= 0x3f);
if ((cnt->number & 0x20) == (which << 6) &&
cnt->supported(env)) {
pmceid |= (1 << (cnt->number & 0x1f));
if (cnt->supported(&cpu->env)) {
supported_event_map[cnt->number] = i;
uint64_t event_mask = 1 << (cnt->number & 0x1f);
if (cnt->number & 0x20) {
cpu->pmceid1 |= event_mask;
} else {
cpu->pmceid0 |= event_mask;
}
}
}
return pmceid;
}
/*
@ -10447,7 +10452,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
uint64_t ttbr;
hwaddr descaddr, indexmask, indexmask_grainsize;
uint32_t tableattrs;
target_ulong page_size, top_bits;
target_ulong page_size;
uint32_t attrs;
int32_t stride;
int addrsize, inputsize;
@ -10487,12 +10492,19 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address,
* We determined the region when collecting the parameters, but we
* have not yet validated that the address is valid for the region.
* Extract the top bits and verify that they all match select.
*
* For aa32, if inputsize == addrsize, then we have selected the
* region by exclusion in aa32_va_parameters and there is no more
* validation to do here.
*/
top_bits = sextract64(address, inputsize, addrsize - inputsize);
if (-top_bits != param.select || (param.select && !ttbr1_valid)) {
/* In the gap between the two regions, this is a Translation fault */
fault_type = ARMFault_Translation;
goto do_fault;
if (inputsize < addrsize) {
target_ulong top_bits = sextract64(address, inputsize,
addrsize - inputsize);
if (-top_bits != param.select || (param.select && !ttbr1_valid)) {
/* The gap between the two regions is a Translation fault */
fault_type = ARMFault_Translation;
goto do_fault;
}
}
if (param.using64k) {
@ -11071,18 +11083,19 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address,
}
}
}
/* The IDAU will override the SAU lookup results if it specifies
* higher security than the SAU does.
*/
if (!idau_ns) {
if (sattrs->ns || (!idau_nsc && sattrs->nsc)) {
sattrs->ns = false;
sattrs->nsc = idau_nsc;
}
}
break;
}
/*
* The IDAU will override the SAU lookup results if it specifies
* higher security than the SAU does.
*/
if (!idau_ns) {
if (sattrs->ns || (!idau_nsc && sattrs->nsc)) {
sattrs->ns = false;
sattrs->nsc = idau_nsc;
}
}
}
static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address,

View File

@ -315,6 +315,31 @@ QTestState *qtest_initf(const char *fmt, ...)
return s;
}
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd)
{
int sock_fd_init;
char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX";
QTestState *qts;
g_assert_true(mkdtemp(sock_dir) != NULL);
sock_path = g_strdup_printf("%s/sock", sock_dir);
sock_fd_init = init_socket(sock_path);
qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s",
sock_path, extra_args);
*sock_fd = socket_accept(sock_fd_init);
unlink(sock_path);
g_free(sock_path);
rmdir(sock_dir);
g_assert_true(*sock_fd >= 0);
return qts;
}
void qtest_quit(QTestState *s)
{
g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s));

View File

@ -62,6 +62,17 @@ QTestState *qtest_init(const char *extra_args);
*/
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
/**
* qtest_init_with_serial:
* @extra_args: other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation.
* @sock_fd: pointer to store the socket file descriptor for
* connection with serial.
*
* Returns: #QTestState instance.
*/
QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd);
/**
* qtest_quit:
* @s: #QTestState instance to operate on.

View File

@ -19,8 +19,142 @@
#include "libqtest.h"
#include "hw/arm/nrf51.h"
#include "hw/char/nrf51_uart.h"
#include "hw/gpio/nrf51_gpio.h"
#include "hw/timer/nrf51_timer.h"
#include "hw/i2c/microbit_i2c.h"
static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr)
{
time_t now, start = time(NULL);
while (true) {
if (qtest_readl(qts, event_addr) == 1) {
qtest_writel(qts, event_addr, 0x00);
return true;
}
/* Wait at most 10 minutes */
now = time(NULL);
if (now - start > 600) {
break;
}
g_usleep(10000);
}
return false;
}
static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in,
char *out)
{
int i, in_len = strlen(in);
g_assert_true(write(sock_fd, in, in_len) == in_len);
for (i = 0; i < in_len; i++) {
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE +
A_UART_RXDRDY));
out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD);
}
out[i] = '\0';
}
static void uart_w_to_txd(QTestState *qts, const char *in)
{
int i, in_len = strlen(in);
for (i = 0; i < in_len; i++) {
qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]);
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE +
A_UART_TXDRDY));
}
}
static void test_nrf51_uart(void)
{
int sock_fd;
char s[10];
QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd);
g_assert_true(write(sock_fd, "c", 1) == 1);
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00);
qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04);
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01);
g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY));
qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00);
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c');
qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04);
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04);
qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04);
g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00);
uart_rw_to_rxd(qts, sock_fd, "hello", s);
g_assert_true(memcmp(s, "hello", 5) == 0);
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01);
uart_w_to_txd(qts, "d");
g_assert_true(read(sock_fd, s, 10) == 1);
g_assert_cmphex(s[0], ==, 'd');
qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01);
qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h');
qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01);
uart_w_to_txd(qts, "world");
g_assert_true(read(sock_fd, s, 10) == 5);
g_assert_true(memcmp(s, "world", 5) == 0);
close(sock_fd);
qtest_quit(qts);
}
/* Read a byte from I2C device at @addr from register @reg */
static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg)
{
uint32_t val;
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg);
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT);
g_assert_cmpuint(val, ==, 1);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1);
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY);
g_assert_cmpuint(val, ==, 1);
val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1);
return val;
}
static void test_microbit_i2c(void)
{
uint32_t val;
QTestState *qts = qtest_init("-M microbit");
/* We don't program pins/irqs but at least enable the device */
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5);
/* MMA8653 magnetometer detection */
val = i2c_read_byte(qts, 0x3A, 0x0D);
g_assert_cmpuint(val, ==, 0x5A);
val = i2c_read_byte(qts, 0x3A, 0x0D);
g_assert_cmpuint(val, ==, 0x5A);
/* LSM303 accelerometer detection */
val = i2c_read_byte(qts, 0x3C, 0x4F);
g_assert_cmpuint(val, ==, 0x40);
qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0);
qtest_quit(qts);
}
static void test_nrf51_gpio(void)
{
@ -37,219 +171,229 @@ static void test_nrf51_gpio(void)
{NRF51_GPIO_REG_DIRCLR, 0x00000000}
};
QTestState *qts = qtest_init("-M microbit");
/* Check reset state */
for (i = 0; i < ARRAY_SIZE(reset_state); i++) {
expected = reset_state[i].expected;
actual = readl(NRF51_GPIO_BASE + reset_state[i].addr);
actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr);
g_assert_cmpuint(actual, ==, expected);
}
for (i = 0; i < NRF51_GPIO_PINS; i++) {
expected = 0x00000002;
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START + i * 4);
actual = qtest_readl(qts, NRF51_GPIO_BASE +
NRF51_GPIO_REG_CNF_START + i * 4);
g_assert_cmpuint(actual, ==, expected);
}
/* Check dir bit consistency between dir and cnf */
/* Check set via DIRSET */
expected = 0x80000001;
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
g_assert_cmpuint(actual, ==, expected);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
& 0x01;
g_assert_cmpuint(actual, ==, 0x01);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
/* Check clear via DIRCLR */
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
g_assert_cmpuint(actual, ==, 0x00000000);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
& 0x01;
g_assert_cmpuint(actual, ==, 0x00);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
g_assert_cmpuint(actual, ==, 0x00);
/* Check set via DIR */
expected = 0x80000001;
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR);
g_assert_cmpuint(actual, ==, expected);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START)
& 0x01;
g_assert_cmpuint(actual, ==, 0x01);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
/* Reset DIR */
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000);
/* Check Input propagates */
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00);
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00);
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x00);
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
/* Check pull-up working */
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x00);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
/* Check pull-down working */
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x00);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02);
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1);
/* Check Output propagates */
irq_intercept_out("/machine/nrf51");
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
g_assert_true(get_irq(0));
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
g_assert_false(get_irq(0));
qtest_irq_intercept_out(qts, "/machine/nrf51");
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
g_assert_true(qtest_get_irq(qts, 0));
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
g_assert_false(qtest_get_irq(qts, 0));
/* Check self-stimulation */
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
actual = readl(NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01);
actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01;
g_assert_cmpuint(actual, ==, 0x00);
/*
* Check short-circuit - generates an guest_error which must be checked
* manually as long as qtest can not scan qemu_log messages
*/
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
writel(NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
qtest_set_irq_in(global_qtest, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01);
qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01);
qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0);
qtest_quit(qts);
}
static void timer_task(hwaddr task)
static void timer_task(QTestState *qts, hwaddr task)
{
writel(NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK);
qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK);
}
static void timer_clear_event(hwaddr event)
static void timer_clear_event(QTestState *qts, hwaddr event)
{
writel(NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR);
qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR);
}
static void timer_set_bitmode(uint8_t mode)
static void timer_set_bitmode(QTestState *qts, uint8_t mode)
{
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode);
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode);
}
static void timer_set_prescaler(uint8_t prescaler)
static void timer_set_prescaler(QTestState *qts, uint8_t prescaler)
{
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler);
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler);
}
static void timer_set_cc(size_t idx, uint32_t value)
static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value)
{
writel(NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value);
qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value);
}
static void timer_assert_events(uint32_t ev0, uint32_t ev1, uint32_t ev2,
uint32_t ev3)
static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1,
uint32_t ev2, uint32_t ev3)
{
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) == ev0);
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) == ev1);
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) == ev2);
g_assert(readl(NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) == ev3);
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0)
== ev0);
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1)
== ev1);
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2)
== ev2);
g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3)
== ev3);
}
static void test_nrf51_timer(void)
{
uint32_t steps_to_overflow = 408;
QTestState *qts = qtest_init("-M microbit");
/* Compare Match */
timer_task(NRF51_TIMER_TASK_STOP);
timer_task(NRF51_TIMER_TASK_CLEAR);
timer_task(qts, NRF51_TIMER_TASK_STOP);
timer_task(qts, NRF51_TIMER_TASK_CLEAR);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3);
timer_set_bitmode(NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */
timer_set_prescaler(0);
timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */
timer_set_prescaler(qts, 0);
/* Swept over in first step */
timer_set_cc(0, 2);
timer_set_cc(qts, 0, 2);
/* Barely miss on first step */
timer_set_cc(1, 162);
timer_set_cc(qts, 1, 162);
/* Spot on on third step */
timer_set_cc(2, 480);
timer_set_cc(qts, 2, 480);
timer_assert_events(0, 0, 0, 0);
timer_assert_events(qts, 0, 0, 0, 0);
timer_task(NRF51_TIMER_TASK_START);
clock_step(10000);
timer_assert_events(1, 0, 0, 0);
timer_task(qts, NRF51_TIMER_TASK_START);
qtest_clock_step(qts, 10000);
timer_assert_events(qts, 1, 0, 0, 0);
/* Swept over on first overflow */
timer_set_cc(3, 114);
timer_set_cc(qts, 3, 114);
clock_step(10000);
timer_assert_events(1, 1, 0, 0);
qtest_clock_step(qts, 10000);
timer_assert_events(qts, 1, 1, 0, 0);
clock_step(10000);
timer_assert_events(1, 1, 1, 0);
qtest_clock_step(qts, 10000);
timer_assert_events(qts, 1, 1, 1, 0);
/* Wrap time until internal counter overflows */
while (steps_to_overflow--) {
timer_assert_events(1, 1, 1, 0);
clock_step(10000);
timer_assert_events(qts, 1, 1, 1, 0);
qtest_clock_step(qts, 10000);
}
timer_assert_events(1, 1, 1, 1);
timer_assert_events(qts, 1, 1, 1, 1);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_0);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_1);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_2);
timer_clear_event(NRF51_TIMER_EVENT_COMPARE_3);
timer_assert_events(0, 0, 0, 0);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2);
timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3);
timer_assert_events(qts, 0, 0, 0, 0);
timer_task(NRF51_TIMER_TASK_STOP);
timer_task(qts, NRF51_TIMER_TASK_STOP);
/* Test Proposal: Stop/Shutdown */
/* Test Proposal: Shortcut Compare -> Clear */
/* Test Proposal: Shortcut Compare -> Stop */
/* Test Proposal: Counter Mode */
qtest_quit(qts);
}
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
global_qtest = qtest_initf("-machine microbit");
qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart);
qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio);
qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer);
qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c);
ret = g_test_run();
qtest_quit(global_qtest);
return ret;
return g_test_run();
}