-
Notifications
You must be signed in to change notification settings - Fork 7.3k
boards: arm: add QEMU support for Cortex-M0 #19479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
4285ea1
boards: arm: add QEMU support for Cortex-M0
ioannisg 5b701d4
boards: arm: qemu_cortex_m0: adding documentation
ioannisg feedbe2
CODEOWNERS: adding code owner for ARM Qemu Cortex-M* platforms
ioannisg 369de34
boards: arm: qemu_cortex_m0: configure board with own timer driver
ioannisg 01cf5c4
drivers: timer: nrf: minor comment fix
ioannisg 2cb0883
boards: arm: qemu_cortex_m0: implement custom system clock driver
ioannisg 093c286
boards: arm: qemu_cortex_m0: filter out a couple of failing tests
ioannisg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# | ||
# Copyright (c) 2019, Nordic Semiconductor ASA | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
|
||
zephyr_sources_if_kconfig( nrf_timer_timer.c) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Kconfig - QEMU Cortex-M0 board configuration | ||
# | ||
# Copyright (c) 2019 Nordic Semiconductor ASA. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config BOARD_QEMU_CORTEX_M0 | ||
bool "Cortex-M0 Emulation (QEMU)" | ||
depends on SOC_NRF51822_QFAA | ||
select QEMU_TARGET |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Kconfig - QEMU Cortex-M0 board configuration | ||
# | ||
# Copyright (c) 2019 Nordic Semiconductor ASA. | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
if BOARD_QEMU_CORTEX_M0 | ||
|
||
config BOARD | ||
default "qemu_cortex_m0" | ||
|
||
if SYS_CLOCK_EXISTS | ||
|
||
config NRF_TIMER_TIMER | ||
bool "nRF Timer Counter (NRF_TIMER0) Timer" | ||
depends on CLOCK_CONTROL | ||
depends on SOC_COMPATIBLE_NRF | ||
select TICKLESS_CAPABLE | ||
default y | ||
help | ||
This module implements a kernel device driver for the nRF Timer | ||
Counter NRF_TIMER0 and provides the standard "system clock driver" | ||
interfaces. | ||
|
||
config NRF_RTC_TIMER | ||
default n | ||
|
||
endif # SYS_CLOCK_EXISTS | ||
|
||
config SYS_CLOCK_HW_CYCLES_PER_SEC | ||
int | ||
default 1000000 | ||
|
||
config SYS_CLOCK_TICKS_PER_SEC | ||
int | ||
default 100 | ||
|
||
config ENTROPY_NRF_FORCE_ALT | ||
bool | ||
default y | ||
|
||
endif # BOARD_QEMU_CORTEX_M0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# | ||
# Copyright (c) 2019, Nordic Semiconductor ASA | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
set(EMU_PLATFORM qemu) | ||
|
||
set(QEMU_CPU_TYPE_${ARCH} cortex-m0) | ||
set(QEMU_FLAGS_${ARCH} | ||
-cpu ${QEMU_CPU_TYPE_${ARCH}} | ||
-machine microbit | ||
-nographic | ||
-vga none | ||
) | ||
|
||
board_set_debugger_ifnset(qemu) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
.. _qemu_cortex_m0: | ||
|
||
ARM Cortex-M0 Emulation (QEMU) | ||
############################## | ||
|
||
Overview | ||
******** | ||
|
||
This board configuration will use QEMU to emulate the | ||
BBC Microbit (Nordic nRF51822) platform. | ||
|
||
.. figure:: qemu_cortex_m0.png | ||
:width: 600px | ||
:align: center | ||
:alt: Qemu | ||
|
||
Qemu (Credit: qemu.org) | ||
|
||
This configuration provides support for an ARM Cortex-M0 CPU and these devices: | ||
|
||
* Nested Vectored Interrupt Controller | ||
* TIMER (nRF TIMER System Clock) | ||
|
||
.. note:: | ||
This board configuration makes no claims about its suitability for use | ||
with an actual nRF51 Microbit hardware system, or any other hardware system. | ||
|
||
Hardware | ||
******** | ||
Supported Features | ||
================== | ||
|
||
The following hardware features are supported: | ||
|
||
+--------------+------------+----------------------+ | ||
| Interface | Controller | Driver/Component | | ||
+==============+============+======================+ | ||
| NVIC | on-chip | nested vectored | | ||
| | | interrupt controller | | ||
+--------------+------------+----------------------+ | ||
| nRF | on-chip | serial port | | ||
| UART | | | | ||
+--------------+------------+----------------------+ | ||
| nRF TIMER | on-chip | system clock | | ||
+--------------+------------+----------------------+ | ||
|
||
The kernel currently does not support other hardware features on this platform. | ||
|
||
Devices | ||
======== | ||
System Clock | ||
------------ | ||
|
||
This board configuration uses a system clock frequency of 1 MHz. | ||
|
||
Serial Port | ||
----------- | ||
|
||
This board configuration uses a single serial communication channel with the | ||
CPU's UART0. | ||
|
||
Known Problems or Limitations | ||
============================== | ||
|
||
The following platform features are unsupported: | ||
|
||
* Writing to the hardware's flash memory | ||
|
||
|
||
Programming and Debugging | ||
************************* | ||
|
||
Use this configuration to run basic Zephyr applications and kernel tests in the QEMU | ||
emulated environment, for example, with the :ref:`synchronization_sample`: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/synchronization | ||
:host-os: unix | ||
:board: qemu_cortex_m0 | ||
:goals: run | ||
|
||
This will build an image with the synchronization sample app, boot it using | ||
QEMU, and display the following console output: | ||
|
||
.. code-block:: console | ||
|
||
***** BOOTING ZEPHYR OS v1.8.99 - BUILD: Jun 27 2017 13:09:26 ***** | ||
threadA: Hello World from arm! | ||
threadB: Hello World from arm! | ||
threadA: Hello World from arm! | ||
threadB: Hello World from arm! | ||
threadA: Hello World from arm! | ||
threadB: Hello World from arm! | ||
threadA: Hello World from arm! | ||
threadB: Hello World from arm! | ||
threadA: Hello World from arm! | ||
threadB: Hello World from arm! | ||
|
||
Exit QEMU by pressing :kbd:`CTRL+A` :kbd:`x`. | ||
|
||
Debugging | ||
========= | ||
|
||
Refer to the detailed overview about :ref:`application_debugging`. | ||
|
||
Networking | ||
========== | ||
|
||
References | ||
********** | ||
|
||
1. The Definitive Guide to the ARM Cortex-M0, Second Edition by Joseph Yiu (ISBN | ||
978-0-12-803278-7) | ||
2. ARMv6-M Architecture Technical Reference Manual (ARM DDI 0419D 0403D ID051917) | ||
3. Procedure Call Standard for the ARM Architecture (ARM IHI 0042E, current | ||
through ABI release 2.09, 2012/11/30) | ||
4. Cortex-M0 Revision r2p1 Technical Reference Manual (ARM DDI 0432C ID113009) | ||
5. Cortex-M0 Devices Generic User Guide (ARM DUI 0497A ID112109) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
/* | ||
* Copyright (c) 2016-2019 Nordic Semiconductor ASA | ||
* Copyright (c) 2018 Intel Corporation | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <soc.h> | ||
#include <drivers/clock_control.h> | ||
#include <drivers/clock_control/nrf_clock_control.h> | ||
#include <drivers/timer/system_timer.h> | ||
#include <sys_clock.h> | ||
#include <hal/nrf_timer.h> | ||
#include <spinlock.h> | ||
|
||
#define TIMER NRF_TIMER0 | ||
|
||
#define COUNTER_MAX 0xffffffff | ||
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \ | ||
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC) | ||
#define MAX_TICKS ((COUNTER_MAX - CYC_PER_TICK) / CYC_PER_TICK) | ||
|
||
static struct k_spinlock lock; | ||
|
||
static u32_t last_count; | ||
|
||
static u32_t counter_sub(u32_t a, u32_t b) | ||
{ | ||
return (a - b) & COUNTER_MAX; | ||
} | ||
|
||
static void set_comparator(u32_t cyc) | ||
{ | ||
nrf_timer_cc_write(TIMER, 0, cyc & COUNTER_MAX); | ||
} | ||
|
||
static u32_t counter(void) | ||
{ | ||
nrf_timer_task_trigger(TIMER, nrf_timer_capture_task_get(1)); | ||
|
||
return nrf_timer_cc_read(TIMER, 1); | ||
} | ||
|
||
void timer0_nrf_isr(void *arg) | ||
{ | ||
ARG_UNUSED(arg); | ||
TIMER->EVENTS_COMPARE[0] = 0; | ||
|
||
k_spinlock_key_t key = k_spin_lock(&lock); | ||
u32_t t = counter(); | ||
u32_t dticks = counter_sub(t, last_count) / CYC_PER_TICK; | ||
|
||
last_count += dticks * CYC_PER_TICK; | ||
|
||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | ||
u32_t next = last_count + CYC_PER_TICK; | ||
|
||
/* As below: we're guaranteed to get an interrupt as | ||
* long as it's set two or more cycles in the future | ||
*/ | ||
if (counter_sub(next, t) < 3) { | ||
next += CYC_PER_TICK; | ||
} | ||
set_comparator(next); | ||
} | ||
|
||
k_spin_unlock(&lock, key); | ||
z_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? dticks : 1); | ||
} | ||
|
||
int z_clock_driver_init(struct device *device) | ||
{ | ||
struct device *clock; | ||
|
||
ARG_UNUSED(device); | ||
|
||
clock = device_get_binding(DT_INST_0_NORDIC_NRF_CLOCK_LABEL "_16M"); | ||
if (!clock) { | ||
return -1; | ||
} | ||
|
||
/* turn on clock in blocking mode. */ | ||
clock_control_on(clock, (void *)1); | ||
|
||
nrf_timer_frequency_set(TIMER, NRF_TIMER_FREQ_1MHz); | ||
nrf_timer_bit_width_set(TIMER, NRF_TIMER_BIT_WIDTH_32); | ||
nrf_timer_cc_write(TIMER, 0, CYC_PER_TICK); | ||
nrf_timer_int_enable(TIMER, TIMER_INTENSET_COMPARE0_Msk); | ||
|
||
/* Clear the event flag and possible pending interrupt */ | ||
nrf_timer_event_clear(TIMER, NRF_TIMER_EVENT_COMPARE0); | ||
NVIC_ClearPendingIRQ(TIMER0_IRQn); | ||
|
||
IRQ_CONNECT(TIMER0_IRQn, 1, timer0_nrf_isr, 0, 0); | ||
irq_enable(TIMER0_IRQn); | ||
|
||
nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_CLEAR); | ||
nrf_timer_task_trigger(TIMER, NRF_TIMER_TASK_START); | ||
|
||
if (!IS_ENABLED(TICKLESS_KERNEL)) { | ||
set_comparator(counter() + CYC_PER_TICK); | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
void z_clock_set_timeout(s32_t ticks, bool idle) | ||
{ | ||
ARG_UNUSED(idle); | ||
|
||
#ifdef CONFIG_TICKLESS_KERNEL | ||
ticks = (ticks == K_FOREVER) ? MAX_TICKS : ticks; | ||
ticks = MAX(MIN(ticks - 1, (s32_t)MAX_TICKS), 0); | ||
|
||
k_spinlock_key_t key = k_spin_lock(&lock); | ||
u32_t cyc, dt, t = counter(); | ||
bool zli_fixup = IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS); | ||
|
||
/* Round up to next tick boundary */ | ||
cyc = ticks * CYC_PER_TICK + 1 + counter_sub(t, last_count); | ||
cyc += (CYC_PER_TICK - 1); | ||
cyc = (cyc / CYC_PER_TICK) * CYC_PER_TICK; | ||
cyc += last_count; | ||
|
||
if (counter_sub(cyc, t) > 2) { | ||
set_comparator(cyc); | ||
} else { | ||
set_comparator(cyc); | ||
dt = counter_sub(cyc, counter()); | ||
if (dt == 0 || dt > 0x7fffff) { | ||
/* Missed it! */ | ||
NVIC_SetPendingIRQ(TIMER0_IRQn); | ||
if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { | ||
zli_fixup = false; | ||
} | ||
} else if (dt == 1) { | ||
/* Too soon, interrupt won't arrive. */ | ||
set_comparator(cyc + 2); | ||
} | ||
/* Otherwise it was two cycles out, we're fine */ | ||
} | ||
|
||
#ifdef CONFIG_ZERO_LATENCY_IRQS | ||
/* Failsafe. ZLIs can preempt us even though interrupts are | ||
* masked, blowing up the sensitive timing above. If the | ||
* feature is enabled and we haven't recorded the presence of | ||
* a pending interrupt then we need a final check (in a loop! | ||
* because this too can be interrupted) to confirm that the | ||
* comparator is still in the future. Don't bother being | ||
* fancy with cycle counting here, just set an interrupt | ||
* "soon" that we know will get the timer back to a known | ||
* state. This handles (via some hairy modular expressions) | ||
* the wraparound cases where we are preempted for as much as | ||
* half the counter space. | ||
*/ | ||
if (zli_fixup && counter_sub(cyc, counter()) <= 0x7fffff) { | ||
while (counter_sub(cyc, counter() + 2) > 0x7fffff) { | ||
cyc = counter() + 3; | ||
set_comparator(cyc); | ||
} | ||
} | ||
#endif | ||
|
||
k_spin_unlock(&lock, key); | ||
#endif /* CONFIG_TICKLESS_KERNEL */ | ||
} | ||
|
||
u32_t z_clock_elapsed(void) | ||
{ | ||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { | ||
return 0; | ||
} | ||
|
||
k_spinlock_key_t key = k_spin_lock(&lock); | ||
u32_t ret = counter_sub(counter(), last_count) / CYC_PER_TICK; | ||
|
||
k_spin_unlock(&lock, key); | ||
return ret; | ||
} | ||
|
||
u32_t z_timer_cycle_get_32(void) | ||
{ | ||
k_spinlock_key_t key = k_spin_lock(&lock); | ||
u32_t ret = counter_sub(counter(), last_count) + last_count; | ||
|
||
k_spin_unlock(&lock, key); | ||
return ret; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did the whole driver have to be copied to the board directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's because there is no point having a second system clock driver based on Timer peripheral , besides the basic RTC driver. I only added the driver because the qemu does not emulate the RTC :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm confused. Where does the emulation come from then? I mean, it looks like nrf_timer_cc_read() is returning your counter value from somewhere, right? So the HAL is doing the emulation? If that's true, why won't the default driver work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andyross the default nRF driver, used in Zephyr, is based on the NRF RTC peripheral. This peripheral is, however, not supported in the QEMU emulation of the Microbit platform (nRF51). Instead, the QEMU guys have added support for the NRF TIMER peripheral. So, I had to write a system timer driver, based on the NRF TIMER peripheral.
But there is no need to have this driver in the /drivers/timer directory as a generic timer driver; cause there is no use case for that; nRF SoCs use the nRF RTC timer that can utilize the low-frequency low-power clock source. This driver is not, even, specific to Microbit; actual microbit HW boards can use the default nRF RTC driver. The new driver is specific for the qemu_cortex_m0 platform. Its sole purpose is to add system timer support for qemu cortex-m0, so we get coverage on ARMv6-M architecture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. The default driver is a different peripheral. The "corresponding function, there, is called
nrf_rtc_cc_read()
. But as I said, this peripheral does not yet work in qemu.