Qemu-NUC980(四):SDRAM Interface Controller
概述
本文描述了980 CPU下SDRAM 接口的qemu框架代码。为了便于阅读,我删除了部分代码,完整的代码请查看文后的工程链接。
添加步骤
1、在include/hw/misc/目录下创建nuc980_sdic.h,如下所示:
+号部分为新增加内容
diff --git a/include/hw/misc/nuc980_sdic.h b/include/hw/misc/nuc980_sdic.h
new file mode 100644
index 00000000..39564fe6
--- /dev/null
+++ b/include/hw/misc/nuc980_sdic.h
@@ -0,0 +1,60 @@
+/*
+ * NUC980 SDRAM Interface Controller emulation.
+ *
+ * Copyright (c) 2025 ~ by yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#ifndef NUC980_SDIC__H
+#define NUC980_SDIC__H
+
+#include "hw/sysbus.h"
+
+#define SDIC_OPMCTL 0x00
+#define SDIC_CMD 0x04
+#define SDIC_REFCTL 0x08
+#define SDIC_SIZE0 0x10
+#define SDIC_SIZE1 0x14
+#define SDIC_MR 0x18
+#define SDIC_EMR 0x1c
+#define SDIC_EMR2 0x20
+#define SDIC_EMR3 0x24
+#define SDIC_TIME 0x28
+#define SDIC_DQSODS 0x30
+#define SDIC_CKDQSDS 0x34
+#define SDIC_DAENSEL 0x38
+
+#define SDRAM_BASE 0x0000000
+#define SDRAM_SIZE (64 *1024 * 1024)
+
+#define SDRAM_SIZE_32M (0x5 << 0)
+
+#define TYPE_NUC980_SDIC "nuc980-sdic"
+#define NUC980_SDIC(obj) \
+ OBJECT_CHECK(NUC980SDICState, (obj), TYPE_NUC980_SDIC)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+
+ uint32_t sdic_opmctl;
+ uint32_t sdic_cmd;
+ uint32_t sdic_refctl;
+ uint32_t sdic_size0;
+ uint32_t sdic_size1;
+ uint32_t sdic_mr;
+ uint32_t sdic_emr;
+ uint32_t sdic_emr2;
+ uint32_t sdic_emr3;
+ uint32_t sdic_time;
+ uint32_t sdic_dqsods;
+ uint32_t sdic_ckdqsds;
+ uint32_t sdic_daensel;
+
+} NUC980SDICState;
+
+#endif
2、在hw/misc/目录下创建nuc980_sdic.c,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/nuc980_sdic.c b/hw/misc/nuc980_sdic.c
new file mode 100644
index 00000000..488965fe
--- /dev/null
+++ b/hw/misc/nuc980_sdic.c
@@ -0,0 +1,219 @@
+/*
+ * NUC980 SDRAM Interface Controller emulation.
+ *
+ * Copyright (c) 2025 ~ by 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_sdic.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+
+#ifndef ADC_ERR_DEBUG
+#define ADC_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+ if (ADC_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_sdic_reset(DeviceState *dev)
+{......
+}
+
+static uint64_t nuc980_sdic_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{......
+}
+
+static void nuc980_sdic_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{......
+}
+
+static const MemoryRegionOps nuc980_sdic_ops = {
+ .read = nuc980_sdic_read,
+ .write = nuc980_sdic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_nuc980_sdic = {
+ .name = TYPE_NUC980_SDIC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(sdic_opmctl, NUC980SDICState),
+ VMSTATE_UINT32(sdic_cmd, NUC980SDICState),
+ VMSTATE_UINT32(sdic_refctl, NUC980SDICState),
+ VMSTATE_UINT32(sdic_size0, NUC980SDICState),
+ VMSTATE_UINT32(sdic_size1, NUC980SDICState),
+ VMSTATE_UINT32(sdic_mr, NUC980SDICState),
+ VMSTATE_UINT32(sdic_emr, NUC980SDICState),
+ VMSTATE_UINT32(sdic_emr2, NUC980SDICState),
+ VMSTATE_UINT32(sdic_emr3, NUC980SDICState),
+ VMSTATE_UINT32(sdic_time, NUC980SDICState),
+ VMSTATE_UINT32(sdic_dqsods, NUC980SDICState),
+ VMSTATE_UINT32(sdic_ckdqsds, NUC980SDICState),
+ VMSTATE_UINT32(sdic_daensel, NUC980SDICState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void nuc980_sdic_realize(DeviceState *dev, Error **errp)
+{
+ NUC980SDICState *s = NUC980_SDIC(dev);
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &nuc980_sdic_ops, s,
+ TYPE_NUC980_SDIC, 0x1000);
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void nuc980_sdic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = nuc980_sdic_realize;
+ dc->reset = nuc980_sdic_reset;
+ dc->vmsd = &vmstate_nuc980_sdic;
+}
+
+static const TypeInfo nuc980_sdic_info = {
+ .name = TYPE_NUC980_SDIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(NUC980SDICState),
+ .class_init = nuc980_sdic_class_init,
+};
+
+static void nuc980_sdic_register_types(void)
+{
+ type_register_static(&nuc980_sdic_info);
+}
+
+type_init(nuc980_sdic_register_types)
3、修改hw/misc/目录下创建Kconfig,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index de6dc940..cc261841 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -131,4 +131,7 @@ config NUC980_SYSconfig NUC980_CLKbool+config NUC980_SDIC
+ bool
+ source macio/Kconfig
4、修改hw/misc目录下创建Makefile.objs,如下所示:
+号部分为新增加内容
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c3c352d5..4c6fbfab 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -85,4 +85,5 @@ common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.oobj-$(CONFIG_NUC980_SYS) += nuc980_sys.oobj-$(CONFIG_NUC980_CLK) += nuc980_clk.o
+obj-$(CONFIG_NUC980_SDIC) += nuc980_sdic.o
5、修改hw/arm/目录下创建Kconfig,如下所示:
+号部分为新增加内容
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index db86365b..3d24f9f0 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -117,6 +117,7 @@ config NUC980boolselect NUC980_CLKselect NUC980_SYS
+ select NUC980_SDICconfig NUC980_EVBbool
6、修改hw/arm/目录下创建nuc980_soc.c,如下所示:
+号部分为新增加内容
diff --git a/hw/arm/nuc980_soc.c b/hw/arm/nuc980_soc.c
index 0223d550..1b0a05f2 100644
--- a/hw/arm/nuc980_soc.c
+++ b/hw/arm/nuc980_soc.c
@@ -31,6 +31,9 @@ static void nuc980_init(Object *obj)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);
+ /* sdic*/
+ sysbus_init_child_obj(obj, "sdic", &s->sdic, sizeof(s->sdic), TYPE_NUC980_SDIC);
+}static void nuc980_realize(DeviceState *dev, Error **errp)
@@ -55,6 +58,13 @@ static void nuc980_realize(DeviceState *dev, Error **errp)/* system manage */sysbus_mmio_map(SYS_BUS_DEVICE(&s->sys), 0, SYSTEM_MANAGE_BASE);+ /* sdic */
+ object_property_set_bool(OBJECT(&s->sdic), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdic), 0, SDIC_BASE);}static void nuc980_class_init(ObjectClass *oc, void *data)
7、修改/include/hw/arm/目录下创建nuc980.h,如下所示:
+号部分为新增加内容
diff --git a/include/hw/arm/nuc980.h b/include/hw/arm/nuc980.h
index 353adb72..f24d5a82 100644
--- a/include/hw/arm/nuc980.h
+++ b/include/hw/arm/nuc980.h
@@ -15,6 +15,7 @@#include "hw/misc/nuc980_clk.h"#include "hw/misc/nuc980_sys.h"
+#include "hw/misc/nuc980_sdic.h"#define SDRAM_BASE 0x0000000#define SDRAM_SIZE (64 *1024 * 1024)
@@ -25,6 +26,8 @@#define SDRAM_SIZE_32M (0x5 << 0)+#define SDIC_BASE 0xb0002000
+#define TYPE_NUC980 "nuc980"#define NUC980(obj) OBJECT_CHECK(NUC980State, (obj), TYPE_NUC980)@@ -39,6 +42,7 @@ typedef struct NUC980State {NUC980ClockState clk;NUC980SystemState sys;
+ NUC980SDICState sdic;} NUC980State;
总结
本文描述了添加980 CPU中SDRAM Interface Controller的qemu框架代码的添加步骤,在描述过程中,删除了部分代码。
工程链接
https://gitee.com/yanl1229/qemu.git