概述
本文描述了NUC980 System Manager的控制的qemu模拟实现。
添加步骤
1、在include/hw/misc/目录下创建nuc980_sys.h,如下所示:
+号部分为新增加内容
diff --git a/include/hw/misc/nuc980_sys.h b/include/hw/misc/nuc980_sys.h
new file mode 100644
index 00000000..38386210
--- /dev/null
+++ b/include/hw/misc/nuc980_sys.h
@@ -0,0 +1,96 @@
+/*
+ * NUC980 SOC System emulation.
+ *
+ * Copyright (c) 2023- yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#ifndef NUC980_SYS__H
+#define NUC980_SYS__H
+
+#include "hw/sysbus.h"
+
+#define SYS_PDID 0x00
+#define SYS_PWRON 0x04
+#define SYS_LVRDCR 0x20
+#define SYS_MISCFCR 0x30
+#define SYS_MISCIER 0x40
+#define SYS_MISCISR 0x44
+#define SYS_WKUPSER0 0x50
+#define SYS_WKUPSER1 0x54
+#define SYS_WKUPSSR0 0x58
+#define SYS_WKUPSSR1 0x5c
+#define SYS_AHBIPRST 0x60
+#define SYS_APBIPRST0 0x64
+#define SYS_APBIPRST1 0x68
+#define SYS_RSTSTS 0x6c
+#define SYS_GPA_MFPL 0x70
+#define SYS_GPA_MFPH 0x74
+#define SYS_GPB_MFPL 0x78
+#define SYS_GPB_MFPH 0x7c
+#define SYS_GPC_MFPL 0x80
+#define SYS_GPC_MFPH 0x84
+#define SYS_GPD_MFPL 0x88
+#define SYS_GPD_MFPH 0x8c
+#define SYS_GPE_MFPL 0x90
+#define SYS_GPE_MFPH 0x94
+#define SYS_GPF_MFPL 0x98
+#define SYS_GPF_MFPH 0x9c
+#define SYS_GPG_MFPL 0xa0
+#define SYS_GPG_MFPH 0xa4
+#define SYS_DDR_DSCTL 0xf0
+#define SYS_GPBL_DSCTL 0xf4
+#define SYS_PORDISCR 0x100
+#define SYS_RSTDEBCTL 0x10c
+#define SYS_REGWPCTL 0x1fc
+
+#define TYPE_NUC980_SYSTEM "nuc980-system"
+#define NUC980_SYSTEM(obj) \
+ OBJECT_CHECK(NUC980SystemState, (obj), TYPE_NUC980_SYSTEM)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+
+ uint32_t sys_pdid;
+ uint32_t sys_pwron;
+ uint32_t sys_lvrdcr;
+ uint32_t sys_miscfcr;
+ uint32_t sys_miscier;
+ uint32_t sys_miscisr;
+ uint32_t sys_wkupser0;
+ uint32_t sys_wkupser1;
+ uint32_t sys_wkupssr0;
+ uint32_t sys_wkupssr1;
+ uint32_t sys_ahbiprst;
+ uint32_t sys_apbiprst0;
+ uint32_t sys_apbiprst1;
+ uint32_t sys_rststr;
+ uint32_t sys_gpa_mfpl;
+ uint32_t sys_gpa_mfph;
+ uint32_t sys_gpb_mfpl;
+ uint32_t sys_gpb_mfph;
+ uint32_t sys_gpc_mfpl;
+ uint32_t sys_gpc_mfph;
+ uint32_t sys_gpd_mfpl;
+ uint32_t sys_gpd_mfph;
+ uint32_t sys_gpe_mfpl;
+ uint32_t sys_gpe_mfph;
+ uint32_t sys_gpf_mfpl;
+ uint32_t sys_gpf_mfph;
+ uint32_t sys_gpg_mfpl;
+ uint32_t sys_gpg_mfph;
+ uint32_t sys_ddr_dsctl;
+ uint32_t sys_gpbl_dsctl;
+ uint32_t sys_pordiscr;
+ uint32_t sys_rstdebctl;
+ uint32_t sys_regwpctl;
+ uint32_t disable_write_protect;
+
+} NUC980SystemState;
+
+#endif
2、在hw/misc/目录下创建nuc980_sys.c,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/nuc980_sys.c b/hw/misc/nuc980_sys.c
new file mode 100644
index 00000000..2450e35a
--- /dev/null
+++ b/hw/misc/nuc980_sys.c
@@ -0,0 +1,349 @@
+/*
+ * 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_sys.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 void nuc980_system_reset(DeviceState *dev)
+{
+ NUC980SystemState *s = NUC980_SYSTEM(dev);
+ s->sys_pdid = 0x1030d016;
+ s->sys_pwron = 0x00000000;
+ s->sys_lvrdcr = 0x00000001;
+ s->sys_miscfcr = 0x00000200;
+ s->sys_miscier = 0x00000000;
+ s->sys_miscisr = 0x00010000;
+ s->sys_wkupser0 = 0x00000000;
+ s->sys_wkupser1 = 0x00000000;
+ s->sys_wkupssr0 = 0x00000000;
+ s->sys_wkupssr1 = 0x00000000;
+ s->sys_ahbiprst = 0x00000000;
+ s->sys_apbiprst0 = 0x00000000;
+ s->sys_apbiprst1 = 0x00000000;
+ s->sys_rststr = 0x00000000;
+ s->sys_gpa_mfpl = 0x00000000;
+ s->sys_gpa_mfph = 0x00000000;
+ s->sys_gpb_mfpl = 0x00000000;
+ s->sys_gpb_mfph = 0x00000000;
+ s->sys_gpc_mfpl = 0x00000000;
+ s->sys_gpc_mfph = 0x00000000;
+ s->sys_gpd_mfpl = 0x00000000;
+ s->sys_gpd_mfph = 0x00000000;
+ s->sys_gpe_mfpl = 0x00000000;
+ s->sys_gpe_mfph = 0x00000000;
+ s->sys_gpf_mfpl = 0x00000000;
+ s->sys_gpf_mfph = 0x00000000;
+ s->sys_gpg_mfpl = 0x00000000;
+ s->sys_gpg_mfph = 0x00000000;
+ s->sys_ddr_dsctl = 0x00000000;
+ s->sys_gpbl_dsctl = 0x44444444;
+ s->sys_pordiscr = 0x00000000;
+ s->sys_rstdebctl = 0x000004b0;
+ s->sys_regwpctl = 0x00000000;
+ s->disable_write_protect = 0;
+}
+
+static uint64_t nuc980_system_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ NUC980SystemState *s = opaque;
+ uint64_t retvalue = 0;
+
+ DB_PRINT("Address: 0x%" HWADDR_PRIx "\n", addr);
+ switch(addr) {
+ case SYS_PDID:
+ retvalue = s->sys_pdid;
+ break;
+ case SYS_LVRDCR:
+ retvalue = s->sys_lvrdcr;
+ break;
+ case SYS_MISCFCR:
+ retvalue = s->sys_miscfcr;
+ break;
+ case SYS_MISCIER:
+ retvalue = s->sys_miscier;
+ break;
+ case SYS_MISCISR:
+ retvalue = s->sys_miscisr;
+ break;
+ case SYS_WKUPSER0:
+ retvalue = s->sys_wkupser0;
+ break;
+ case SYS_WKUPSER1:
+ retvalue = s->sys_wkupser1;
+ break;
+ case SYS_WKUPSSR0:
+ retvalue = s->sys_wkupssr0;
+ break;
+ case SYS_WKUPSSR1:
+ retvalue = s->sys_wkupssr1;
+ break;
+ case SYS_AHBIPRST:
+ retvalue = s->sys_ahbiprst;
+ break;
+ case SYS_APBIPRST0:
+ retvalue = s->sys_apbiprst0;
+ break;
+ case SYS_APBIPRST1:
+ retvalue = s->sys_apbiprst1;
+ break;
+ case SYS_RSTSTS:
+ retvalue = s->sys_rststr;
+ break;
+ case SYS_GPA_MFPL:
+ retvalue = s->sys_gpa_mfpl;
+ break;
+ case SYS_GPA_MFPH:
+ retvalue = s->sys_gpa_mfph;
+ break;
+ case SYS_GPB_MFPL:
+ retvalue = s->sys_gpb_mfpl;
+ break;
+ case SYS_GPB_MFPH:
+ retvalue = s->sys_gpb_mfph;
+ break;
+ case SYS_GPC_MFPL:
+ retvalue = s->sys_gpc_mfpl;
+ break;
+ case SYS_GPC_MFPH:
+ retvalue = s->sys_gpc_mfph;
+ break;
+ case SYS_GPD_MFPL:
+ retvalue = s->sys_gpd_mfpl;
+ break;
+ case SYS_GPD_MFPH:
+ retvalue = s->sys_gpd_mfph;
+ break;
+ case SYS_GPE_MFPL:
+ retvalue = s->sys_gpe_mfpl;
+ break;
+ case SYS_GPE_MFPH:
+ retvalue = s->sys_gpe_mfph;
+ break;
+ case SYS_GPF_MFPL:
+ retvalue = s->sys_gpf_mfpl;
+ break;
+ case SYS_GPF_MFPH:
+ retvalue = s->sys_gpf_mfph;
+ break;
+ case SYS_GPG_MFPL:
+ retvalue = s->sys_gpg_mfpl;
+ break;
+ case SYS_GPG_MFPH:
+ retvalue = s->sys_gpg_mfph;
+ break;
+ case SYS_DDR_DSCTL:
+ retvalue = s->sys_ddr_dsctl;
+ break;
+ case SYS_GPBL_DSCTL:
+ retvalue = s->sys_gpbl_dsctl;
+ break;
+ case SYS_PORDISCR:
+ retvalue = s->sys_pordiscr;
+ break;
+ case SYS_RSTDEBCTL:
+ retvalue = s->sys_rstdebctl;
+ break;
+ case SYS_REGWPCTL:
+ retvalue = s->sys_regwpctl;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ retvalue = 0;
+ break;
+ }
+ return retvalue;
+}
+
+static void nuc980_system_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ NUC980SystemState *s = opaque;
+ uint32_t value = val64;
+
+ DB_PRINT("Address: 0x%" HWADDR_PRIx ", Value: 0x%x\n", addr, value);
+ switch(addr) {
+ case SYS_PWRON:
+ s->sys_pwron = value;
+ break;
+ case SYS_LVRDCR:
+ s->sys_lvrdcr = value;
+ break;
+ case SYS_MISCFCR:
+ s->sys_miscfcr = value;
+ break;
+ case SYS_MISCIER:
+ s->sys_miscier = value;
+ break;
+ case SYS_MISCISR:
+ s->sys_miscisr = value;
+ break;
+ case SYS_WKUPSER0:
+ s->sys_wkupser0 = value;
+ break;
+ case SYS_WKUPSER1:
+ s->sys_wkupser1 = value;
+ break;
+ case SYS_WKUPSSR0:
+ s->sys_wkupssr0 = value;
+ break;
+ case SYS_WKUPSSR1:
+ s->sys_wkupssr1 = value;
+ break;
+ case SYS_AHBIPRST:
+ s->sys_ahbiprst = value;
+ break;
+ case SYS_APBIPRST0:
+ s->sys_apbiprst0 = value;
+ break;
+ case SYS_APBIPRST1:
+ s->sys_apbiprst1 = value;
+ break;
+ case SYS_RSTSTS:
+ s->sys_rststr = value;
+ break;
+ case SYS_GPA_MFPL:
+ s->sys_gpa_mfpl = value;
+ break;
+ case SYS_GPA_MFPH:
+ s->sys_gpa_mfph = value;
+ break;
+ case SYS_GPB_MFPL:
+ s->sys_gpb_mfpl = value;
+ break;
+ case SYS_GPB_MFPH:
+ s->sys_gpb_mfph = value;
+ break;
+ case SYS_GPC_MFPL:
+ s->sys_gpc_mfpl = value;
+ break;
+ case SYS_GPC_MFPH:
+ s->sys_gpc_mfph = value;
+ break;
+ case SYS_GPD_MFPL:
+ s->sys_gpd_mfpl = value;
+ break;
+ case SYS_GPD_MFPH:
+ s->sys_gpd_mfph = value;
+ break;
+ case SYS_GPE_MFPL:
+ s->sys_gpe_mfpl = value;
+ break;
+ case SYS_GPE_MFPH:
+ s->sys_gpe_mfph = value;
+ break;
+ case SYS_GPF_MFPL:
+ s->sys_gpf_mfpl = value;
+ break;
+ case SYS_GPF_MFPH:
+ s->sys_gpf_mfph = value;
+ break;
+ case SYS_GPG_MFPL:
+ s->sys_gpg_mfpl = value;
+ break;
+ case SYS_GPG_MFPH:
+ s->sys_gpg_mfph = value;
+ break;
+ case SYS_DDR_DSCTL:
+ s->sys_ddr_dsctl = value;
+ break;
+ case SYS_GPBL_DSCTL:
+ s->sys_gpbl_dsctl = value;
+ break;
+ case SYS_PORDISCR:
+ s->sys_pordiscr = value;
+ break;
+ case SYS_RSTDEBCTL:
+ s->sys_rstdebctl = value;
+ break;
+ case SYS_REGWPCTL:
+ s->sys_regwpctl = value;
+ if ((value == 0x59) || (value == 0x16) || (value == 0x88))
+ s->disable_write_protect = 1;
+ else
+ s->disable_write_protect = 0;
+
+ if (s->disable_write_protect == 1)
+ s->sys_regwpctl = 1;
+ else
+ s->sys_regwpctl = value;
+
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps nuc980_system_ops = {
+ .read = nuc980_system_read,
+ .write = nuc980_system_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_nuc980_system = {
+ .name = TYPE_NUC980_SYSTEM,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sys_pdid, NUC980SystemState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void nuc980_system_init(Object *obj)
+{
+ NUC980SystemState *s = NUC980_SYSTEM(obj);
+
+ memory_region_init_io(&s->mmio, obj, &nuc980_system_ops, s,
+ TYPE_NUC980_SYSTEM, 0x1ff);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void nuc980_system_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = nuc980_system_reset;
+ dc->vmsd = &vmstate_nuc980_system;
+}
+
+static const TypeInfo nuc980_system_info = {
+ .name = TYPE_NUC980_SYSTEM,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NUC980SystemState),
+ .instance_init = nuc980_system_init,
+ .class_init = nuc980_system_class_init,
+};
+
+static void nuc980_system_register_types(void)
+{
+ type_register_static(&nuc980_system_info);
+}
+
+type_init(nuc980_system_register_types)
3、修改hw/misc/目录下的Kconfig,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cd32904b..de6dc940 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -125,6 +125,9 @@ config MAC_VIA
select MOS6522
select ADB
+config NUC980_SYS
+ bool
+
config NUC980_CLK
bool
4、修改hw/misc/目录下的Makefile.objs,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 792be5db..c3c352d5 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -83,5 +83,6 @@ obj-$(CONFIG_MAC_VIA) += mac_via.o
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
+obj-$(CONFIG_NUC980_SYS) += nuc980_sys.o
obj-$(CONFIG_NUC980_CLK) += nuc980_clk.o
5、修改include/hw/arm/目录下的nuc980.h,如下所示:
+号部分为新增加内容
diff --git a/include/hw/arm/nuc980.h b/include/hw/arm/nuc980.h
index 5c573682..353adb72 100644
--- a/include/hw/arm/nuc980.h
+++ b/include/hw/arm/nuc980.h
@@ -14,10 +14,13 @@
#include "target/arm/cpu.h"
#include "hw/misc/nuc980_clk.h"
+#include "hw/misc/nuc980_sys.h"
#define SDRAM_BASE 0x0000000
#define SDRAM_SIZE (64 *1024 * 1024)
+#define SYSTEM_MANAGE_BASE 0xb0000000
+
#define CLK_CTL_BASE 0xb0000200
#define SDRAM_SIZE_32M (0x5 << 0)
@@ -35,6 +38,7 @@ typedef struct NUC980State {
ARMCPU cpu;
NUC980ClockState clk;
+ NUC980SystemState sys;
} NUC980State;
6、修改hw/arm/Kconfig/目录下的Kconfig,如下所示:
+号部分为新增加内容
config NUC980
bool
select NUC980_CLK
+ select NUC980_SYS
7、修改hw/arm目录下的nuc980_soc.c,如下所示:
+号部分为新增加内容
diff --git a/hw/arm/nuc980_soc.c b/hw/arm/nuc980_soc.c
index cec0c97d..0223d550 100644
--- a/hw/arm/nuc980_soc.c
+++ b/hw/arm/nuc980_soc.c
@@ -29,6 +29,8 @@ static void nuc980_init(Object *obj)
/* clk */
sysbus_init_child_obj(obj, "clk", &s->clk, sizeof(s->clk), TYPE_NUC980_CLK);
+ /* system manage */
+ sysbus_init_child_obj(obj, "sys", &s->sys, sizeof(s->sys), TYPE_NUC980_SYSTEM);
}
static void nuc980_realize(DeviceState *dev, Error **errp)
@@ -50,6 +52,8 @@ static void nuc980_realize(DeviceState *dev, Error **errp)
}
/* clock control */
sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, CLK_CTL_BASE);
+ /* system manage */
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sys), 0, SYSTEM_MANAGE_BASE);
}
总结
本文描述了NUC980芯片中的system Manager控制器寄存器读写模拟框架。