Qemu-NUC980(二):时钟clock代码添加
概述
本文描述了在qemu下模拟实现时钟框架的代码,在讲述过程中,为了便于阅读,删减了完成代码,感兴趣的同学可以去文末链接,查看工程链接。
本文主要讲述的模拟980的时钟的框架步骤,不细节上讲述每一个实现的原理。
添加步骤
1、在include/hw/misc/目录下创建nuc980_clk.h,如下所示:
+号部分为新增加内容
diff --git a/include/hw/misc/nuc980_clk.h b/include/hw/misc/nuc980_clk.h
new file mode 100644
index 00000000..2f25399a
--- /dev/null
+++ b/include/hw/misc/nuc980_clk.h
@@ -0,0 +1,72 @@
+/*
+ * NUC980 SOC System emulation.
+ *
+ * Copyright (c) 2023- yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#ifndef NUC980_CLK__H
+#define NUC980_CLK__H
+
+#include "hw/sysbus.h"
+
+#define CLK_PMCON 0x00
+#define CLK_HCLKEN 0x10
+#define CLK_PCLKEN0 0x18
+#define CLK_PCLKEN1 0x1c
+#define CLK_DIVCTL0 0x20
+#define CLK_DIVCTL1 0x24
+#define CLK_DIVCTL2 0x28
+#define CLK_DIVCTL3 0x2c
+#define CLK_DIVCTL4 0x30
+#define CLK_DIVCTL5 0x34
+#define CLK_DIVCTL6 0x38
+#define CLK_DIVCTL7 0x3c
+#define CLK_DIVCTL8 0x40
+#define CLK_DIVCTL9 0x44
+#define CLK_APLLCON 0x60
+#define CLK_UPLLCON 0x64
+#define CLK_PLLSTBCNTR 0x80
+
+#define TYPE_NUC980_CLK "nuc980-clk"
+#define NUC980_CLK(obj) \
+ OBJECT_CHECK(NUC980ClockState, (obj), TYPE_NUC980_CLK)
+
+#define SYS_UPLL 1
+#define SYS_APLL 2
+#define SYS_SYSTEM 3
+#define SYS_HCLK 4
+#define SYS_PCLK01 5
+#define SYS_PCLK2 6
+#define SYS_CPU 7
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+
+ uint32_t clk_pmcon;
+ uint32_t clk_hclken;
+ uint32_t clk_pclken0;
+ uint32_t clk_pclken1;
+ uint32_t clk_divctrl0;
+ uint32_t clk_divctrl1;
+ uint32_t clk_divctrl2;
+ uint32_t clk_divctrl3;
+ uint32_t clk_divctrl4;
+ uint32_t clk_divctrl5;
+ uint32_t clk_divctrl6;
+ uint32_t clk_divctrl7;
+ uint32_t clk_divctrl8;
+ uint32_t clk_divctrl9;
+ uint32_t clk_applcon;
+ uint32_t clk_upllcon;
+ uint32_t clk_pllstbcntr;
+
+} NUC980ClockState;
+
+extern uint64_t nuc980_get_timer_clock(int id);
+#endif
2、修改hw/misc/目录下创建nuc980_clk.c,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/nuc980_clk.c b/hw/misc/nuc980_clk.c
new file mode 100644
index 00000000..2a4ad4f6
--- /dev/null
+++ b/hw/misc/nuc980_clk.c
@@ -0,0 +1,396 @@
+/*
+ * NUC980 SOC System emulation.
+ *
+ * Copyright (c) 2023- yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/misc/nuc980_clk.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+
+#ifndef SYS_ERR_DEBUG
+#define SYS_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+ if (SYS_ERR_DEBUG >= lvl) { \
+ qemu_log("%s: " fmt, __func__, ## args); \
+ } \
+} while (0);
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static NUC980ClockState *g_clk_dev = NULL;
+
+static void nuc980_clk_reset(DeviceState *dev)
+{
+ NUC980ClockState *s = NUC980_CLK(dev);
+......
+}
+
+static uint64_t nuc980_clk_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ NUC980ClockState *s = opaque;
+ uint64_t retvalue = 0;
+
+ DB_PRINT("Address: 0x%" HWADDR_PRIx "\n", addr);
+ switch(addr) {
+ case CLK_PMCON:
+ retvalue = s->clk_pmcon;......
+ return retvalue;
+}
+
+static void nuc980_clk_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ NUC980ClockState *s = opaque;
+ uint32_t value = val64;
+ ......
+}
+
+static uint32_t sysGetPLL(uint32_t reg)
+{
+ uint32_t N,M,P;
+
+ N =((reg & 0x007F)>>0)+1;
+ M =((reg & 0x1F80)>>7)+1;
+ P =((reg & 0xE000)>>13)+1;
+
+ return (12*N/(M*P));
+}......
+static const MemoryRegionOps nuc980_clk_ops = {
+ .read = nuc980_clk_read,
+ .write = nuc980_clk_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_nuc980_clk = {
+ .name = TYPE_NUC980_CLK,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(clk_pmcon, NUC980ClockState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void nuc980_clk_init(Object *obj)
+{
+ NUC980ClockState *s = NUC980_CLK(obj);
+
+ memory_region_init_io(&s->mmio, obj, &nuc980_clk_ops, s,
+ TYPE_NUC980_CLK, 0x100);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void nuc980_clk_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = nuc980_clk_reset;
+ dc->vmsd = &vmstate_nuc980_clk;
+}
+
+static const TypeInfo nuc980_clk_info = {
+ .name = TYPE_NUC980_CLK,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NUC980ClockState),
+ .instance_init = nuc980_clk_init,
+ .class_init = nuc980_clk_class_init,
+};
+
+static void nuc980_clk_register_types(void)
+{
+ type_register_static(&nuc980_clk_info);
+}
+
+type_init(nuc980_clk_register_types)
3、修改hw/misc/Kconfig,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 21646465..cd32904b 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -125,4 +125,7 @@ config MAC_VIAselect MOS6522select ADB+config NUC980_CLK
+ bool
+
4、修改hw/misc/Makefile.objs,如下所示:,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index ba898a57..792be5db 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -82,3 +82,6 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.oobj-$(CONFIG_MAC_VIA) += mac_via.ocommon-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
+
+obj-$(CONFIG_NUC980_CLK) += nuc980_clk.o
+
5、修改hw/arm/目录下的nuc980_soc.c,如下所示:
+号部分为新增加内容
diff --git a/hw/arm/nuc980_soc.c b/hw/arm/nuc980_soc.c
index 3de9d712..cec0c97d 100644
--- a/hw/arm/nuc980_soc.c
+++ b/hw/arm/nuc980_soc.c
@@ -20,10 +20,37 @@static void nuc980_init(Object *obj){
+
+ NUC980State *s = NUC980(obj);
+
+ object_initialize_child(obj, "cpu", &s->cpu, sizeof(s->cpu),
+ ARM_CPU_TYPE_NAME("arm926"),
+ &error_abort, NULL);
+
+ /* clk */
+ sysbus_init_child_obj(obj, "clk", &s->clk, sizeof(s->clk), TYPE_NUC980_CLK);}static void nuc980_realize(DeviceState *dev, Error **errp){
+ NUC980State *s = NUC980(dev);
+ Error *err = NULL;
+ /* By default A9,A15 and ARM1176 CPUs have EL3 enabled. This board
+ * does not currently support EL3 so the CPU EL3 property is disabled
+ * before realization.
+ */
+ if (object_property_find(OBJECT(&s->cpu), "has_el3", NULL)) {
+ object_property_set_bool(OBJECT(&s->cpu), false, "has_el3", &error_fatal);
+ }
+
+ object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ /* clock control */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, CLK_CTL_BASE);
+}
6、修改hw/arm/目录下的Kconfig,如下所示:
+号部分为新增加内容
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 2a604c83..b1f4410a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -115,6 +115,7 @@ config NSERIESconfig NUC980bool
+ select NUC980_CLK
总结
本文描述了980时钟的模拟实现,在qemu模拟中,只能模拟寄存器的功能,不能模拟时序特性。
工程链接
https://gitee.com/yanl1229/qemu.git