[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC 5/8] arm: Add NRF51 random number generator peripheral
From: |
Steffen Görtz |
Subject: |
[Qemu-devel] [RFC 5/8] arm: Add NRF51 random number generator peripheral |
Date: |
Wed, 27 Jun 2018 09:33:48 +0200 |
Add a model of the NRF51 random number generator peripheral.
Changes since v2:
- Add missing 'qapi/error.h' for error_abort
Changes since v1:
- Add implementation access size hints to MemoryRegionOps
- Fail on error if qcrypto_random_bytes fails
- Add references to Nrf51 datasheets
Signed-off-by: Steffen Görtz <address@hidden>
---
hw/misc/Makefile.objs | 1 +
hw/misc/nrf51_rng.c | 243 ++++++++++++++++++++++++++++++++++++
include/hw/misc/nrf51_rng.h | 73 +++++++++++
3 files changed, 317 insertions(+)
create mode 100644 hw/misc/nrf51_rng.c
create mode 100644 include/hw/misc/nrf51_rng.h
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 00e834d0f0..fd8cc97249 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -70,3 +70,4 @@ obj-$(CONFIG_AUX) += auxbus.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o
obj-y += mmio_interface.o
obj-$(CONFIG_MSF2) += msf2-sysreg.o
+obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c
new file mode 100644
index 0000000000..46bdb21850
--- /dev/null
+++ b/hw/misc/nrf51_rng.c
@@ -0,0 +1,243 @@
+/*
+ * nrf51_rng.c
+ *
+ * Copyright 2018 Steffen Görtz <address@hidden>
+ *
+ * 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 "qapi/error.h"
+#include "hw/misc/nrf51_rng.h"
+#include "crypto/random.h"
+
+#define NRF51_RNG_SIZE 0x1000
+
+#define NRF51_RNG_TASK_START 0x000
+#define NRF51_RNG_TASK_STOP 0x004
+#define NRF51_RNG_EVENT_VALRDY 0x100
+#define NRF51_RNG_REG_SHORTS 0x200
+#define NRF51_RNG_REG_SHORTS_VALRDY_STOP 0
+#define NRF51_RNG_REG_INTEN 0x300
+#define NRF51_RNG_REG_INTEN_VALRDY 0
+#define NRF51_RNG_REG_INTENSET 0x304
+#define NRF51_RNG_REG_INTENCLR 0x308
+#define NRF51_RNG_REG_CONFIG 0x504
+#define NRF51_RNG_REG_CONFIG_DECEN 0
+#define NRF51_RNG_REG_VALUE 0x508
+
+#define NRF51_TRIGGER_TASK 0x01
+#define NRF51_EVENT_CLEAR 0x00
+
+
+static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size)
+{
+ Nrf51RNGState *s = NRF51_RNG(opaque);
+ uint64_t r = 0;
+
+ switch (offset) {
+ case NRF51_RNG_EVENT_VALRDY:
+ r = s->state.event_valrdy;
+ break;
+ case NRF51_RNG_REG_SHORTS:
+ r = s->state.shortcut_stop_on_valrdy;
+ break;
+ case NRF51_RNG_REG_INTEN:
+ case NRF51_RNG_REG_INTENSET:
+ case NRF51_RNG_REG_INTENCLR:
+ r = s->state.interrupt_enabled;
+ break;
+ case NRF51_RNG_REG_CONFIG:
+ r = s->state.filter_enabled;
+ break;
+ case NRF51_RNG_REG_VALUE:
+ r = s->value;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ }
+
+ return r;
+}
+
+static int64_t calc_next_timeout(Nrf51RNGState *s)
+{
+ int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL);
+ if (s->state.filter_enabled) {
+ timeout += s->period_filtered_us;
+ } else {
+ timeout += s->period_unfiltered_us;
+ }
+
+ return timeout;
+}
+
+
+static void rng_update_timer(Nrf51RNGState *s)
+{
+ if (s->state.active) {
+ timer_mod(&s->timer, calc_next_timeout(s));
+ } else {
+ timer_del(&s->timer);
+ }
+}
+
+
+static void rng_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned int size)
+{
+ Nrf51RNGState *s = NRF51_RNG(opaque);
+
+ switch (offset) {
+ case NRF51_RNG_TASK_START:
+ if (value == NRF51_TRIGGER_TASK) {
+ s->state.active = 1;
+ rng_update_timer(s);
+ }
+ break;
+ case NRF51_RNG_TASK_STOP:
+ if (value == NRF51_TRIGGER_TASK) {
+ s->state.active = 0;
+ rng_update_timer(s);
+ }
+ break;
+ case NRF51_RNG_EVENT_VALRDY:
+ if (value == NRF51_EVENT_CLEAR) {
+ s->state.event_valrdy = 0;
+ qemu_set_irq(s->eep_valrdy, 0);
+ }
+ break;
+ case NRF51_RNG_REG_SHORTS:
+ s->state.shortcut_stop_on_valrdy =
+ (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0;
+ break;
+ case NRF51_RNG_REG_INTEN:
+ s->state.interrupt_enabled =
+ (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0;
+ break;
+ case NRF51_RNG_REG_INTENSET:
+ if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
+ s->state.interrupt_enabled = 1;
+ }
+ break;
+ case NRF51_RNG_REG_INTENCLR:
+ if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) {
+ s->state.interrupt_enabled = 0;
+ }
+ break;
+ case NRF51_RNG_REG_CONFIG:
+ s->state.filter_enabled =
+ (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0;
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+ __func__, offset);
+ }
+}
+
+static const MemoryRegionOps rng_ops = {
+ .read = rng_read,
+ .write = rng_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4
+};
+
+static void nrf51_rng_timer_expire(void *opaque)
+{
+ Nrf51RNGState *s = NRF51_RNG(opaque);
+
+ qcrypto_random_bytes(&s->value, 1, &error_abort);
+
+ s->state.event_valrdy = 1;
+ qemu_set_irq(s->eep_valrdy, 1);
+
+ if (s->state.interrupt_enabled) {
+ qemu_irq_pulse(s->irq);
+ }
+
+ if (s->state.shortcut_stop_on_valrdy) {
+ s->state.active = 0;
+ }
+
+ rng_update_timer(s);
+}
+
+static void nrf51_rng_tep_start(void *opaque, int n, int level)
+{
+ Nrf51RNGState *s = NRF51_RNG(opaque);
+
+ if (level) {
+ s->state.active = 1;
+ rng_update_timer(s);
+ }
+}
+
+static void nrf51_rng_tep_stop(void *opaque, int n, int level)
+{
+ Nrf51RNGState *s = NRF51_RNG(opaque);
+
+ if (level) {
+ s->state.active = 0;
+ rng_update_timer(s);
+ }
+}
+
+
+static void nrf51_rng_init(Object *obj)
+{
+ Nrf51RNGState *s = NRF51_RNG(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ memory_region_init_io(&s->mmio, obj, &rng_ops, s,
+ TYPE_NRF51_RNG, NRF51_RNG_SIZE);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s);
+
+ qdev_init_gpio_out_named(DEVICE(s), &s->irq, "irq", 1);
+
+ /* Tasks */
+ qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1);
+ qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1);
+
+ /* Events */
+ qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1);
+}
+
+static Property nrf51_rng_properties[] = {
+ DEFINE_PROP_UINT16("period_unfiltered_us", Nrf51RNGState,
+ period_unfiltered_us, 167),
+ DEFINE_PROP_UINT16("period_filtered_us", Nrf51RNGState,
+ period_filtered_us, 660),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void nrf51_rng_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = nrf51_rng_properties;
+}
+
+static const TypeInfo nrf51_rng_info = {
+ .name = TYPE_NRF51_RNG,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Nrf51RNGState),
+ .instance_init = nrf51_rng_init,
+ .class_init = nrf51_rng_class_init
+};
+
+static void nrf51_rng_register_types(void)
+{
+ type_register_static(&nrf51_rng_info);
+}
+
+type_init(nrf51_rng_register_types)
diff --git a/include/hw/misc/nrf51_rng.h b/include/hw/misc/nrf51_rng.h
new file mode 100644
index 0000000000..a8f0630a98
--- /dev/null
+++ b/include/hw/misc/nrf51_rng.h
@@ -0,0 +1,73 @@
+/*
+ * nrf51_rng.h
+ *
+ * Copyright 2018 Steffen Görtz <address@hidden>
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ *
+ * See NRF51 reference manual section 21 Random Number Generator
+ * See NRF51 product specification section 8.16 Random Number Generator
+ *
+ * QEMU interface:
+ * + Property "period_unfiltered_us": Time between two biased values in
+ * microseconds.
+ * + Property "period_filtered_us": Time between two unbiased values in
+ * microseconds.
+ * + sysbus MMIO regions 0: Memory Region with tasks, events and registers
+ * to be mapped to the peripherals instance address by the SOC.
+ * + Named GPIO output "irq": Interrupt line of the peripheral. Must be
+ * connected to the associated peripheral interrupt line of the NVIC.
+ * + Named GPIO output "eep_valrdy": Event set when new random value is ready
+ * to be read.
+ * + Named GPIO input "tep_start": Task that triggers start of continuous
+ * generation of random values.
+ * + Named GPIO input "tep_stop": Task that ends continuous generation of
+ * random values.
+ *
+ * Accuracy of the peripheral model:
+ * + Stochastic properties of different configurations of the random source
+ * are not modeled.
+ * + Generation of unfiltered and filtered random values take at least the
+ * average generation time stated in the production specification;
+ * non-deterministic generation times are not modeled.
+ *
+ */
+#ifndef NRF51_RNG_H
+#define NRF51_RNG_H
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#define TYPE_NRF51_RNG "nrf51_soc.rng"
+#define NRF51_RNG(obj) OBJECT_CHECK(Nrf51RNGState, (obj), TYPE_NRF51_RNG)
+
+typedef struct Nrf51RNGState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion mmio;
+ qemu_irq irq;
+
+ /* Event End Points */
+ qemu_irq eep_valrdy;
+
+ QEMUTimer timer;
+
+ /* Time between generation of successive unfiltered values in us */
+ uint16_t period_unfiltered_us;
+ /* Time between generation of successive filtered values in us */
+ uint16_t period_filtered_us;
+
+ uint8_t value;
+
+ struct {
+ uint32_t active:1;
+ uint32_t event_valrdy:1;
+ uint32_t shortcut_stop_on_valrdy:1;
+ uint32_t interrupt_enabled:1;
+ uint32_t filter_enabled:1;
+ } state;
+
+} Nrf51RNGState;
+
+
+#endif /* NRF51_RNG_H_ */
--
2.17.1
- [Qemu-devel] [RFC 0/8] arm: Changes to Microbit Board and NRF51 SOC, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 2/8] arm: NRF51 Add unimplemented device for MMIO, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 4/8] arm: NRF51 Calculate peripheral id from base address, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 5/8] arm: Add NRF51 random number generator peripheral,
Steffen Görtz <=
- [Qemu-devel] [RFC 3/8] arm: NRF51 create UART in-place, error handling, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 7/8] arm: Add NRF51 SOC non-volatile memory controller, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 1/8] arm: NRF51/Microbit Memory container and SOC variants, Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 8/8] arm: Instantiate NVMC in NRF51., Steffen Görtz, 2018/06/27
- [Qemu-devel] [RFC 6/8] arm: Add UICR/FICR handling to NRF51 SOC, Steffen Görtz, 2018/06/27
- Re: [Qemu-devel] [RFC 0/8] arm: Changes to Microbit Board and NRF51 SOC, Stefan Hajnoczi, 2018/06/27