* [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
@ 2012-09-10 0:19 ` crwulff
2012-09-11 20:19 ` Blue Swirl
` (2 more replies)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB crwulff
` (8 subsequent siblings)
9 siblings, 3 replies; 31+ messages in thread
From: crwulff @ 2012-09-10 0:19 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
target-nios2/Makefile.objs | 5 +
target-nios2/altera_iic.c | 100 +++
target-nios2/cpu-qom.h | 69 +++
target-nios2/cpu.c | 83 +++
target-nios2/cpu.h | 259 ++++++++
target-nios2/exec.h | 60 ++
target-nios2/helper.c | 291 +++++++++
target-nios2/helper.h | 45 ++
target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
target-nios2/instruction.h | 290 +++++++++
target-nios2/machine.c | 33 +
target-nios2/mmu.c | 273 +++++++++
target-nios2/mmu.h | 49 ++
target-nios2/op_helper.c | 125 ++++
target-nios2/translate.c | 252 ++++++++
15 files changed, 3397 insertions(+)
create mode 100644 target-nios2/Makefile.objs
create mode 100644 target-nios2/altera_iic.c
create mode 100644 target-nios2/cpu-qom.h
create mode 100644 target-nios2/cpu.c
create mode 100644 target-nios2/cpu.h
create mode 100644 target-nios2/exec.h
create mode 100644 target-nios2/helper.c
create mode 100644 target-nios2/helper.h
create mode 100644 target-nios2/instruction.c
create mode 100644 target-nios2/instruction.h
create mode 100644 target-nios2/machine.c
create mode 100644 target-nios2/mmu.c
create mode 100644 target-nios2/mmu.h
create mode 100644 target-nios2/op_helper.c
create mode 100644 target-nios2/translate.c
diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
new file mode 100644
index 0000000..d072795
--- /dev/null
+++ b/target-nios2/Makefile.objs
@@ -0,0 +1,5 @@
+obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
+obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
+obj-y += altera_iic.o
+
+$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
new file mode 100644
index 0000000..d45d6fa
--- /dev/null
+++ b/target-nios2/altera_iic.c
@@ -0,0 +1,100 @@
+/*
+ * QEMU Altera Internal Interrupt Controller.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "hw/sysbus.h"
+#include "dyngen-exec.h"
+#include "cpu.h"
+
+struct altera_iic {
+ SysBusDevice busdev;
+ Nios2CPU *cpu;
+ qemu_irq parent_irq;
+};
+
+static void update_irq(struct altera_iic *pv)
+{
+ uint32_t i;
+
+ if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
+ qemu_irq_lower(pv->parent_irq);
+ return;
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (pv->cpu->env.regs[CR_IPENDING] &
+ pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
+ break;
+ }
+ }
+ if (i == 32) {
+ qemu_irq_lower(pv->parent_irq);
+ } else {
+ qemu_irq_raise(pv->parent_irq);
+ }
+}
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ struct altera_iic *pv = opaque;
+
+ pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
+ pv->cpu->env.regs[CR_IPENDING] |= level << irq;
+
+ update_irq(pv);
+}
+
+static int altera_iic_init(SysBusDevice *dev)
+{
+ struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
+ pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
+
+ qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+ sysbus_init_irq(dev, &pv->parent_irq);
+
+ return 0;
+}
+
+static Property altera_iic_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_iic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = altera_iic_init;
+ dc->props = altera_iic_properties;
+}
+
+static TypeInfo altera_iic_info = {
+ .name = "altera,iic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct altera_iic),
+ .class_init = altera_iic_class_init,
+};
+
+static void altera_iic_register(void)
+{
+ type_register_static(&altera_iic_info);
+}
+
+type_init(altera_iic_register)
+
diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
new file mode 100644
index 0000000..be6fa65
--- /dev/null
+++ b/target-nios2/cpu-qom.h
@@ -0,0 +1,69 @@
+/*
+ * QEMU Nios II CPU
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+#ifndef QEMU_NIOS2_CPU_QOM_H
+#define QEMU_NIOS2_CPU_QOM_H
+
+#include "qemu/cpu.h"
+
+#define TYPE_NIOS2_CPU "nios2-cpu"
+
+#define NIOS2_CPU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
+#define NIOS2_CPU(obj) \
+ OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
+#define NIOS2_CPU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
+
+/**
+ * Nios2CPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A Nios2 CPU model.
+ */
+typedef struct Nios2CPUClass {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+
+ void (*parent_reset)(CPUState *cpu);
+} Nios2CPUClass;
+
+/**
+ * Nios2CPU:
+ * @env: #CPUNios2State
+ *
+ * A Nios2 CPU.
+ */
+typedef struct Nios2CPU {
+ /*< private >*/
+ CPUState parent_obj;
+ /*< public >*/
+
+ CPUNios2State env;
+} Nios2CPU;
+
+static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
+{
+ return NIOS2_CPU(container_of(env, Nios2CPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
+
+#endif /* QEMU_NIOS2_CPU_QOM_H */
diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
new file mode 100644
index 0000000..bd819c4
--- /dev/null
+++ b/target-nios2/cpu.c
@@ -0,0 +1,83 @@
+/*
+ * QEMU Nios II CPU
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "cpu.h"
+#include "qemu-common.h"
+
+
+/* CPUClass::reset() */
+static void nios2_cpu_reset(CPUState *s)
+{
+ Nios2CPU *cpu = NIOS2_CPU(s);
+ Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
+ CPUNios2State *env = &cpu->env;
+
+ if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+ qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+ log_cpu_state(env, 0);
+ }
+
+ mcc->parent_reset(s);
+
+ tlb_flush(env, 1);
+
+ memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
+ env->regs[R_PC] = env->reset_addr;
+
+#if defined(CONFIG_USER_ONLY)
+ /* start in user mode with interrupts enabled. */
+ env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
+#else
+ mmu_init(&env->mmu);
+#endif
+}
+
+static void nios2_cpu_initfn(Object *obj)
+{
+ Nios2CPU *cpu = NIOS2_CPU(obj);
+ CPUNios2State *env = &cpu->env;
+
+ cpu_exec_init(env);
+}
+
+static void nios2_cpu_class_init(ObjectClass *oc, void *data)
+{
+ CPUClass *cc = CPU_CLASS(oc);
+ Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
+
+ mcc->parent_reset = cc->reset;
+ cc->reset = nios2_cpu_reset;
+}
+
+static const TypeInfo nios2_cpu_type_info = {
+ .name = TYPE_NIOS2_CPU,
+ .parent = TYPE_CPU,
+ .instance_size = sizeof(Nios2CPU),
+ .instance_init = nios2_cpu_initfn,
+ .class_size = sizeof(Nios2CPUClass),
+ .class_init = nios2_cpu_class_init,
+};
+
+static void nios2_cpu_register_types(void)
+{
+ type_register_static(&nios2_cpu_type_info);
+}
+
+type_init(nios2_cpu_register_types)
diff --git a/target-nios2/cpu.h b/target-nios2/cpu.h
new file mode 100644
index 0000000..0ee22e6
--- /dev/null
+++ b/target-nios2/cpu.h
@@ -0,0 +1,259 @@
+/*
+ * Altera Nios II virtual CPU header
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+#ifndef CPU_NIOS2_H
+#define CPU_NIOS2_H
+
+#include "config.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+
+#define CPUArchState struct CPUNios2State
+
+#include "cpu-defs.h"
+#include "softfloat.h"
+struct CPUNios2State;
+typedef struct CPUNios2State CPUNios2State;
+#if !defined(CONFIG_USER_ONLY)
+#include "mmu.h"
+#endif
+
+#define TARGET_HAS_ICE 1
+
+/* Configuration options for Nios II */
+#define RESET_ADDRESS 0x00000000
+#define EXCEPTION_ADDRESS 0x00000004
+#define FAST_TLB_MISS_ADDRESS 0x00000008
+
+
+#define ELF_MACHINE EM_ALTERA_NIOS2
+
+/* GP regs + CR regs + PC */
+#define NUM_CORE_REGS (32 + 32 + 1)
+
+/* General purpose egister aliases */
+#define R_ZERO 0
+#define R_AT 1
+#define R_RET0 2
+#define R_RET1 3
+#define R_ARG0 4
+#define R_ARG1 5
+#define R_ARG2 6
+#define R_ARG3 7
+#define R_ET 24
+#define R_BT 25
+#define R_GP 26
+#define R_SP 27
+#define R_FP 28
+#define R_EA 29
+#define R_BA 30
+#define R_RA 31
+
+/* Control register aliases */
+#define CR_BASE 32
+#define CR_STATUS (CR_BASE + 0)
+#define CR_STATUS_PIE (1<<0)
+#define CR_STATUS_U (1<<1)
+#define CR_STATUS_EH (1<<2)
+#define CR_STATUS_IH (1<<3)
+#define CR_STATUS_IL (63<<4)
+#define CR_STATUS_CRS (63<<10)
+#define CR_STATUS_PRS (63<<16)
+#define CR_STATUS_NMI (1<<22)
+#define CR_STATUS_RSIE (1<<23)
+#define CR_ESTATUS (CR_BASE + 1)
+#define CR_BSTATUS (CR_BASE + 2)
+#define CR_IENABLE (CR_BASE + 3)
+#define CR_IPENDING (CR_BASE + 4)
+#define CR_CPUID (CR_BASE + 5)
+#define CR_EXCEPTION (CR_BASE + 7)
+#define CR_PTEADDR (CR_BASE + 8)
+#define CR_PTEADDR_PTBASE_SHIFT 22
+#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
+#define CR_PTEADDR_VPN_SHIFT 2
+#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
+#define CR_TLBACC (CR_BASE + 9)
+#define CR_TLBACC_IGN_SHIFT 25
+#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
+#define CR_TLBACC_C (1<<24)
+#define CR_TLBACC_R (1<<23)
+#define CR_TLBACC_W (1<<22)
+#define CR_TLBACC_X (1<<21)
+#define CR_TLBACC_G (1<<20)
+#define CR_TLBACC_PFN_MASK 0x000FFFFF
+#define CR_TLBMISC (CR_BASE + 10)
+#define CR_TLBMISC_WAY_SHIFT 20
+#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
+#define CR_TLBMISC_RD (1<<19)
+#define CR_TLBMISC_WR (1<<18)
+#define CR_TLBMISC_PID_SHIFT 4
+#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
+#define CR_TLBMISC_DBL (1<<3)
+#define CR_TLBMISC_BAD (1<<2)
+#define CR_TLBMISC_PERM (1<<1)
+#define CR_TLBMISC_D (1<<0)
+#define CR_BADADDR (CR_BASE + 12)
+#define CR_CONFIG (CR_BASE + 13)
+#define CR_MPUBASE (CR_BASE + 14)
+#define CR_MPUACC (CR_BASE + 15)
+
+/* Other registers */
+#define R_PC 64
+
+/* Exceptions */
+#define EXCP_BREAK -1
+#define EXCP_RESET 0
+#define EXCP_PRESET 1
+#define EXCP_IRQ 2
+#define EXCP_TRAP 3
+#define EXCP_UNIMPL 4
+#define EXCP_ILLEGAL 5
+#define EXCP_UNALIGN 6
+#define EXCP_UNALIGND 7
+#define EXCP_DIV 8
+#define EXCP_SUPERA 9
+#define EXCP_SUPERI 10
+#define EXCP_SUPERD 11
+#define EXCP_TLBD 12
+#define EXCP_TLBX 13
+#define EXCP_TLBR 14
+#define EXCP_TLBW 15
+#define EXCP_MPUI 16
+#define EXCP_MPUD 17
+
+#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
+
+#define NB_MMU_MODES 2
+
+typedef struct CPUNios2State {
+ uint32_t regs[NUM_CORE_REGS];
+
+ /* Addresses that are hard-coded in the FPGA build settings */
+ uint32_t reset_addr;
+ uint32_t exception_addr;
+ uint32_t fast_tlb_miss_addr;
+
+#if !defined(CONFIG_USER_ONLY)
+ struct nios2_mmu mmu;
+#endif
+
+ CPU_COMMON
+} CPUNios2State;
+
+#include "cpu-qom.h"
+
+extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
+extern int cpu_nios2_exec(CPUNios2State *s);
+extern void cpu_nios2_close(CPUNios2State *s);
+extern void do_interrupt(CPUNios2State *env);
+extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
+extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+extern Nios2CPU *g_cpu;
+
+static inline CPUNios2State *cpu_init(const char *cpu_model)
+{
+ Nios2CPU *cpu = cpu_nios2_init(cpu_model);
+ if (cpu == NULL) {
+ return NULL;
+ }
+ return &cpu->env;
+}
+
+#define cpu_exec cpu_nios2_exec
+#define cpu_gen_code cpu_nios2_gen_code
+#define cpu_signal_handler cpu_nios2_signal_handler
+
+#define CPU_SAVE_VERSION 1
+
+#define TARGET_PAGE_BITS 12
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_SUPERVISOR_IDX 0
+#define MMU_USER_IDX 1
+
+static inline int cpu_mmu_index(CPUNios2State *env)
+{
+ return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+ MMU_SUPERVISOR_IDX;
+}
+
+int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+ int rw, int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
+{
+ if (newsp) {
+ env->regs[R_SP] = newsp;
+ }
+ env->regs[R_RET0] = 0;
+}
+#endif
+
+static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
+{
+}
+
+static inline int cpu_interrupts_enabled(CPUNios2State *env)
+{
+ return env->regs[CR_STATUS] & CR_STATUS_PIE;
+}
+
+#include "cpu-all.h"
+
+static inline target_ulong cpu_get_pc(CPUNios2State *env)
+{
+ return env->regs[R_PC];
+}
+
+static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
+ target_ulong *cs_base, int *flags)
+{
+ *pc = env->regs[R_PC];
+ *cs_base = 0;
+ *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size);
+#endif
+
+static inline bool cpu_has_work(CPUNios2State *env)
+{
+ return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+#include "exec-all.h"
+
+static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
+{
+ env->regs[R_PC] = tb->pc;
+}
+
+#endif /* CPU_NIOS2_H */
+
diff --git a/target-nios2/exec.h b/target-nios2/exec.h
new file mode 100644
index 0000000..cd4446b
--- /dev/null
+++ b/target-nios2/exec.h
@@ -0,0 +1,60 @@
+/*
+ * Altera Nios II execution defines
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef NIOS2_EXEC_H
+#define NIOS2_EXEC_H
+
+#include "dyngen-exec.h"
+
+register struct CPUNios2State *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+static inline int cpu_has_work(CPUState *env)
+{
+ return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+ if (!env->halted) {
+ return 0;
+ }
+
+ if (cpu_has_work(env)) {
+ env->halted = 0;
+ return 0;
+ }
+
+ return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+ env->regs[R_PC] = tb->pc;
+}
+
+#endif /* NIOS2_EXEC_H */
+
diff --git a/target-nios2/helper.c b/target-nios2/helper.c
new file mode 100644
index 0000000..9b200c9
--- /dev/null
+++ b/target-nios2/helper.c
@@ -0,0 +1,291 @@
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "host-utils.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt(CPUNios2State *env)
+{
+ env->exception_index = -1;
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+}
+
+int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+ int rw, int mmu_idx, int is_softmmu)
+{
+ env->exception_index = 0xaa;
+ cpu_dump_state(env, stderr, fprintf, 0);
+ return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+bool has_mmu = true;
+
+void do_interrupt(CPUNios2State *env)
+{
+ switch (env->exception_index) {
+ case EXCP_IRQ:
+ assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
+
+ qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
+
+ env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+ env->regs[CR_STATUS] |= CR_STATUS_IH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+ env->regs[R_PC] = env->exception_addr;
+ break;
+
+ case EXCP_TLBD:
+ if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+ qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
+ env->regs[R_PC]);
+ log_cpu_state(env, 0);
+
+ /* Fast TLB miss */
+ /* Variation from the spec. Table 3-35 of the cpu reference shows
+ * estatus not being changed for TLB miss but this appears to
+ * be incorrect. */
+ env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
+ env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+ env->regs[R_PC] = env->fast_tlb_miss_addr;
+ } else {
+ qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
+ env->regs[R_PC]);
+
+ /* Double TLB miss */
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
+
+ env->regs[R_PC] = env->exception_addr;
+ }
+ break;
+
+ case EXCP_TLBR:
+ case EXCP_TLBW:
+ case EXCP_TLBX:
+ qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
+ log_cpu_state(env, 0);
+
+ env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+ env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+ }
+
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+ env->regs[R_PC] = env->exception_addr;
+ break;
+
+ case EXCP_SUPERA:
+ case EXCP_SUPERI:
+ case EXCP_SUPERD:
+ qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
+ env->regs[R_PC]);
+
+ if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+ env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+ }
+
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[R_PC] = env->exception_addr;
+ break;
+
+ case EXCP_ILLEGAL:
+ case EXCP_TRAP:
+ qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
+ env->regs[R_PC]);
+
+ if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+ env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+ env->regs[R_EA] = env->regs[R_PC] + 4;
+ }
+
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[R_PC] = env->exception_addr;
+ break;
+
+ case EXCP_BREAK:
+ if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+ env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
+ env->regs[R_BA] = env->regs[R_PC] + 4;
+ }
+
+ env->regs[CR_STATUS] |= CR_STATUS_EH;
+ env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+ env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+ env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
+
+ env->regs[R_PC] = env->exception_addr;
+ break;
+
+ default:
+ cpu_abort(env, "unhandled exception type=%d\n",
+ env->exception_index);
+ break;
+ }
+}
+
+static int cpu_nios2_handle_virtual_page(
+ CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
+{
+ target_ulong vaddr, paddr;
+ struct nios2_mmu_lookup lu;
+ unsigned int hit;
+ hit = mmu_translate(env, &lu, address, rw, mmu_idx);
+ if (hit) {
+ vaddr = address & TARGET_PAGE_MASK;
+ paddr = lu.paddr + vaddr - lu.vaddr;
+
+ if (((rw == 0) && (lu.prot & PAGE_READ)) ||
+ ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
+ ((rw == 2) && (lu.prot & PAGE_EXEC))) {
+
+ tlb_set_page(env, vaddr, paddr, lu.prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+ return 0;
+ } else {
+ /* Permission violation */
+ env->exception_index = (rw == 0) ? EXCP_TLBR :
+ ((rw == 1) ? EXCP_TLBW :
+ EXCP_TLBX);
+ }
+ } else {
+ env->exception_index = EXCP_TLBD;
+ }
+
+ if (rw == 2) {
+ env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
+ } else {
+ env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
+ }
+ env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
+ env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
+ env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
+ env->regs[CR_BADADDR] = address;
+ return 1;
+}
+
+int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+ int rw, int mmu_idx, int is_softmmu)
+{
+ if (has_mmu) {
+ if (MMU_SUPERVISOR_IDX == mmu_idx) {
+ if (address >= 0xC0000000) {
+ /* Kernel physical page - TLB bypassed */
+ address &= TARGET_PAGE_MASK;
+ tlb_set_page(env, address, address, PAGE_BITS,
+ mmu_idx, TARGET_PAGE_SIZE);
+ } else if (address >= 0x80000000) {
+ /* Kernel virtual page */
+ return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+ } else {
+ /* User virtual page */
+ return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+ }
+ } else {
+ if (address >= 0x80000000) {
+ /* Illegal access from user mode */
+ env->exception_index = EXCP_SUPERA;
+ env->regs[CR_BADADDR] = address;
+ return 1;
+ } else {
+ /* User virtual page */
+ return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+ }
+ }
+ } else {
+ /* No MMU */
+ address &= TARGET_PAGE_MASK;
+ tlb_set_page(env, address, address, PAGE_BITS,
+ mmu_idx, TARGET_PAGE_SIZE);
+ }
+
+ return 0;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
+ target_ulong addr)
+{
+ target_ulong vaddr, paddr = 0;
+ struct nios2_mmu_lookup lu;
+ unsigned int hit;
+
+ if (has_mmu && (addr < 0xC0000000)) {
+ hit = mmu_translate(env, &lu, addr, 0, 0);
+ if (hit) {
+ vaddr = addr & TARGET_PAGE_MASK;
+ paddr = lu.paddr + vaddr - lu.vaddr;
+ } else {
+ cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);
+ }
+ } else {
+ paddr = addr & TARGET_PAGE_MASK;
+ }
+
+ return paddr;
+}
+
+#endif /* !CONFIG_USER_ONLY */
+
diff --git a/target-nios2/helper.h b/target-nios2/helper.h
new file mode 100644
index 0000000..69b6684
--- /dev/null
+++ b/target-nios2/helper.h
@@ -0,0 +1,45 @@
+/*
+ * Altera Nios II helper routines header.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "def-helper.h"
+
+/* Define this to enable tracing calls/returns */
+/* #define CALL_TRACING */
+
+#ifdef CALL_TRACING
+DEF_HELPER_2(call_status, void, i32, i32)
+DEF_HELPER_1(eret_status, void, i32)
+DEF_HELPER_1(ret_status, void, i32)
+#endif
+
+DEF_HELPER_1(raise_exception, void, i32)
+
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(mmu_read, i32, i32)
+DEF_HELPER_2(mmu_write, void, i32, i32)
+#endif
+
+DEF_HELPER_2(divs, i32, i32, i32)
+DEF_HELPER_2(divu, i32, i32, i32)
+
+DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
+
+#include "def-helper.h"
+
diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
new file mode 100644
index 0000000..4dc06cd
--- /dev/null
+++ b/target-nios2/instruction.c
@@ -0,0 +1,1463 @@
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * (Portions of this file that were originally from nios2sim-ng.)
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <stdio.h>
+
+#include "instruction.h"
+#include "exec-all.h"
+#include "helper.h"
+
+#define GEN_HELPER 1
+#include "helper.h"
+
+static inline uint32_t get_opcode(uint32_t code)
+{
+ I_TYPE(instr, code);
+ return instr->op;
+}
+
+static inline uint32_t get_opxcode(uint32_t code)
+{
+ R_TYPE(instr, code);
+ return instr->opx6;
+}
+
+static inline void t_gen_helper_raise_exception(DisasContext *dc,
+ uint32_t index)
+{
+ TCGv_i32 tmp = tcg_const_i32(index);
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
+ gen_helper_raise_exception(tmp);
+ tcg_temp_free_i32(tmp);
+ dc->is_jmp = DISAS_UPDATE;
+}
+
+/*
+ * Instructions not implemented by the simulator.
+ */
+static void unimplemented(DisasContext *dc, uint32_t code)
+{
+ t_gen_helper_raise_exception(dc, EXCP_UNIMPL);
+}
+
+/*
+ * Illegal instruction
+ */
+static void illegal_instruction(DisasContext *dc,
+ uint32_t code __attribute__((unused)))
+{
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+static void gen_check_supervisor(DisasContext *dc, int label)
+{
+ int l1 = gen_new_label();
+
+ TCGv_i32 tmp = tcg_temp_new();
+ tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
+ tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
+ t_gen_helper_raise_exception(dc, EXCP_SUPERI);
+ tcg_gen_br(label);
+
+ gen_set_label(l1);
+ tcg_temp_free_i32(tmp);
+
+ /* If we aren't taking the exception, update the PC to the
+ * next instruction */
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4);
+}
+
+static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ switch (size) {
+ case 1:
+ tcg_gen_qemu_ld8u(dst, addr, mem_index);
+ break;
+ case 2:
+ tcg_gen_qemu_ld16u(dst, addr, mem_index);
+ break;
+ case 4:
+ tcg_gen_qemu_ld32u(dst, addr, mem_index);
+ break;
+ default:
+ cpu_abort(dc->env, "Incorrect load size %d\n", size);
+ break;
+ }
+}
+
+static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ switch (size) {
+ case 1:
+ tcg_gen_qemu_ld8s(dst, addr, mem_index);
+ break;
+ case 2:
+ tcg_gen_qemu_ld16s(dst, addr, mem_index);
+ break;
+ case 4:
+ tcg_gen_qemu_ld32s(dst, addr, mem_index);
+ break;
+ default:
+ cpu_abort(dc->env, "Incorrect load size %d\n", size);
+ break;
+ }
+}
+
+static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
+ unsigned int size)
+{
+ int mem_index = cpu_mmu_index(dc->env);
+
+ switch (size) {
+ case 1:
+ tcg_gen_qemu_st8(val, addr, mem_index);
+ break;
+ case 2:
+ tcg_gen_qemu_st16(val, addr, mem_index);
+ break;
+ case 4:
+ tcg_gen_qemu_st32(val, addr, mem_index);
+ break;
+ default:
+ cpu_abort(dc->env, "Incorrect load size %d\n", size);
+ break;
+ }
+}
+
+/*
+ * Used as a placeholder for all instructions which do not have an effect on the
+ * simulator (e.g. flush, sync)
+ */
+static void nop(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute__((unused)))
+{
+ /* Nothing to do here */
+}
+
+/*
+ * J-Type instructions
+ */
+
+/*
+ * ra <- PC + 4
+ * PC <- (PC(31..28) : IMM26 * 4)
+ */
+static void call(DisasContext *dc, uint32_t code)
+{
+ J_TYPE(instr, code);
+
+#ifdef CALL_TRACING
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4));
+ gen_helper_call_status(tmp, tmp2);
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(tmp2);
+#endif
+
+ tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- (PC(31..28) : IMM26 * 4) */
+static void jmpi(DisasContext *dc, uint32_t code)
+{
+ J_TYPE(instr, code);
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/*
+ * I-Type instructions
+ */
+
+/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
+static void ldbu(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/* rB <- rA + IMM16 */
+static void addi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv imm = tcg_temp_new();
+ tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
+
+ tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
+
+ tcg_temp_free(imm);
+}
+
+/* Mem8[rA + @(IMM16)] <- rB(7..0) */
+static void stb(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/* PC <- PC + 4 + IMM16 */
+static void br(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem8[rA + @(IMM16)]) */
+static void ldb(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA >= (signed) @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmpgei(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_GE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- 0x0000 : Mem16[rA + @IMM16)] */
+static void ldhu(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/* rB <- rA & IMM16 */
+static void andi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/* Mem16[rA + @(IMM16)] <- rB(15..0) */
+static void sth(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA >= (signed) rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void bge(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- Mem16[rA + @IMM16)] */
+static void ldh(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA < (signed) @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmplti(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_LT, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* Initializes the data cache line currently caching address rA + @(IMM16) */
+static void initda(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute__((unused)))
+{
+ /* TODO */
+}
+
+/* rB <- rA | IMM16 */
+static void ori(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/* Mem32[rA + @(IMM16)] <- rB */
+static void stw(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 4);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA < (signed) rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void blt(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem32[rA + @(IMM16)]) */
+static void ldw(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA != (signed) @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmpnei(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_NE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- rA ^ IMM16 */
+static void xori(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/*
+ * if (rA != rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void bne(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/*
+ * if ((signed) rA == (signed) @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmpeqi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_EQ, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
+static void ldbuio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/* rB <- (rA * @(IMM16))(31..0) */
+static void muli(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv imm = tcg_temp_new();
+ tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
+ tcg_temp_free(imm);
+}
+
+/* Mem8[rA + @(IMM16)] <- rB(7..0) (bypassing cache) */
+static void stbio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if (rA == rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void beq(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem8[rA + @(IMM16)]) (bypassing cache) */
+static void ldbio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if (rA >= 0x0000 : @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmpgeui(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_GEU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- 0x0000 : Mem16[rA + @IMM16)] (bypassing cache) */
+static void ldhuio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/* rB <- rA & (IMM16 : 0x0000) */
+static void andhi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ instr->imm16 << 16);
+}
+
+/* Mem16[rA + @(IMM16)] <- rB(15..0) (bypassing cache) */
+static void sthio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if (rA >= rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void bgeu(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- Mem16[rA + @IMM16)] (bypassing cache) */
+static void ldhio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if (rA < 0x0000 : @(IMM16))
+ * rB <- 1
+ * else
+ * rB <- 0
+ */
+static void cmpltui(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_setcondi_tl(TCG_COND_LTU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ (int32_t)((int16_t)instr->imm16));
+}
+
+/* */
+static void initd(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute((unused)))
+{
+ /* TODO */
+}
+
+/* rB <- rA | (IMM16 : 0x0000) */
+static void orhi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ instr->imm16 << 16);
+}
+
+/* Mem32[rA + @(IMM16)] <- rB (bypassing cache) */
+static void stwio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_store(dc, dc->cpu_R[instr->b], addr, 4);
+
+ tcg_temp_free(addr);
+}
+
+/*
+ * if ((unsigned) rA < (unsigned) rB)
+ * PC <- PC + 4 + @(IMM16)
+ * else
+ * PC <- PC + 4
+ */
+static void bltu(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+
+ tcg_gen_movi_tl(dc->cpu_R[R_PC],
+ dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+ tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b], l1);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+ gen_set_label(l1);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem32[rA + @(IMM16)]) (bypassing cache) */
+static void ldwio(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ TCGv addr = tcg_temp_new();
+ tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+ tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+ gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
+
+ tcg_temp_free(addr);
+}
+
+/* Prototype only, defined below */
+static void handle_r_type_instr(DisasContext *dc, uint32_t code);
+
+/* rB <- rA ^ (IMM16 : 0x0000) */
+static void xorhi(DisasContext *dc, uint32_t code)
+{
+ I_TYPE(instr, code);
+
+ tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+ instr->imm16 << 16);
+}
+
+static struct instruction i_type_instructions[I_TYPE_COUNT] = {
+ [CALL] = INSTRUCTION(call),
+ [JMPI] = INSTRUCTION(jmpi),
+ [0x02] = INSTRUCTION_ILLEGAL(),
+ [LDBU] = INSTRUCTION(ldbu),
+ [ADDI] = INSTRUCTION(addi),
+ [STB] = INSTRUCTION(stb),
+ [BR] = INSTRUCTION(br),
+ [LDB] = INSTRUCTION(ldb),
+ [CMPGEI] = INSTRUCTION(cmpgei),
+ [0x09] = INSTRUCTION_ILLEGAL(),
+ [0x0a] = INSTRUCTION_ILLEGAL(),
+ [LDHU] = INSTRUCTION(ldhu),
+ [ANDI] = INSTRUCTION(andi),
+ [STH] = INSTRUCTION(sth),
+ [BGE] = INSTRUCTION(bge),
+ [LDH] = INSTRUCTION(ldh),
+ [CMPLTI] = INSTRUCTION(cmplti),
+ [0x11] = INSTRUCTION_ILLEGAL(),
+ [0x12] = INSTRUCTION_ILLEGAL(),
+ [INITDA] = INSTRUCTION(initda),
+ [ORI] = INSTRUCTION(ori),
+ [STW] = INSTRUCTION(stw),
+ [BLT] = INSTRUCTION(blt),
+ [LDW] = INSTRUCTION(ldw),
+ [CMPNEI] = INSTRUCTION(cmpnei),
+ [0x19] = INSTRUCTION_ILLEGAL(),
+ [0x1a] = INSTRUCTION_ILLEGAL(),
+ [FLUSHDA] = INSTRUCTION_NOP(flushda),
+ [XORI] = INSTRUCTION(xori),
+ [0x1d] = INSTRUCTION_ILLEGAL(),
+ [BNE] = INSTRUCTION(bne),
+ [0x1f] = INSTRUCTION_ILLEGAL(),
+ [CMPEQI] = INSTRUCTION(cmpeqi),
+ [0x21] = INSTRUCTION_ILLEGAL(),
+ [0x22] = INSTRUCTION_ILLEGAL(),
+ [LDBUIO] = INSTRUCTION(ldbuio),
+ [MULI] = INSTRUCTION(muli),
+ [STBIO] = INSTRUCTION(stbio),
+ [BEQ] = INSTRUCTION(beq),
+ [LDBIO] = INSTRUCTION(ldbio),
+ [CMPGEUI] = INSTRUCTION(cmpgeui),
+ [0x29] = INSTRUCTION_ILLEGAL(),
+ [0x2a] = INSTRUCTION_ILLEGAL(),
+ [LDHUIO] = INSTRUCTION(ldhuio),
+ [ANDHI] = INSTRUCTION(andhi),
+ [STHIO] = INSTRUCTION(sthio),
+ [BGEU] = INSTRUCTION(bgeu),
+ [LDHIO] = INSTRUCTION(ldhio),
+ [CMPLTUI] = INSTRUCTION(cmpltui),
+ [0x31] = INSTRUCTION_ILLEGAL(),
+ [CUSTOM] = INSTRUCTION_UNIMPLEMENTED(custom),
+ [INITD] = INSTRUCTION(initd),
+ [ORHI] = INSTRUCTION(orhi),
+ [STWIO] = INSTRUCTION(stwio),
+ [BLTU] = INSTRUCTION(bltu),
+ [LDWIO] = INSTRUCTION(ldwio),
+ [RDPRS] = INSTRUCTION_UNIMPLEMENTED(rdprs),
+ [0x39] = INSTRUCTION_ILLEGAL(),
+ [R_TYPE] = { "<R-type instruction>", handle_r_type_instr },
+ [FLUSHD] = INSTRUCTION_NOP(flushd),
+ [XORHI] = INSTRUCTION(xorhi),
+ [0x3d] = INSTRUCTION_ILLEGAL(),
+ [0x3e] = INSTRUCTION_ILLEGAL(),
+ [0x3f] = INSTRUCTION_ILLEGAL(),
+};
+
+/*
+ * R-Type instructions
+ */
+
+/*
+ * status <- estatus
+ * PC <- ea
+ */
+static void eret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+#ifdef CALL_TRACING
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ gen_helper_eret_status(tmp);
+ tcg_temp_free_i32(tmp);
+#endif
+
+ tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA rotated left IMM5 bit positions */
+static void roli(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+
+ tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
+ tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+ tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
+
+ tcg_temp_free(t0);
+}
+
+/* rC <- rA rotated left rB(4..0) bit positions */
+static void rol(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+ tcg_gen_movi_tl(t1, 32);
+ tcg_gen_sub_tl(t1, t1, t0);
+ tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
+ tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+ tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* */
+static void flushp(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute__((unused)))
+{
+ /* TODO */
+}
+
+/* PC <- ra */
+static void ret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+#ifdef CALL_TRACING
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ gen_helper_ret_status(tmp);
+ tcg_temp_free_i32(tmp);
+#endif
+
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- ~(A | rB) */
+static void nor(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_nor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
+static void mulxuu(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
+ tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA >= rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmpge(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_GE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* PC <- ba */
+static void bret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA rotated right rb(4..0) bit positions */
+static void ror(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+ TCGv t1 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+ tcg_gen_movi_tl(t1, 32);
+ tcg_gen_sub_tl(t1, t1, t0);
+ tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
+ tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+ tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
+
+ tcg_temp_free(t0);
+ tcg_temp_free(t1);
+}
+
+/* */
+static void flushi(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute__((unused)))
+{
+ /* TODO */
+}
+
+/* PC <- rA */
+static void jmp(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA & rB */
+static void and(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_and_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/*
+ * if ((signed) rA < (signed) rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmplt(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_LT, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA << IMM5 */
+static void slli(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- rA << rB(4..0) */
+static void sll(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+ tcg_gen_shl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+ tcg_temp_free(t0);
+}
+
+/* rC <- rA | rB */
+static void or(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
+static void mulxsu(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
+ tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA != rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmpne(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_NE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- (unsigned) rA >> ((unsigned) IMM5)*/
+static void srli(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- (unsigned) rA >> ((unsigned) rB(4..0))*/
+static void srl(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+ tcg_gen_shr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+ tcg_temp_free(t0);
+}
+
+/* rC <- PC + 4 */
+static void nextpc(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_movi_tl(dc->cpu_R[instr->c], dc->pc + 4);
+}
+
+/*
+ * ra <- PC + 4
+ * PC <- rA
+ */
+static void callr(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+#ifdef CALL_TRACING
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
+ tcg_temp_free_i32(tmp);
+#endif
+
+ tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA ^ rB */
+static void xor(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_xor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((signed)rA * (signed)rB))(31..0) */
+static void mulxss(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv_i64 t0, t1;
+
+ t0 = tcg_temp_new_i64();
+ t1 = tcg_temp_new_i64();
+
+ tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
+ tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
+ tcg_gen_mul_i64(t0, t0, t1);
+
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
+
+ tcg_temp_free_i64(t0);
+ tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA == rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmpeq(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_EQ, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA / rB */
+static void divu(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA / rB */
+static void _div(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- ctlN */
+static void rdctl(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+ gen_check_supervisor(dc, l1);
+
+ switch (instr->imm5 + 32) {
+ case CR_PTEADDR:
+ case CR_TLBACC:
+ case CR_TLBMISC:
+ {
+ TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+ gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
+ tcg_temp_free_i32(tmp);
+ break;
+ }
+
+ default:
+ tcg_gen_mov_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->imm5 + 32]);
+ break;
+ }
+
+ gen_set_label(l1);
+}
+
+/* rC <- (rA * rB))(31..0) */
+static void mul(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_mul_i32(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/*
+ * if (rA >= rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmpgeu(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_GEU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* */
+static void initi(DisasContext *dc __attribute__((unused)),
+ uint32_t code __attribute__((unused)))
+{
+ /* TODO */
+}
+
+/*
+ * estatus <- status
+ * PIE <- 0
+ * U <- 0
+ * ea <- PC + 4
+ * PC <- exception handler address
+ */
+static void trap(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+ t_gen_helper_raise_exception(dc, EXCP_TRAP);
+}
+
+/* ctlN <- rA */
+static void wrctl(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ int l1 = gen_new_label();
+ gen_check_supervisor(dc, l1);
+
+ switch (instr->imm5 + 32) {
+ case CR_PTEADDR:
+ case CR_TLBACC:
+ case CR_TLBMISC:
+ {
+ TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+ gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
+ tcg_temp_free_i32(tmp);
+ break;
+ }
+
+ default:
+ tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
+ break;
+ }
+
+ gen_set_label(l1);
+}
+
+/*
+ * if (rA < rB)
+ * rC <- 1
+ * else
+ * rC <- 0
+ */
+static void cmpltu(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_setcond_tl(TCG_COND_LTU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA + rB */
+static void add(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_add_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/*
+ * bstatus ← status
+ * PIE <- 0
+ * U <- 0
+ * ba <- PC + 4
+ * PC <- break handler address
+ */
+static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+ t_gen_helper_raise_exception(dc, EXCP_BREAK);
+}
+
+/* rC <- rA - rB */
+static void sub(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_sub_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+ dc->cpu_R[instr->b]);
+}
+
+/* rC <- (signed) rA >> ((unsigned) IMM5) */
+static void srai(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_sari_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- (signed) rA >> ((unsigned) rB(4..0)) */
+static void sra(DisasContext *dc, uint32_t code)
+{
+ R_TYPE(instr, code);
+
+ TCGv t0 = tcg_temp_new();
+
+ tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+ tcg_gen_sar_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+ tcg_temp_free(t0);
+}
+
+static struct instruction r_type_instructions[R_TYPE_COUNT] = {
+ [0x00] = INSTRUCTION_ILLEGAL(),
+ [ERET] = INSTRUCTION(eret),
+ [ROLI] = INSTRUCTION(roli),
+ [ROL] = INSTRUCTION(rol),
+ [FLUSHP] = INSTRUCTION(flushp),
+ [RET] = INSTRUCTION(ret),
+ [NOR] = INSTRUCTION(nor),
+ [MULXUU] = INSTRUCTION(mulxuu),
+ [CMPGE] = INSTRUCTION(cmpge),
+ [BRET] = INSTRUCTION(bret),
+ [0x0a] = INSTRUCTION_ILLEGAL(),
+ [ROR] = INSTRUCTION(ror),
+ [FLUSHI] = INSTRUCTION(flushi),
+ [JMP] = INSTRUCTION(jmp),
+ [AND] = INSTRUCTION(and),
+ [0x0f] = INSTRUCTION_ILLEGAL(),
+ [CMPLT] = INSTRUCTION(cmplt),
+ [0x11] = INSTRUCTION_ILLEGAL(),
+ [SLLI] = INSTRUCTION(slli),
+ [SLL] = INSTRUCTION(sll),
+ [WRPRS] = INSTRUCTION_UNIMPLEMENTED(wrprs),
+ [0x15] = INSTRUCTION_ILLEGAL(),
+ [OR] = INSTRUCTION(or),
+ [MULXSU] = INSTRUCTION(mulxsu),
+ [CMPNE] = INSTRUCTION(cmpne),
+ [0x19] = INSTRUCTION_ILLEGAL(),
+ [SRLI] = INSTRUCTION(srli),
+ [SRL] = INSTRUCTION(srl),
+ [NEXTPC] = INSTRUCTION(nextpc),
+ [CALLR] = INSTRUCTION(callr),
+ [XOR] = INSTRUCTION(xor),
+ [MULXSS] = INSTRUCTION(mulxss),
+ [CMPEQ] = INSTRUCTION(cmpeq),
+ [0x21] = INSTRUCTION_ILLEGAL(),
+ [0x22] = INSTRUCTION_ILLEGAL(),
+ [0x23] = INSTRUCTION_ILLEGAL(),
+ [DIVU] = INSTRUCTION(divu),
+ [DIV] = { "div", _div },
+ [RDCTL] = INSTRUCTION(rdctl),
+ [MUL] = INSTRUCTION(mul),
+ [CMPGEU] = INSTRUCTION(cmpgeu),
+ [INITI] = INSTRUCTION(initi),
+ [0x2a] = INSTRUCTION_ILLEGAL(),
+ [0x2b] = INSTRUCTION_ILLEGAL(),
+ [0x2c] = INSTRUCTION_ILLEGAL(),
+ [TRAP] = INSTRUCTION(trap),
+ [WRCTL] = INSTRUCTION(wrctl),
+ [0x2f] = INSTRUCTION_ILLEGAL(),
+ [CMPLTU] = INSTRUCTION(cmpltu),
+ [ADD] = INSTRUCTION(add),
+ [0x32] = INSTRUCTION_ILLEGAL(),
+ [0x33] = INSTRUCTION_ILLEGAL(),
+ [BREAK] = { "break", __break },
+ [0x35] = INSTRUCTION_ILLEGAL(),
+ [SYNC] = INSTRUCTION(nop),
+ [0x37] = INSTRUCTION_ILLEGAL(),
+ [0x38] = INSTRUCTION_ILLEGAL(),
+ [SUB] = INSTRUCTION(sub),
+ [SRAI] = INSTRUCTION(srai),
+ [SRA] = INSTRUCTION(sra),
+ [0x3c] = INSTRUCTION_ILLEGAL(),
+ [0x3d] = INSTRUCTION_ILLEGAL(),
+ [0x3e] = INSTRUCTION_ILLEGAL(),
+ [0x3f] = INSTRUCTION_ILLEGAL(),
+};
+
+static void handle_r_type_instr(DisasContext *dc, uint32_t code)
+{
+ uint32_t opx;
+ instruction_handler handle_instr;
+
+ opx = get_opxcode(code);
+ if (unlikely(opx >= R_TYPE_COUNT)) {
+ goto illegal_op;
+ }
+
+ LOG_DIS("R: %s (%08x)\n", r_type_instructions[opx].name, code);
+ handle_instr = r_type_instructions[opx].handler;
+
+ handle_instr(dc, code);
+
+ return;
+
+illegal_op:
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+void handle_instruction(DisasContext *dc)
+{
+ uint32_t insn = ldl_code(dc->pc);
+ uint32_t op = get_opcode(insn);
+
+ LOG_DIS("%8.8x\t", insn);
+
+ if (unlikely(op >= I_TYPE_COUNT)) {
+ goto illegal_op;
+ }
+
+ if (op != R_TYPE) {
+ LOG_DIS("I: %s (%08x)\n", i_type_instructions[op].name, insn);
+ }
+ i_type_instructions[op].handler(dc, insn);
+
+ return;
+
+illegal_op:
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+const char *instruction_get_string(uint32_t code)
+{
+ uint32_t op = get_opcode(code);
+
+ if (unlikely(op >= I_TYPE_COUNT)) {
+ return "";
+ } else if (op == R_TYPE) {
+ uint32_t opx = get_opxcode(code);
+ if (unlikely(opx >= R_TYPE_COUNT)) {
+ return "";
+ }
+ return r_type_instructions[opx].name;
+ } else {
+ return i_type_instructions[op].name;
+ }
+}
+
diff --git a/target-nios2/instruction.h b/target-nios2/instruction.h
new file mode 100644
index 0000000..fe29933
--- /dev/null
+++ b/target-nios2/instruction.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2010 chysun2000@gmail.com
+ * (Portions of this file that were originally from nios2sim-ng.)
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#ifndef _INSTRUCTION_H_
+#define _INSTRUCTION_H_
+
+#include <stdint.h>
+#include "cpu.h"
+#include "tcg-op.h"
+
+/*
+ * Instruction Word Formats
+ */
+
+/* I-Type instruction */
+struct i_type {
+ uint32_t op:6;
+ uint32_t imm16:16;
+ uint32_t b:5;
+ uint32_t a:5;
+} __attribute__((packed));
+
+union i_type_u {
+ uint32_t v;
+ struct i_type i;
+};
+
+#define I_TYPE(instr, op) \
+ union i_type_u instr_u = { .v = op }; \
+ struct i_type *instr = &instr_u.i
+
+/* R-Type instruction */
+struct r_type {
+ uint32_t op:6;
+ /*
+ * Some R-Type instructions embed a small immediate value in the
+ * low-order bits of OPX.
+ */
+ uint32_t imm5:5;
+ uint32_t opx6:6;
+ uint32_t c:5;
+ uint32_t b:5;
+ uint32_t a:5;
+} __attribute__((packed));
+
+union r_type_u {
+ uint32_t v;
+ struct r_type i;
+};
+
+#define R_TYPE(instr, op) \
+ union r_type_u instr_u = { .v = op }; \
+ struct r_type *instr = &instr_u.i
+
+/* J-Type instruction */
+struct j_type {
+ uint32_t op:6;
+ uint32_t imm26:26;
+} __attribute__((packed));
+
+#define J_TYPE(instr, op) \
+ struct j_type *instr = (struct j_type *) &op
+
+/*
+ * Instruction Opcodes
+ */
+
+/*
+ * OP Encodings for I-Type instructions (except for CALL and JMPI, which are
+ * J-type instructions)
+ */
+enum {
+ CALL = 0x00, /* J-type */
+ JMPI = 0x01, /* J-type */
+ /* 0x02 */
+ LDBU = 0x03,
+ ADDI = 0x04,
+ STB = 0x05,
+ BR = 0x06,
+ LDB = 0x07,
+ CMPGEI = 0x08,
+ /* 0x09 */
+ /* 0x0A */
+ LDHU = 0x0B,
+ ANDI = 0x0C,
+ STH = 0x0D,
+ BGE = 0x0E,
+ LDH = 0x0F,
+ CMPLTI = 0x10,
+ /* 0x11 */
+ /* 0x12 */
+ INITDA = 0x13,
+ ORI = 0x14,
+ STW = 0x15,
+ BLT = 0x16,
+ LDW = 0x17,
+ CMPNEI = 0x18,
+ /* 0x19 */
+ /* 0x1A */
+ FLUSHDA = 0x1B,
+ XORI = 0x1C,
+ /* 0x1D */
+ BNE = 0x1E,
+ /* 0x1F */
+ CMPEQI = 0x20,
+ /* 0x21 */
+ /* 0x22 */
+ LDBUIO = 0x23,
+ MULI = 0x24,
+ STBIO = 0x25,
+ BEQ = 0x26,
+ LDBIO = 0x27,
+ CMPGEUI = 0x28,
+ /* 0x29 */
+ /* 0x2A */
+ LDHUIO = 0x2B,
+ ANDHI = 0x2C,
+ STHIO = 0x2D,
+ BGEU = 0x2E,
+ LDHIO = 0x2F,
+ CMPLTUI = 0x30,
+ /* 0x31 */
+ CUSTOM = 0x32,
+ INITD = 0x33,
+ ORHI = 0x34,
+ STWIO = 0x35,
+ BLTU = 0x36,
+ LDWIO = 0x37,
+ RDPRS = 0x38,
+ /* 0x39 */
+ R_TYPE = 0x3A,
+ FLUSHD = 0x3B,
+ XORHI = 0x3C,
+ /* 0x3D */
+ /* 0x3E */
+ /* 0x3F */
+};
+#define I_TYPE_COUNT 0x40
+
+/* OPX Encodings for R-Type instructions */
+enum {
+ /* 0x00 */
+ ERET = 0x01,
+ ROLI = 0x02,
+ ROL = 0x03,
+ FLUSHP = 0x04,
+ RET = 0x05,
+ NOR = 0x06,
+ MULXUU = 0x07,
+ CMPGE = 0x08,
+ BRET = 0x09,
+ /* 0x0A */
+ ROR = 0x0B,
+ FLUSHI = 0x0C,
+ JMP = 0x0D,
+ AND = 0x0E,
+ /* 0x0F */
+ CMPLT = 0x10,
+ /* 0x11 */
+ SLLI = 0x12,
+ SLL = 0x13,
+ WRPRS = 0x14,
+ /* 0x15 */
+ OR = 0x16,
+ MULXSU = 0x17,
+ CMPNE = 0x18,
+ /* 0x19 */
+ SRLI = 0x1A,
+ SRL = 0x1B,
+ NEXTPC = 0x1C,
+ CALLR = 0x1D,
+ XOR = 0x1E,
+ MULXSS = 0x1F,
+ CMPEQ = 0x20,
+ /* 0x21 */
+ /* 0x22 */
+ /* 0x23 */
+ DIVU = 0x24,
+ DIV = 0x25,
+ RDCTL = 0x26,
+ MUL = 0x27,
+ CMPGEU = 0x28,
+ INITI = 0x29,
+ /* 0x2A */
+ /* 0x2B */
+ /* 0x2C */
+ TRAP = 0x2D,
+ WRCTL = 0x2E,
+ /* 0x2F */
+ CMPLTU = 0x30,
+ ADD = 0x31,
+ /* 0x32 */
+ /* 0x33 */
+ BREAK = 0x34,
+ /* 0x35 */
+ SYNC = 0x36,
+ /* 0x37 */
+ /* 0x38 */
+ SUB = 0x39,
+ SRAI = 0x3A,
+ SRA = 0x3B,
+ /* 0x3C */
+ /* 0x3D */
+ /* 0x3E */
+ /* 0x3F */
+};
+#define R_TYPE_COUNT 0x40
+
+/*
+ * Return values for instruction handlers
+ */
+#define INSTR_UNIMPL -2 /* Unimplemented instruction */
+#define INSTR_ERR -1 /* Error in instruction */
+#define PC_INC_NORMAL 0 /* Normal PC increment after instruction */
+#define PC_INC_BY_INSTR 1 /* PC got incremented by instruction */
+#define INSTR_BREAK 2 /* Break encountered */
+#define INSTR_EXCEPTION 255 /* Instruction generated an exception
+ (the exception cause will be stored
+ in struct nios2 */
+
+#define EXCEPTION(cpu, cause) \
+ ({ \
+ (cpu)->exception_cause = cause; \
+ INSTR_EXCEPTION; \
+ })
+
+typedef struct DisasContext {
+ CPUNios2State *env;
+ TCGv *cpu_R;
+ int is_jmp;
+ target_ulong pc;
+ struct TranslationBlock *tb;
+} DisasContext;
+
+typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
+
+struct instruction {
+ const char *name;
+ instruction_handler handler;
+};
+
+/*
+ * Stringification macro (taken from Linux Kernel source code)
+ */
+
+/* Indirect stringification. Doing two levels allows the parameter to be a
+ * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x...) #x
+#define __stringify(x...) __stringify_1(x)
+
+#define INSTRUCTION(name) { __stringify(name), name }
+#define INSTRUCTION_NOP(name) { __stringify(name), nop }
+#define INSTRUCTION_UNIMPLEMENTED(name) { __stringify(name), unimplemented }
+#define INSTRUCTION_ILLEGAL() { "", illegal_instruction }
+
+extern void handle_instruction(DisasContext *dc);
+extern const char *instruction_get_string(uint32_t code);
+
+#define SIM_COMPAT 0
+#define DISAS_GNU 1 /* Disassembly via GNU gdb derived routines */
+#define DISAS_NIOS2 0 /* Disassembly via routines in instruction.c */
+#if DISAS_NIOS2 && !SIM_COMPAT
+# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+# define LOG_DIS(...) do { } while (0)
+#endif
+
+#endif /* _INSTRUCTION_H_ */
diff --git a/target-nios2/machine.c b/target-nios2/machine.c
new file mode 100644
index 0000000..09198db
--- /dev/null
+++ b/target-nios2/machine.c
@@ -0,0 +1,33 @@
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+ /* TODO */
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+ /* TODO */
+ return 0;
+}
diff --git a/target-nios2/mmu.c b/target-nios2/mmu.c
new file mode 100644
index 0000000..a52fa1b
--- /dev/null
+++ b/target-nios2/mmu.c
@@ -0,0 +1,273 @@
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+
+/* Define this to enable MMU debug messages */
+/* #define DEBUG_MMU */
+
+#ifdef DEBUG_MMU
+#define MMU_LOG(x) x
+#else
+#define MMU_LOG(x)
+#endif
+
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
+{
+ switch (rn) {
+ case CR_TLBACC:
+ MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
+ break;
+
+ case CR_TLBMISC:
+ MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
+ break;
+
+ case CR_PTEADDR:
+ MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
+ break;
+
+ default:
+ break;
+ }
+ return env->regs[rn];
+}
+
+/* rw - 0 = read, 1 = write, 2 = fetch. */
+unsigned int mmu_translate(CPUNios2State *env,
+ struct nios2_mmu_lookup *lu,
+ target_ulong vaddr, int rw, int mmu_idx)
+{
+ int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+ int vpn = vaddr >> 12;
+
+ MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
+ vaddr, pid, vpn));
+
+ int way;
+ for (way = 0; way < env->mmu.tlb_num_ways; way++) {
+
+ struct nios2_tlb_entry *entry =
+ &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask)];
+
+ MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
+ (way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask),
+ entry->tag, (entry->tag >> 12)));
+
+ if (((entry->tag >> 12) != vpn) ||
+ (((entry->tag & (1<<11)) == 0) &&
+ ((entry->tag & ((1<<env->mmu.pid_bits)-1)) != pid))) {
+ continue;
+ }
+ lu->vaddr = vaddr & TARGET_PAGE_MASK;
+ lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
+ lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
+ ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
+ ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
+
+ MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
+ (way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask),
+ lu->vaddr, lu->paddr, lu->prot));
+ return 1;
+ }
+ return 0;
+}
+
+static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
+{
+ int idx;
+ MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
+
+ for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
+ struct nios2_tlb_entry *entry = &env->mmu.tlb[idx];
+
+ MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
+ idx, entry->tag, entry->data));
+
+ if ((entry->tag & (1<<10)) && (!(entry->tag & (1<<11))) &&
+ ((entry->tag & ((1<<env->mmu.pid_bits)-1)) == pid)) {
+ uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
+
+ MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
+
+ tlb_flush_page(env, vaddr);
+ }
+ }
+}
+
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+ MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
+
+ switch (rn) {
+ case CR_TLBACC:
+ MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
+ v >> CR_TLBACC_IGN_SHIFT,
+ (v & CR_TLBACC_C) ? 'C' : '.',
+ (v & CR_TLBACC_R) ? 'R' : '.',
+ (v & CR_TLBACC_W) ? 'W' : '.',
+ (v & CR_TLBACC_X) ? 'X' : '.',
+ (v & CR_TLBACC_G) ? 'G' : '.',
+ v & CR_TLBACC_PFN_MASK));
+
+ /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
+ if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
+ int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
+ int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+ int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+ int g = (v & CR_TLBACC_G) ? 1 : 0;
+ int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
+ struct nios2_tlb_entry *entry =
+ &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask)];
+ uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
+ uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
+ CR_TLBACC_X | CR_TLBACC_PFN_MASK);
+
+ if ((entry->tag != newTag) || (entry->data != newData)) {
+ if (entry->tag & (1<<10)) {
+ /* Flush existing entry */
+ MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
+ entry->tag & TARGET_PAGE_MASK));
+ tlb_flush_page(env, entry->tag & TARGET_PAGE_MASK);
+ }
+ entry->tag = newTag;
+ entry->data = newData;
+ MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
+ (way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask),
+ entry->tag, entry->data));
+ }
+ /* Auto-increment tlbmisc.WAY */
+ env->regs[CR_TLBMISC] =
+ (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
+ (((way+1) & (env->mmu.tlb_num_ways-1)) << CR_TLBMISC_WAY_SHIFT);
+ }
+
+ /* Writes to TLBACC don't change the read-back value */
+ env->mmu.tlbacc_wr = v;
+ break;
+
+ case CR_TLBMISC:
+ MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
+ v >> CR_TLBMISC_WAY_SHIFT,
+ (v & CR_TLBMISC_RD) ? 'R' : '.',
+ (v & CR_TLBMISC_WR) ? 'W' : '.',
+ (v & CR_TLBMISC_DBL) ? '2' : '.',
+ (v & CR_TLBMISC_BAD) ? 'B' : '.',
+ (v & CR_TLBMISC_PERM) ? 'P' : '.',
+ (v & CR_TLBMISC_D) ? 'D' : '.',
+ (v & CR_TLBMISC_PID_MASK) >> 4));
+
+ if ((v & CR_TLBMISC_PID_MASK) !=
+ (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
+ mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
+ CR_TLBMISC_PID_SHIFT);
+ }
+ /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
+ if (v & CR_TLBMISC_RD) {
+ int way = (v >> CR_TLBMISC_WAY_SHIFT);
+ int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+ struct nios2_tlb_entry *entry =
+ &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+ (vpn & env->mmu.tlb_entry_mask)];
+
+ env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
+ env->regs[CR_TLBACC] |= entry->data |
+ ((entry->tag & (1<<11)) ? CR_TLBACC_G : 0);
+ env->regs[CR_TLBMISC] =
+ (v & ~CR_TLBMISC_PID_MASK) |
+ ((entry->tag & ((1<<env->mmu.pid_bits)-1)) <<
+ CR_TLBMISC_PID_SHIFT);
+ env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
+ env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
+ MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
+ "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
+ way, vpn, entry->tag, entry->data,
+ env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
+ env->regs[CR_PTEADDR]));
+ } else {
+ env->regs[CR_TLBMISC] = v;
+ }
+
+ env->mmu.tlbmisc_wr = v;
+ break;
+
+ case CR_PTEADDR:
+ MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
+ v >> CR_PTEADDR_PTBASE_SHIFT,
+ (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
+
+ /* Writes to PTEADDR don't change the read-back VPN value */
+ env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
+ (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
+ env->mmu.pteaddr_wr = v;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void mmu_init(struct nios2_mmu *mmu)
+{
+ MMU_LOG(qemu_log("mmu_init\n"));
+
+ mmu->pid_bits = 8; /* TODO: get this from ALTR,pid-num-bits */
+ mmu->tlb_num_ways = 16; /* TODO: get this from ALTR,tlb-num-ways */
+ mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */
+ mmu->tlb_entry_mask = (mmu->tlb_num_entries/mmu->tlb_num_ways) - 1;
+
+ mmu->tlb = (struct nios2_tlb_entry *)g_malloc0(
+ sizeof(struct nios2_tlb_entry) * mmu->tlb_num_entries);
+}
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env)
+{
+ int i;
+ cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n",
+ env->mmu.tlb_num_ways, env->mmu.tlb_num_entries,
+ env->mmu.pid_bits);
+
+ for (i = 0; i < env->mmu.tlb_num_entries; i++) {
+ struct nios2_tlb_entry *entry = &env->mmu.tlb[i];
+ cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X "
+ "PID %02X %c PFN %05X %c%c%c%c\n",
+ i, entry->tag, entry->data,
+ (entry->tag & (1<<10)) ? 'V' : '-',
+ entry->tag >> 12, entry->tag & ((1<<env->mmu.pid_bits)-1),
+ (entry->tag & (1<<11)) ? 'G' : '-',
+ entry->data & CR_TLBACC_PFN_MASK,
+ (entry->data & CR_TLBACC_C) ? 'C' : '-',
+ (entry->data & CR_TLBACC_R) ? 'R' : '-',
+ (entry->data & CR_TLBACC_W) ? 'W' : '-',
+ (entry->data & CR_TLBACC_X) ? 'X' : '-');
+ }
+}
+
diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
new file mode 100644
index 0000000..01d67cf
--- /dev/null
+++ b/target-nios2/mmu.h
@@ -0,0 +1,49 @@
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+struct nios2_tlb_entry {
+ target_ulong tag;
+ target_ulong data;
+};
+
+struct nios2_mmu {
+ int pid_bits;
+ int tlb_num_ways;
+ int tlb_num_entries;
+ int tlb_entry_mask;
+ uint32_t pteaddr_wr;
+ uint32_t tlbacc_wr;
+ uint32_t tlbmisc_wr;
+ struct nios2_tlb_entry *tlb;
+};
+
+struct nios2_mmu_lookup {
+ target_ulong vaddr;
+ target_ulong paddr;
+ int prot;
+};
+
+void mmu_flip_um(CPUNios2State *env, unsigned int um);
+unsigned int mmu_translate(CPUNios2State *env,
+ struct nios2_mmu_lookup *lu,
+ target_ulong vaddr, int rw, int mmu_idx);
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn);
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v);
+void mmu_init(struct nios2_mmu *mmu);
diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
new file mode 100644
index 0000000..37bac66
--- /dev/null
+++ b/target-nios2/op_helper.c
@@ -0,0 +1,125 @@
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "cpu.h"
+#include "dyngen-exec.h"
+#include "helper.h"
+#include "host-utils.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MMUSUFFIX _mmu
+#define SHIFT 0
+#include "softmmu_template.h"
+#define SHIFT 1
+#include "softmmu_template.h"
+#define SHIFT 2
+#include "softmmu_template.h"
+#define SHIFT 3
+#include "softmmu_template.h"
+
+void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
+ uintptr_t retaddr)
+{
+ TranslationBlock *tb;
+ CPUNios2State *saved_env;
+ int ret;
+
+ saved_env = env;
+ env = env1;
+
+ ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+ if (unlikely(ret)) {
+ if (retaddr) {
+ /* now we have a real cpu fault */
+ tb = tb_find_pc(retaddr);
+ if (tb) {
+ /* The PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, retaddr);
+ }
+ }
+ cpu_loop_exit(env);
+ }
+ env = saved_env;
+}
+
+void helper_raise_exception(uint32_t index)
+{
+ env->exception_index = index;
+ cpu_loop_exit(env);
+}
+
+uint32_t helper_mmu_read(uint32_t rn)
+{
+ return mmu_read(env, rn);
+}
+
+void helper_mmu_write(uint32_t rn, uint32_t v)
+{
+ mmu_write(env, rn, v);
+}
+
+void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
+{
+ if (addr & mask) {
+ qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
+ addr, mask, wr, dr);
+ env->regs[CR_BADADDR] = addr;
+ env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
+ helper_raise_exception(EXCP_UNALIGN);
+ }
+}
+
+void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
+ int is_write, int is_exec, int is_asi, int size)
+{
+ qemu_log("unassigned access to %08X\n", addr);
+}
+
+uint32_t helper_divs(uint32_t a, uint32_t b)
+{
+ return (int32_t)a / (int32_t)b;
+}
+
+uint32_t helper_divu(uint32_t a, uint32_t b)
+{
+ return a / b;
+}
+
+#ifdef CALL_TRACING
+void helper_call_status(uint32_t pc, uint32_t target)
+{
+ qemu_log("%08X: CALL %08X %s\n", pc, target, lookup_symbol(target));
+}
+
+void helper_eret_status(uint32_t pc)
+{
+ qemu_log("%08X: ERET STATUS %08X, ESTATUS %08X, EA %08X\n",
+ pc, env->regs[CR_STATUS], env->regs[CR_ESTATUS], env->regs[R_EA]);
+}
+
+void helper_ret_status(uint32_t pc)
+{
+ qemu_log("%08X: RET RA %08X\n", pc, env->regs[R_RA]);
+}
+#endif
+
+#endif /* !CONFIG_USER_ONLY */
+
diff --git a/target-nios2/translate.c b/target-nios2/translate.c
new file mode 100644
index 0000000..5d0979f
--- /dev/null
+++ b/target-nios2/translate.c
@@ -0,0 +1,252 @@
+/*
+ * Altera Nios II emulation for qemu: main translation routines.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "helper.h"
+#include "qemu-common.h"
+
+#include "instruction.h"
+
+#define GEN_HELPER 1
+#include "helper.h"
+
+static const char *regnames[] = {
+ "zero", "at", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "et", "bt", "gp", "sp",
+ "fp", "ea", "ba", "ra",
+ "status", "estatus", "bstatus", "ienable",
+ "ipending", "cpuid", "reserved", "exception",
+ "pteaddr", "tlbacc", "tlbmisc", "reserved",
+ "badaddr", "config", "mpubase", "mpuacc",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved", "reserved", "reserved",
+ "reserved", "reserved", "reserved", "reserved",
+ "rpc"
+};
+
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[NUM_CORE_REGS];
+
+#include "gen-icount.h"
+
+/* generate intermediate code for basic block 'tb'. */
+static void gen_intermediate_code_internal(
+ CPUNios2State *env, TranslationBlock *tb, int search_pc)
+{
+ DisasContext dc1, *dc = &dc1;
+ int num_insns;
+ int max_insns;
+ uint32_t next_page_start;
+ int j, lj = -1;
+ uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+ /* Initialize DC */
+ dc->env = env;
+ dc->cpu_R = cpu_R;
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = tb->pc;
+ dc->tb = tb;
+
+ /* Dump the CPU state to the log */
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("--------------\n");
+ log_cpu_state(env, 0);
+ }
+
+ /* Set up instruction counts */
+ num_insns = 0;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0) {
+ max_insns = CF_COUNT_MASK;
+ }
+ next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+
+ gen_icount_start();
+ do {
+ /* Mark instruction start with associated PC */
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ if (lj < j) {
+ lj++;
+ while (lj < j) {
+ gen_opc_instr_start[lj++] = 0;
+ }
+ }
+ gen_opc_pc[lj] = dc->pc;
+ gen_opc_instr_start[lj] = 1;
+ gen_opc_icount[lj] = num_insns;
+ }
+
+ LOG_DIS("%8.8x:\t", dc->pc);
+
+ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+ gen_io_start();
+ }
+
+ /* Decode an instruction */
+ handle_instruction(dc);
+
+ dc->pc += 4;
+ num_insns++;
+
+ /* Translation stops when a conditional branch is encountered.
+ * Otherwise the subsequent code could get translated several times.
+ * Also stop translation when a page boundary is reached. This
+ * ensures prefetch aborts occur at the right place. */
+ } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+ !env->singlestep_enabled &&
+ !singlestep &&
+ dc->pc < next_page_start &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ /* Indicate where the next block should start */
+ switch (dc->is_jmp) {
+ case DISAS_NEXT:
+ /* Save the current PC back into the CPU register */
+ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+ tcg_gen_exit_tb(0);
+ break;
+
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* The jump will already have updated the PC register */
+ tcg_gen_exit_tb(0);
+ break;
+
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+
+ /* End off the block */
+ gen_icount_end(tb, num_insns);
+ *gen_opc_ptr = INDEX_op_end;
+
+ /* Mark instruction starts for the final generated instruction */
+ if (search_pc) {
+ j = gen_opc_ptr - gen_opc_buf;
+ lj++;
+ while (lj <= j) {
+ gen_opc_instr_start[lj++] = 0;
+ }
+ } else {
+ tb->size = dc->pc - tb->pc;
+ tb->icount = num_insns;
+ }
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+ qemu_log("----------------\n");
+ qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+ log_target_disas(tb->pc, dc->pc - tb->pc, 0);
+ qemu_log("\nisize=%d osize=%td\n",
+ dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
+ }
+#endif
+}
+
+void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
+{
+ gen_intermediate_code_internal(env, tb, 1);
+}
+
+void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ int i;
+
+ if (!env || !f) {
+ return;
+ }
+
+ cpu_fprintf(f, "IN: PC=%x %s\n",
+ env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
+
+ for (i = 0; i < NUM_CORE_REGS; i++) {
+ cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+ if ((i + 1) % 4 == 0) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+ cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
+ env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
+ (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
+ env->mmu.tlbacc_wr);
+ cpu_fprintf(f, "\n\n");
+}
+
+Nios2CPU *cpu_nios2_init(const char *cpu_model)
+{
+ Nios2CPU *cpu;
+ int i;
+
+ cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
+
+ cpu->env.reset_addr = RESET_ADDRESS;
+ cpu->env.exception_addr = EXCEPTION_ADDRESS;
+ cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
+
+ cpu_reset(CPU(cpu));
+ qemu_init_vcpu(&cpu->env);
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ for (i = 0; i < NUM_CORE_REGS; i++) {
+ cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
+ offsetof(CPUNios2State, regs[i]),
+ regnames[i]);
+ }
+
+#define GEN_HELPER 2
+#include "helper.h"
+
+ return cpu;
+}
+
+void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
+{
+ env->regs[R_PC] = gen_opc_pc[pc_pos];
+}
+
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
@ 2012-09-11 20:19 ` Blue Swirl
2012-09-14 3:30 ` Chris Wulff
2012-09-11 21:34 ` Aurelien Jarno
2012-09-15 14:55 ` Andreas Färber
2 siblings, 1 reply; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 20:19 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:19 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> target-nios2/Makefile.objs | 5 +
> target-nios2/altera_iic.c | 100 +++
> target-nios2/cpu-qom.h | 69 +++
> target-nios2/cpu.c | 83 +++
> target-nios2/cpu.h | 259 ++++++++
> target-nios2/exec.h | 60 ++
> target-nios2/helper.c | 291 +++++++++
> target-nios2/helper.h | 45 ++
> target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
> target-nios2/instruction.h | 290 +++++++++
> target-nios2/machine.c | 33 +
> target-nios2/mmu.c | 273 +++++++++
> target-nios2/mmu.h | 49 ++
> target-nios2/op_helper.c | 125 ++++
> target-nios2/translate.c | 252 ++++++++
> 15 files changed, 3397 insertions(+)
> create mode 100644 target-nios2/Makefile.objs
> create mode 100644 target-nios2/altera_iic.c
> create mode 100644 target-nios2/cpu-qom.h
> create mode 100644 target-nios2/cpu.c
> create mode 100644 target-nios2/cpu.h
> create mode 100644 target-nios2/exec.h
> create mode 100644 target-nios2/helper.c
> create mode 100644 target-nios2/helper.h
> create mode 100644 target-nios2/instruction.c
> create mode 100644 target-nios2/instruction.h
> create mode 100644 target-nios2/machine.c
> create mode 100644 target-nios2/mmu.c
> create mode 100644 target-nios2/mmu.h
> create mode 100644 target-nios2/op_helper.c
> create mode 100644 target-nios2/translate.c
>
> diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
> new file mode 100644
> index 0000000..d072795
> --- /dev/null
> +++ b/target-nios2/Makefile.objs
> @@ -0,0 +1,5 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
> +obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
> +obj-y += altera_iic.o
> +
> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
> new file mode 100644
> index 0000000..d45d6fa
> --- /dev/null
> +++ b/target-nios2/altera_iic.c
> @@ -0,0 +1,100 @@
> +/*
> + * QEMU Altera Internal Interrupt Controller.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {
CamelCase
> + SysBusDevice busdev;
> + Nios2CPU *cpu;
> + qemu_irq parent_irq;
> +};
> +
> +static void update_irq(struct altera_iic *pv)
> +{
> + uint32_t i;
> +
> + if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
> + qemu_irq_lower(pv->parent_irq);
> + return;
> + }
> +
> + for (i = 0; i < 32; i++) {
> + if (pv->cpu->env.regs[CR_IPENDING] &
> + pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
> + break;
> + }
> + }
> + if (i == 32) {
> + qemu_irq_lower(pv->parent_irq);
> + } else {
> + qemu_irq_raise(pv->parent_irq);
> + }
> +}
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> + struct altera_iic *pv = opaque;
> +
> + pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
> + pv->cpu->env.regs[CR_IPENDING] |= level << irq;
> +
> + update_irq(pv);
> +}
> +
> +static int altera_iic_init(SysBusDevice *dev)
> +{
> + struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
> + pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
> +
> + qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> + sysbus_init_irq(dev, &pv->parent_irq);
> +
> + return 0;
> +}
> +
> +static Property altera_iic_properties[] = {
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_iic_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = altera_iic_init;
> + dc->props = altera_iic_properties;
> +}
> +
> +static TypeInfo altera_iic_info = {
> + .name = "altera,iic",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct altera_iic),
> + .class_init = altera_iic_class_init,
> +};
> +
> +static void altera_iic_register(void)
> +{
> + type_register_static(&altera_iic_info);
> +}
> +
> +type_init(altera_iic_register)
> +
> diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
> new file mode 100644
> index 0000000..be6fa65
> --- /dev/null
> +++ b/target-nios2/cpu-qom.h
> @@ -0,0 +1,69 @@
> +/*
> + * QEMU Nios II CPU
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +#ifndef QEMU_NIOS2_CPU_QOM_H
> +#define QEMU_NIOS2_CPU_QOM_H
> +
> +#include "qemu/cpu.h"
> +
> +#define TYPE_NIOS2_CPU "nios2-cpu"
> +
> +#define NIOS2_CPU_CLASS(klass) \
> + OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU(obj) \
> + OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU_GET_CLASS(obj) \
> + OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
> +
> +/**
> + * Nios2CPUClass:
> + * @parent_reset: The parent class' reset handler.
> + *
> + * A Nios2 CPU model.
> + */
> +typedef struct Nios2CPUClass {
> + /*< private >*/
> + CPUClass parent_class;
> + /*< public >*/
> +
> + void (*parent_reset)(CPUState *cpu);
> +} Nios2CPUClass;
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> + /*< private >*/
> + CPUState parent_obj;
> + /*< public >*/
> +
> + CPUNios2State env;
> +} Nios2CPU;
> +
> +static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
> +{
> + return NIOS2_CPU(container_of(env, Nios2CPU, env));
> +}
> +
> +#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
> +
> +#endif /* QEMU_NIOS2_CPU_QOM_H */
> diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
> new file mode 100644
> index 0000000..bd819c4
> --- /dev/null
> +++ b/target-nios2/cpu.c
> @@ -0,0 +1,83 @@
> +/*
> + * QEMU Nios II CPU
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "cpu.h"
> +#include "qemu-common.h"
> +
> +
> +/* CPUClass::reset() */
> +static void nios2_cpu_reset(CPUState *s)
> +{
> + Nios2CPU *cpu = NIOS2_CPU(s);
> + Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
> + CPUNios2State *env = &cpu->env;
> +
> + if (qemu_loglevel_mask(CPU_LOG_RESET)) {
> + qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
> + log_cpu_state(env, 0);
> + }
> +
> + mcc->parent_reset(s);
> +
> + tlb_flush(env, 1);
> +
> + memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
> + env->regs[R_PC] = env->reset_addr;
> +
> +#if defined(CONFIG_USER_ONLY)
> + /* start in user mode with interrupts enabled. */
> + env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
> +#else
> + mmu_init(&env->mmu);
> +#endif
> +}
> +
> +static void nios2_cpu_initfn(Object *obj)
> +{
> + Nios2CPU *cpu = NIOS2_CPU(obj);
> + CPUNios2State *env = &cpu->env;
> +
> + cpu_exec_init(env);
> +}
> +
> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
> +{
> + CPUClass *cc = CPU_CLASS(oc);
> + Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
> +
> + mcc->parent_reset = cc->reset;
> + cc->reset = nios2_cpu_reset;
> +}
> +
> +static const TypeInfo nios2_cpu_type_info = {
> + .name = TYPE_NIOS2_CPU,
> + .parent = TYPE_CPU,
> + .instance_size = sizeof(Nios2CPU),
> + .instance_init = nios2_cpu_initfn,
> + .class_size = sizeof(Nios2CPUClass),
> + .class_init = nios2_cpu_class_init,
> +};
> +
> +static void nios2_cpu_register_types(void)
> +{
> + type_register_static(&nios2_cpu_type_info);
> +}
> +
> +type_init(nios2_cpu_register_types)
> diff --git a/target-nios2/cpu.h b/target-nios2/cpu.h
> new file mode 100644
> index 0000000..0ee22e6
> --- /dev/null
> +++ b/target-nios2/cpu.h
> @@ -0,0 +1,259 @@
> +/*
> + * Altera Nios II virtual CPU header
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +#ifndef CPU_NIOS2_H
> +#define CPU_NIOS2_H
> +
> +#include "config.h"
> +#include "qemu-common.h"
> +
> +#define TARGET_LONG_BITS 32
> +
> +#define CPUArchState struct CPUNios2State
> +
> +#include "cpu-defs.h"
> +#include "softfloat.h"
> +struct CPUNios2State;
> +typedef struct CPUNios2State CPUNios2State;
> +#if !defined(CONFIG_USER_ONLY)
> +#include "mmu.h"
> +#endif
> +
> +#define TARGET_HAS_ICE 1
> +
> +/* Configuration options for Nios II */
> +#define RESET_ADDRESS 0x00000000
> +#define EXCEPTION_ADDRESS 0x00000004
> +#define FAST_TLB_MISS_ADDRESS 0x00000008
> +
> +
> +#define ELF_MACHINE EM_ALTERA_NIOS2
> +
> +/* GP regs + CR regs + PC */
> +#define NUM_CORE_REGS (32 + 32 + 1)
> +
> +/* General purpose egister aliases */
> +#define R_ZERO 0
> +#define R_AT 1
> +#define R_RET0 2
> +#define R_RET1 3
> +#define R_ARG0 4
> +#define R_ARG1 5
> +#define R_ARG2 6
> +#define R_ARG3 7
> +#define R_ET 24
> +#define R_BT 25
> +#define R_GP 26
> +#define R_SP 27
> +#define R_FP 28
> +#define R_EA 29
> +#define R_BA 30
> +#define R_RA 31
> +
> +/* Control register aliases */
> +#define CR_BASE 32
> +#define CR_STATUS (CR_BASE + 0)
> +#define CR_STATUS_PIE (1<<0)
> +#define CR_STATUS_U (1<<1)
> +#define CR_STATUS_EH (1<<2)
> +#define CR_STATUS_IH (1<<3)
> +#define CR_STATUS_IL (63<<4)
> +#define CR_STATUS_CRS (63<<10)
> +#define CR_STATUS_PRS (63<<16)
> +#define CR_STATUS_NMI (1<<22)
> +#define CR_STATUS_RSIE (1<<23)
> +#define CR_ESTATUS (CR_BASE + 1)
> +#define CR_BSTATUS (CR_BASE + 2)
> +#define CR_IENABLE (CR_BASE + 3)
> +#define CR_IPENDING (CR_BASE + 4)
> +#define CR_CPUID (CR_BASE + 5)
> +#define CR_EXCEPTION (CR_BASE + 7)
> +#define CR_PTEADDR (CR_BASE + 8)
> +#define CR_PTEADDR_PTBASE_SHIFT 22
> +#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
> +#define CR_PTEADDR_VPN_SHIFT 2
> +#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
> +#define CR_TLBACC (CR_BASE + 9)
> +#define CR_TLBACC_IGN_SHIFT 25
> +#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
> +#define CR_TLBACC_C (1<<24)
> +#define CR_TLBACC_R (1<<23)
> +#define CR_TLBACC_W (1<<22)
> +#define CR_TLBACC_X (1<<21)
> +#define CR_TLBACC_G (1<<20)
> +#define CR_TLBACC_PFN_MASK 0x000FFFFF
> +#define CR_TLBMISC (CR_BASE + 10)
> +#define CR_TLBMISC_WAY_SHIFT 20
> +#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
> +#define CR_TLBMISC_RD (1<<19)
> +#define CR_TLBMISC_WR (1<<18)
> +#define CR_TLBMISC_PID_SHIFT 4
> +#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
> +#define CR_TLBMISC_DBL (1<<3)
> +#define CR_TLBMISC_BAD (1<<2)
> +#define CR_TLBMISC_PERM (1<<1)
> +#define CR_TLBMISC_D (1<<0)
> +#define CR_BADADDR (CR_BASE + 12)
> +#define CR_CONFIG (CR_BASE + 13)
> +#define CR_MPUBASE (CR_BASE + 14)
> +#define CR_MPUACC (CR_BASE + 15)
> +
> +/* Other registers */
> +#define R_PC 64
> +
> +/* Exceptions */
> +#define EXCP_BREAK -1
> +#define EXCP_RESET 0
> +#define EXCP_PRESET 1
> +#define EXCP_IRQ 2
> +#define EXCP_TRAP 3
> +#define EXCP_UNIMPL 4
> +#define EXCP_ILLEGAL 5
> +#define EXCP_UNALIGN 6
> +#define EXCP_UNALIGND 7
> +#define EXCP_DIV 8
> +#define EXCP_SUPERA 9
> +#define EXCP_SUPERI 10
> +#define EXCP_SUPERD 11
> +#define EXCP_TLBD 12
> +#define EXCP_TLBX 13
> +#define EXCP_TLBR 14
> +#define EXCP_TLBW 15
> +#define EXCP_MPUI 16
> +#define EXCP_MPUD 17
> +
> +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
> +
> +#define NB_MMU_MODES 2
> +
> +typedef struct CPUNios2State {
> + uint32_t regs[NUM_CORE_REGS];
> +
> + /* Addresses that are hard-coded in the FPGA build settings */
> + uint32_t reset_addr;
> + uint32_t exception_addr;
> + uint32_t fast_tlb_miss_addr;
> +
> +#if !defined(CONFIG_USER_ONLY)
> + struct nios2_mmu mmu;
> +#endif
> +
> + CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
'extern' is useless for functions.
> +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;
Just say no to globals.
> +
> +static inline CPUNios2State *cpu_init(const char *cpu_model)
> +{
> + Nios2CPU *cpu = cpu_nios2_init(cpu_model);
> + if (cpu == NULL) {
> + return NULL;
> + }
> + return &cpu->env;
> +}
> +
> +#define cpu_exec cpu_nios2_exec
> +#define cpu_gen_code cpu_nios2_gen_code
> +#define cpu_signal_handler cpu_nios2_signal_handler
> +
> +#define CPU_SAVE_VERSION 1
> +
> +#define TARGET_PAGE_BITS 12
> +
> +/* MMU modes definitions */
> +#define MMU_MODE0_SUFFIX _kernel
> +#define MMU_MODE1_SUFFIX _user
> +#define MMU_SUPERVISOR_IDX 0
> +#define MMU_USER_IDX 1
> +
> +static inline int cpu_mmu_index(CPUNios2State *env)
> +{
> + return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
> + MMU_SUPERVISOR_IDX;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu);
> +#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
> +
> +#if defined(CONFIG_USER_ONLY)
> +static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
> +{
> + if (newsp) {
> + env->regs[R_SP] = newsp;
> + }
> + env->regs[R_RET0] = 0;
> +}
> +#endif
> +
> +static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
> +{
> +}
> +
> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
> +{
> + return env->regs[CR_STATUS] & CR_STATUS_PIE;
> +}
> +
> +#include "cpu-all.h"
> +
> +static inline target_ulong cpu_get_pc(CPUNios2State *env)
> +{
> + return env->regs[R_PC];
> +}
> +
> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
> + target_ulong *cs_base, int *flags)
> +{
> + *pc = env->regs[R_PC];
> + *cs_base = 0;
> + *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
> +}
> +
> +#if !defined(CONFIG_USER_ONLY)
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> + int is_write, int is_exec, int is_asi, int size);
> +#endif
> +
> +static inline bool cpu_has_work(CPUNios2State *env)
> +{
> + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +#include "exec-all.h"
> +
> +static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
> +{
> + env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* CPU_NIOS2_H */
> +
> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
> new file mode 100644
> index 0000000..cd4446b
> --- /dev/null
> +++ b/target-nios2/exec.h
> @@ -0,0 +1,60 @@
> +/*
> + * Altera Nios II execution defines
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#ifndef NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"
Dyngen model is being obsoleted in coming days. Please make all
helpers which need access to CPU state take the state as parameter.
> +
> +register struct CPUNios2State *env asm(AREG0);
This is already defined in dyngen-exec.h, but see above. Maybe you are
not using Git HEAD? No target uses exec.h anymore.
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +
The functions below should go to cpu.h.
> +static inline int cpu_has_work(CPUState *env)
> +{
> + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +static inline int cpu_halted(CPUState *env)
> +{
> + if (!env->halted) {
> + return 0;
> + }
> +
> + if (cpu_has_work(env)) {
> + env->halted = 0;
> + return 0;
> + }
> +
> + return EXCP_HALTED;
> +}
> +
> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
> +{
> + env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* NIOS2_EXEC_H */
> +
> diff --git a/target-nios2/helper.c b/target-nios2/helper.c
> new file mode 100644
> index 0000000..9b200c9
> --- /dev/null
> +++ b/target-nios2/helper.c
> @@ -0,0 +1,291 @@
> +/*
> + * Altera Nios II helper routines.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "host-utils.h"
> +
> +#if defined(CONFIG_USER_ONLY)
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> + env->exception_index = -1;
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu)
> +{
> + env->exception_index = 0xaa;
> + cpu_dump_state(env, stderr, fprintf, 0);
> + return 1;
> +}
> +
> +#else /* !CONFIG_USER_ONLY */
> +
> +bool has_mmu = true;
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> + switch (env->exception_index) {
> + case EXCP_IRQ:
> + assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
I hope this is not a guest triggerable assert.
> +
> + qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
> +
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_IH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_TLBD:
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
> + env->regs[R_PC]);
> + log_cpu_state(env, 0);
> +
> + /* Fast TLB miss */
> + /* Variation from the spec. Table 3-35 of the cpu reference shows
> + * estatus not being changed for TLB miss but this appears to
> + * be incorrect. */
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->fast_tlb_miss_addr;
> + } else {
> + qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
> + env->regs[R_PC]);
> +
> + /* Double TLB miss */
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
> +
> + env->regs[R_PC] = env->exception_addr;
> + }
> + break;
> +
> + case EXCP_TLBR:
> + case EXCP_TLBW:
> + case EXCP_TLBX:
> + qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
> + log_cpu_state(env, 0);
> +
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> + }
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_SUPERA:
> + case EXCP_SUPERI:
> + case EXCP_SUPERD:
> + qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
> + env->regs[R_PC]);
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_ILLEGAL:
> + case EXCP_TRAP:
> + qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
> + env->regs[R_PC]);
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_BREAK:
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
> + env->regs[R_BA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + default:
> + cpu_abort(env, "unhandled exception type=%d\n",
> + env->exception_index);
> + break;
> + }
> +}
> +
> +static int cpu_nios2_handle_virtual_page(
> + CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
> +{
> + target_ulong vaddr, paddr;
> + struct nios2_mmu_lookup lu;
> + unsigned int hit;
> + hit = mmu_translate(env, &lu, address, rw, mmu_idx);
> + if (hit) {
> + vaddr = address & TARGET_PAGE_MASK;
> + paddr = lu.paddr + vaddr - lu.vaddr;
> +
> + if (((rw == 0) && (lu.prot & PAGE_READ)) ||
> + ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
> + ((rw == 2) && (lu.prot & PAGE_EXEC))) {
> +
> + tlb_set_page(env, vaddr, paddr, lu.prot,
> + mmu_idx, TARGET_PAGE_SIZE);
> + return 0;
> + } else {
> + /* Permission violation */
> + env->exception_index = (rw == 0) ? EXCP_TLBR :
> + ((rw == 1) ? EXCP_TLBW :
> + EXCP_TLBX);
> + }
> + } else {
> + env->exception_index = EXCP_TLBD;
> + }
> +
> + if (rw == 2) {
> + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
> + } else {
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
> + }
> + env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
> + env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
> + env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
> + env->regs[CR_BADADDR] = address;
> + return 1;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu)
> +{
> + if (has_mmu) {
> + if (MMU_SUPERVISOR_IDX == mmu_idx) {
> + if (address >= 0xC0000000) {
> + /* Kernel physical page - TLB bypassed */
> + address &= TARGET_PAGE_MASK;
> + tlb_set_page(env, address, address, PAGE_BITS,
> + mmu_idx, TARGET_PAGE_SIZE);
> + } else if (address >= 0x80000000) {
> + /* Kernel virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + } else {
> + /* User virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + }
> + } else {
> + if (address >= 0x80000000) {
> + /* Illegal access from user mode */
> + env->exception_index = EXCP_SUPERA;
> + env->regs[CR_BADADDR] = address;
> + return 1;
> + } else {
> + /* User virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + }
> + }
> + } else {
> + /* No MMU */
> + address &= TARGET_PAGE_MASK;
> + tlb_set_page(env, address, address, PAGE_BITS,
> + mmu_idx, TARGET_PAGE_SIZE);
> + }
> +
> + return 0;
> +}
> +
> +target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
> + target_ulong addr)
> +{
> + target_ulong vaddr, paddr = 0;
> + struct nios2_mmu_lookup lu;
> + unsigned int hit;
> +
> + if (has_mmu && (addr < 0xC0000000)) {
> + hit = mmu_translate(env, &lu, addr, 0, 0);
> + if (hit) {
> + vaddr = addr & TARGET_PAGE_MASK;
> + paddr = lu.paddr + vaddr - lu.vaddr;
> + } else {
> + cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);
cpu_get_phys_page_debug is used for memory access from monitor.
Aborting because user specified a bad address is rather draconian.
> + }
> + } else {
> + paddr = addr & TARGET_PAGE_MASK;
> + }
> +
> + return paddr;
> +}
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/helper.h b/target-nios2/helper.h
> new file mode 100644
> index 0000000..69b6684
> --- /dev/null
> +++ b/target-nios2/helper.h
> @@ -0,0 +1,45 @@
> +/*
> + * Altera Nios II helper routines header.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "def-helper.h"
> +
> +/* Define this to enable tracing calls/returns */
> +/* #define CALL_TRACING */
> +
> +#ifdef CALL_TRACING
> +DEF_HELPER_2(call_status, void, i32, i32)
> +DEF_HELPER_1(eret_status, void, i32)
> +DEF_HELPER_1(ret_status, void, i32)
> +#endif
> +
> +DEF_HELPER_1(raise_exception, void, i32)
> +
> +#if !defined(CONFIG_USER_ONLY)
> +DEF_HELPER_1(mmu_read, i32, i32)
> +DEF_HELPER_2(mmu_write, void, i32, i32)
> +#endif
> +
> +DEF_HELPER_2(divs, i32, i32, i32)
> +DEF_HELPER_2(divu, i32, i32, i32)
> +
> +DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
> +
> +#include "def-helper.h"
> +
> diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
> new file mode 100644
> index 0000000..4dc06cd
> --- /dev/null
> +++ b/target-nios2/instruction.c
> @@ -0,0 +1,1463 @@
> +/*
> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
> + * (Portions of this file that were originally from nios2sim-ng.)
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +
> +#include "instruction.h"
> +#include "exec-all.h"
> +#include "helper.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.h"
> +
> +static inline uint32_t get_opcode(uint32_t code)
> +{
> + I_TYPE(instr, code);
> + return instr->op;
> +}
> +
> +static inline uint32_t get_opxcode(uint32_t code)
> +{
> + R_TYPE(instr, code);
> + return instr->opx6;
> +}
> +
> +static inline void t_gen_helper_raise_exception(DisasContext *dc,
> + uint32_t index)
> +{
> + TCGv_i32 tmp = tcg_const_i32(index);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
> + gen_helper_raise_exception(tmp);
> + tcg_temp_free_i32(tmp);
> + dc->is_jmp = DISAS_UPDATE;
> +}
> +
> +/*
> + * Instructions not implemented by the simulator.
> + */
> +static void unimplemented(DisasContext *dc, uint32_t code)
> +{
> + t_gen_helper_raise_exception(dc, EXCP_UNIMPL);
> +}
> +
> +/*
> + * Illegal instruction
> + */
> +static void illegal_instruction(DisasContext *dc,
> + uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +static void gen_check_supervisor(DisasContext *dc, int label)
> +{
> + int l1 = gen_new_label();
> +
> + TCGv_i32 tmp = tcg_temp_new();
> + tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
> + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
> + t_gen_helper_raise_exception(dc, EXCP_SUPERI);
> + tcg_gen_br(label);
> +
> + gen_set_label(l1);
> + tcg_temp_free_i32(tmp);
> +
> + /* If we aren't taking the exception, update the PC to the
> + * next instruction */
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4);
> +}
> +
> +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
> +
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_ld8u(dst, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_ld16u(dst, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_ld32u(dst, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
Maybe assert instead.
> + break;
> + }
> +}
> +
> +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
> +
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_ld8s(dst, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_ld16s(dst, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_ld32s(dst, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> + break;
> + }
> +}
> +
> +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
> +
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_st8(val, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_st16(val, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_st32(val, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> + break;
> + }
> +}
> +
> +/*
> + * Used as a placeholder for all instructions which do not have an effect on the
> + * simulator (e.g. flush, sync)
> + */
> +static void nop(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* Nothing to do here */
> +}
> +
> +/*
> + * J-Type instructions
> + */
> +
> +/*
> + * ra <- PC + 4
> + * PC <- (PC(31..28) : IMM26 * 4)
> + */
> +static void call(DisasContext *dc, uint32_t code)
> +{
> + J_TYPE(instr, code);
> +
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4));
> + gen_helper_call_status(tmp, tmp2);
> + tcg_temp_free_i32(tmp);
> + tcg_temp_free_i32(tmp2);
> +#endif
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* PC <- (PC(31..28) : IMM26 * 4) */
> +static void jmpi(DisasContext *dc, uint32_t code)
> +{
> + J_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/*
> + * I-Type instructions
> + */
> +
> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
> +static void ldbu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA + IMM16 */
> +static void addi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv imm = tcg_temp_new();
> + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +
> + tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> +
> + tcg_temp_free(imm);
> +}
> +
> +/* Mem8[rA + @(IMM16)] <- rB(7..0) */
> +static void stb(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* PC <- PC + 4 + IMM16 */
> +static void br(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) */
> +static void ldb(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA >= (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpgei(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_GE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x0000 : Mem16[rA + @IMM16)] */
> +static void ldhu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA & IMM16 */
> +static void andi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/* Mem16[rA + @(IMM16)] <- rB(15..0) */
> +static void sth(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA >= (signed) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bge(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] */
> +static void ldh(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmplti(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_LT, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* Initializes the data cache line currently caching address rA + @(IMM16) */
> +static void initda(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* rB <- rA | IMM16 */
> +static void ori(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/* Mem32[rA + @(IMM16)] <- rB */
> +static void stw(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void blt(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) */
> +static void ldw(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA != (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpnei(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_NE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- rA ^ IMM16 */
> +static void xori(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/*
> + * if (rA != rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bne(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/*
> + * if ((signed) rA == (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpeqi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_EQ, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
> +static void ldbuio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- (rA * @(IMM16))(31..0) */
> +static void muli(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv imm = tcg_temp_new();
> + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> + tcg_temp_free(imm);
> +}
> +
> +/* Mem8[rA + @(IMM16)] <- rB(7..0) (bypassing cache) */
> +static void stbio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA == rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void beq(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) (bypassing cache) */
> +static void ldbio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA >= 0x0000 : @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpgeui(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_GEU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x0000 : Mem16[rA + @IMM16)] (bypassing cache) */
> +static void ldhuio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA & (IMM16 : 0x0000) */
> +static void andhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +/* Mem16[rA + @(IMM16)] <- rB(15..0) (bypassing cache) */
> +static void sthio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bgeu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] (bypassing cache) */
> +static void ldhio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA < 0x0000 : @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpltui(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_LTU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* */
> +static void initd(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* rB <- rA | (IMM16 : 0x0000) */
> +static void orhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +/* Mem32[rA + @(IMM16)] <- rB (bypassing cache) */
> +static void stwio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((unsigned) rA < (unsigned) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bltu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) (bypassing cache) */
> +static void ldwio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* Prototype only, defined below */
> +static void handle_r_type_instr(DisasContext *dc, uint32_t code);
> +
> +/* rB <- rA ^ (IMM16 : 0x0000) */
> +static void xorhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +static struct instruction i_type_instructions[I_TYPE_COUNT] = {
'const'?
> + [CALL] = INSTRUCTION(call),
> + [JMPI] = INSTRUCTION(jmpi),
> + [0x02] = INSTRUCTION_ILLEGAL(),
> + [LDBU] = INSTRUCTION(ldbu),
> + [ADDI] = INSTRUCTION(addi),
> + [STB] = INSTRUCTION(stb),
> + [BR] = INSTRUCTION(br),
> + [LDB] = INSTRUCTION(ldb),
> + [CMPGEI] = INSTRUCTION(cmpgei),
> + [0x09] = INSTRUCTION_ILLEGAL(),
> + [0x0a] = INSTRUCTION_ILLEGAL(),
> + [LDHU] = INSTRUCTION(ldhu),
> + [ANDI] = INSTRUCTION(andi),
> + [STH] = INSTRUCTION(sth),
> + [BGE] = INSTRUCTION(bge),
> + [LDH] = INSTRUCTION(ldh),
> + [CMPLTI] = INSTRUCTION(cmplti),
> + [0x11] = INSTRUCTION_ILLEGAL(),
> + [0x12] = INSTRUCTION_ILLEGAL(),
> + [INITDA] = INSTRUCTION(initda),
> + [ORI] = INSTRUCTION(ori),
> + [STW] = INSTRUCTION(stw),
> + [BLT] = INSTRUCTION(blt),
> + [LDW] = INSTRUCTION(ldw),
> + [CMPNEI] = INSTRUCTION(cmpnei),
> + [0x19] = INSTRUCTION_ILLEGAL(),
> + [0x1a] = INSTRUCTION_ILLEGAL(),
> + [FLUSHDA] = INSTRUCTION_NOP(flushda),
> + [XORI] = INSTRUCTION(xori),
> + [0x1d] = INSTRUCTION_ILLEGAL(),
> + [BNE] = INSTRUCTION(bne),
> + [0x1f] = INSTRUCTION_ILLEGAL(),
> + [CMPEQI] = INSTRUCTION(cmpeqi),
> + [0x21] = INSTRUCTION_ILLEGAL(),
> + [0x22] = INSTRUCTION_ILLEGAL(),
> + [LDBUIO] = INSTRUCTION(ldbuio),
> + [MULI] = INSTRUCTION(muli),
> + [STBIO] = INSTRUCTION(stbio),
> + [BEQ] = INSTRUCTION(beq),
> + [LDBIO] = INSTRUCTION(ldbio),
> + [CMPGEUI] = INSTRUCTION(cmpgeui),
> + [0x29] = INSTRUCTION_ILLEGAL(),
> + [0x2a] = INSTRUCTION_ILLEGAL(),
> + [LDHUIO] = INSTRUCTION(ldhuio),
> + [ANDHI] = INSTRUCTION(andhi),
> + [STHIO] = INSTRUCTION(sthio),
> + [BGEU] = INSTRUCTION(bgeu),
> + [LDHIO] = INSTRUCTION(ldhio),
> + [CMPLTUI] = INSTRUCTION(cmpltui),
> + [0x31] = INSTRUCTION_ILLEGAL(),
> + [CUSTOM] = INSTRUCTION_UNIMPLEMENTED(custom),
> + [INITD] = INSTRUCTION(initd),
> + [ORHI] = INSTRUCTION(orhi),
> + [STWIO] = INSTRUCTION(stwio),
> + [BLTU] = INSTRUCTION(bltu),
> + [LDWIO] = INSTRUCTION(ldwio),
> + [RDPRS] = INSTRUCTION_UNIMPLEMENTED(rdprs),
> + [0x39] = INSTRUCTION_ILLEGAL(),
> + [R_TYPE] = { "<R-type instruction>", handle_r_type_instr },
> + [FLUSHD] = INSTRUCTION_NOP(flushd),
> + [XORHI] = INSTRUCTION(xorhi),
> + [0x3d] = INSTRUCTION_ILLEGAL(),
> + [0x3e] = INSTRUCTION_ILLEGAL(),
> + [0x3f] = INSTRUCTION_ILLEGAL(),
> +};
> +
> +/*
> + * R-Type instructions
> + */
> +
> +/*
> + * status <- estatus
> + * PC <- ea
> + */
> +static void eret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_eret_status(tmp);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA rotated left IMM5 bit positions */
> +static void roli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- rA rotated left rB(4..0) bit positions */
> +static void rol(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> + TCGv t1 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_movi_tl(t1, 32);
> + tcg_gen_sub_tl(t1, t1, t0);
> + tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> + tcg_temp_free(t0);
> + tcg_temp_free(t1);
> +}
> +
> +/* */
> +static void flushp(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* PC <- ra */
> +static void ret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_ret_status(tmp);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- ~(A | rB) */
> +static void nor(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_nor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
> +static void mulxuu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpge(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_GE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* PC <- ba */
> +static void bret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA rotated right rb(4..0) bit positions */
> +static void ror(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> + TCGv t1 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_movi_tl(t1, 32);
> + tcg_gen_sub_tl(t1, t1, t0);
> + tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
> + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> + tcg_temp_free(t0);
> + tcg_temp_free(t1);
> +}
> +
> +/* */
> +static void flushi(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* PC <- rA */
> +static void jmp(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA & rB */
> +static void and(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_and_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmplt(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_LT, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA << IMM5 */
> +static void slli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- rA << rB(4..0) */
> +static void sll(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_shl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- rA | rB */
> +static void or(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
> +static void mulxsu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA != rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpne(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_NE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- (unsigned) rA >> ((unsigned) IMM5)*/
> +static void srli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- (unsigned) rA >> ((unsigned) rB(4..0))*/
> +static void srl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_shr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- PC + 4 */
> +static void nextpc(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[instr->c], dc->pc + 4);
> +}
> +
> +/*
> + * ra <- PC + 4
> + * PC <- rA
> + */
> +static void callr(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA ^ rB */
> +static void xor(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_xor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((signed)rA * (signed)rB))(31..0) */
> +static void mulxss(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA == rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpeq(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_EQ, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA / rB */
> +static void divu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA / rB */
> +static void _div(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ctlN */
> +static void rdctl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> + gen_check_supervisor(dc, l1);
> +
> + switch (instr->imm5 + 32) {
> + case CR_PTEADDR:
> + case CR_TLBACC:
> + case CR_TLBMISC:
> + {
> + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> + gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
> + tcg_temp_free_i32(tmp);
> + break;
> + }
> +
> + default:
> + tcg_gen_mov_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->imm5 + 32]);
> + break;
> + }
> +
> + gen_set_label(l1);
> +}
> +
> +/* rC <- (rA * rB))(31..0) */
> +static void mul(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_mul_i32(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpgeu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_GEU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* */
> +static void initi(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/*
> + * estatus <- status
> + * PIE <- 0
> + * U <- 0
> + * ea <- PC + 4
> + * PC <- exception handler address
> + */
> +static void trap(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_TRAP);
> +}
> +
> +/* ctlN <- rA */
> +static void wrctl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> + gen_check_supervisor(dc, l1);
> +
> + switch (instr->imm5 + 32) {
> + case CR_PTEADDR:
> + case CR_TLBACC:
> + case CR_TLBMISC:
> + {
> + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> + gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
> + tcg_temp_free_i32(tmp);
> + break;
> + }
> +
> + default:
> + tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
> + break;
> + }
> +
> + gen_set_label(l1);
> +}
> +
> +/*
> + * if (rA < rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpltu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_LTU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA + rB */
> +static void add(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_add_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * bstatus ← status
> + * PIE <- 0
> + * U <- 0
> + * ba <- PC + 4
> + * PC <- break handler address
> + */
> +static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_BREAK);
> +}
> +
> +/* rC <- rA - rB */
> +static void sub(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_sub_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- (signed) rA >> ((unsigned) IMM5) */
> +static void srai(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_sari_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- (signed) rA >> ((unsigned) rB(4..0)) */
> +static void sra(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_sar_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +static struct instruction r_type_instructions[R_TYPE_COUNT] = {
> + [0x00] = INSTRUCTION_ILLEGAL(),
> + [ERET] = INSTRUCTION(eret),
> + [ROLI] = INSTRUCTION(roli),
> + [ROL] = INSTRUCTION(rol),
> + [FLUSHP] = INSTRUCTION(flushp),
> + [RET] = INSTRUCTION(ret),
> + [NOR] = INSTRUCTION(nor),
> + [MULXUU] = INSTRUCTION(mulxuu),
> + [CMPGE] = INSTRUCTION(cmpge),
> + [BRET] = INSTRUCTION(bret),
> + [0x0a] = INSTRUCTION_ILLEGAL(),
> + [ROR] = INSTRUCTION(ror),
> + [FLUSHI] = INSTRUCTION(flushi),
> + [JMP] = INSTRUCTION(jmp),
> + [AND] = INSTRUCTION(and),
> + [0x0f] = INSTRUCTION_ILLEGAL(),
> + [CMPLT] = INSTRUCTION(cmplt),
> + [0x11] = INSTRUCTION_ILLEGAL(),
> + [SLLI] = INSTRUCTION(slli),
> + [SLL] = INSTRUCTION(sll),
> + [WRPRS] = INSTRUCTION_UNIMPLEMENTED(wrprs),
> + [0x15] = INSTRUCTION_ILLEGAL(),
> + [OR] = INSTRUCTION(or),
> + [MULXSU] = INSTRUCTION(mulxsu),
> + [CMPNE] = INSTRUCTION(cmpne),
> + [0x19] = INSTRUCTION_ILLEGAL(),
> + [SRLI] = INSTRUCTION(srli),
> + [SRL] = INSTRUCTION(srl),
> + [NEXTPC] = INSTRUCTION(nextpc),
> + [CALLR] = INSTRUCTION(callr),
> + [XOR] = INSTRUCTION(xor),
> + [MULXSS] = INSTRUCTION(mulxss),
> + [CMPEQ] = INSTRUCTION(cmpeq),
> + [0x21] = INSTRUCTION_ILLEGAL(),
> + [0x22] = INSTRUCTION_ILLEGAL(),
> + [0x23] = INSTRUCTION_ILLEGAL(),
> + [DIVU] = INSTRUCTION(divu),
> + [DIV] = { "div", _div },
> + [RDCTL] = INSTRUCTION(rdctl),
> + [MUL] = INSTRUCTION(mul),
> + [CMPGEU] = INSTRUCTION(cmpgeu),
> + [INITI] = INSTRUCTION(initi),
> + [0x2a] = INSTRUCTION_ILLEGAL(),
> + [0x2b] = INSTRUCTION_ILLEGAL(),
> + [0x2c] = INSTRUCTION_ILLEGAL(),
> + [TRAP] = INSTRUCTION(trap),
> + [WRCTL] = INSTRUCTION(wrctl),
> + [0x2f] = INSTRUCTION_ILLEGAL(),
> + [CMPLTU] = INSTRUCTION(cmpltu),
> + [ADD] = INSTRUCTION(add),
> + [0x32] = INSTRUCTION_ILLEGAL(),
> + [0x33] = INSTRUCTION_ILLEGAL(),
> + [BREAK] = { "break", __break },
> + [0x35] = INSTRUCTION_ILLEGAL(),
> + [SYNC] = INSTRUCTION(nop),
> + [0x37] = INSTRUCTION_ILLEGAL(),
> + [0x38] = INSTRUCTION_ILLEGAL(),
> + [SUB] = INSTRUCTION(sub),
> + [SRAI] = INSTRUCTION(srai),
> + [SRA] = INSTRUCTION(sra),
> + [0x3c] = INSTRUCTION_ILLEGAL(),
> + [0x3d] = INSTRUCTION_ILLEGAL(),
> + [0x3e] = INSTRUCTION_ILLEGAL(),
> + [0x3f] = INSTRUCTION_ILLEGAL(),
> +};
> +
> +static void handle_r_type_instr(DisasContext *dc, uint32_t code)
> +{
> + uint32_t opx;
> + instruction_handler handle_instr;
> +
> + opx = get_opxcode(code);
> + if (unlikely(opx >= R_TYPE_COUNT)) {
> + goto illegal_op;
> + }
> +
> + LOG_DIS("R: %s (%08x)\n", r_type_instructions[opx].name, code);
> + handle_instr = r_type_instructions[opx].handler;
> +
> + handle_instr(dc, code);
> +
> + return;
> +
> +illegal_op:
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +void handle_instruction(DisasContext *dc)
> +{
> + uint32_t insn = ldl_code(dc->pc);
> + uint32_t op = get_opcode(insn);
> +
> + LOG_DIS("%8.8x\t", insn);
> +
> + if (unlikely(op >= I_TYPE_COUNT)) {
> + goto illegal_op;
> + }
> +
> + if (op != R_TYPE) {
> + LOG_DIS("I: %s (%08x)\n", i_type_instructions[op].name, insn);
> + }
> + i_type_instructions[op].handler(dc, insn);
> +
> + return;
> +
> +illegal_op:
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +const char *instruction_get_string(uint32_t code)
> +{
> + uint32_t op = get_opcode(code);
> +
> + if (unlikely(op >= I_TYPE_COUNT)) {
> + return "";
> + } else if (op == R_TYPE) {
> + uint32_t opx = get_opxcode(code);
> + if (unlikely(opx >= R_TYPE_COUNT)) {
> + return "";
> + }
> + return r_type_instructions[opx].name;
> + } else {
> + return i_type_instructions[op].name;
> + }
> +}
> +
> diff --git a/target-nios2/instruction.h b/target-nios2/instruction.h
> new file mode 100644
> index 0000000..fe29933
> --- /dev/null
> +++ b/target-nios2/instruction.h
> @@ -0,0 +1,290 @@
> +/*
> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
> + * Copyright (C) 2010 chysun2000@gmail.com
> + * (Portions of this file that were originally from nios2sim-ng.)
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#ifndef _INSTRUCTION_H_
> +#define _INSTRUCTION_H_
> +
> +#include <stdint.h>
> +#include "cpu.h"
> +#include "tcg-op.h"
> +
> +/*
> + * Instruction Word Formats
> + */
> +
> +/* I-Type instruction */
> +struct i_type {
IType
> + uint32_t op:6;
> + uint32_t imm16:16;
> + uint32_t b:5;
> + uint32_t a:5;
> +} __attribute__((packed));
QEMU_PACKED
> +
> +union i_type_u {
> + uint32_t v;
> + struct i_type i;
> +};
> +
> +#define I_TYPE(instr, op) \
> + union i_type_u instr_u = { .v = op }; \
> + struct i_type *instr = &instr_u.i
> +
> +/* R-Type instruction */
> +struct r_type {
> + uint32_t op:6;
> + /*
> + * Some R-Type instructions embed a small immediate value in the
> + * low-order bits of OPX.
> + */
> + uint32_t imm5:5;
> + uint32_t opx6:6;
> + uint32_t c:5;
> + uint32_t b:5;
> + uint32_t a:5;
> +} __attribute__((packed));
> +
> +union r_type_u {
> + uint32_t v;
> + struct r_type i;
> +};
> +
> +#define R_TYPE(instr, op) \
> + union r_type_u instr_u = { .v = op }; \
> + struct r_type *instr = &instr_u.i
> +
> +/* J-Type instruction */
> +struct j_type {
> + uint32_t op:6;
> + uint32_t imm26:26;
> +} __attribute__((packed));
> +
> +#define J_TYPE(instr, op) \
> + struct j_type *instr = (struct j_type *) &op
> +
> +/*
> + * Instruction Opcodes
> + */
> +
> +/*
> + * OP Encodings for I-Type instructions (except for CALL and JMPI, which are
> + * J-type instructions)
> + */
> +enum {
> + CALL = 0x00, /* J-type */
> + JMPI = 0x01, /* J-type */
> + /* 0x02 */
> + LDBU = 0x03,
> + ADDI = 0x04,
> + STB = 0x05,
> + BR = 0x06,
> + LDB = 0x07,
> + CMPGEI = 0x08,
> + /* 0x09 */
> + /* 0x0A */
> + LDHU = 0x0B,
> + ANDI = 0x0C,
> + STH = 0x0D,
> + BGE = 0x0E,
> + LDH = 0x0F,
> + CMPLTI = 0x10,
> + /* 0x11 */
> + /* 0x12 */
> + INITDA = 0x13,
> + ORI = 0x14,
> + STW = 0x15,
> + BLT = 0x16,
> + LDW = 0x17,
> + CMPNEI = 0x18,
> + /* 0x19 */
> + /* 0x1A */
> + FLUSHDA = 0x1B,
> + XORI = 0x1C,
> + /* 0x1D */
> + BNE = 0x1E,
> + /* 0x1F */
> + CMPEQI = 0x20,
> + /* 0x21 */
> + /* 0x22 */
> + LDBUIO = 0x23,
> + MULI = 0x24,
> + STBIO = 0x25,
> + BEQ = 0x26,
> + LDBIO = 0x27,
> + CMPGEUI = 0x28,
> + /* 0x29 */
> + /* 0x2A */
> + LDHUIO = 0x2B,
> + ANDHI = 0x2C,
> + STHIO = 0x2D,
> + BGEU = 0x2E,
> + LDHIO = 0x2F,
> + CMPLTUI = 0x30,
> + /* 0x31 */
> + CUSTOM = 0x32,
> + INITD = 0x33,
> + ORHI = 0x34,
> + STWIO = 0x35,
> + BLTU = 0x36,
> + LDWIO = 0x37,
> + RDPRS = 0x38,
> + /* 0x39 */
> + R_TYPE = 0x3A,
> + FLUSHD = 0x3B,
> + XORHI = 0x3C,
> + /* 0x3D */
> + /* 0x3E */
> + /* 0x3F */
> +};
> +#define I_TYPE_COUNT 0x40
> +
> +/* OPX Encodings for R-Type instructions */
> +enum {
> + /* 0x00 */
> + ERET = 0x01,
> + ROLI = 0x02,
> + ROL = 0x03,
> + FLUSHP = 0x04,
> + RET = 0x05,
> + NOR = 0x06,
> + MULXUU = 0x07,
> + CMPGE = 0x08,
> + BRET = 0x09,
> + /* 0x0A */
> + ROR = 0x0B,
> + FLUSHI = 0x0C,
> + JMP = 0x0D,
> + AND = 0x0E,
> + /* 0x0F */
> + CMPLT = 0x10,
> + /* 0x11 */
> + SLLI = 0x12,
> + SLL = 0x13,
> + WRPRS = 0x14,
> + /* 0x15 */
> + OR = 0x16,
> + MULXSU = 0x17,
> + CMPNE = 0x18,
> + /* 0x19 */
> + SRLI = 0x1A,
> + SRL = 0x1B,
> + NEXTPC = 0x1C,
> + CALLR = 0x1D,
> + XOR = 0x1E,
> + MULXSS = 0x1F,
> + CMPEQ = 0x20,
> + /* 0x21 */
> + /* 0x22 */
> + /* 0x23 */
> + DIVU = 0x24,
> + DIV = 0x25,
> + RDCTL = 0x26,
> + MUL = 0x27,
> + CMPGEU = 0x28,
> + INITI = 0x29,
> + /* 0x2A */
> + /* 0x2B */
> + /* 0x2C */
> + TRAP = 0x2D,
> + WRCTL = 0x2E,
> + /* 0x2F */
> + CMPLTU = 0x30,
> + ADD = 0x31,
> + /* 0x32 */
> + /* 0x33 */
> + BREAK = 0x34,
> + /* 0x35 */
> + SYNC = 0x36,
> + /* 0x37 */
> + /* 0x38 */
> + SUB = 0x39,
> + SRAI = 0x3A,
> + SRA = 0x3B,
> + /* 0x3C */
> + /* 0x3D */
> + /* 0x3E */
> + /* 0x3F */
> +};
> +#define R_TYPE_COUNT 0x40
> +
> +/*
> + * Return values for instruction handlers
> + */
> +#define INSTR_UNIMPL -2 /* Unimplemented instruction */
> +#define INSTR_ERR -1 /* Error in instruction */
> +#define PC_INC_NORMAL 0 /* Normal PC increment after instruction */
> +#define PC_INC_BY_INSTR 1 /* PC got incremented by instruction */
> +#define INSTR_BREAK 2 /* Break encountered */
> +#define INSTR_EXCEPTION 255 /* Instruction generated an exception
> + (the exception cause will be stored
> + in struct nios2 */
> +
> +#define EXCEPTION(cpu, cause) \
> + ({ \
> + (cpu)->exception_cause = cause; \
> + INSTR_EXCEPTION; \
> + })
> +
> +typedef struct DisasContext {
> + CPUNios2State *env;
> + TCGv *cpu_R;
> + int is_jmp;
> + target_ulong pc;
> + struct TranslationBlock *tb;
> +} DisasContext;
> +
> +typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
> +
> +struct instruction {
> + const char *name;
> + instruction_handler handler;
> +};
> +
> +/*
> + * Stringification macro (taken from Linux Kernel source code)
> + */
> +
> +/* Indirect stringification. Doing two levels allows the parameter to be a
> + * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
> + * converts to "bar".
> + */
> +
> +#define __stringify_1(x...) #x
> +#define __stringify(x...) __stringify_1(x)
We already have glue and stringify, no need to add another.
> +
> +#define INSTRUCTION(name) { __stringify(name), name }
> +#define INSTRUCTION_NOP(name) { __stringify(name), nop }
> +#define INSTRUCTION_UNIMPLEMENTED(name) { __stringify(name), unimplemented }
> +#define INSTRUCTION_ILLEGAL() { "", illegal_instruction }
> +
> +extern void handle_instruction(DisasContext *dc);
> +extern const char *instruction_get_string(uint32_t code);
> +
> +#define SIM_COMPAT 0
> +#define DISAS_GNU 1 /* Disassembly via GNU gdb derived routines */
> +#define DISAS_NIOS2 0 /* Disassembly via routines in instruction.c */
> +#if DISAS_NIOS2 && !SIM_COMPAT
> +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
> +#else
> +# define LOG_DIS(...) do { } while (0)
> +#endif
> +
> +#endif /* _INSTRUCTION_H_ */
> diff --git a/target-nios2/machine.c b/target-nios2/machine.c
> new file mode 100644
> index 0000000..09198db
> --- /dev/null
> +++ b/target-nios2/machine.c
> @@ -0,0 +1,33 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/boards.h"
> +
> +void cpu_save(QEMUFile *f, void *opaque)
> +{
> + /* TODO */
> +}
> +
> +int cpu_load(QEMUFile *f, void *opaque, int version_id)
> +{
> + /* TODO */
> + return 0;
> +}
> diff --git a/target-nios2/mmu.c b/target-nios2/mmu.c
> new file mode 100644
> index 0000000..a52fa1b
> --- /dev/null
> +++ b/target-nios2/mmu.c
> @@ -0,0 +1,273 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +/* Define this to enable MMU debug messages */
> +/* #define DEBUG_MMU */
> +
> +#ifdef DEBUG_MMU
> +#define MMU_LOG(x) x
> +#else
> +#define MMU_LOG(x)
> +#endif
> +
> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
> +{
> + switch (rn) {
> + case CR_TLBACC:
> + MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
> + break;
> +
> + case CR_TLBMISC:
> + MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
> + break;
> +
> + case CR_PTEADDR:
> + MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
> + break;
> +
> + default:
> + break;
> + }
> + return env->regs[rn];
> +}
> +
> +/* rw - 0 = read, 1 = write, 2 = fetch. */
> +unsigned int mmu_translate(CPUNios2State *env,
> + struct nios2_mmu_lookup *lu,
> + target_ulong vaddr, int rw, int mmu_idx)
> +{
> + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> + int vpn = vaddr >> 12;
> +
> + MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
> + vaddr, pid, vpn));
> +
> + int way;
> + for (way = 0; way < env->mmu.tlb_num_ways; way++) {
> +
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> +
> + MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + entry->tag, (entry->tag >> 12)));
> +
> + if (((entry->tag >> 12) != vpn) ||
> + (((entry->tag & (1<<11)) == 0) &&
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) != pid))) {
> + continue;
> + }
> + lu->vaddr = vaddr & TARGET_PAGE_MASK;
> + lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
> + lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
> + ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
> + ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
> +
> + MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + lu->vaddr, lu->paddr, lu->prot));
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
> +{
> + int idx;
> + MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
> +
> + for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
> + struct nios2_tlb_entry *entry = &env->mmu.tlb[idx];
> +
> + MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
> + idx, entry->tag, entry->data));
> +
> + if ((entry->tag & (1<<10)) && (!(entry->tag & (1<<11))) &&
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) == pid)) {
> + uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
> +
> + MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
> +
> + tlb_flush_page(env, vaddr);
> + }
> + }
> +}
> +
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
> +{
> + MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
> +
> + switch (rn) {
> + case CR_TLBACC:
> + MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
> + v >> CR_TLBACC_IGN_SHIFT,
> + (v & CR_TLBACC_C) ? 'C' : '.',
> + (v & CR_TLBACC_R) ? 'R' : '.',
> + (v & CR_TLBACC_W) ? 'W' : '.',
> + (v & CR_TLBACC_X) ? 'X' : '.',
> + (v & CR_TLBACC_G) ? 'G' : '.',
> + v & CR_TLBACC_PFN_MASK));
> +
> + /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
> + if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
> + int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
> + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> + int g = (v & CR_TLBACC_G) ? 1 : 0;
> + int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> + uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
> + uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
> + CR_TLBACC_X | CR_TLBACC_PFN_MASK);
> +
> + if ((entry->tag != newTag) || (entry->data != newData)) {
> + if (entry->tag & (1<<10)) {
> + /* Flush existing entry */
> + MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
> + entry->tag & TARGET_PAGE_MASK));
> + tlb_flush_page(env, entry->tag & TARGET_PAGE_MASK);
> + }
> + entry->tag = newTag;
> + entry->data = newData;
> + MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + entry->tag, entry->data));
> + }
> + /* Auto-increment tlbmisc.WAY */
> + env->regs[CR_TLBMISC] =
> + (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
> + (((way+1) & (env->mmu.tlb_num_ways-1)) << CR_TLBMISC_WAY_SHIFT);
> + }
> +
> + /* Writes to TLBACC don't change the read-back value */
> + env->mmu.tlbacc_wr = v;
> + break;
> +
> + case CR_TLBMISC:
> + MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
> + v >> CR_TLBMISC_WAY_SHIFT,
> + (v & CR_TLBMISC_RD) ? 'R' : '.',
> + (v & CR_TLBMISC_WR) ? 'W' : '.',
> + (v & CR_TLBMISC_DBL) ? '2' : '.',
> + (v & CR_TLBMISC_BAD) ? 'B' : '.',
> + (v & CR_TLBMISC_PERM) ? 'P' : '.',
> + (v & CR_TLBMISC_D) ? 'D' : '.',
> + (v & CR_TLBMISC_PID_MASK) >> 4));
> +
> + if ((v & CR_TLBMISC_PID_MASK) !=
> + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
> + mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
> + CR_TLBMISC_PID_SHIFT);
> + }
> + /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
> + if (v & CR_TLBMISC_RD) {
> + int way = (v >> CR_TLBMISC_WAY_SHIFT);
> + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> +
> + env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
> + env->regs[CR_TLBACC] |= entry->data |
> + ((entry->tag & (1<<11)) ? CR_TLBACC_G : 0);
> + env->regs[CR_TLBMISC] =
> + (v & ~CR_TLBMISC_PID_MASK) |
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) <<
> + CR_TLBMISC_PID_SHIFT);
> + env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
> + env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
> + MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
> + "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
> + way, vpn, entry->tag, entry->data,
> + env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
> + env->regs[CR_PTEADDR]));
> + } else {
> + env->regs[CR_TLBMISC] = v;
> + }
> +
> + env->mmu.tlbmisc_wr = v;
> + break;
> +
> + case CR_PTEADDR:
> + MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
> + v >> CR_PTEADDR_PTBASE_SHIFT,
> + (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
> +
> + /* Writes to PTEADDR don't change the read-back VPN value */
> + env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
> + (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
> + env->mmu.pteaddr_wr = v;
> + break;
> +
> + default:
> + break;
> + }
> +}
> +
> +void mmu_init(struct nios2_mmu *mmu)
> +{
> + MMU_LOG(qemu_log("mmu_init\n"));
> +
> + mmu->pid_bits = 8; /* TODO: get this from ALTR,pid-num-bits */
> + mmu->tlb_num_ways = 16; /* TODO: get this from ALTR,tlb-num-ways */
> + mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */
> + mmu->tlb_entry_mask = (mmu->tlb_num_entries/mmu->tlb_num_ways) - 1;
> +
> + mmu->tlb = (struct nios2_tlb_entry *)g_malloc0(
> + sizeof(struct nios2_tlb_entry) * mmu->tlb_num_entries);
> +}
> +
> +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env)
> +{
> + int i;
> + cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n",
> + env->mmu.tlb_num_ways, env->mmu.tlb_num_entries,
> + env->mmu.pid_bits);
> +
> + for (i = 0; i < env->mmu.tlb_num_entries; i++) {
> + struct nios2_tlb_entry *entry = &env->mmu.tlb[i];
> + cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X "
> + "PID %02X %c PFN %05X %c%c%c%c\n",
> + i, entry->tag, entry->data,
> + (entry->tag & (1<<10)) ? 'V' : '-',
> + entry->tag >> 12, entry->tag & ((1<<env->mmu.pid_bits)-1),
> + (entry->tag & (1<<11)) ? 'G' : '-',
> + entry->data & CR_TLBACC_PFN_MASK,
> + (entry->data & CR_TLBACC_C) ? 'C' : '-',
> + (entry->data & CR_TLBACC_R) ? 'R' : '-',
> + (entry->data & CR_TLBACC_W) ? 'W' : '-',
> + (entry->data & CR_TLBACC_X) ? 'X' : '-');
> + }
> +}
> +
> diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
> new file mode 100644
> index 0000000..01d67cf
> --- /dev/null
> +++ b/target-nios2/mmu.h
> @@ -0,0 +1,49 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +struct nios2_tlb_entry {
> + target_ulong tag;
> + target_ulong data;
> +};
> +
> +struct nios2_mmu {
> + int pid_bits;
> + int tlb_num_ways;
> + int tlb_num_entries;
> + int tlb_entry_mask;
> + uint32_t pteaddr_wr;
> + uint32_t tlbacc_wr;
> + uint32_t tlbmisc_wr;
> + struct nios2_tlb_entry *tlb;
> +};
> +
> +struct nios2_mmu_lookup {
> + target_ulong vaddr;
> + target_ulong paddr;
> + int prot;
> +};
> +
> +void mmu_flip_um(CPUNios2State *env, unsigned int um);
> +unsigned int mmu_translate(CPUNios2State *env,
> + struct nios2_mmu_lookup *lu,
> + target_ulong vaddr, int rw, int mmu_idx);
> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn);
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v);
> +void mmu_init(struct nios2_mmu *mmu);
> diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
> new file mode 100644
> index 0000000..37bac66
> --- /dev/null
> +++ b/target-nios2/op_helper.c
> @@ -0,0 +1,125 @@
> +/*
> + * Altera Nios II helper routines.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "cpu.h"
> +#include "dyngen-exec.h"
Remove.
> +#include "helper.h"
> +#include "host-utils.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#define MMUSUFFIX _mmu
> +#define SHIFT 0
> +#include "softmmu_template.h"
> +#define SHIFT 1
> +#include "softmmu_template.h"
> +#define SHIFT 2
> +#include "softmmu_template.h"
> +#define SHIFT 3
> +#include "softmmu_template.h"
> +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> + uintptr_t retaddr)
> +{
> + TranslationBlock *tb;
> + CPUNios2State *saved_env;
> + int ret;
> +
> + saved_env = env;
> + env = env1;
References to global env should be removed, the CPU state should be
passed around instead. Please check for example Sparc or recent
commits to s390x.
> +
> + ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
> + if (unlikely(ret)) {
> + if (retaddr) {
> + /* now we have a real cpu fault */
> + tb = tb_find_pc(retaddr);
> + if (tb) {
> + /* The PC is inside the translated code. It means that we have
> + a virtual CPU fault */
> + cpu_restore_state(tb, env, retaddr);
> + }
> + }
> + cpu_loop_exit(env);
> + }
> + env = saved_env;
> +}
> +
> +void helper_raise_exception(uint32_t index)
> +{
> + env->exception_index = index;
> + cpu_loop_exit(env);
> +}
> +
> +uint32_t helper_mmu_read(uint32_t rn)
> +{
> + return mmu_read(env, rn);
> +}
> +
> +void helper_mmu_write(uint32_t rn, uint32_t v)
> +{
> + mmu_write(env, rn, v);
> +}
> +
> +void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
> +{
> + if (addr & mask) {
> + qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
> + addr, mask, wr, dr);
> + env->regs[CR_BADADDR] = addr;
> + env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
> + helper_raise_exception(EXCP_UNALIGN);
> + }
> +}
> +
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> + int is_write, int is_exec, int is_asi, int size)
> +{
> + qemu_log("unassigned access to %08X\n", addr);
> +}
> +
> +uint32_t helper_divs(uint32_t a, uint32_t b)
> +{
> + return (int32_t)a / (int32_t)b;
> +}
> +
> +uint32_t helper_divu(uint32_t a, uint32_t b)
> +{
> + return a / b;
> +}
> +
> +#ifdef CALL_TRACING
> +void helper_call_status(uint32_t pc, uint32_t target)
> +{
> + qemu_log("%08X: CALL %08X %s\n", pc, target, lookup_symbol(target));
> +}
> +
> +void helper_eret_status(uint32_t pc)
> +{
> + qemu_log("%08X: ERET STATUS %08X, ESTATUS %08X, EA %08X\n",
> + pc, env->regs[CR_STATUS], env->regs[CR_ESTATUS], env->regs[R_EA]);
> +}
> +
> +void helper_ret_status(uint32_t pc)
> +{
> + qemu_log("%08X: RET RA %08X\n", pc, env->regs[R_RA]);
> +}
> +#endif
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/translate.c b/target-nios2/translate.c
> new file mode 100644
> index 0000000..5d0979f
> --- /dev/null
> +++ b/target-nios2/translate.c
> @@ -0,0 +1,252 @@
> +/*
> + * Altera Nios II emulation for qemu: main translation routines.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdarg.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <inttypes.h>
> +#include <assert.h>
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "disas.h"
> +#include "helper.h"
> +#include "qemu-common.h"
> +
> +#include "instruction.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.h"
> +
> +static const char *regnames[] = {
> + "zero", "at", "r2", "r3",
> + "r4", "r5", "r6", "r7",
> + "r8", "r9", "r10", "r11",
> + "r12", "r13", "r14", "r15",
> + "r16", "r17", "r18", "r19",
> + "r20", "r21", "r22", "r23",
> + "et", "bt", "gp", "sp",
> + "fp", "ea", "ba", "ra",
> + "status", "estatus", "bstatus", "ienable",
> + "ipending", "cpuid", "reserved", "exception",
> + "pteaddr", "tlbacc", "tlbmisc", "reserved",
> + "badaddr", "config", "mpubase", "mpuacc",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "rpc"
> +};
> +
> +static TCGv_ptr cpu_env;
> +static TCGv cpu_R[NUM_CORE_REGS];
> +
> +#include "gen-icount.h"
> +
> +/* generate intermediate code for basic block 'tb'. */
> +static void gen_intermediate_code_internal(
> + CPUNios2State *env, TranslationBlock *tb, int search_pc)
> +{
> + DisasContext dc1, *dc = &dc1;
> + int num_insns;
> + int max_insns;
> + uint32_t next_page_start;
> + int j, lj = -1;
> + uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
> +
> + /* Initialize DC */
> + dc->env = env;
> + dc->cpu_R = cpu_R;
> + dc->is_jmp = DISAS_NEXT;
> + dc->pc = tb->pc;
> + dc->tb = tb;
> +
> + /* Dump the CPU state to the log */
> + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> + qemu_log("--------------\n");
> + log_cpu_state(env, 0);
> + }
> +
> + /* Set up instruction counts */
> + num_insns = 0;
> + max_insns = tb->cflags & CF_COUNT_MASK;
> + if (max_insns == 0) {
> + max_insns = CF_COUNT_MASK;
> + }
> + next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
> +
> + gen_icount_start();
> + do {
> + /* Mark instruction start with associated PC */
> + if (search_pc) {
> + j = gen_opc_ptr - gen_opc_buf;
> + if (lj < j) {
> + lj++;
> + while (lj < j) {
> + gen_opc_instr_start[lj++] = 0;
> + }
> + }
> + gen_opc_pc[lj] = dc->pc;
> + gen_opc_instr_start[lj] = 1;
> + gen_opc_icount[lj] = num_insns;
> + }
> +
> + LOG_DIS("%8.8x:\t", dc->pc);
> +
> + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
> + gen_io_start();
> + }
> +
> + /* Decode an instruction */
> + handle_instruction(dc);
> +
> + dc->pc += 4;
> + num_insns++;
> +
> + /* Translation stops when a conditional branch is encountered.
> + * Otherwise the subsequent code could get translated several times.
> + * Also stop translation when a page boundary is reached. This
> + * ensures prefetch aborts occur at the right place. */
> + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
> + !env->singlestep_enabled &&
> + !singlestep &&
> + dc->pc < next_page_start &&
> + num_insns < max_insns);
> +
> + if (tb->cflags & CF_LAST_IO) {
> + gen_io_end();
> + }
> +
> + /* Indicate where the next block should start */
> + switch (dc->is_jmp) {
> + case DISAS_NEXT:
> + /* Save the current PC back into the CPU register */
> + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
> + tcg_gen_exit_tb(0);
> + break;
> +
> + default:
> + case DISAS_JUMP:
> + case DISAS_UPDATE:
> + /* The jump will already have updated the PC register */
> + tcg_gen_exit_tb(0);
> + break;
> +
> + case DISAS_TB_JUMP:
> + /* nothing more to generate */
> + break;
> + }
> +
> + /* End off the block */
> + gen_icount_end(tb, num_insns);
> + *gen_opc_ptr = INDEX_op_end;
> +
> + /* Mark instruction starts for the final generated instruction */
> + if (search_pc) {
> + j = gen_opc_ptr - gen_opc_buf;
> + lj++;
> + while (lj <= j) {
> + gen_opc_instr_start[lj++] = 0;
> + }
> + } else {
> + tb->size = dc->pc - tb->pc;
> + tb->icount = num_insns;
> + }
> +
> +#ifdef DEBUG_DISAS
> + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> + qemu_log("----------------\n");
> + qemu_log("IN: %s\n", lookup_symbol(tb->pc));
> + log_target_disas(tb->pc, dc->pc - tb->pc, 0);
> + qemu_log("\nisize=%d osize=%td\n",
> + dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
> + }
> +#endif
> +}
> +
> +void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> + gen_intermediate_code_internal(env, tb, 0);
> +}
> +
> +void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> + gen_intermediate_code_internal(env, tb, 1);
> +}
> +
> +void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
> + int flags)
> +{
> + int i;
> +
> + if (!env || !f) {
> + return;
> + }
> +
> + cpu_fprintf(f, "IN: PC=%x %s\n",
> + env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
> +
> + for (i = 0; i < NUM_CORE_REGS; i++) {
> + cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
> + if ((i + 1) % 4 == 0) {
> + cpu_fprintf(f, "\n");
> + }
> + }
> + cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
> + env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
> + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
> + env->mmu.tlbacc_wr);
> + cpu_fprintf(f, "\n\n");
> +}
> +
> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
> +{
> + Nios2CPU *cpu;
> + int i;
> +
> + cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
> +
> + cpu->env.reset_addr = RESET_ADDRESS;
> + cpu->env.exception_addr = EXCEPTION_ADDRESS;
> + cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
> +
> + cpu_reset(CPU(cpu));
> + qemu_init_vcpu(&cpu->env);
> +
> + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
> +
> + for (i = 0; i < NUM_CORE_REGS; i++) {
> + cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
> + offsetof(CPUNios2State, regs[i]),
> + regnames[i]);
> + }
> +
> +#define GEN_HELPER 2
> +#include "helper.h"
> +
> + return cpu;
> +}
> +
> +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
> +{
> + env->regs[R_PC] = gen_opc_pc[pc_pos];
> +}
> +
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-11 20:19 ` Blue Swirl
@ 2012-09-14 3:30 ` Chris Wulff
0 siblings, 0 replies; 31+ messages in thread
From: Chris Wulff @ 2012-09-14 3:30 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 4587 bytes --]
Thanks for the feedback. Nothing jumps out at me that I disagree with. I'll
get it updated and another patch out once I get the chance to incorporate
the info.
> +#include "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {
CamelCase
Yep. I missed that one in reviewing the coding standard and the checkpatch
script apparently doesn't flag it. I'll clean up that one an the others
like it.
> +
> +#if !defined(CONFIG_USER_ONLY)
> + struct nios2_mmu mmu;
> +#endif
> +
> + CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
'extern' is useless for functions.
True. I will remove for consistency with other code.
> +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;
Just say no to globals.
> +#ifndef NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"
Dyngen model is being obsoleted in coming days. Please make all
helpers which need access to CPU state take the state as parameter.
> +
> +register struct CPUNios2State *env asm(AREG0);
This is already defined in dyngen-exec.h, but see above. Maybe you are
not using Git HEAD? No target uses exec.h anymore.
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +
The functions below should go to cpu.h.
I've been working on this on and off for months now so it is very well
possible that I referenced older code that still did use some of this
stuff. I'll review the latest to see how this is being done now. I agree
that globals aren't good. I'll see if I can find how to get the information
passed along to the appropriate places that need it.
> +void do_interrupt(CPUNios2State *env)
> +{
> + switch (env->exception_index) {
> + case EXCP_IRQ:
> + assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
I hope this is not a guest triggerable assert.
It shouldn't be. But maybe this should just be a debug message and not
trigger an exception if this is the case. We should never be trying to send
an IRQ exception to the guest if interrupts are globally disabled.
> + if (has_mmu && (addr < 0xC0000000)) {
> + hit = mmu_translate(env, &lu, addr, 0, 0);
> + if (hit) {
> + vaddr = addr & TARGET_PAGE_MASK;
> + paddr = lu.paddr + vaddr - lu.vaddr;
> + } else {
> + cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);
cpu_get_phys_page_debug is used for memory access from monitor.
Aborting because user specified a bad address is rather draconian.
I was seeing this come from the disassembly code when I had a mismatch
between the TLB in the processor emulation and the one that QEMU knows
about (due to improper removal of QEMU tlb entries when they were
overwritten in the NiosII TLB.) I can make this into a debug message though
instead. I'm not seeing this come out at all now that I've fixed my TLB
implementation.
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
Maybe assert instead.
> + break;
> + }
> +}
> + */
> +
> +/* I-Type instruction */
> +struct i_type {
IType
> + uint32_t op:6;
> + uint32_t imm16:16;
> + uint32_t b:5;
> + uint32_t a:5;
> +} __attribute__((packed));
QEMU_PACKED
Yep. I will change this and any others.
> +#define __stringify_1(x...) #x
> +#define __stringify(x...) __stringify_1(x)
We already have glue and stringify, no need to add another.
Ok, didn't see those. This was some of the bits I originally got from
nios2sim-ng, but I can easily clean that up.
> +#include "cpu.h"
> +#include "dyngen-exec.h"
Remove.
> +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> + uintptr_t retaddr)
> +{
> + TranslationBlock *tb;
> + CPUNios2State *saved_env;
> + int ret;
> +
> + saved_env = env;
> + env = env1;
References to global env should be removed, the CPU state should be
passed around instead. Please check for example Sparc or recent
commits to s390x.
Ok, I'll take a look at that example. Definitely good to get rid of globals.
-- Chris Wulff
[-- Attachment #2: Type: text/html, Size: 5909 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
2012-09-11 20:19 ` Blue Swirl
@ 2012-09-11 21:34 ` Aurelien Jarno
2012-09-11 22:30 ` Richard Henderson
` (3 more replies)
2012-09-15 14:55 ` Andreas Färber
2 siblings, 4 replies; 31+ messages in thread
From: Aurelien Jarno @ 2012-09-11 21:34 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> target-nios2/Makefile.objs | 5 +
> target-nios2/altera_iic.c | 100 +++
> target-nios2/cpu-qom.h | 69 +++
> target-nios2/cpu.c | 83 +++
> target-nios2/cpu.h | 259 ++++++++
> target-nios2/exec.h | 60 ++
> target-nios2/helper.c | 291 +++++++++
> target-nios2/helper.h | 45 ++
> target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
> target-nios2/instruction.h | 290 +++++++++
> target-nios2/machine.c | 33 +
> target-nios2/mmu.c | 273 +++++++++
> target-nios2/mmu.h | 49 ++
> target-nios2/op_helper.c | 125 ++++
> target-nios2/translate.c | 252 ++++++++
> 15 files changed, 3397 insertions(+)
> create mode 100644 target-nios2/Makefile.objs
> create mode 100644 target-nios2/altera_iic.c
> create mode 100644 target-nios2/cpu-qom.h
> create mode 100644 target-nios2/cpu.c
> create mode 100644 target-nios2/cpu.h
> create mode 100644 target-nios2/exec.h
> create mode 100644 target-nios2/helper.c
> create mode 100644 target-nios2/helper.h
> create mode 100644 target-nios2/instruction.c
> create mode 100644 target-nios2/instruction.h
> create mode 100644 target-nios2/machine.c
> create mode 100644 target-nios2/mmu.c
> create mode 100644 target-nios2/mmu.h
> create mode 100644 target-nios2/op_helper.c
> create mode 100644 target-nios2/translate.c
>
> diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
> new file mode 100644
> index 0000000..d072795
> --- /dev/null
> +++ b/target-nios2/Makefile.objs
> @@ -0,0 +1,5 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
> +obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
> +obj-y += altera_iic.o
> +
> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
> new file mode 100644
> index 0000000..d45d6fa
> --- /dev/null
> +++ b/target-nios2/altera_iic.c
> @@ -0,0 +1,100 @@
> +/*
> + * QEMU Altera Internal Interrupt Controller.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {
> + SysBusDevice busdev;
> + Nios2CPU *cpu;
> + qemu_irq parent_irq;
> +};
> +
> +static void update_irq(struct altera_iic *pv)
> +{
> + uint32_t i;
> +
> + if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
> + qemu_irq_lower(pv->parent_irq);
> + return;
> + }
> +
> + for (i = 0; i < 32; i++) {
> + if (pv->cpu->env.regs[CR_IPENDING] &
> + pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
> + break;
> + }
> + }
> + if (i == 32) {
> + qemu_irq_lower(pv->parent_irq);
> + } else {
> + qemu_irq_raise(pv->parent_irq);
> + }
> +}
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> + struct altera_iic *pv = opaque;
> +
> + pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
> + pv->cpu->env.regs[CR_IPENDING] |= level << irq;
> +
> + update_irq(pv);
> +}
> +
> +static int altera_iic_init(SysBusDevice *dev)
> +{
> + struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
> + pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
> +
> + qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> + sysbus_init_irq(dev, &pv->parent_irq);
> +
> + return 0;
> +}
> +
> +static Property altera_iic_properties[] = {
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_iic_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = altera_iic_init;
> + dc->props = altera_iic_properties;
> +}
> +
> +static TypeInfo altera_iic_info = {
> + .name = "altera,iic",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct altera_iic),
> + .class_init = altera_iic_class_init,
> +};
> +
> +static void altera_iic_register(void)
> +{
> + type_register_static(&altera_iic_info);
> +}
> +
> +type_init(altera_iic_register)
> +
> diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
> new file mode 100644
> index 0000000..be6fa65
> --- /dev/null
> +++ b/target-nios2/cpu-qom.h
> @@ -0,0 +1,69 @@
> +/*
> + * QEMU Nios II CPU
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +#ifndef QEMU_NIOS2_CPU_QOM_H
> +#define QEMU_NIOS2_CPU_QOM_H
> +
> +#include "qemu/cpu.h"
> +
> +#define TYPE_NIOS2_CPU "nios2-cpu"
> +
> +#define NIOS2_CPU_CLASS(klass) \
> + OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU(obj) \
> + OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
> +#define NIOS2_CPU_GET_CLASS(obj) \
> + OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
> +
> +/**
> + * Nios2CPUClass:
> + * @parent_reset: The parent class' reset handler.
> + *
> + * A Nios2 CPU model.
> + */
> +typedef struct Nios2CPUClass {
> + /*< private >*/
> + CPUClass parent_class;
> + /*< public >*/
> +
> + void (*parent_reset)(CPUState *cpu);
> +} Nios2CPUClass;
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> + /*< private >*/
> + CPUState parent_obj;
> + /*< public >*/
> +
> + CPUNios2State env;
> +} Nios2CPU;
> +
> +static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
> +{
> + return NIOS2_CPU(container_of(env, Nios2CPU, env));
> +}
> +
> +#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
> +
> +#endif /* QEMU_NIOS2_CPU_QOM_H */
> diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
> new file mode 100644
> index 0000000..bd819c4
> --- /dev/null
> +++ b/target-nios2/cpu.c
> @@ -0,0 +1,83 @@
> +/*
> + * QEMU Nios II CPU
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "cpu.h"
> +#include "qemu-common.h"
> +
> +
> +/* CPUClass::reset() */
> +static void nios2_cpu_reset(CPUState *s)
> +{
> + Nios2CPU *cpu = NIOS2_CPU(s);
> + Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
> + CPUNios2State *env = &cpu->env;
> +
> + if (qemu_loglevel_mask(CPU_LOG_RESET)) {
> + qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
> + log_cpu_state(env, 0);
> + }
> +
> + mcc->parent_reset(s);
> +
> + tlb_flush(env, 1);
> +
> + memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
> + env->regs[R_PC] = env->reset_addr;
> +
> +#if defined(CONFIG_USER_ONLY)
> + /* start in user mode with interrupts enabled. */
> + env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
> +#else
> + mmu_init(&env->mmu);
> +#endif
> +}
> +
> +static void nios2_cpu_initfn(Object *obj)
> +{
> + Nios2CPU *cpu = NIOS2_CPU(obj);
> + CPUNios2State *env = &cpu->env;
> +
> + cpu_exec_init(env);
> +}
> +
> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
> +{
> + CPUClass *cc = CPU_CLASS(oc);
> + Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
> +
> + mcc->parent_reset = cc->reset;
> + cc->reset = nios2_cpu_reset;
> +}
> +
> +static const TypeInfo nios2_cpu_type_info = {
> + .name = TYPE_NIOS2_CPU,
> + .parent = TYPE_CPU,
> + .instance_size = sizeof(Nios2CPU),
> + .instance_init = nios2_cpu_initfn,
> + .class_size = sizeof(Nios2CPUClass),
> + .class_init = nios2_cpu_class_init,
> +};
> +
> +static void nios2_cpu_register_types(void)
> +{
> + type_register_static(&nios2_cpu_type_info);
> +}
> +
> +type_init(nios2_cpu_register_types)
> diff --git a/target-nios2/cpu.h b/target-nios2/cpu.h
> new file mode 100644
> index 0000000..0ee22e6
> --- /dev/null
> +++ b/target-nios2/cpu.h
> @@ -0,0 +1,259 @@
> +/*
> + * Altera Nios II virtual CPU header
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +#ifndef CPU_NIOS2_H
> +#define CPU_NIOS2_H
> +
> +#include "config.h"
> +#include "qemu-common.h"
> +
> +#define TARGET_LONG_BITS 32
> +
> +#define CPUArchState struct CPUNios2State
> +
> +#include "cpu-defs.h"
> +#include "softfloat.h"
> +struct CPUNios2State;
> +typedef struct CPUNios2State CPUNios2State;
> +#if !defined(CONFIG_USER_ONLY)
> +#include "mmu.h"
> +#endif
> +
> +#define TARGET_HAS_ICE 1
> +
> +/* Configuration options for Nios II */
> +#define RESET_ADDRESS 0x00000000
> +#define EXCEPTION_ADDRESS 0x00000004
> +#define FAST_TLB_MISS_ADDRESS 0x00000008
> +
> +
> +#define ELF_MACHINE EM_ALTERA_NIOS2
> +
> +/* GP regs + CR regs + PC */
> +#define NUM_CORE_REGS (32 + 32 + 1)
> +
> +/* General purpose egister aliases */
> +#define R_ZERO 0
> +#define R_AT 1
> +#define R_RET0 2
> +#define R_RET1 3
> +#define R_ARG0 4
> +#define R_ARG1 5
> +#define R_ARG2 6
> +#define R_ARG3 7
> +#define R_ET 24
> +#define R_BT 25
> +#define R_GP 26
> +#define R_SP 27
> +#define R_FP 28
> +#define R_EA 29
> +#define R_BA 30
> +#define R_RA 31
> +
> +/* Control register aliases */
> +#define CR_BASE 32
> +#define CR_STATUS (CR_BASE + 0)
> +#define CR_STATUS_PIE (1<<0)
> +#define CR_STATUS_U (1<<1)
> +#define CR_STATUS_EH (1<<2)
> +#define CR_STATUS_IH (1<<3)
> +#define CR_STATUS_IL (63<<4)
> +#define CR_STATUS_CRS (63<<10)
> +#define CR_STATUS_PRS (63<<16)
> +#define CR_STATUS_NMI (1<<22)
> +#define CR_STATUS_RSIE (1<<23)
> +#define CR_ESTATUS (CR_BASE + 1)
> +#define CR_BSTATUS (CR_BASE + 2)
> +#define CR_IENABLE (CR_BASE + 3)
> +#define CR_IPENDING (CR_BASE + 4)
> +#define CR_CPUID (CR_BASE + 5)
> +#define CR_EXCEPTION (CR_BASE + 7)
> +#define CR_PTEADDR (CR_BASE + 8)
> +#define CR_PTEADDR_PTBASE_SHIFT 22
> +#define CR_PTEADDR_PTBASE_MASK (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
> +#define CR_PTEADDR_VPN_SHIFT 2
> +#define CR_PTEADDR_VPN_MASK (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
> +#define CR_TLBACC (CR_BASE + 9)
> +#define CR_TLBACC_IGN_SHIFT 25
> +#define CR_TLBACC_IGN_MASK (0x7F << CR_TLBACC_IGN_SHIFT)
> +#define CR_TLBACC_C (1<<24)
> +#define CR_TLBACC_R (1<<23)
> +#define CR_TLBACC_W (1<<22)
> +#define CR_TLBACC_X (1<<21)
> +#define CR_TLBACC_G (1<<20)
> +#define CR_TLBACC_PFN_MASK 0x000FFFFF
> +#define CR_TLBMISC (CR_BASE + 10)
> +#define CR_TLBMISC_WAY_SHIFT 20
> +#define CR_TLBMISC_WAY_MASK (0xF << CR_TLBMISC_WAY_SHIFT)
> +#define CR_TLBMISC_RD (1<<19)
> +#define CR_TLBMISC_WR (1<<18)
> +#define CR_TLBMISC_PID_SHIFT 4
> +#define CR_TLBMISC_PID_MASK (0x3FFF << CR_TLBMISC_PID_SHIFT)
> +#define CR_TLBMISC_DBL (1<<3)
> +#define CR_TLBMISC_BAD (1<<2)
> +#define CR_TLBMISC_PERM (1<<1)
> +#define CR_TLBMISC_D (1<<0)
> +#define CR_BADADDR (CR_BASE + 12)
> +#define CR_CONFIG (CR_BASE + 13)
> +#define CR_MPUBASE (CR_BASE + 14)
> +#define CR_MPUACC (CR_BASE + 15)
> +
> +/* Other registers */
> +#define R_PC 64
> +
> +/* Exceptions */
> +#define EXCP_BREAK -1
> +#define EXCP_RESET 0
> +#define EXCP_PRESET 1
> +#define EXCP_IRQ 2
> +#define EXCP_TRAP 3
> +#define EXCP_UNIMPL 4
> +#define EXCP_ILLEGAL 5
> +#define EXCP_UNALIGN 6
> +#define EXCP_UNALIGND 7
> +#define EXCP_DIV 8
> +#define EXCP_SUPERA 9
> +#define EXCP_SUPERI 10
> +#define EXCP_SUPERD 11
> +#define EXCP_TLBD 12
> +#define EXCP_TLBX 13
> +#define EXCP_TLBR 14
> +#define EXCP_TLBW 15
> +#define EXCP_MPUI 16
> +#define EXCP_MPUD 17
> +
> +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3
> +
> +#define NB_MMU_MODES 2
> +
> +typedef struct CPUNios2State {
> + uint32_t regs[NUM_CORE_REGS];
> +
> + /* Addresses that are hard-coded in the FPGA build settings */
> + uint32_t reset_addr;
> + uint32_t exception_addr;
> + uint32_t fast_tlb_miss_addr;
> +
> +#if !defined(CONFIG_USER_ONLY)
> + struct nios2_mmu mmu;
> +#endif
> +
> + CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
> +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;
> +
> +static inline CPUNios2State *cpu_init(const char *cpu_model)
> +{
> + Nios2CPU *cpu = cpu_nios2_init(cpu_model);
> + if (cpu == NULL) {
> + return NULL;
> + }
> + return &cpu->env;
> +}
> +
> +#define cpu_exec cpu_nios2_exec
> +#define cpu_gen_code cpu_nios2_gen_code
> +#define cpu_signal_handler cpu_nios2_signal_handler
> +
> +#define CPU_SAVE_VERSION 1
> +
> +#define TARGET_PAGE_BITS 12
> +
> +/* MMU modes definitions */
> +#define MMU_MODE0_SUFFIX _kernel
> +#define MMU_MODE1_SUFFIX _user
> +#define MMU_SUPERVISOR_IDX 0
> +#define MMU_USER_IDX 1
> +
> +static inline int cpu_mmu_index(CPUNios2State *env)
> +{
> + return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
> + MMU_SUPERVISOR_IDX;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu);
> +#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
> +
> +#if defined(CONFIG_USER_ONLY)
> +static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
> +{
> + if (newsp) {
> + env->regs[R_SP] = newsp;
> + }
> + env->regs[R_RET0] = 0;
> +}
> +#endif
> +
> +static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
> +{
> +}
> +
> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
> +{
> + return env->regs[CR_STATUS] & CR_STATUS_PIE;
> +}
> +
> +#include "cpu-all.h"
> +
> +static inline target_ulong cpu_get_pc(CPUNios2State *env)
> +{
> + return env->regs[R_PC];
> +}
> +
> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
> + target_ulong *cs_base, int *flags)
> +{
> + *pc = env->regs[R_PC];
> + *cs_base = 0;
> + *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
> +}
> +
> +#if !defined(CONFIG_USER_ONLY)
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> + int is_write, int is_exec, int is_asi, int size);
> +#endif
> +
> +static inline bool cpu_has_work(CPUNios2State *env)
> +{
> + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +#include "exec-all.h"
> +
> +static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
> +{
> + env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* CPU_NIOS2_H */
> +
> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
> new file mode 100644
> index 0000000..cd4446b
> --- /dev/null
> +++ b/target-nios2/exec.h
> @@ -0,0 +1,60 @@
> +/*
> + * Altera Nios II execution defines
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#ifndef NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"
> +
> +register struct CPUNios2State *env asm(AREG0);
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +
> +static inline int cpu_has_work(CPUState *env)
> +{
> + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +static inline int cpu_halted(CPUState *env)
> +{
> + if (!env->halted) {
> + return 0;
> + }
> +
> + if (cpu_has_work(env)) {
> + env->halted = 0;
> + return 0;
> + }
> +
> + return EXCP_HALTED;
> +}
> +
> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
> +{
> + env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* NIOS2_EXEC_H */
> +
> diff --git a/target-nios2/helper.c b/target-nios2/helper.c
> new file mode 100644
> index 0000000..9b200c9
> --- /dev/null
> +++ b/target-nios2/helper.c
> @@ -0,0 +1,291 @@
> +/*
> + * Altera Nios II helper routines.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "host-utils.h"
> +
> +#if defined(CONFIG_USER_ONLY)
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> + env->exception_index = -1;
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu)
> +{
> + env->exception_index = 0xaa;
> + cpu_dump_state(env, stderr, fprintf, 0);
> + return 1;
> +}
> +
> +#else /* !CONFIG_USER_ONLY */
> +
> +bool has_mmu = true;
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> + switch (env->exception_index) {
> + case EXCP_IRQ:
> + assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
> +
> + qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
> +
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_IH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_TLBD:
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
> + env->regs[R_PC]);
> + log_cpu_state(env, 0);
> +
> + /* Fast TLB miss */
> + /* Variation from the spec. Table 3-35 of the cpu reference shows
> + * estatus not being changed for TLB miss but this appears to
> + * be incorrect. */
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->fast_tlb_miss_addr;
> + } else {
> + qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
> + env->regs[R_PC]);
> +
> + /* Double TLB miss */
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
> +
> + env->regs[R_PC] = env->exception_addr;
> + }
> + break;
> +
> + case EXCP_TLBR:
> + case EXCP_TLBW:
> + case EXCP_TLBX:
> + qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
> + log_cpu_state(env, 0);
> +
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
> + }
> +
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_SUPERA:
> + case EXCP_SUPERI:
> + case EXCP_SUPERD:
> + qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
> + env->regs[R_PC]);
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_ILLEGAL:
> + case EXCP_TRAP:
> + qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
> + env->regs[R_PC]);
> +
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
> + env->regs[R_EA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + case EXCP_BREAK:
> + if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
> + env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
> + env->regs[R_BA] = env->regs[R_PC] + 4;
> + }
> +
> + env->regs[CR_STATUS] |= CR_STATUS_EH;
> + env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
> +
> + env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
> + env->regs[CR_EXCEPTION] |= (env->exception_index & 0x1F) << 2;
> +
> + env->regs[R_PC] = env->exception_addr;
> + break;
> +
> + default:
> + cpu_abort(env, "unhandled exception type=%d\n",
> + env->exception_index);
> + break;
> + }
> +}
> +
> +static int cpu_nios2_handle_virtual_page(
> + CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
> +{
> + target_ulong vaddr, paddr;
> + struct nios2_mmu_lookup lu;
> + unsigned int hit;
> + hit = mmu_translate(env, &lu, address, rw, mmu_idx);
> + if (hit) {
> + vaddr = address & TARGET_PAGE_MASK;
> + paddr = lu.paddr + vaddr - lu.vaddr;
> +
> + if (((rw == 0) && (lu.prot & PAGE_READ)) ||
> + ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
> + ((rw == 2) && (lu.prot & PAGE_EXEC))) {
> +
> + tlb_set_page(env, vaddr, paddr, lu.prot,
> + mmu_idx, TARGET_PAGE_SIZE);
> + return 0;
> + } else {
> + /* Permission violation */
> + env->exception_index = (rw == 0) ? EXCP_TLBR :
> + ((rw == 1) ? EXCP_TLBW :
> + EXCP_TLBX);
> + }
> + } else {
> + env->exception_index = EXCP_TLBD;
> + }
> +
> + if (rw == 2) {
> + env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
> + } else {
> + env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
> + }
> + env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
> + env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
> + env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
> + env->regs[CR_BADADDR] = address;
> + return 1;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> + int rw, int mmu_idx, int is_softmmu)
> +{
> + if (has_mmu) {
> + if (MMU_SUPERVISOR_IDX == mmu_idx) {
> + if (address >= 0xC0000000) {
> + /* Kernel physical page - TLB bypassed */
> + address &= TARGET_PAGE_MASK;
> + tlb_set_page(env, address, address, PAGE_BITS,
> + mmu_idx, TARGET_PAGE_SIZE);
> + } else if (address >= 0x80000000) {
> + /* Kernel virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + } else {
> + /* User virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + }
> + } else {
> + if (address >= 0x80000000) {
> + /* Illegal access from user mode */
> + env->exception_index = EXCP_SUPERA;
> + env->regs[CR_BADADDR] = address;
> + return 1;
> + } else {
> + /* User virtual page */
> + return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> + }
> + }
> + } else {
> + /* No MMU */
> + address &= TARGET_PAGE_MASK;
> + tlb_set_page(env, address, address, PAGE_BITS,
> + mmu_idx, TARGET_PAGE_SIZE);
> + }
> +
> + return 0;
> +}
> +
> +target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
> + target_ulong addr)
> +{
> + target_ulong vaddr, paddr = 0;
> + struct nios2_mmu_lookup lu;
> + unsigned int hit;
> +
> + if (has_mmu && (addr < 0xC0000000)) {
> + hit = mmu_translate(env, &lu, addr, 0, 0);
> + if (hit) {
> + vaddr = addr & TARGET_PAGE_MASK;
> + paddr = lu.paddr + vaddr - lu.vaddr;
> + } else {
> + cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);
> + }
> + } else {
> + paddr = addr & TARGET_PAGE_MASK;
> + }
> +
> + return paddr;
> +}
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/helper.h b/target-nios2/helper.h
> new file mode 100644
> index 0000000..69b6684
> --- /dev/null
> +++ b/target-nios2/helper.h
> @@ -0,0 +1,45 @@
> +/*
> + * Altera Nios II helper routines header.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "def-helper.h"
> +
> +/* Define this to enable tracing calls/returns */
> +/* #define CALL_TRACING */
> +
> +#ifdef CALL_TRACING
> +DEF_HELPER_2(call_status, void, i32, i32)
> +DEF_HELPER_1(eret_status, void, i32)
> +DEF_HELPER_1(ret_status, void, i32)
> +#endif
> +
> +DEF_HELPER_1(raise_exception, void, i32)
> +
> +#if !defined(CONFIG_USER_ONLY)
> +DEF_HELPER_1(mmu_read, i32, i32)
> +DEF_HELPER_2(mmu_write, void, i32, i32)
> +#endif
> +
> +DEF_HELPER_2(divs, i32, i32, i32)
> +DEF_HELPER_2(divu, i32, i32, i32)
> +
> +DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
> +
> +#include "def-helper.h"
> +
> diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
> new file mode 100644
> index 0000000..4dc06cd
> --- /dev/null
> +++ b/target-nios2/instruction.c
> @@ -0,0 +1,1463 @@
> +/*
> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
> + * (Portions of this file that were originally from nios2sim-ng.)
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +
> +#include "instruction.h"
> +#include "exec-all.h"
> +#include "helper.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.h"
> +
> +static inline uint32_t get_opcode(uint32_t code)
> +{
> + I_TYPE(instr, code);
> + return instr->op;
> +}
> +
> +static inline uint32_t get_opxcode(uint32_t code)
> +{
> + R_TYPE(instr, code);
> + return instr->opx6;
> +}
> +
> +static inline void t_gen_helper_raise_exception(DisasContext *dc,
> + uint32_t index)
> +{
> + TCGv_i32 tmp = tcg_const_i32(index);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
> + gen_helper_raise_exception(tmp);
> + tcg_temp_free_i32(tmp);
> + dc->is_jmp = DISAS_UPDATE;
> +}
> +
> +/*
> + * Instructions not implemented by the simulator.
> + */
> +static void unimplemented(DisasContext *dc, uint32_t code)
> +{
> + t_gen_helper_raise_exception(dc, EXCP_UNIMPL);
> +}
> +
> +/*
> + * Illegal instruction
> + */
> +static void illegal_instruction(DisasContext *dc,
> + uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +static void gen_check_supervisor(DisasContext *dc, int label)
> +{
> + int l1 = gen_new_label();
> +
> + TCGv_i32 tmp = tcg_temp_new();
> + tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
> + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
> + t_gen_helper_raise_exception(dc, EXCP_SUPERI);
> + tcg_gen_br(label);
> +
> + gen_set_label(l1);
> + tcg_temp_free_i32(tmp);
> +
> + /* If we aren't taking the exception, update the PC to the
> + * next instruction */
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4);
You probably don't want to do that. It's quite expensive to have a
branch in every priviledged instruction.
Given that CR_STATUS_U is use for the flags in cpu_get_tb_cpu_state(),
for a given value of CR_STATUS_U, only TB with a corresponding
CR_STATUS_U will be reused.
For that you should copy tb->flags in DisasContext and use that instead.
> +}
> +
> +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
In general you should not use env in instructions.c, unless it is for
accessing a value that doesn't change during the execution (for example
CPU features/capabilities), as there is no guarantee that the value will
be the same when the TB is actually executed.
In this case it should work correctly because cpu_mmu_index() only
access CR_STATUS_U which is in the TB flags, but it is technically
wrong. Either make cpu_mmu_index check the tb flags (see above to add
them in DisasContext), or add a mem_index flag in DisasContext, computed
from TB flags at the beginning of gen_intermediate_code_internal.
In the latter case, you might even remove these functions and directly
call tcg_gen_qemu_ldXXu(dst, addr, ctx->mem_index) when needed.
> +
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_ld8u(dst, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_ld16u(dst, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_ld32u(dst, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> + break;
> + }
> +}
> +
> +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
> +
Ditto
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_ld8s(dst, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_ld16s(dst, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_ld32s(dst, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> + break;
> + }
> +}
> +
> +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> + unsigned int size)
> +{
> + int mem_index = cpu_mmu_index(dc->env);
> +
Ditto
> + switch (size) {
> + case 1:
> + tcg_gen_qemu_st8(val, addr, mem_index);
> + break;
> + case 2:
> + tcg_gen_qemu_st16(val, addr, mem_index);
> + break;
> + case 4:
> + tcg_gen_qemu_st32(val, addr, mem_index);
> + break;
> + default:
> + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> + break;
> + }
> +}
> +
> +/*
> + * Used as a placeholder for all instructions which do not have an effect on the
> + * simulator (e.g. flush, sync)
> + */
> +static void nop(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* Nothing to do here */
> +}
> +
> +/*
> + * J-Type instructions
> + */
> +
> +/*
> + * ra <- PC + 4
> + * PC <- (PC(31..28) : IMM26 * 4)
> + */
> +static void call(DisasContext *dc, uint32_t code)
> +{
> + J_TYPE(instr, code);
> +
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4));
> + gen_helper_call_status(tmp, tmp2);
> + tcg_temp_free_i32(tmp);
> + tcg_temp_free_i32(tmp2);
> +#endif
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
You probably want to add some tcg_gen_goto_tb() for static jumps, so
that TB linking is possible. It greatly improves the speed of the
emulation.
> +/* PC <- (PC(31..28) : IMM26 * 4) */
> +static void jmpi(DisasContext *dc, uint32_t code)
> +{
> + J_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/*
> + * I-Type instructions
> + */
> +
> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
> +static void ldbu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
Minor nitpick: it's better to write:
tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);
as the code generator on non-RISC hosts usually generate a slightly tiny
better code.
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA + IMM16 */
> +static void addi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv imm = tcg_temp_new();
> + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +
> + tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> +
You can also call tcg_gen_addi_tl() instead, so that you don't need
to load the constant by yourself. As a bonus the immediate = 0 case
is optimized earlier in the code generation.
> + tcg_temp_free(imm);
> +}
> +
> +/* Mem8[rA + @(IMM16)] <- rB(7..0) */
> +static void stb(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
Ditto
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* PC <- PC + 4 + IMM16 */
> +static void br(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) */
> +static void ldb(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA >= (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpgei(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_GE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x0000 : Mem16[rA + @IMM16)] */
> +static void ldhu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA & IMM16 */
> +static void andi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/* Mem16[rA + @(IMM16)] <- rB(15..0) */
> +static void sth(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA >= (signed) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bge(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] */
> +static void ldh(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmplti(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_LT, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* Initializes the data cache line currently caching address rA + @(IMM16) */
> +static void initda(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* rB <- rA | IMM16 */
> +static void ori(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/* Mem32[rA + @(IMM16)] <- rB */
> +static void stw(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void blt(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) */
> +static void ldw(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((signed) rA != (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpnei(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_NE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- rA ^ IMM16 */
> +static void xori(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
> +}
> +
> +/*
> + * if (rA != rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bne(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/*
> + * if ((signed) rA == (signed) @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpeqi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_EQ, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
> +static void ldbuio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- (rA * @(IMM16))(31..0) */
> +static void muli(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv imm = tcg_temp_new();
> + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
tcg_gen_muli_tl() also exists.
> + tcg_temp_free(imm);
> +}
> +
> +/* Mem8[rA + @(IMM16)] <- rB(7..0) (bypassing cache) */
> +static void stbio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA == rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void beq(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) (bypassing cache) */
> +static void ldbio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA >= 0x0000 : @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpgeui(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_GEU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* rB <- 0x0000 : Mem16[rA + @IMM16)] (bypassing cache) */
> +static void ldhuio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* rB <- rA & (IMM16 : 0x0000) */
> +static void andhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +/* Mem16[rA + @(IMM16)] <- rB(15..0) (bypassing cache) */
> +static void sthio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bgeu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] (bypassing cache) */
> +static void ldhio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if (rA < 0x0000 : @(IMM16))
> + * rB <- 1
> + * else
> + * rB <- 0
> + */
> +static void cmpltui(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_setcondi_tl(TCG_COND_LTU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + (int32_t)((int16_t)instr->imm16));
> +}
> +
> +/* */
> +static void initd(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* rB <- rA | (IMM16 : 0x0000) */
> +static void orhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +/* Mem32[rA + @(IMM16)] <- rB (bypassing cache) */
> +static void stwio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/*
> + * if ((unsigned) rA < (unsigned) rB)
> + * PC <- PC + 4 + @(IMM16)
> + * else
> + * PC <- PC + 4
> + */
> +static void bltu(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> + dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> + tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b], l1);
> + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> + gen_set_label(l1);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) (bypassing cache) */
> +static void ldwio(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + TCGv addr = tcg_temp_new();
> + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> + gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> + tcg_temp_free(addr);
> +}
> +
> +/* Prototype only, defined below */
> +static void handle_r_type_instr(DisasContext *dc, uint32_t code);
> +
> +/* rB <- rA ^ (IMM16 : 0x0000) */
> +static void xorhi(DisasContext *dc, uint32_t code)
> +{
> + I_TYPE(instr, code);
> +
> + tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> + instr->imm16 << 16);
> +}
> +
> +static struct instruction i_type_instructions[I_TYPE_COUNT] = {
> + [CALL] = INSTRUCTION(call),
> + [JMPI] = INSTRUCTION(jmpi),
> + [0x02] = INSTRUCTION_ILLEGAL(),
> + [LDBU] = INSTRUCTION(ldbu),
> + [ADDI] = INSTRUCTION(addi),
> + [STB] = INSTRUCTION(stb),
> + [BR] = INSTRUCTION(br),
> + [LDB] = INSTRUCTION(ldb),
> + [CMPGEI] = INSTRUCTION(cmpgei),
> + [0x09] = INSTRUCTION_ILLEGAL(),
> + [0x0a] = INSTRUCTION_ILLEGAL(),
> + [LDHU] = INSTRUCTION(ldhu),
> + [ANDI] = INSTRUCTION(andi),
> + [STH] = INSTRUCTION(sth),
> + [BGE] = INSTRUCTION(bge),
> + [LDH] = INSTRUCTION(ldh),
> + [CMPLTI] = INSTRUCTION(cmplti),
> + [0x11] = INSTRUCTION_ILLEGAL(),
> + [0x12] = INSTRUCTION_ILLEGAL(),
> + [INITDA] = INSTRUCTION(initda),
> + [ORI] = INSTRUCTION(ori),
> + [STW] = INSTRUCTION(stw),
> + [BLT] = INSTRUCTION(blt),
> + [LDW] = INSTRUCTION(ldw),
> + [CMPNEI] = INSTRUCTION(cmpnei),
> + [0x19] = INSTRUCTION_ILLEGAL(),
> + [0x1a] = INSTRUCTION_ILLEGAL(),
> + [FLUSHDA] = INSTRUCTION_NOP(flushda),
> + [XORI] = INSTRUCTION(xori),
> + [0x1d] = INSTRUCTION_ILLEGAL(),
> + [BNE] = INSTRUCTION(bne),
> + [0x1f] = INSTRUCTION_ILLEGAL(),
> + [CMPEQI] = INSTRUCTION(cmpeqi),
> + [0x21] = INSTRUCTION_ILLEGAL(),
> + [0x22] = INSTRUCTION_ILLEGAL(),
> + [LDBUIO] = INSTRUCTION(ldbuio),
> + [MULI] = INSTRUCTION(muli),
> + [STBIO] = INSTRUCTION(stbio),
> + [BEQ] = INSTRUCTION(beq),
> + [LDBIO] = INSTRUCTION(ldbio),
> + [CMPGEUI] = INSTRUCTION(cmpgeui),
> + [0x29] = INSTRUCTION_ILLEGAL(),
> + [0x2a] = INSTRUCTION_ILLEGAL(),
> + [LDHUIO] = INSTRUCTION(ldhuio),
> + [ANDHI] = INSTRUCTION(andhi),
> + [STHIO] = INSTRUCTION(sthio),
> + [BGEU] = INSTRUCTION(bgeu),
> + [LDHIO] = INSTRUCTION(ldhio),
> + [CMPLTUI] = INSTRUCTION(cmpltui),
> + [0x31] = INSTRUCTION_ILLEGAL(),
> + [CUSTOM] = INSTRUCTION_UNIMPLEMENTED(custom),
> + [INITD] = INSTRUCTION(initd),
> + [ORHI] = INSTRUCTION(orhi),
> + [STWIO] = INSTRUCTION(stwio),
> + [BLTU] = INSTRUCTION(bltu),
> + [LDWIO] = INSTRUCTION(ldwio),
> + [RDPRS] = INSTRUCTION_UNIMPLEMENTED(rdprs),
> + [0x39] = INSTRUCTION_ILLEGAL(),
> + [R_TYPE] = { "<R-type instruction>", handle_r_type_instr },
> + [FLUSHD] = INSTRUCTION_NOP(flushd),
> + [XORHI] = INSTRUCTION(xorhi),
> + [0x3d] = INSTRUCTION_ILLEGAL(),
> + [0x3e] = INSTRUCTION_ILLEGAL(),
> + [0x3f] = INSTRUCTION_ILLEGAL(),
> +};
> +
> +/*
> + * R-Type instructions
> + */
> +
> +/*
> + * status <- estatus
> + * PC <- ea
> + */
> +static void eret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_eret_status(tmp);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA rotated left IMM5 bit positions */
> +static void roli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> +
Looks like you want to use tcg_gen_rotli, which is optimized on some
hosts (like x86).
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- rA rotated left rB(4..0) bit positions */
> +static void rol(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> + TCGv t1 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_movi_tl(t1, 32);
> + tcg_gen_sub_tl(t1, t1, t0);
> + tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
Same, tcg_gen_rotl exists.
> + tcg_temp_free(t0);
> + tcg_temp_free(t1);
> +}
> +
> +/* */
> +static void flushp(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* PC <- ra */
> +static void ret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_ret_status(tmp);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- ~(A | rB) */
> +static void nor(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_nor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
> +static void mulxuu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpge(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_GE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* PC <- ba */
> +static void bret(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA rotated right rb(4..0) bit positions */
> +static void ror(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> + TCGv t1 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_movi_tl(t1, 32);
> + tcg_gen_sub_tl(t1, t1, t0);
> + tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
> + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> + tcg_temp_free(t0);
> + tcg_temp_free(t1);
> +}
> +
> +/* */
> +static void flushi(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/* PC <- rA */
> +static void jmp(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA & rB */
> +static void and(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_and_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * if ((signed) rA < (signed) rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmplt(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_LT, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA << IMM5 */
> +static void slli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- rA << rB(4..0) */
> +static void sll(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_shl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- rA | rB */
> +static void or(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
> +static void mulxsu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA != rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpne(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_NE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- (unsigned) rA >> ((unsigned) IMM5)*/
> +static void srli(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- (unsigned) rA >> ((unsigned) rB(4..0))*/
> +static void srl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_shr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +/* rC <- PC + 4 */
> +static void nextpc(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_movi_tl(dc->cpu_R[instr->c], dc->pc + 4);
> +}
> +
> +/*
> + * ra <- PC + 4
> + * PC <- rA
> + */
> +static void callr(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> +#ifdef CALL_TRACING
> + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> + gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
> + tcg_temp_free_i32(tmp);
> +#endif
> +
> + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
> +
> + dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rC <- rA ^ rB */
> +static void xor(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_xor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ((signed)rA * (signed)rB))(31..0) */
> +static void mulxss(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv_i64 t0, t1;
> +
> + t0 = tcg_temp_new_i64();
> + t1 = tcg_temp_new_i64();
> +
> + tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> + tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
> + tcg_gen_mul_i64(t0, t0, t1);
> +
> + tcg_gen_shri_i64(t0, t0, 32);
> + tcg_gen_trunc_i64_i32(dc->cpu_R[instr->c], t0);
> +
> + tcg_temp_free_i64(t0);
> + tcg_temp_free_i64(t1);
> +}
> +
> +/*
> + * if (rA == rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpeq(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_EQ, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA / rB */
> +static void divu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA / rB */
> +static void _div(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- ctlN */
> +static void rdctl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> + gen_check_supervisor(dc, l1);
> +
> + switch (instr->imm5 + 32) {
> + case CR_PTEADDR:
> + case CR_TLBACC:
> + case CR_TLBMISC:
> + {
> + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> + gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
> + tcg_temp_free_i32(tmp);
> + break;
> + }
> +
> + default:
> + tcg_gen_mov_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->imm5 + 32]);
> + break;
> + }
> +
> + gen_set_label(l1);
> +}
> +
> +/* rC <- (rA * rB))(31..0) */
> +static void mul(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_mul_i32(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * if (rA >= rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpgeu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_GEU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* */
> +static void initi(DisasContext *dc __attribute__((unused)),
> + uint32_t code __attribute__((unused)))
> +{
> + /* TODO */
> +}
> +
> +/*
> + * estatus <- status
> + * PIE <- 0
> + * U <- 0
> + * ea <- PC + 4
> + * PC <- exception handler address
> + */
> +static void trap(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_TRAP);
> +}
> +
> +/* ctlN <- rA */
> +static void wrctl(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + int l1 = gen_new_label();
> + gen_check_supervisor(dc, l1);
> +
> + switch (instr->imm5 + 32) {
> + case CR_PTEADDR:
> + case CR_TLBACC:
> + case CR_TLBMISC:
> + {
> + TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> + gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
> + tcg_temp_free_i32(tmp);
> + break;
> + }
> +
> + default:
> + tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
> + break;
> + }
> +
> + gen_set_label(l1);
> +}
> +
> +/*
> + * if (rA < rB)
> + * rC <- 1
> + * else
> + * rC <- 0
> + */
> +static void cmpltu(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_setcond_tl(TCG_COND_LTU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- rA + rB */
> +static void add(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_add_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/*
> + * bstatus ← status
> + * PIE <- 0
> + * U <- 0
> + * ba <- PC + 4
> + * PC <- break handler address
> + */
> +static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))
> +{
> + t_gen_helper_raise_exception(dc, EXCP_BREAK);
> +}
> +
> +/* rC <- rA - rB */
> +static void sub(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_sub_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> + dc->cpu_R[instr->b]);
> +}
> +
> +/* rC <- (signed) rA >> ((unsigned) IMM5) */
> +static void srai(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + tcg_gen_sari_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +}
> +
> +/* rC <- (signed) rA >> ((unsigned) rB(4..0)) */
> +static void sra(DisasContext *dc, uint32_t code)
> +{
> + R_TYPE(instr, code);
> +
> + TCGv t0 = tcg_temp_new();
> +
> + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> + tcg_gen_sar_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +
> + tcg_temp_free(t0);
> +}
> +
> +static struct instruction r_type_instructions[R_TYPE_COUNT] = {
> + [0x00] = INSTRUCTION_ILLEGAL(),
> + [ERET] = INSTRUCTION(eret),
> + [ROLI] = INSTRUCTION(roli),
> + [ROL] = INSTRUCTION(rol),
> + [FLUSHP] = INSTRUCTION(flushp),
> + [RET] = INSTRUCTION(ret),
> + [NOR] = INSTRUCTION(nor),
> + [MULXUU] = INSTRUCTION(mulxuu),
> + [CMPGE] = INSTRUCTION(cmpge),
> + [BRET] = INSTRUCTION(bret),
> + [0x0a] = INSTRUCTION_ILLEGAL(),
> + [ROR] = INSTRUCTION(ror),
> + [FLUSHI] = INSTRUCTION(flushi),
> + [JMP] = INSTRUCTION(jmp),
> + [AND] = INSTRUCTION(and),
> + [0x0f] = INSTRUCTION_ILLEGAL(),
> + [CMPLT] = INSTRUCTION(cmplt),
> + [0x11] = INSTRUCTION_ILLEGAL(),
> + [SLLI] = INSTRUCTION(slli),
> + [SLL] = INSTRUCTION(sll),
> + [WRPRS] = INSTRUCTION_UNIMPLEMENTED(wrprs),
> + [0x15] = INSTRUCTION_ILLEGAL(),
> + [OR] = INSTRUCTION(or),
> + [MULXSU] = INSTRUCTION(mulxsu),
> + [CMPNE] = INSTRUCTION(cmpne),
> + [0x19] = INSTRUCTION_ILLEGAL(),
> + [SRLI] = INSTRUCTION(srli),
> + [SRL] = INSTRUCTION(srl),
> + [NEXTPC] = INSTRUCTION(nextpc),
> + [CALLR] = INSTRUCTION(callr),
> + [XOR] = INSTRUCTION(xor),
> + [MULXSS] = INSTRUCTION(mulxss),
> + [CMPEQ] = INSTRUCTION(cmpeq),
> + [0x21] = INSTRUCTION_ILLEGAL(),
> + [0x22] = INSTRUCTION_ILLEGAL(),
> + [0x23] = INSTRUCTION_ILLEGAL(),
> + [DIVU] = INSTRUCTION(divu),
> + [DIV] = { "div", _div },
> + [RDCTL] = INSTRUCTION(rdctl),
> + [MUL] = INSTRUCTION(mul),
> + [CMPGEU] = INSTRUCTION(cmpgeu),
> + [INITI] = INSTRUCTION(initi),
> + [0x2a] = INSTRUCTION_ILLEGAL(),
> + [0x2b] = INSTRUCTION_ILLEGAL(),
> + [0x2c] = INSTRUCTION_ILLEGAL(),
> + [TRAP] = INSTRUCTION(trap),
> + [WRCTL] = INSTRUCTION(wrctl),
> + [0x2f] = INSTRUCTION_ILLEGAL(),
> + [CMPLTU] = INSTRUCTION(cmpltu),
> + [ADD] = INSTRUCTION(add),
> + [0x32] = INSTRUCTION_ILLEGAL(),
> + [0x33] = INSTRUCTION_ILLEGAL(),
> + [BREAK] = { "break", __break },
> + [0x35] = INSTRUCTION_ILLEGAL(),
> + [SYNC] = INSTRUCTION(nop),
> + [0x37] = INSTRUCTION_ILLEGAL(),
> + [0x38] = INSTRUCTION_ILLEGAL(),
> + [SUB] = INSTRUCTION(sub),
> + [SRAI] = INSTRUCTION(srai),
> + [SRA] = INSTRUCTION(sra),
> + [0x3c] = INSTRUCTION_ILLEGAL(),
> + [0x3d] = INSTRUCTION_ILLEGAL(),
> + [0x3e] = INSTRUCTION_ILLEGAL(),
> + [0x3f] = INSTRUCTION_ILLEGAL(),
> +};
> +
> +static void handle_r_type_instr(DisasContext *dc, uint32_t code)
> +{
> + uint32_t opx;
> + instruction_handler handle_instr;
> +
> + opx = get_opxcode(code);
> + if (unlikely(opx >= R_TYPE_COUNT)) {
> + goto illegal_op;
> + }
> +
> + LOG_DIS("R: %s (%08x)\n", r_type_instructions[opx].name, code);
> + handle_instr = r_type_instructions[opx].handler;
> +
> + handle_instr(dc, code);
> +
> + return;
> +
> +illegal_op:
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +void handle_instruction(DisasContext *dc)
> +{
> + uint32_t insn = ldl_code(dc->pc);
> + uint32_t op = get_opcode(insn);
> +
> + LOG_DIS("%8.8x\t", insn);
> +
> + if (unlikely(op >= I_TYPE_COUNT)) {
> + goto illegal_op;
> + }
> +
> + if (op != R_TYPE) {
> + LOG_DIS("I: %s (%08x)\n", i_type_instructions[op].name, insn);
> + }
> + i_type_instructions[op].handler(dc, insn);
> +
> + return;
> +
> +illegal_op:
> + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
> +}
> +
> +const char *instruction_get_string(uint32_t code)
> +{
> + uint32_t op = get_opcode(code);
> +
> + if (unlikely(op >= I_TYPE_COUNT)) {
> + return "";
> + } else if (op == R_TYPE) {
> + uint32_t opx = get_opxcode(code);
> + if (unlikely(opx >= R_TYPE_COUNT)) {
> + return "";
> + }
> + return r_type_instructions[opx].name;
> + } else {
> + return i_type_instructions[op].name;
> + }
> +}
> +
> diff --git a/target-nios2/instruction.h b/target-nios2/instruction.h
> new file mode 100644
> index 0000000..fe29933
> --- /dev/null
> +++ b/target-nios2/instruction.h
> @@ -0,0 +1,290 @@
> +/*
> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
> + * Copyright (C) 2010 chysun2000@gmail.com
> + * (Portions of this file that were originally from nios2sim-ng.)
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#ifndef _INSTRUCTION_H_
> +#define _INSTRUCTION_H_
> +
> +#include <stdint.h>
> +#include "cpu.h"
> +#include "tcg-op.h"
> +
> +/*
> + * Instruction Word Formats
> + */
> +
> +/* I-Type instruction */
> +struct i_type {
> + uint32_t op:6;
> + uint32_t imm16:16;
> + uint32_t b:5;
> + uint32_t a:5;
> +} __attribute__((packed));
> +
> +union i_type_u {
> + uint32_t v;
> + struct i_type i;
> +};
> +
> +#define I_TYPE(instr, op) \
> + union i_type_u instr_u = { .v = op }; \
> + struct i_type *instr = &instr_u.i
> +
> +/* R-Type instruction */
> +struct r_type {
> + uint32_t op:6;
> + /*
> + * Some R-Type instructions embed a small immediate value in the
> + * low-order bits of OPX.
> + */
> + uint32_t imm5:5;
> + uint32_t opx6:6;
> + uint32_t c:5;
> + uint32_t b:5;
> + uint32_t a:5;
> +} __attribute__((packed));
> +
> +union r_type_u {
> + uint32_t v;
> + struct r_type i;
> +};
> +
> +#define R_TYPE(instr, op) \
> + union r_type_u instr_u = { .v = op }; \
> + struct r_type *instr = &instr_u.i
> +
> +/* J-Type instruction */
> +struct j_type {
> + uint32_t op:6;
> + uint32_t imm26:26;
> +} __attribute__((packed));
> +
> +#define J_TYPE(instr, op) \
> + struct j_type *instr = (struct j_type *) &op
> +
> +/*
> + * Instruction Opcodes
> + */
> +
> +/*
> + * OP Encodings for I-Type instructions (except for CALL and JMPI, which are
> + * J-type instructions)
> + */
> +enum {
> + CALL = 0x00, /* J-type */
> + JMPI = 0x01, /* J-type */
> + /* 0x02 */
> + LDBU = 0x03,
> + ADDI = 0x04,
> + STB = 0x05,
> + BR = 0x06,
> + LDB = 0x07,
> + CMPGEI = 0x08,
> + /* 0x09 */
> + /* 0x0A */
> + LDHU = 0x0B,
> + ANDI = 0x0C,
> + STH = 0x0D,
> + BGE = 0x0E,
> + LDH = 0x0F,
> + CMPLTI = 0x10,
> + /* 0x11 */
> + /* 0x12 */
> + INITDA = 0x13,
> + ORI = 0x14,
> + STW = 0x15,
> + BLT = 0x16,
> + LDW = 0x17,
> + CMPNEI = 0x18,
> + /* 0x19 */
> + /* 0x1A */
> + FLUSHDA = 0x1B,
> + XORI = 0x1C,
> + /* 0x1D */
> + BNE = 0x1E,
> + /* 0x1F */
> + CMPEQI = 0x20,
> + /* 0x21 */
> + /* 0x22 */
> + LDBUIO = 0x23,
> + MULI = 0x24,
> + STBIO = 0x25,
> + BEQ = 0x26,
> + LDBIO = 0x27,
> + CMPGEUI = 0x28,
> + /* 0x29 */
> + /* 0x2A */
> + LDHUIO = 0x2B,
> + ANDHI = 0x2C,
> + STHIO = 0x2D,
> + BGEU = 0x2E,
> + LDHIO = 0x2F,
> + CMPLTUI = 0x30,
> + /* 0x31 */
> + CUSTOM = 0x32,
> + INITD = 0x33,
> + ORHI = 0x34,
> + STWIO = 0x35,
> + BLTU = 0x36,
> + LDWIO = 0x37,
> + RDPRS = 0x38,
> + /* 0x39 */
> + R_TYPE = 0x3A,
> + FLUSHD = 0x3B,
> + XORHI = 0x3C,
> + /* 0x3D */
> + /* 0x3E */
> + /* 0x3F */
> +};
> +#define I_TYPE_COUNT 0x40
> +
> +/* OPX Encodings for R-Type instructions */
> +enum {
> + /* 0x00 */
> + ERET = 0x01,
> + ROLI = 0x02,
> + ROL = 0x03,
> + FLUSHP = 0x04,
> + RET = 0x05,
> + NOR = 0x06,
> + MULXUU = 0x07,
> + CMPGE = 0x08,
> + BRET = 0x09,
> + /* 0x0A */
> + ROR = 0x0B,
> + FLUSHI = 0x0C,
> + JMP = 0x0D,
> + AND = 0x0E,
> + /* 0x0F */
> + CMPLT = 0x10,
> + /* 0x11 */
> + SLLI = 0x12,
> + SLL = 0x13,
> + WRPRS = 0x14,
> + /* 0x15 */
> + OR = 0x16,
> + MULXSU = 0x17,
> + CMPNE = 0x18,
> + /* 0x19 */
> + SRLI = 0x1A,
> + SRL = 0x1B,
> + NEXTPC = 0x1C,
> + CALLR = 0x1D,
> + XOR = 0x1E,
> + MULXSS = 0x1F,
> + CMPEQ = 0x20,
> + /* 0x21 */
> + /* 0x22 */
> + /* 0x23 */
> + DIVU = 0x24,
> + DIV = 0x25,
> + RDCTL = 0x26,
> + MUL = 0x27,
> + CMPGEU = 0x28,
> + INITI = 0x29,
> + /* 0x2A */
> + /* 0x2B */
> + /* 0x2C */
> + TRAP = 0x2D,
> + WRCTL = 0x2E,
> + /* 0x2F */
> + CMPLTU = 0x30,
> + ADD = 0x31,
> + /* 0x32 */
> + /* 0x33 */
> + BREAK = 0x34,
> + /* 0x35 */
> + SYNC = 0x36,
> + /* 0x37 */
> + /* 0x38 */
> + SUB = 0x39,
> + SRAI = 0x3A,
> + SRA = 0x3B,
> + /* 0x3C */
> + /* 0x3D */
> + /* 0x3E */
> + /* 0x3F */
> +};
> +#define R_TYPE_COUNT 0x40
> +
> +/*
> + * Return values for instruction handlers
> + */
> +#define INSTR_UNIMPL -2 /* Unimplemented instruction */
> +#define INSTR_ERR -1 /* Error in instruction */
> +#define PC_INC_NORMAL 0 /* Normal PC increment after instruction */
> +#define PC_INC_BY_INSTR 1 /* PC got incremented by instruction */
> +#define INSTR_BREAK 2 /* Break encountered */
> +#define INSTR_EXCEPTION 255 /* Instruction generated an exception
> + (the exception cause will be stored
> + in struct nios2 */
> +
> +#define EXCEPTION(cpu, cause) \
> + ({ \
> + (cpu)->exception_cause = cause; \
> + INSTR_EXCEPTION; \
> + })
> +
> +typedef struct DisasContext {
> + CPUNios2State *env;
> + TCGv *cpu_R;
> + int is_jmp;
> + target_ulong pc;
> + struct TranslationBlock *tb;
> +} DisasContext;
> +
> +typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
> +
> +struct instruction {
> + const char *name;
> + instruction_handler handler;
> +};
> +
> +/*
> + * Stringification macro (taken from Linux Kernel source code)
> + */
> +
> +/* Indirect stringification. Doing two levels allows the parameter to be a
> + * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
> + * converts to "bar".
> + */
> +
> +#define __stringify_1(x...) #x
> +#define __stringify(x...) __stringify_1(x)
> +
> +#define INSTRUCTION(name) { __stringify(name), name }
> +#define INSTRUCTION_NOP(name) { __stringify(name), nop }
> +#define INSTRUCTION_UNIMPLEMENTED(name) { __stringify(name), unimplemented }
> +#define INSTRUCTION_ILLEGAL() { "", illegal_instruction }
> +
> +extern void handle_instruction(DisasContext *dc);
> +extern const char *instruction_get_string(uint32_t code);
> +
> +#define SIM_COMPAT 0
> +#define DISAS_GNU 1 /* Disassembly via GNU gdb derived routines */
> +#define DISAS_NIOS2 0 /* Disassembly via routines in instruction.c */
> +#if DISAS_NIOS2 && !SIM_COMPAT
> +# define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
> +#else
> +# define LOG_DIS(...) do { } while (0)
> +#endif
> +
> +#endif /* _INSTRUCTION_H_ */
> diff --git a/target-nios2/machine.c b/target-nios2/machine.c
> new file mode 100644
> index 0000000..09198db
> --- /dev/null
> +++ b/target-nios2/machine.c
> @@ -0,0 +1,33 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "hw/hw.h"
> +#include "hw/boards.h"
> +
> +void cpu_save(QEMUFile *f, void *opaque)
> +{
> + /* TODO */
> +}
> +
> +int cpu_load(QEMUFile *f, void *opaque, int version_id)
> +{
> + /* TODO */
> + return 0;
> +}
> diff --git a/target-nios2/mmu.c b/target-nios2/mmu.c
> new file mode 100644
> index 0000000..a52fa1b
> --- /dev/null
> +++ b/target-nios2/mmu.c
> @@ -0,0 +1,273 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +/* Define this to enable MMU debug messages */
> +/* #define DEBUG_MMU */
> +
> +#ifdef DEBUG_MMU
> +#define MMU_LOG(x) x
> +#else
> +#define MMU_LOG(x)
> +#endif
> +
> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
> +{
> + switch (rn) {
> + case CR_TLBACC:
> + MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
> + break;
> +
> + case CR_TLBMISC:
> + MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
> + break;
> +
> + case CR_PTEADDR:
> + MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
> + break;
> +
> + default:
> + break;
> + }
> + return env->regs[rn];
> +}
> +
> +/* rw - 0 = read, 1 = write, 2 = fetch. */
> +unsigned int mmu_translate(CPUNios2State *env,
> + struct nios2_mmu_lookup *lu,
> + target_ulong vaddr, int rw, int mmu_idx)
> +{
> + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> + int vpn = vaddr >> 12;
> +
> + MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
> + vaddr, pid, vpn));
> +
> + int way;
> + for (way = 0; way < env->mmu.tlb_num_ways; way++) {
> +
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> +
> + MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + entry->tag, (entry->tag >> 12)));
> +
> + if (((entry->tag >> 12) != vpn) ||
> + (((entry->tag & (1<<11)) == 0) &&
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) != pid))) {
> + continue;
> + }
> + lu->vaddr = vaddr & TARGET_PAGE_MASK;
> + lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
> + lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
> + ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
> + ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
> +
> + MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + lu->vaddr, lu->paddr, lu->prot));
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
> +{
> + int idx;
> + MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
> +
> + for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
> + struct nios2_tlb_entry *entry = &env->mmu.tlb[idx];
> +
> + MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
> + idx, entry->tag, entry->data));
> +
> + if ((entry->tag & (1<<10)) && (!(entry->tag & (1<<11))) &&
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) == pid)) {
> + uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
> +
> + MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
> +
> + tlb_flush_page(env, vaddr);
> + }
> + }
> +}
> +
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
> +{
> + MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
> +
> + switch (rn) {
> + case CR_TLBACC:
> + MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
> + v >> CR_TLBACC_IGN_SHIFT,
> + (v & CR_TLBACC_C) ? 'C' : '.',
> + (v & CR_TLBACC_R) ? 'R' : '.',
> + (v & CR_TLBACC_W) ? 'W' : '.',
> + (v & CR_TLBACC_X) ? 'X' : '.',
> + (v & CR_TLBACC_G) ? 'G' : '.',
> + v & CR_TLBACC_PFN_MASK));
> +
> + /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
> + if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
> + int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
> + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> + int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
> + int g = (v & CR_TLBACC_G) ? 1 : 0;
> + int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> + uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
> + uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
> + CR_TLBACC_X | CR_TLBACC_PFN_MASK);
> +
> + if ((entry->tag != newTag) || (entry->data != newData)) {
> + if (entry->tag & (1<<10)) {
> + /* Flush existing entry */
> + MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
> + entry->tag & TARGET_PAGE_MASK));
> + tlb_flush_page(env, entry->tag & TARGET_PAGE_MASK);
> + }
> + entry->tag = newTag;
> + entry->data = newData;
> + MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
> + (way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask),
> + entry->tag, entry->data));
> + }
> + /* Auto-increment tlbmisc.WAY */
> + env->regs[CR_TLBMISC] =
> + (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
> + (((way+1) & (env->mmu.tlb_num_ways-1)) << CR_TLBMISC_WAY_SHIFT);
> + }
> +
> + /* Writes to TLBACC don't change the read-back value */
> + env->mmu.tlbacc_wr = v;
> + break;
> +
> + case CR_TLBMISC:
> + MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
> + v >> CR_TLBMISC_WAY_SHIFT,
> + (v & CR_TLBMISC_RD) ? 'R' : '.',
> + (v & CR_TLBMISC_WR) ? 'W' : '.',
> + (v & CR_TLBMISC_DBL) ? '2' : '.',
> + (v & CR_TLBMISC_BAD) ? 'B' : '.',
> + (v & CR_TLBMISC_PERM) ? 'P' : '.',
> + (v & CR_TLBMISC_D) ? 'D' : '.',
> + (v & CR_TLBMISC_PID_MASK) >> 4));
> +
> + if ((v & CR_TLBMISC_PID_MASK) !=
> + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
> + mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
> + CR_TLBMISC_PID_SHIFT);
> + }
> + /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
> + if (v & CR_TLBMISC_RD) {
> + int way = (v >> CR_TLBMISC_WAY_SHIFT);
> + int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
> + struct nios2_tlb_entry *entry =
> + &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
> + (vpn & env->mmu.tlb_entry_mask)];
> +
> + env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
> + env->regs[CR_TLBACC] |= entry->data |
> + ((entry->tag & (1<<11)) ? CR_TLBACC_G : 0);
> + env->regs[CR_TLBMISC] =
> + (v & ~CR_TLBMISC_PID_MASK) |
> + ((entry->tag & ((1<<env->mmu.pid_bits)-1)) <<
> + CR_TLBMISC_PID_SHIFT);
> + env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
> + env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
> + MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
> + "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
> + way, vpn, entry->tag, entry->data,
> + env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
> + env->regs[CR_PTEADDR]));
> + } else {
> + env->regs[CR_TLBMISC] = v;
> + }
> +
> + env->mmu.tlbmisc_wr = v;
> + break;
> +
> + case CR_PTEADDR:
> + MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
> + v >> CR_PTEADDR_PTBASE_SHIFT,
> + (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
> +
> + /* Writes to PTEADDR don't change the read-back VPN value */
> + env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
> + (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
> + env->mmu.pteaddr_wr = v;
> + break;
> +
> + default:
> + break;
> + }
> +}
> +
> +void mmu_init(struct nios2_mmu *mmu)
> +{
> + MMU_LOG(qemu_log("mmu_init\n"));
> +
> + mmu->pid_bits = 8; /* TODO: get this from ALTR,pid-num-bits */
> + mmu->tlb_num_ways = 16; /* TODO: get this from ALTR,tlb-num-ways */
> + mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */
> + mmu->tlb_entry_mask = (mmu->tlb_num_entries/mmu->tlb_num_ways) - 1;
> +
> + mmu->tlb = (struct nios2_tlb_entry *)g_malloc0(
> + sizeof(struct nios2_tlb_entry) * mmu->tlb_num_entries);
> +}
> +
> +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env)
> +{
> + int i;
> + cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n",
> + env->mmu.tlb_num_ways, env->mmu.tlb_num_entries,
> + env->mmu.pid_bits);
> +
> + for (i = 0; i < env->mmu.tlb_num_entries; i++) {
> + struct nios2_tlb_entry *entry = &env->mmu.tlb[i];
> + cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X "
> + "PID %02X %c PFN %05X %c%c%c%c\n",
> + i, entry->tag, entry->data,
> + (entry->tag & (1<<10)) ? 'V' : '-',
> + entry->tag >> 12, entry->tag & ((1<<env->mmu.pid_bits)-1),
> + (entry->tag & (1<<11)) ? 'G' : '-',
> + entry->data & CR_TLBACC_PFN_MASK,
> + (entry->data & CR_TLBACC_C) ? 'C' : '-',
> + (entry->data & CR_TLBACC_R) ? 'R' : '-',
> + (entry->data & CR_TLBACC_W) ? 'W' : '-',
> + (entry->data & CR_TLBACC_X) ? 'X' : '-');
> + }
> +}
> +
> diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
> new file mode 100644
> index 0000000..01d67cf
> --- /dev/null
> +++ b/target-nios2/mmu.h
> @@ -0,0 +1,49 @@
> +/*
> + * Altera Nios II MMU emulation for qemu.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +struct nios2_tlb_entry {
> + target_ulong tag;
> + target_ulong data;
> +};
> +
> +struct nios2_mmu {
> + int pid_bits;
> + int tlb_num_ways;
> + int tlb_num_entries;
> + int tlb_entry_mask;
> + uint32_t pteaddr_wr;
> + uint32_t tlbacc_wr;
> + uint32_t tlbmisc_wr;
> + struct nios2_tlb_entry *tlb;
> +};
> +
> +struct nios2_mmu_lookup {
> + target_ulong vaddr;
> + target_ulong paddr;
> + int prot;
> +};
> +
> +void mmu_flip_um(CPUNios2State *env, unsigned int um);
> +unsigned int mmu_translate(CPUNios2State *env,
> + struct nios2_mmu_lookup *lu,
> + target_ulong vaddr, int rw, int mmu_idx);
> +uint32_t mmu_read(CPUNios2State *env, uint32_t rn);
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v);
> +void mmu_init(struct nios2_mmu *mmu);
> diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
> new file mode 100644
> index 0000000..37bac66
> --- /dev/null
> +++ b/target-nios2/op_helper.c
> @@ -0,0 +1,125 @@
> +/*
> + * Altera Nios II helper routines.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "cpu.h"
> +#include "dyngen-exec.h"
> +#include "helper.h"
> +#include "host-utils.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#define MMUSUFFIX _mmu
> +#define SHIFT 0
> +#include "softmmu_template.h"
> +#define SHIFT 1
> +#include "softmmu_template.h"
> +#define SHIFT 2
> +#include "softmmu_template.h"
> +#define SHIFT 3
> +#include "softmmu_template.h"
> +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> + uintptr_t retaddr)
> +{
> + TranslationBlock *tb;
> + CPUNios2State *saved_env;
> + int ret;
> +
> + saved_env = env;
> + env = env1;
> +
> + ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
> + if (unlikely(ret)) {
> + if (retaddr) {
> + /* now we have a real cpu fault */
> + tb = tb_find_pc(retaddr);
> + if (tb) {
> + /* The PC is inside the translated code. It means that we have
> + a virtual CPU fault */
> + cpu_restore_state(tb, env, retaddr);
> + }
> + }
> + cpu_loop_exit(env);
> + }
> + env = saved_env;
> +}
> +
> +void helper_raise_exception(uint32_t index)
> +{
> + env->exception_index = index;
> + cpu_loop_exit(env);
> +}
> +
> +uint32_t helper_mmu_read(uint32_t rn)
> +{
> + return mmu_read(env, rn);
> +}
> +
> +void helper_mmu_write(uint32_t rn, uint32_t v)
> +{
> + mmu_write(env, rn, v);
> +}
> +
> +void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
> +{
> + if (addr & mask) {
> + qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
> + addr, mask, wr, dr);
> + env->regs[CR_BADADDR] = addr;
> + env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
> + helper_raise_exception(EXCP_UNALIGN);
> + }
> +}
> +
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> + int is_write, int is_exec, int is_asi, int size)
> +{
> + qemu_log("unassigned access to %08X\n", addr);
> +}
> +
> +uint32_t helper_divs(uint32_t a, uint32_t b)
> +{
> + return (int32_t)a / (int32_t)b;
> +}
> +
> +uint32_t helper_divu(uint32_t a, uint32_t b)
> +{
> + return a / b;
> +}
> +
> +#ifdef CALL_TRACING
> +void helper_call_status(uint32_t pc, uint32_t target)
> +{
> + qemu_log("%08X: CALL %08X %s\n", pc, target, lookup_symbol(target));
> +}
> +
> +void helper_eret_status(uint32_t pc)
> +{
> + qemu_log("%08X: ERET STATUS %08X, ESTATUS %08X, EA %08X\n",
> + pc, env->regs[CR_STATUS], env->regs[CR_ESTATUS], env->regs[R_EA]);
> +}
> +
> +void helper_ret_status(uint32_t pc)
> +{
> + qemu_log("%08X: RET RA %08X\n", pc, env->regs[R_RA]);
> +}
> +#endif
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/translate.c b/target-nios2/translate.c
> new file mode 100644
> index 0000000..5d0979f
> --- /dev/null
> +++ b/target-nios2/translate.c
> @@ -0,0 +1,252 @@
> +/*
> + * Altera Nios II emulation for qemu: main translation routines.
> + *
> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <stdarg.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <inttypes.h>
> +#include <assert.h>
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "disas.h"
> +#include "helper.h"
> +#include "qemu-common.h"
> +
> +#include "instruction.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.h"
> +
> +static const char *regnames[] = {
> + "zero", "at", "r2", "r3",
> + "r4", "r5", "r6", "r7",
> + "r8", "r9", "r10", "r11",
> + "r12", "r13", "r14", "r15",
> + "r16", "r17", "r18", "r19",
> + "r20", "r21", "r22", "r23",
> + "et", "bt", "gp", "sp",
> + "fp", "ea", "ba", "ra",
> + "status", "estatus", "bstatus", "ienable",
> + "ipending", "cpuid", "reserved", "exception",
> + "pteaddr", "tlbacc", "tlbmisc", "reserved",
> + "badaddr", "config", "mpubase", "mpuacc",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "reserved", "reserved", "reserved", "reserved",
> + "rpc"
> +};
> +
> +static TCGv_ptr cpu_env;
> +static TCGv cpu_R[NUM_CORE_REGS];
> +
> +#include "gen-icount.h"
> +
> +/* generate intermediate code for basic block 'tb'. */
> +static void gen_intermediate_code_internal(
> + CPUNios2State *env, TranslationBlock *tb, int search_pc)
> +{
> + DisasContext dc1, *dc = &dc1;
> + int num_insns;
> + int max_insns;
> + uint32_t next_page_start;
> + int j, lj = -1;
> + uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
> +
> + /* Initialize DC */
> + dc->env = env;
> + dc->cpu_R = cpu_R;
> + dc->is_jmp = DISAS_NEXT;
> + dc->pc = tb->pc;
> + dc->tb = tb;
> +
> + /* Dump the CPU state to the log */
> + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> + qemu_log("--------------\n");
> + log_cpu_state(env, 0);
> + }
> +
> + /* Set up instruction counts */
> + num_insns = 0;
> + max_insns = tb->cflags & CF_COUNT_MASK;
> + if (max_insns == 0) {
> + max_insns = CF_COUNT_MASK;
> + }
> + next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
> +
> + gen_icount_start();
> + do {
> + /* Mark instruction start with associated PC */
> + if (search_pc) {
> + j = gen_opc_ptr - gen_opc_buf;
> + if (lj < j) {
> + lj++;
> + while (lj < j) {
> + gen_opc_instr_start[lj++] = 0;
> + }
> + }
> + gen_opc_pc[lj] = dc->pc;
> + gen_opc_instr_start[lj] = 1;
> + gen_opc_icount[lj] = num_insns;
> + }
> +
> + LOG_DIS("%8.8x:\t", dc->pc);
> +
> + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
> + gen_io_start();
> + }
> +
> + /* Decode an instruction */
> + handle_instruction(dc);
> +
> + dc->pc += 4;
> + num_insns++;
> +
> + /* Translation stops when a conditional branch is encountered.
> + * Otherwise the subsequent code could get translated several times.
> + * Also stop translation when a page boundary is reached. This
> + * ensures prefetch aborts occur at the right place. */
> + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
> + !env->singlestep_enabled &&
> + !singlestep &&
> + dc->pc < next_page_start &&
> + num_insns < max_insns);
> +
> + if (tb->cflags & CF_LAST_IO) {
> + gen_io_end();
> + }
> +
> + /* Indicate where the next block should start */
> + switch (dc->is_jmp) {
> + case DISAS_NEXT:
> + /* Save the current PC back into the CPU register */
> + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
> + tcg_gen_exit_tb(0);
> + break;
> +
> + default:
> + case DISAS_JUMP:
> + case DISAS_UPDATE:
> + /* The jump will already have updated the PC register */
> + tcg_gen_exit_tb(0);
> + break;
> +
> + case DISAS_TB_JUMP:
> + /* nothing more to generate */
> + break;
> + }
> +
> + /* End off the block */
> + gen_icount_end(tb, num_insns);
> + *gen_opc_ptr = INDEX_op_end;
> +
> + /* Mark instruction starts for the final generated instruction */
> + if (search_pc) {
> + j = gen_opc_ptr - gen_opc_buf;
> + lj++;
> + while (lj <= j) {
> + gen_opc_instr_start[lj++] = 0;
> + }
> + } else {
> + tb->size = dc->pc - tb->pc;
> + tb->icount = num_insns;
> + }
> +
> +#ifdef DEBUG_DISAS
> + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> + qemu_log("----------------\n");
> + qemu_log("IN: %s\n", lookup_symbol(tb->pc));
> + log_target_disas(tb->pc, dc->pc - tb->pc, 0);
> + qemu_log("\nisize=%d osize=%td\n",
> + dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
> + }
> +#endif
> +}
> +
> +void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> + gen_intermediate_code_internal(env, tb, 0);
> +}
> +
> +void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> + gen_intermediate_code_internal(env, tb, 1);
> +}
> +
> +void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
> + int flags)
> +{
> + int i;
> +
> + if (!env || !f) {
> + return;
> + }
> +
> + cpu_fprintf(f, "IN: PC=%x %s\n",
> + env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
> +
> + for (i = 0; i < NUM_CORE_REGS; i++) {
> + cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
> + if ((i + 1) % 4 == 0) {
> + cpu_fprintf(f, "\n");
> + }
> + }
> + cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
> + env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
> + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
> + env->mmu.tlbacc_wr);
> + cpu_fprintf(f, "\n\n");
> +}
> +
> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
> +{
> + Nios2CPU *cpu;
> + int i;
> +
> + cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
> +
> + cpu->env.reset_addr = RESET_ADDRESS;
> + cpu->env.exception_addr = EXCEPTION_ADDRESS;
> + cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
> +
> + cpu_reset(CPU(cpu));
> + qemu_init_vcpu(&cpu->env);
> +
> + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
> +
> + for (i = 0; i < NUM_CORE_REGS; i++) {
> + cpu_R[i] = tcg_global_mem_new(TCG_AREG0,
> + offsetof(CPUNios2State, regs[i]),
> + regnames[i]);
> + }
> +
> +#define GEN_HELPER 2
> +#include "helper.h"
> +
> + return cpu;
> +}
> +
> +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
> +{
> + env->regs[R_PC] = gen_opc_pc[pc_pos];
> +}
> +
> --
> 1.7.9.5
>
>
>
--
Aurelien Jarno GPG: 1024D/F1BCDB73
aurelien@aurel32.net http://www.aurel32.net
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-11 21:34 ` Aurelien Jarno
@ 2012-09-11 22:30 ` Richard Henderson
2012-09-11 23:18 ` Richard Henderson
` (2 subsequent siblings)
3 siblings, 0 replies; 31+ messages in thread
From: Richard Henderson @ 2012-09-11 22:30 UTC (permalink / raw)
To: Aurelien Jarno; +Cc: crwulff, qemu-devel
On 09/11/2012 02:34 PM, Aurelien Jarno wrote:
> Minor nitpick: it's better to write:
>
> tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);
>
> as the code generator on non-RISC hosts usually generate a slightly tiny
> better code.
You know... we already swap operands for constants. It would be trivial
have tcg_constant_folding swap for matching operands at the same time.
r~
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-11 21:34 ` Aurelien Jarno
2012-09-11 22:30 ` Richard Henderson
@ 2012-09-11 23:18 ` Richard Henderson
2012-09-14 3:42 ` Chris Wulff
2012-09-15 15:33 ` Chris Wulff
3 siblings, 0 replies; 31+ messages in thread
From: Richard Henderson @ 2012-09-11 23:18 UTC (permalink / raw)
To: qemu-devel
Somehow the original patch set never arrived here. Replying indirectly...
> On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
>> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
...
>> +static inline int cpu_has_work(CPUState *env)
>> +{
>> + return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
>> +}
>> +
>> +static inline int cpu_halted(CPUState *env)
>> +{
>> + if (!env->halted) {
>> + return 0;
>> + }
>> +
>> + if (cpu_has_work(env)) {
>> + env->halted = 0;
>> + return 0;
>> + }
>> +
>> + return EXCP_HALTED;
>> +}
>> +
>> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
>> +{
>> + env->regs[R_PC] = tb->pc;
>> +}
Do my eyes deceive me or do you have duplicates of these from cpu.h?
>> +++ b/target-nios2/instruction.c
Any particular reason you split this file out from translate.c?
>> +static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))
Leading un is reserved to the compiler. break1? break_?
>> +/* I-Type instruction */
>> +struct i_type {
>> + uint32_t op:6;
>> + uint32_t imm16:16;
>> + uint32_t b:5;
>> + uint32_t a:5;
>> +} __attribute__((packed));
>> +
>> +union i_type_u {
>> + uint32_t v;
>> + struct i_type i;
>> +};
>> +
>> +#define I_TYPE(instr, op) \
>> + union i_type_u instr_u = { .v = op }; \
>> + struct i_type *instr = &instr_u.i
This is an extremely unportable idea.
Bit field layout differs from big-endian to little-endian, and between
compiler abis. The only reliable method of picking out a specific set
of bits is to shift and mask by hand.
Which you can still do with your I_TYPE/R_TYPE macros (which I do like),
but instead with different structure definitions and initialization.
>> +typedef struct DisasContext {
>> + CPUNios2State *env;
>> + TCGv *cpu_R;
>> + int is_jmp;
>> + target_ulong pc;
>> + struct TranslationBlock *tb;
>> +} DisasContext;
Why are you copying cpu_R here, and using s->cpu_R everywhere?
Why not directly use the global variable cpu_R like everyone else?
I suppose it's related to translate.c vs instruction.c, but I've
already expressed an opinion there...
>> +/* Indirect stringification. Doing two levels allows the parameter to be a
>> + * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
>> + * converts to "bar".
>> + */
Is there any reason you'd want to do that for the instruction tables?
>> +#define __stringify_1(x...) #x
>> +#define __stringify(x...) __stringify_1(x)
... because there's that leading underscore again, and honestly you don't need
anything but
#define INSTRUCTION(N) { #N, N }
>> +#include "dyngen-exec.h"
This is going away.
>> +void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
>> +{
>> + if (addr & mask) {
>> + qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
>> + addr, mask, wr, dr);
>> + env->regs[CR_BADADDR] = addr;
>> + env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
>> + helper_raise_exception(EXCP_UNALIGN);
>> + }
>> +}
This should be done with
#define ALIGNED_ONLY
directly in the softmmu_template.h helpers. C.f. target-sparc/ldst_helper.c.
>> +uint32_t helper_divs(uint32_t a, uint32_t b)
>> +{
>> + return (int32_t)a / (int32_t)b;
>> +}
>> +
>> +uint32_t helper_divu(uint32_t a, uint32_t b)
>> +{
>> + return a / b;
>> +}
(1) Missing divide by zero check. This will generally put qemu into a loop.
(2) You could (and probably should) use tcg_gen_div{,u}_tl.
I would only suggest external helper functions if you have to check for
additional exceptions apart from X / 0, like -INT_MIN / -1.
>> + /* Dump the CPU state to the log */
>> + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
>> + qemu_log("--------------\n");
>> + log_cpu_state(env, 0);
>> + }
Don't log cpu state for in_asm. That's a common bug across translators,
and all it does is cause double logging for "-d cpu,in_asm".
>> + LOG_DIS("%8.8x:\t", dc->pc);
Use tcg_gen_debug_insn_start, which makes the tcg opcodes dumps pretty too.
r~
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-11 21:34 ` Aurelien Jarno
2012-09-11 22:30 ` Richard Henderson
2012-09-11 23:18 ` Richard Henderson
@ 2012-09-14 3:42 ` Chris Wulff
2012-09-15 15:33 ` Chris Wulff
3 siblings, 0 replies; 31+ messages in thread
From: Chris Wulff @ 2012-09-14 3:42 UTC (permalink / raw)
To: Aurelien Jarno; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 8633 bytes --]
Thanks for the feedback. I'll see about getting things updated once I have
the chance. I wasn't too terribly concerned with performance as the target
processor being emulated is much slower than the host (about 100MHz). Even
the way things are now, Linux runs several times faster in emulation that
it does on the actual target CPU.
Many of the optimizations you point out look pretty simple to incorporate
so I will definitely see about doing that. I'll review the places you've
pointed out where the translation vs. execution time CPU state is being
mis-handled. It seems to be working, but those are definitely the types of
things that will introduce hard to find bugs.
-- Chris Wulff
> > +static void gen_check_supervisor(DisasContext *dc, int label)
> > +{
> > + int l1 = gen_new_label();
> > +
> > + TCGv_i32 tmp = tcg_temp_new();
> > + tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
> > + tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
> > + t_gen_helper_raise_exception(dc, EXCP_SUPERI);
> > + tcg_gen_br(label);
> > +
> > + gen_set_label(l1);
> > + tcg_temp_free_i32(tmp);
> > +
> > + /* If we aren't taking the exception, update the PC to the
> > + * next instruction */
> > + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4);
>
> You probably don't want to do that. It's quite expensive to have a
> branch in every priviledged instruction.
>
> Given that CR_STATUS_U is use for the flags in cpu_get_tb_cpu_state(),
> for a given value of CR_STATUS_U, only TB with a corresponding
> CR_STATUS_U will be reused.
>
> For that you should copy tb->flags in DisasContext and use that instead.
>
> > +}
> > +
> > +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> > + unsigned int size)
> > +{
> > + int mem_index = cpu_mmu_index(dc->env);
>
> In general you should not use env in instructions.c, unless it is for
> accessing a value that doesn't change during the execution (for example
> CPU features/capabilities), as there is no guarantee that the value will
> be the same when the TB is actually executed.
>
> In this case it should work correctly because cpu_mmu_index() only
> access CR_STATUS_U which is in the TB flags, but it is technically
> wrong. Either make cpu_mmu_index check the tb flags (see above to add
> them in DisasContext), or add a mem_index flag in DisasContext, computed
> from TB flags at the beginning of gen_intermediate_code_internal.
>
> In the latter case, you might even remove these functions and directly
> call tcg_gen_qemu_ldXXu(dst, addr, ctx->mem_index) when needed.
>
>
> > +
> > + switch (size) {
> > + case 1:
> > + tcg_gen_qemu_ld8u(dst, addr, mem_index);
> > + break;
> > + case 2:
> > + tcg_gen_qemu_ld16u(dst, addr, mem_index);
> > + break;
> > + case 4:
> > + tcg_gen_qemu_ld32u(dst, addr, mem_index);
> > + break;
> > + default:
> > + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > + break;
> > + }
> > +}
> > +
> > +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> > + unsigned int size)
> > +{
> > + int mem_index = cpu_mmu_index(dc->env);
> > +
>
> Ditto
>
> > + switch (size) {
> > + case 1:
> > + tcg_gen_qemu_ld8s(dst, addr, mem_index);
> > + break;
> > + case 2:
> > + tcg_gen_qemu_ld16s(dst, addr, mem_index);
> > + break;
> > + case 4:
> > + tcg_gen_qemu_ld32s(dst, addr, mem_index);
> > + break;
> > + default:
> > + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > + break;
> > + }
> > +}
> > +
> > +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> > + unsigned int size)
> > +{
> > + int mem_index = cpu_mmu_index(dc->env);
> > +
>
> Ditto
>
> > + switch (size) {
> > + case 1:
> > + tcg_gen_qemu_st8(val, addr, mem_index);
> > + break;
> > + case 2:
> > + tcg_gen_qemu_st16(val, addr, mem_index);
> > + break;
> > + case 4:
> > + tcg_gen_qemu_st32(val, addr, mem_index);
> > + break;
> > + default:
> > + cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > + break;
> > + }
> > +}
> > +
> > +/*
> > + * Used as a placeholder for all instructions which do not have an
> effect on the
> > + * simulator (e.g. flush, sync)
> > + */
> > +static void nop(DisasContext *dc __attribute__((unused)),
> > + uint32_t code __attribute__((unused)))
> > +{
> > + /* Nothing to do here */
> > +}
> > +
> > +/*
> > + * J-Type instructions
> > + */
> > +
> > +/*
> > + * ra <- PC + 4
> > + * PC <- (PC(31..28) : IMM26 * 4)
> > + */
> > +static void call(DisasContext *dc, uint32_t code)
> > +{
> > + J_TYPE(instr, code);
> > +
> > +#ifdef CALL_TRACING
> > + TCGv_i32 tmp = tcg_const_i32(dc->pc);
> > + TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26
> * 4));
> > + gen_helper_call_status(tmp, tmp2);
> > + tcg_temp_free_i32(tmp);
> > + tcg_temp_free_i32(tmp2);
> > +#endif
> > +
> > + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> > + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > + dc->is_jmp = DISAS_JUMP;
> > +}
> > +
>
> You probably want to add some tcg_gen_goto_tb() for static jumps, so
> that TB linking is possible. It greatly improves the speed of the
> emulation.
>
>
> > +/* PC <- (PC(31..28) : IMM26 * 4) */
> > +static void jmpi(DisasContext *dc, uint32_t code)
> > +{
> > + J_TYPE(instr, code);
> > +
> > + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > + dc->is_jmp = DISAS_JUMP;
> > +}
> > +
> > +/*
> > + * I-Type instructions
> > + */
> > +
> > +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
> > +static void ldbu(DisasContext *dc, uint32_t code)
> > +{
> > + I_TYPE(instr, code);
> > +
> > + TCGv addr = tcg_temp_new();
> > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
>
> Minor nitpick: it's better to write:
>
> tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);
>
> as the code generator on non-RISC hosts usually generate a slightly tiny
> better code.
>
> > +
> > + gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> > +
> > + tcg_temp_free(addr);
> > +}
> > +
> > +/* rB <- rA + IMM16 */
> > +static void addi(DisasContext *dc, uint32_t code)
> > +{
> > + I_TYPE(instr, code);
> > +
> > + TCGv imm = tcg_temp_new();
> > + tcg_gen_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> > +
> > + tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> > +
>
> You can also call tcg_gen_addi_tl() instead, so that you don't need
> to load the constant by yourself. As a bonus the immediate = 0 case
> is optimized earlier in the code generation.
>
> > + tcg_temp_free(imm);
> > +}
> > +
> > +/* Mem8[rA + @(IMM16)] <- rB(7..0) */
> > +static void stb(DisasContext *dc, uint32_t code)
> > +{
> > + I_TYPE(instr, code);
> > +
> > + TCGv addr = tcg_temp_new();
> > + tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> > + tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
>
> Ditto
>
>
> > +/* rC <- rA rotated left IMM5 bit positions */
> > +static void roli(DisasContext *dc, uint32_t code)
> > +{
> > + R_TYPE(instr, code);
> > +
> > + TCGv t0 = tcg_temp_new();
> > +
> > + tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> > + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> instr->imm5);
> > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> > +
>
> Looks like you want to use tcg_gen_rotli, which is optimized on some
> hosts (like x86).
>
> > + tcg_temp_free(t0);
> > +}
> > +
> > +/* rC <- rA rotated left rB(4..0) bit positions */
> > +static void rol(DisasContext *dc, uint32_t code)
> > +{
> > + R_TYPE(instr, code);
> > +
> > + TCGv t0 = tcg_temp_new();
> > + TCGv t1 = tcg_temp_new();
> > +
> > + tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> > + tcg_gen_movi_tl(t1, 32);
> > + tcg_gen_sub_tl(t1, t1, t0);
> > + tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> > + tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> > + tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> > +
>
> Same, tcg_gen_rotl exists.
>
>
> --
> Aurelien Jarno GPG: 1024D/F1BCDB73
> aurelien@aurel32.net http://www.aurel32.net
>
[-- Attachment #2: Type: text/html, Size: 11040 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-11 21:34 ` Aurelien Jarno
` (2 preceding siblings ...)
2012-09-14 3:42 ` Chris Wulff
@ 2012-09-15 15:33 ` Chris Wulff
3 siblings, 0 replies; 31+ messages in thread
From: Chris Wulff @ 2012-09-15 15:33 UTC (permalink / raw)
To: Aurelien Jarno; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 896 bytes --]
On Tue, Sep 11, 2012 at 5:34 PM, Aurelien Jarno <aurelien@aurel32.net>wrote:
> On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > Signed-off-by: Chris Wulff <crwulff@gmail.com>
>
> > + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> > + tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > + (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > + dc->is_jmp = DISAS_JUMP;
> > +}
> > +
>
> You probably want to add some tcg_gen_goto_tb() for static jumps, so
> that TB linking is possible. It greatly improves the speed of the
> emulation.
>
>
>
Doing this actually made quite a big difference. The reported bogomips from
linux went up 15x and the observable speed by about 2x. The rest of your
suggested changes didn't have much noticeable effect but were at least good
things to clean up anyway.
-- Chris Wulff
[-- Attachment #2: Type: text/html, Size: 1447 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
2012-09-11 20:19 ` Blue Swirl
2012-09-11 21:34 ` Aurelien Jarno
@ 2012-09-15 14:55 ` Andreas Färber
2 siblings, 0 replies; 31+ messages in thread
From: Andreas Färber @ 2012-09-15 14:55 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
Am 10.09.2012 02:19, schrieb crwulff@gmail.com:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> target-nios2/Makefile.objs | 5 +
> target-nios2/altera_iic.c | 100 +++
> target-nios2/cpu-qom.h | 69 +++
> target-nios2/cpu.c | 83 +++
> target-nios2/cpu.h | 259 ++++++++
> target-nios2/exec.h | 60 ++
> target-nios2/helper.c | 291 +++++++++
> target-nios2/helper.h | 45 ++
> target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
> target-nios2/instruction.h | 290 +++++++++
> target-nios2/machine.c | 33 +
> target-nios2/mmu.c | 273 +++++++++
> target-nios2/mmu.h | 49 ++
> target-nios2/op_helper.c | 125 ++++
> target-nios2/translate.c | 252 ++++++++
> 15 files changed, 3397 insertions(+)
Some general comments: You're introducing a new target here, so if you
design your API cleanly (using Nios2CPU where possible) you don't really
need a separate cpu-qom.h file in addition to cpu.h, cf. target-or32.
Please prefer passing Nios2CPU as opaque rather than CPUNios2State
(e.g., 3/9; cf. target-arm). Reason is that fields are being moved from
CPUxxxState to CPUState and this will simplify the migration.
Thanks,
Andreas
--
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-11 19:58 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII crwulff
` (7 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
Makefile.objs | 2 +
nios2-dis.c | 417 +++++++++++
nios2-isa.h | 2150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
nios2-opc.c | 540 +++++++++++++++
nios2.h | 366 ++++++++++
5 files changed, 3475 insertions(+)
create mode 100644 nios2-dis.c
create mode 100644 nios2-isa.h
create mode 100644 nios2-opc.c
create mode 100644 nios2.h
diff --git a/Makefile.objs b/Makefile.objs
index 4412757..16cbe8b 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -133,6 +133,8 @@ libdis-$(CONFIG_IA64_DIS) += ia64-dis.o
libdis-$(CONFIG_M68K_DIS) += m68k-dis.o
libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o
libdis-$(CONFIG_MIPS_DIS) += mips-dis.o
+libdis-$(CONFIG_NIOS2_DIS) += nios2-dis.o
+libdis-$(CONFIG_NIOS2_DIS) += nios2-opc.o
libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
libdis-$(CONFIG_S390_DIS) += s390-dis.o
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
diff --git a/nios2-dis.c b/nios2-dis.c
new file mode 100644
index 0000000..09c79eb
--- /dev/null
+++ b/nios2-dis.c
@@ -0,0 +1,417 @@
+/* nios2-dis.c -- Altera New Jersey disassemble routines.
+
+ Copyright (C) 2003
+ by Nigel Gray (ngray@altera.com).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "dis-asm.h"
+#include "nios2.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/* length of New Jersey instruction in bytes */
+#define INSNLEN 4
+
+/* helper function prototypes */
+static int nios2_disassemble(bfd_vma, unsigned long, disassemble_info *);
+static void nios2_init_opcode_hash(void);
+
+
+static int nios2_print_insn_arg(const char *argptr, unsigned long opcode,
+ bfd_vma address, disassemble_info *info);
+
+
+/* print_insn_nios2 is the main disassemble function for New Jersey.
+ The function diassembler(abfd) (source in disassemble.c) returns a
+ pointer to this either print_insn_big_nios2 or
+ print_insn_little_nios2, which in turn call this function, when the
+ bfd machine type is New Jersey. print_insn_nios2 reads the
+ instruction word at the address given, and prints the disassembled
+ instruction on the stream info->stream using info->fprintf_func. */
+
+int
+print_insn_nios2(bfd_vma address, disassemble_info *info)
+{
+ /* buffer into which the instruction bytes are written */
+ bfd_byte buffer[INSNLEN];
+ /* used to indicate return status from function calls */
+ int status;
+
+ assert(info != NULL);
+
+ status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
+ if (status == 0) {
+ unsigned long insn;
+ insn = (unsigned long) bfd_getl32(buffer);
+ status = nios2_disassemble(address, insn, info);
+ } else {
+ (*info->memory_error_func) (status, address, info);
+ status = -1;
+ }
+ return status;
+}
+
+/* Data structures used by the opcode hash table */
+
+typedef struct _nios2_opcode_hash {
+ const struct nios2_opcode *opcode;
+ struct _nios2_opcode_hash *next;
+} nios2_opcode_hash;
+
+static bfd_boolean nios2_hash_init;
+static nios2_opcode_hash *nios2_hash[(OP_MASK_OP) + 1];
+
+/* separate hash table for pseudo-ops */
+static nios2_opcode_hash *nios2_ps_hash[(OP_MASK_OP) + 1];
+
+/* Function to initialize the opcode hash table */
+
+void
+nios2_init_opcode_hash(void)
+{
+ unsigned int i;
+ register const struct nios2_opcode *op;
+ nios2_opcode_hash *tmp_hash;
+
+ for (i = 0; i <= OP_MASK_OP; ++i) {
+ nios2_hash[0] = NULL;
+ }
+ for (i = 0; i <= OP_MASK_OP; i++) {
+ for (op = nios2_opcodes; op < &nios2_opcodes[NUMOPCODES]; op++) {
+ if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO) {
+ if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP) &&
+ (op->pinfo &
+ (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI) &
+ 0x7fffffff) != 0) {
+ tmp_hash = nios2_ps_hash[i];
+ if (tmp_hash == NULL) {
+ tmp_hash =
+ (nios2_opcode_hash *)
+ malloc(sizeof(nios2_opcode_hash));
+ nios2_ps_hash[i] = tmp_hash;
+ } else {
+ while (tmp_hash->next != NULL) {
+ tmp_hash = tmp_hash->next;
+ }
+ tmp_hash->next =
+ (nios2_opcode_hash *)
+ malloc(sizeof(nios2_opcode_hash));
+ tmp_hash = tmp_hash->next;
+ }
+ if (tmp_hash == NULL) {
+ fprintf(stderr,
+ "error allocating memory...broken disassembler\n");
+ abort();
+ }
+ tmp_hash->opcode = op;
+ tmp_hash->next = NULL;
+ }
+ } else if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) {
+ tmp_hash = nios2_hash[i];
+ if (tmp_hash == NULL) {
+ tmp_hash =
+ (nios2_opcode_hash *)malloc(sizeof(nios2_opcode_hash));
+ nios2_hash[i] = tmp_hash;
+ } else {
+ while (tmp_hash->next != NULL) {
+ tmp_hash = tmp_hash->next;
+ }
+ tmp_hash->next =
+ (nios2_opcode_hash *)malloc(sizeof(nios2_opcode_hash));
+ tmp_hash = tmp_hash->next;
+ }
+ if (tmp_hash == NULL) {
+ fprintf(stderr,
+ "error allocating memory...broken disassembler\n");
+ abort();
+ }
+ tmp_hash->opcode = op;
+ tmp_hash->next = NULL;
+ }
+ }
+ }
+ nios2_hash_init = 1;
+#ifdef DEBUG_HASHTABLE
+ for (i = 0; i <= OP_MASK_OP; ++i) {
+ printf("index: 0x%02X ops: ", i);
+ tmp_hash = nios2_hash[i];
+ if (tmp_hash != NULL) {
+ while (tmp_hash != NULL) {
+ printf("%s ", tmp_hash->opcode->name);
+ tmp_hash = tmp_hash->next;
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i <= OP_MASK_OP; ++i) {
+ printf("index: 0x%02X ops: ", i);
+ tmp_hash = nios2_ps_hash[i];
+ if (tmp_hash != NULL) {
+ while (tmp_hash != NULL) {
+ printf("%s ", tmp_hash->opcode->name);
+ tmp_hash = tmp_hash->next;
+ }
+ }
+ printf("\n");
+ }
+#endif
+}
+
+/* Function which returns a pointer to an nios2_opcode struct for
+ a given instruction opcode, or NULL if there is an error */
+
+const struct nios2_opcode *
+nios2_find_opcode_hash(unsigned long opcode)
+{
+ nios2_opcode_hash *entry;
+
+ /* Build a hash table to shorten the search time. */
+ if (!nios2_hash_init) {
+ nios2_init_opcode_hash();
+ }
+
+ /* first look in the pseudo-op hashtable */
+ entry = nios2_ps_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
+
+ /* look for a match and if we get one, this is the instruction we decode */
+ while (entry != NULL) {
+ if ((entry->opcode->match) == (opcode & entry->opcode->mask)) {
+ return entry->opcode;
+ } else {
+ entry = entry->next;
+ }
+ }
+
+ /* if we haven't yet returned, then we need to look in the main
+ hashtable */
+ entry = nios2_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
+
+ if (entry == NULL) {
+ return NULL;
+ }
+
+ while (entry != NULL) {
+ if ((entry->opcode->match) == (opcode & entry->opcode->mask)) {
+ return entry->opcode;
+ } else {
+ entry = entry->next;
+ }
+ }
+
+ return NULL;
+}
+
+/* nios2_disassemble does all the work of disassembling a New Jersey
+ instruction opcode */
+
+int
+nios2_disassemble(bfd_vma address, unsigned long opcode,
+ disassemble_info *info)
+{
+ const struct nios2_opcode *op;
+ const char *argstr;
+
+ info->bytes_per_line = INSNLEN;
+ info->bytes_per_chunk = INSNLEN;
+ info->display_endian = info->endian;
+ info->insn_info_valid = 1;
+ info->branch_delay_insns = 0;
+ info->data_size = 0;
+ info->insn_type = dis_nonbranch;
+ info->target = 0;
+ info->target2 = 0;
+
+ (*info->fprintf_func) (info->stream, "%08x %08x ",
+ (int)address, (int)opcode);
+
+ /* Find the major opcode and use this to disassemble
+ the instruction and its arguments */
+ op = nios2_find_opcode_hash(opcode);
+
+ if (op != NULL) {
+ bfd_boolean is_nop = FALSE;
+ if (op->pinfo == NIOS2_INSN_MACRO_MOV) {
+ /* check for mov r0, r0 and if it is
+ change to nop */
+ int dst, src;
+ dst = GET_INSN_FIELD(RRD, opcode);
+ src = GET_INSN_FIELD(RRS, opcode);
+ if (dst == 0 && src == 0) {
+ (*info->fprintf_func) (info->stream, "nop");
+ is_nop = TRUE;
+ } else {
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ }
+ } else {
+ (*info->fprintf_func) (info->stream, "%s", op->name);
+ }
+
+ if (!is_nop) {
+ argstr = op->args;
+ if (argstr != NULL && *argstr != '\0') {
+ (*info->fprintf_func) (info->stream, "\t");
+ while (*argstr != '\0') {
+ nios2_print_insn_arg(argstr, opcode, address, info);
+ ++argstr;
+ }
+ }
+ }
+ } else {
+ /* Handle undefined instructions. */
+ info->insn_type = dis_noninsn;
+ (*info->fprintf_func) (info->stream, "0x%x", (int)opcode);
+ }
+ /* this tells the caller how far to advance the program counter */
+ return INSNLEN;
+}
+
+/* The function nios2_print_insn_arg uses the character pointed
+ to by argptr to determine how it print the next token or separator
+ character in the arguments to an instruction */
+int
+nios2_print_insn_arg(const char *argptr,
+ unsigned long opcode, bfd_vma address,
+ disassemble_info *info)
+{
+ unsigned long i = 0;
+ unsigned long reg_base;
+
+ assert(argptr != NULL);
+ assert(info != NULL);
+
+ switch (*argptr) {
+ case ',':
+ case '(':
+ case ')':
+ (*info->fprintf_func) (info->stream, "%c", *argptr);
+ break;
+ case 'd':
+ i = GET_INSN_FIELD(RRD, opcode);
+
+ if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
+ && GET_INSN_FIELD(CUSTOM_C, opcode) == 0) {
+ reg_base = COPROCREGBASE;
+ } else {
+ reg_base = 0;
+ }
+
+ if (i < NUMREGNAMES) {
+ (*info->fprintf_func) (info->stream, "%s",
+ nios2_regs[i + reg_base].name);
+ } else {
+ (*info->fprintf_func) (info->stream, "unknown");
+ }
+ break;
+ case 's':
+ i = GET_INSN_FIELD(RRS, opcode);
+
+ if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
+ && GET_INSN_FIELD(CUSTOM_A, opcode) == 0) {
+ reg_base = COPROCREGBASE;
+ } else {
+ reg_base = 0;
+ }
+
+ if (i < NUMREGNAMES) {
+ (*info->fprintf_func) (info->stream, "%s",
+ nios2_regs[i + reg_base].name);
+ } else {
+ (*info->fprintf_func) (info->stream, "unknown");
+ }
+ break;
+ case 't':
+ i = GET_INSN_FIELD(RRT, opcode);
+
+ if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
+ && GET_INSN_FIELD(CUSTOM_B, opcode) == 0) {
+ reg_base = COPROCREGBASE;
+ } else {
+ reg_base = 0;
+ }
+
+ if (i < NUMREGNAMES) {
+ (*info->fprintf_func) (info->stream, "%s",
+ nios2_regs[i + reg_base].name);
+ } else {
+ (*info->fprintf_func) (info->stream, "unknown");
+ }
+ break;
+ case 'i':
+ /* 16-bit signed immediate */
+ i = (signed) (GET_INSN_FIELD(IMM16, opcode) << 16) >> 16;
+ (*info->fprintf_func) (info->stream, "%d", (int)i);
+ break;
+ case 'u':
+ /* 16-bit unsigned immediate */
+ i = GET_INSN_FIELD(IMM16, opcode);
+ (*info->fprintf_func) (info->stream, "%d", (int)i);
+ break;
+ case 'o':
+ /* 16-bit signed immediate address offset */
+ i = (signed) (GET_INSN_FIELD(IMM16, opcode) << 16) >> 16;
+ address = address + 4 + i; /* NG changed to byte offset 1/9/03 */
+ (*info->print_address_func) (address, info);
+ break;
+ case 'p':
+ /* 5-bit unsigned immediate */
+ i = GET_INSN_FIELD(CACHE_OPX, opcode);
+ (*info->fprintf_func) (info->stream, "%d", (int)i);
+ break;
+ case 'j':
+ /* 5-bit unsigned immediate */
+ i = GET_INSN_FIELD(IMM5, opcode);
+ (*info->fprintf_func) (info->stream, "%d", (int)i);
+ break;
+ case 'l':
+ /* 8-bit unsigned immediate */
+ /* FIXME - not yet implemented */
+ i = GET_INSN_FIELD(CUSTOM_N, opcode);
+ (*info->fprintf_func) (info->stream, "%u", (int)i);
+ break;
+ case 'm':
+ /* 26-bit unsigned immediate */
+ i = GET_INSN_FIELD(IMM26, opcode);
+ /* this translates to an address because its only used in
+ call instructions */
+ address = (address & 0xf0000000) | (i << 2);
+ (*info->print_address_func) (address, info);
+ break;
+ case 'c':
+ i = GET_INSN_FIELD(IMM5, opcode); /* ctrl register index */
+ (*info->fprintf_func) (info->stream, "%s",
+ nios2_regs[CTLREGBASE + i].name);
+ break;
+ case 'b':
+ i = GET_INSN_FIELD(IMM5, opcode);
+ (*info->fprintf_func) (info->stream, "%d", (int)i);
+ break;
+ default:
+ (*info->fprintf_func) (info->stream, "unknown");
+ break;
+ }
+ return 0;
+}
diff --git a/nios2-isa.h b/nios2-isa.h
new file mode 100644
index 0000000..125124c
--- /dev/null
+++ b/nios2-isa.h
@@ -0,0 +1,2150 @@
+/*
+ * This file defines Nios II instruction set constants.
+ * To include it in assembly code (.S file), define ALT_ASM_SRC
+ * before including this file.
+ *
+ * This file is automatically generated by gen_isa.pl - do not edit
+ */
+
+#ifndef _NIOS2_ISA_H_
+#define _NIOS2_ISA_H_
+
+/* OP instruction opcode values (index is OP field) */
+#define NUM_OP_INSTS 64
+
+#ifndef ALT_ASM_SRC
+extern const char *op_names[NUM_OP_INSTS];
+#endif /* ALT_ASM_SRC */
+
+/* OPX instruction opcode values (index is OPX field) */
+#define NUM_OPX_INSTS 64
+
+#ifndef ALT_ASM_SRC
+extern const char *opx_names[NUM_OPX_INSTS];
+#endif /* ALT_ASM_SRC */
+
+/* Constants for instruction fields and ISA */
+#define BREAK_INST_EXC_ID 7
+#define CPU_RESET_CAUSE_ID 1
+#define CPU_RESET_EXC_ID 1
+#define DIV_ERROR_CAUSE_ID 8
+#define DIV_ERROR_EXC_ID 10
+#define EMPTY_SLAVE_DATA_ACCESS_ERROR_CAUSE_ID 30
+#define EMPTY_SLAVE_DATA_ACCESS_ERROR_EXC_ID 22
+#define EMPTY_SLAVE_INST_ACCESS_ERROR_CAUSE_ID 29
+#define EMPTY_SLAVE_INST_ACCESS_ERROR_EXC_ID 21
+#define HBREAK_EXC_ID 2
+#define ILLEGAL_INST_CAUSE_ID 5
+#define ILLEGAL_INST_EXC_ID 6
+#define MAX_CAUSE_ID 31
+#define MISALIGNED_DATA_ADDR_CAUSE_ID 6
+#define MISALIGNED_DATA_ADDR_EXC_ID 8
+#define MISALIGNED_TARGET_PC_CAUSE_ID 7
+#define MISALIGNED_TARGET_PC_EXC_ID 9
+#define MPU_DATA_REGION_VIOLATION_CAUSE_ID 17
+#define MPU_DATA_REGION_VIOLATION_EXC_ID 20
+#define MPU_INST_REGION_VIOLATION_CAUSE_ID 16
+#define MPU_INST_REGION_VIOLATION_EXC_ID 19
+#define NIOS2_DISPLAY_INST_TRACE 1
+#define NIOS2_DISPLAY_MEM_TRAFFIC 2
+#define NONE_EXC_ID 0
+#define NORM_INTR_CAUSE_ID 2
+#define NORM_INTR_EXC_ID 3
+#define NUM_EXC_IDS 24
+#define READONLY_SLAVE_DATA_ACCESS_ERROR_CAUSE_ID 31
+#define READONLY_SLAVE_DATA_ACCESS_ERROR_EXC_ID 23
+#define RECORD_DATA_ADDR 2
+#define RECORD_NOTHING 0
+#define RECORD_TARGET_PCB 1
+#define RESET_CAUSE_ID 0
+#define SUPERVISOR_DATA_ADDR_CAUSE_ID 11
+#define SUPERVISOR_DATA_ADDR_EXC_ID 13
+#define SUPERVISOR_INST_ADDR_CAUSE_ID 9
+#define SUPERVISOR_INST_ADDR_EXC_ID 11
+#define SUPERVISOR_INST_CAUSE_ID 10
+#define SUPERVISOR_INST_EXC_ID 12
+#define TLB_DATA_MISS_EXC_ID 15
+#define TLB_INST_MISS_EXC_ID 14
+#define TLB_MISS_CAUSE_ID 12
+#define TLB_R_PERM_CAUSE_ID 14
+#define TLB_R_PERM_EXC_ID 17
+#define TLB_W_PERM_CAUSE_ID 15
+#define TLB_W_PERM_EXC_ID 18
+#define TLB_X_PERM_CAUSE_ID 13
+#define TLB_X_PERM_EXC_ID 16
+#define TRAP_INST_CAUSE_ID 3
+#define TRAP_INST_EXC_ID 4
+#define UNIMP_INST_CAUSE_ID 4
+#define UNIMP_INST_EXC_ID 5
+#define AT_REGNUM 1
+#define BADADDR_REG_BADDR_LSB 0
+#define BADADDR_REG_BADDR_MSB 31
+#define BADADDR_REG_BADDR_SZ 32
+#define BADADDR_REG_BADDR_MASK 0xffffffff
+#define BADADDR_REG_LSB 0
+#define BADADDR_REG_MSB 31
+#define BADADDR_REG_REGNUM 12
+#define BADADDR_REG_SZ 32
+#define BADADDR_REG_MASK 0xffffffff
+#define BRETADDR_REGNUM 30
+#define BSTATUS_REG_LSB 0
+#define BSTATUS_REG_MMU_LSB 0
+#define BSTATUS_REG_MMU_MSB 2
+#define BSTATUS_REG_MMU_SZ 3
+#define BSTATUS_REG_MMU_MASK 0x7
+#define BSTATUS_REG_MPU_LSB 0
+#define BSTATUS_REG_MPU_MSB 1
+#define BSTATUS_REG_MPU_SZ 2
+#define BSTATUS_REG_MPU_MASK 0x3
+#define BSTATUS_REG_MSB 2
+#define BSTATUS_REG_NO_MMU_LSB 0
+#define BSTATUS_REG_NO_MMU_MSB 0
+#define BSTATUS_REG_NO_MMU_SZ 1
+#define BSTATUS_REG_NO_MMU_MASK 0x1
+#define BSTATUS_REG_REGNUM 2
+#define BSTATUS_REG_SZ 3
+#define BSTATUS_REG_MASK 0x7
+#define BT_REGNUM 25
+#define CACHE_MAX_BYTES 65536
+#define CACHE_MAX_LINE_BYTES 32
+#define CACHE_MIN_LINE_BYTES 4
+#define COMPARE_OP_EQ 0x0
+#define COMPARE_OP_GE 0x1
+#define COMPARE_OP_LSB 3
+#define COMPARE_OP_LT 0x2
+#define COMPARE_OP_MSB 4
+#define COMPARE_OP_NE 0x3
+#define COMPARE_OP_SZ 2
+#define COMPARE_OP_MASK 0x3
+#define CONFIG_REG_LSB 0
+#define CONFIG_REG_MSB 0
+#define CONFIG_REG_PE_LSB 0
+#define CONFIG_REG_PE_MSB 0
+#define CONFIG_REG_PE_SZ 1
+#define CONFIG_REG_PE_MASK 0x1
+#define CONFIG_REG_REGNUM 13
+#define CONFIG_REG_SZ 1
+#define CONFIG_REG_MASK 0x1
+#define CPUID_REG_LSB 0
+#define CPUID_REG_MSB 31
+#define CPUID_REG_REGNUM 5
+#define CPUID_REG_SZ 32
+#define CPUID_REG_MASK 0xffffffff
+#define DATAPATH_LOG2_SZ 5
+#define DATAPATH_LOG2_MASK 0x1f
+#define DATAPATH_LSB 0
+#define DATAPATH_MSB 31
+#define DATAPATH_SZ 32
+#define DATAPATH_MASK 0xffffffff
+#define EMPTY_CRST_IW 127034
+#define EMPTY_HBREAK_IW 4040762
+#define EMPTY_INTR_IW 3926074
+#define EMPTY_NOP_IW 100410
+#define EMPTY_RET_IW 4160759866
+#define ERETADDR_REGNUM 29
+#define ESTATUS_REG_LSB 0
+#define ESTATUS_REG_MMU_LSB 0
+#define ESTATUS_REG_MMU_MSB 2
+#define ESTATUS_REG_MMU_SZ 3
+#define ESTATUS_REG_MMU_MASK 0x7
+#define ESTATUS_REG_MPU_LSB 0
+#define ESTATUS_REG_MPU_MSB 1
+#define ESTATUS_REG_MPU_SZ 2
+#define ESTATUS_REG_MPU_MASK 0x3
+#define ESTATUS_REG_MSB 2
+#define ESTATUS_REG_NO_MMU_LSB 0
+#define ESTATUS_REG_NO_MMU_MSB 0
+#define ESTATUS_REG_NO_MMU_SZ 1
+#define ESTATUS_REG_NO_MMU_MASK 0x1
+#define ESTATUS_REG_REGNUM 1
+#define ESTATUS_REG_SZ 3
+#define ESTATUS_REG_MASK 0x7
+#define ET_REGNUM 24
+#define EXCEPTION_REG_CAUSE_LSB 2
+#define EXCEPTION_REG_CAUSE_MSB 6
+#define EXCEPTION_REG_CAUSE_SZ 5
+#define EXCEPTION_REG_CAUSE_MASK 0x1f
+#define EXCEPTION_REG_LSB 0
+#define EXCEPTION_REG_MEA_LSB 0
+#define EXCEPTION_REG_MEA_MSB 0
+#define EXCEPTION_REG_MEA_SZ 1
+#define EXCEPTION_REG_MEA_MASK 0x1
+#define EXCEPTION_REG_MEE_LSB 1
+#define EXCEPTION_REG_MEE_MSB 1
+#define EXCEPTION_REG_MEE_SZ 1
+#define EXCEPTION_REG_MEE_MASK 0x1
+#define EXCEPTION_REG_MSB 6
+#define EXCEPTION_REG_REGNUM 7
+#define EXCEPTION_REG_SZ 7
+#define EXCEPTION_REG_MASK 0x7f
+#define FP_REGNUM 28
+#define FSTATUS_REG_REGNUM 11
+#define GP_REGNUM 26
+#define IENABLE_REG_LSB 0
+#define IENABLE_REG_MSB 31
+#define IENABLE_REG_REGNUM 3
+#define IENABLE_REG_SZ 32
+#define IENABLE_REG_MASK 0xffffffff
+#define IPENDING_REG_LSB 0
+#define IPENDING_REG_MSB 31
+#define IPENDING_REG_REGNUM 4
+#define IPENDING_REG_SZ 32
+#define IPENDING_REG_MASK 0xffffffff
+#define IW_A_LSB 27
+#define IW_A_MSB 31
+#define IW_A_SZ 5
+#define IW_A_MASK 0x1f
+#define IW_B_LSB 22
+#define IW_B_MSB 26
+#define IW_B_SZ 5
+#define IW_B_MASK 0x1f
+#define IW_C_LSB 17
+#define IW_C_MSB 21
+#define IW_C_SZ 5
+#define IW_C_MASK 0x1f
+#define IW_CONTROL_REGNUM_BASE 0
+#define IW_CONTROL_REGNUM_LSB 6
+#define IW_CONTROL_REGNUM_MSB 9
+#define IW_CONTROL_REGNUM_SZ 4
+#define IW_CONTROL_REGNUM_MASK 0xf
+#define IW_CUSTOM_N_LSB 6
+#define IW_CUSTOM_N_MSB 13
+#define IW_CUSTOM_N_SZ 8
+#define IW_CUSTOM_N_MASK 0xff
+#define IW_CUSTOM_READRA_LSB 16
+#define IW_CUSTOM_READRA_MSB 16
+#define IW_CUSTOM_READRA_SZ 1
+#define IW_CUSTOM_READRA_MASK 0x1
+#define IW_CUSTOM_READRB_LSB 15
+#define IW_CUSTOM_READRB_MSB 15
+#define IW_CUSTOM_READRB_SZ 1
+#define IW_CUSTOM_READRB_MASK 0x1
+#define IW_CUSTOM_WRITERC_LSB 14
+#define IW_CUSTOM_WRITERC_MSB 14
+#define IW_CUSTOM_WRITERC_SZ 1
+#define IW_CUSTOM_WRITERC_MASK 0x1
+#define IW_IMM16_LSB 6
+#define IW_IMM16_MSB 21
+#define IW_IMM16_SZ 16
+#define IW_IMM16_MASK 0xffff
+#define IW_IMM26_LSB 6
+#define IW_IMM26_MSB 31
+#define IW_IMM26_SZ 26
+#define IW_IMM26_MASK 0x3ffffff
+#define IW_MEMSZ_BYTE 0x0
+#define IW_MEMSZ_HWORD 0x1
+#define IW_MEMSZ_LSB 3
+#define IW_MEMSZ_MSB 4
+#define IW_MEMSZ_SZ 2
+#define IW_MEMSZ_MASK 0x3
+#define IW_MEMSZ_WORD_MSB 0x1
+#define IW_OP_LSB 0
+#define IW_OP_MSB 5
+#define IW_OP_SZ 6
+#define IW_OP_MASK 0x3f
+#define IW_OPX_LSB 11
+#define IW_OPX_MSB 16
+#define IW_OPX_SZ 6
+#define IW_OPX_MASK 0x3f
+#define IW_SHIFT_IMM5_LSB 6
+#define IW_SHIFT_IMM5_MSB 10
+#define IW_SHIFT_IMM5_SZ 5
+#define IW_SHIFT_IMM5_MASK 0x1f
+#define IW_SZ 32
+#define IW_MASK 0xffffffff
+#define IW_TRAP_BREAK_IMM5_LSB 6
+#define IW_TRAP_BREAK_IMM5_MSB 10
+#define IW_TRAP_BREAK_IMM5_SZ 5
+#define IW_TRAP_BREAK_IMM5_MASK 0x1f
+#define JMP_CALLR_VS_RET_IS_RET 0
+#define JMP_CALLR_VS_RET_OPX_BIT 3
+#define LOGIC_OP_AND 0x1
+#define LOGIC_OP_LSB 3
+#define LOGIC_OP_MSB 4
+#define LOGIC_OP_NOR 0x0
+#define LOGIC_OP_OR 0x2
+#define LOGIC_OP_SZ 2
+#define LOGIC_OP_MASK 0x3
+#define LOGIC_OP_XOR 0x3
+#define MMU_ADDR_BYPASS_TLB 0x3
+#define MMU_ADDR_BYPASS_TLB_CACHEABLE 0x0
+#define MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB 29
+#define MMU_ADDR_BYPASS_TLB_CACHEABLE_MSB 29
+#define MMU_ADDR_BYPASS_TLB_CACHEABLE_SZ 1
+#define MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK 0x1
+#define MMU_ADDR_BYPASS_TLB_LSB 30
+#define MMU_ADDR_BYPASS_TLB_MSB 31
+#define MMU_ADDR_BYPASS_TLB_PADDR_LSB 0
+#define MMU_ADDR_BYPASS_TLB_PADDR_MSB 28
+#define MMU_ADDR_BYPASS_TLB_PADDR_SZ 29
+#define MMU_ADDR_BYPASS_TLB_PADDR_MASK 0x1fffffff
+#define MMU_ADDR_BYPASS_TLB_SZ 2
+#define MMU_ADDR_BYPASS_TLB_MASK 0x3
+#define MMU_ADDR_IO_REGION 0x7
+#define MMU_ADDR_IO_REGION_LSB 29
+#define MMU_ADDR_IO_REGION_MSB 31
+#define MMU_ADDR_IO_REGION_SZ 3
+#define MMU_ADDR_IO_REGION_MASK 0x7
+#define MMU_ADDR_IO_REGION_VPN 0xe0000
+#define MMU_ADDR_KERNEL_MMU_REGION 0x2
+#define MMU_ADDR_KERNEL_MMU_REGION_LSB 30
+#define MMU_ADDR_KERNEL_MMU_REGION_MSB 31
+#define MMU_ADDR_KERNEL_MMU_REGION_SZ 2
+#define MMU_ADDR_KERNEL_MMU_REGION_MASK 0x3
+#define MMU_ADDR_KERNEL_REGION 0x6
+#define MMU_ADDR_KERNEL_REGION_INT 6
+#define MMU_ADDR_KERNEL_REGION_LSB 29
+#define MMU_ADDR_KERNEL_REGION_MSB 31
+#define MMU_ADDR_KERNEL_REGION_SZ 3
+#define MMU_ADDR_KERNEL_REGION_MASK 0x7
+#define MMU_ADDR_PAGE_OFFSET_LSB 0
+#define MMU_ADDR_PAGE_OFFSET_MSB 11
+#define MMU_ADDR_PAGE_OFFSET_SZ 12
+#define MMU_ADDR_PAGE_OFFSET_MASK 0xfff
+#define MMU_ADDR_PFN_LSB 12
+#define MMU_ADDR_PFN_MSB 31
+#define MMU_ADDR_PFN_SZ 20
+#define MMU_ADDR_PFN_MASK 0xfffff
+#define MMU_ADDR_USER_REGION 0x0
+#define MMU_ADDR_USER_REGION_LSB 31
+#define MMU_ADDR_USER_REGION_MSB 31
+#define MMU_ADDR_USER_REGION_SZ 1
+#define MMU_ADDR_USER_REGION_MASK 0x1
+#define MMU_ADDR_VPN_LSB 12
+#define MMU_ADDR_VPN_MSB 31
+#define MMU_ADDR_VPN_SZ 20
+#define MMU_ADDR_VPN_MASK 0xfffff
+#define MPU_DATA_PERM_SUPER_NONE_USER_NONE 0
+#define MPU_DATA_PERM_SUPER_RD_USER_NONE 1
+#define MPU_DATA_PERM_SUPER_RD_USER_RD 2
+#define MPU_DATA_PERM_SUPER_RW_USER_NONE 4
+#define MPU_DATA_PERM_SUPER_RW_USER_RD 5
+#define MPU_DATA_PERM_SUPER_RW_USER_RW 6
+#define MPU_DATA_PERM_SZ 3
+#define MPU_DATA_PERM_MASK 0x7
+#define MPU_INST_PERM_SUPER_EXEC_USER_EXEC 2
+#define MPU_INST_PERM_SUPER_EXEC_USER_NONE 1
+#define MPU_INST_PERM_SUPER_NONE_USER_NONE 0
+#define MPU_INST_PERM_SZ 2
+#define MPU_INST_PERM_MASK 0x3
+#define MPU_MAX_REGION_SIZE_LOG2 20
+#define MPU_MAX_REGIONS 32
+#define MPU_MIN_REGION_SIZE_LOG2 6
+#define MPU_MIN_REGIONS 1
+#define MPUACC_REG_C_LSB 5
+#define MPUACC_REG_C_MSB 5
+#define MPUACC_REG_C_SZ 1
+#define MPUACC_REG_C_MASK 0x1
+#define MPUACC_REG_LIMIT_LSB 6
+#define MPUACC_REG_LIMIT_MSB 31
+#define MPUACC_REG_LIMIT_SZ 26
+#define MPUACC_REG_LIMIT_MASK 0x3ffffff
+#define MPUACC_REG_LSB 0
+#define MPUACC_REG_MASK_LSB 6
+#define MPUACC_REG_MASK_MSB 30
+#define MPUACC_REG_MASK_SZ 25
+#define MPUACC_REG_MASK_MASK 0x1ffffff
+#define MPUACC_REG_MSB 31
+#define MPUACC_REG_PERM_LSB 2
+#define MPUACC_REG_PERM_MSB 4
+#define MPUACC_REG_PERM_SZ 3
+#define MPUACC_REG_PERM_MASK 0x7
+#define MPUACC_REG_RD_LSB 1
+#define MPUACC_REG_RD_MSB 1
+#define MPUACC_REG_RD_SZ 1
+#define MPUACC_REG_RD_MASK 0x1
+#define MPUACC_REG_REGNUM 15
+#define MPUACC_REG_RSV1_LSB 31
+#define MPUACC_REG_RSV1_MSB 31
+#define MPUACC_REG_RSV1_SZ 1
+#define MPUACC_REG_RSV1_MASK 0x1
+#define MPUACC_REG_SZ 32
+#define MPUACC_REG_MASK 0xffffffff
+#define MPUACC_REG_WR_LSB 0
+#define MPUACC_REG_WR_MSB 0
+#define MPUACC_REG_WR_SZ 1
+#define MPUACC_REG_WR_MASK 0x1
+#define MPUBASE_REG_BASE_LSB 6
+#define MPUBASE_REG_BASE_MSB 30
+#define MPUBASE_REG_BASE_SZ 25
+#define MPUBASE_REG_BASE_MASK 0x1ffffff
+#define MPUBASE_REG_D_LSB 0
+#define MPUBASE_REG_D_MSB 0
+#define MPUBASE_REG_D_SZ 1
+#define MPUBASE_REG_D_MASK 0x1
+#define MPUBASE_REG_INDEX_LSB 1
+#define MPUBASE_REG_INDEX_MSB 5
+#define MPUBASE_REG_INDEX_SZ 5
+#define MPUBASE_REG_INDEX_MASK 0x1f
+#define MPUBASE_REG_LSB 0
+#define MPUBASE_REG_MSB 31
+#define MPUBASE_REG_REGNUM 14
+#define MPUBASE_REG_RSV1_LSB 31
+#define MPUBASE_REG_RSV1_MSB 31
+#define MPUBASE_REG_RSV1_SZ 1
+#define MPUBASE_REG_RSV1_MASK 0x1
+#define MPUBASE_REG_SZ 32
+#define MPUBASE_REG_MASK 0xffffffff
+#define PTEADDR_REG_LSB 0
+#define PTEADDR_REG_MSB 31
+#define PTEADDR_REG_PTBASE_LSB 22
+#define PTEADDR_REG_PTBASE_MSB 31
+#define PTEADDR_REG_PTBASE_SZ 10
+#define PTEADDR_REG_PTBASE_MASK 0x3ff
+#define PTEADDR_REG_REGNUM 8
+#define PTEADDR_REG_RSV_LSB 0
+#define PTEADDR_REG_RSV_MSB 1
+#define PTEADDR_REG_RSV_SZ 2
+#define PTEADDR_REG_RSV_MASK 0x3
+#define PTEADDR_REG_SZ 32
+#define PTEADDR_REG_MASK 0xffffffff
+#define PTEADDR_REG_VPN_LSB 2
+#define PTEADDR_REG_VPN_MSB 21
+#define PTEADDR_REG_VPN_SZ 20
+#define PTEADDR_REG_VPN_MASK 0xfffff
+#define REGNUM_SZ 5
+#define REGNUM_MASK 0x1f
+#define RETADDR_REGNUM 31
+#define RF_ADDR_SZ 5
+#define RF_ADDR_MASK 0x1f
+#define RF_NUM_REG 32
+#define SIM_REG_LSB 0
+#define SIM_REG_MSB 2
+#define SIM_REG_PERF_CNT_CLR_LSB 2
+#define SIM_REG_PERF_CNT_CLR_MSB 2
+#define SIM_REG_PERF_CNT_CLR_SZ 1
+#define SIM_REG_PERF_CNT_CLR_MASK 0x1
+#define SIM_REG_PERF_CNT_EN_LSB 1
+#define SIM_REG_PERF_CNT_EN_MSB 1
+#define SIM_REG_PERF_CNT_EN_SZ 1
+#define SIM_REG_PERF_CNT_EN_MASK 0x1
+#define SIM_REG_REGNUM 6
+#define SIM_REG_SHOW_MMU_REGS_LSB 4
+#define SIM_REG_SHOW_MMU_REGS_MSB 4
+#define SIM_REG_SHOW_MMU_REGS_SZ 1
+#define SIM_REG_SHOW_MMU_REGS_MASK 0x1
+#define SIM_REG_SHOW_TLB_LSB 3
+#define SIM_REG_SHOW_TLB_MSB 3
+#define SIM_REG_SHOW_TLB_SZ 1
+#define SIM_REG_SHOW_TLB_MASK 0x1
+#define SIM_REG_STOP_LSB 0
+#define SIM_REG_STOP_MSB 0
+#define SIM_REG_STOP_SZ 1
+#define SIM_REG_STOP_MASK 0x1
+#define SIM_REG_SZ 3
+#define SIM_REG_MASK 0x7
+#define SP_REGNUM 27
+#define STATUS_REG_EH_LSB 2
+#define STATUS_REG_EH_MSB 2
+#define STATUS_REG_EH_SZ 1
+#define STATUS_REG_EH_MASK 0x1
+#define STATUS_REG_LSB 0
+#define STATUS_REG_MMU_LSB 0
+#define STATUS_REG_MMU_MSB 2
+#define STATUS_REG_MMU_RSV_LSB 3
+#define STATUS_REG_MMU_RSV_MSB 31
+#define STATUS_REG_MMU_RSV_SZ 29
+#define STATUS_REG_MMU_RSV_MASK 0x1fffffff
+#define STATUS_REG_MMU_SZ 3
+#define STATUS_REG_MMU_MASK 0x7
+#define STATUS_REG_MPU_LSB 0
+#define STATUS_REG_MPU_MSB 1
+#define STATUS_REG_MPU_RSV_LSB 2
+#define STATUS_REG_MPU_RSV_MSB 31
+#define STATUS_REG_MPU_RSV_SZ 30
+#define STATUS_REG_MPU_RSV_MASK 0x3fffffff
+#define STATUS_REG_MPU_SZ 2
+#define STATUS_REG_MPU_MASK 0x3
+#define STATUS_REG_MSB 2
+#define STATUS_REG_NO_MMU_LSB 0
+#define STATUS_REG_NO_MMU_MSB 0
+#define STATUS_REG_NO_MMU_RSV_LSB 1
+#define STATUS_REG_NO_MMU_RSV_MSB 31
+#define STATUS_REG_NO_MMU_RSV_SZ 31
+#define STATUS_REG_NO_MMU_RSV_MASK 0x7fffffff
+#define STATUS_REG_NO_MMU_SZ 1
+#define STATUS_REG_NO_MMU_MASK 0x1
+#define STATUS_REG_PIE_LSB 0
+#define STATUS_REG_PIE_MSB 0
+#define STATUS_REG_PIE_SZ 1
+#define STATUS_REG_PIE_MASK 0x1
+#define STATUS_REG_REGNUM 0
+#define STATUS_REG_SZ 3
+#define STATUS_REG_MASK 0x7
+#define STATUS_REG_U_LSB 1
+#define STATUS_REG_U_MSB 1
+#define STATUS_REG_U_SZ 1
+#define STATUS_REG_U_MASK 0x1
+#define TLB_MAX_ENTRIES 1024
+#define TLB_MAX_LINES 128
+#define TLB_MAX_PID_SZ 14
+#define TLB_MAX_PID_MASK 0x3fff
+#define TLB_MAX_PTR_SZ 10
+#define TLB_MAX_PTR_MASK 0x3ff
+#define TLB_MAX_WAYS 16
+#define TLB_MIN_PID_SZ 8
+#define TLB_MIN_PID_MASK 0xff
+#define TLB_MIN_PTR_SZ 7
+#define TLB_MIN_PTR_MASK 0x7f
+#define TLB_MIN_WAYS 8
+#define TLBACC_REG_C_LSB 24
+#define TLBACC_REG_C_MSB 24
+#define TLBACC_REG_C_SZ 1
+#define TLBACC_REG_C_MASK 0x1
+#define TLBACC_REG_G_LSB 20
+#define TLBACC_REG_G_MSB 20
+#define TLBACC_REG_G_SZ 1
+#define TLBACC_REG_G_MASK 0x1
+#define TLBACC_REG_IG_LSB 25
+#define TLBACC_REG_IG_MSB 31
+#define TLBACC_REG_IG_SZ 7
+#define TLBACC_REG_IG_MASK 0x7f
+#define TLBACC_REG_LSB 0
+#define TLBACC_REG_MSB 24
+#define TLBACC_REG_PFN_LSB 0
+#define TLBACC_REG_PFN_MSB 19
+#define TLBACC_REG_PFN_SZ 20
+#define TLBACC_REG_PFN_MASK 0xfffff
+#define TLBACC_REG_R_LSB 23
+#define TLBACC_REG_R_MSB 23
+#define TLBACC_REG_R_SZ 1
+#define TLBACC_REG_R_MASK 0x1
+#define TLBACC_REG_REGNUM 9
+#define TLBACC_REG_SZ 25
+#define TLBACC_REG_MASK 0x1ffffff
+#define TLBACC_REG_W_LSB 22
+#define TLBACC_REG_W_MSB 22
+#define TLBACC_REG_W_SZ 1
+#define TLBACC_REG_W_MASK 0x1
+#define TLBACC_REG_X_LSB 21
+#define TLBACC_REG_X_MSB 21
+#define TLBACC_REG_X_SZ 1
+#define TLBACC_REG_X_MASK 0x1
+#define TLBMISC_REG_BAD_LSB 2
+#define TLBMISC_REG_BAD_MSB 2
+#define TLBMISC_REG_BAD_SZ 1
+#define TLBMISC_REG_BAD_MASK 0x1
+#define TLBMISC_REG_D_LSB 0
+#define TLBMISC_REG_D_MSB 0
+#define TLBMISC_REG_D_SZ 1
+#define TLBMISC_REG_D_MASK 0x1
+#define TLBMISC_REG_DBL_LSB 3
+#define TLBMISC_REG_DBL_MSB 3
+#define TLBMISC_REG_DBL_SZ 1
+#define TLBMISC_REG_DBL_MASK 0x1
+#define TLBMISC_REG_LSB 0
+#define TLBMISC_REG_MSB 23
+#define TLBMISC_REG_PERM_LSB 1
+#define TLBMISC_REG_PERM_MSB 1
+#define TLBMISC_REG_PERM_SZ 1
+#define TLBMISC_REG_PERM_MASK 0x1
+#define TLBMISC_REG_PID_LSB 4
+#define TLBMISC_REG_PID_MSB 17
+#define TLBMISC_REG_PID_SZ 14
+#define TLBMISC_REG_PID_MASK 0x3fff
+#define TLBMISC_REG_RD_LSB 19
+#define TLBMISC_REG_RD_MSB 19
+#define TLBMISC_REG_RD_SZ 1
+#define TLBMISC_REG_RD_MASK 0x1
+#define TLBMISC_REG_REGNUM 10
+#define TLBMISC_REG_RSV1_LSB 24
+#define TLBMISC_REG_RSV1_MSB 31
+#define TLBMISC_REG_RSV1_SZ 8
+#define TLBMISC_REG_RSV1_MASK 0xff
+#define TLBMISC_REG_SZ 24
+#define TLBMISC_REG_MASK 0xffffff
+#define TLBMISC_REG_WAY_LSB 20
+#define TLBMISC_REG_WAY_MSB 23
+#define TLBMISC_REG_WAY_SZ 4
+#define TLBMISC_REG_WAY_MASK 0xf
+#define TLBMISC_REG_WE_LSB 18
+#define TLBMISC_REG_WE_MSB 18
+#define TLBMISC_REG_WE_SZ 1
+#define TLBMISC_REG_WE_MASK 0x1
+
+/* Macros to extract instruction fields */
+#define GET_IW_A(Iw) \
+ (((Iw) >> IW_A_LSB) & IW_A_MASK)
+#define SET_IW_A(Iw, Val) \
+ Iw = (((Iw) & (~(IW_A_MASK << IW_A_LSB))) | \
+ (((Val) & IW_A_MASK) << IW_A_LSB))
+#define GET_IW_B(Iw) \
+ (((Iw) >> IW_B_LSB) & IW_B_MASK)
+#define SET_IW_B(Iw, Val) \
+ Iw = (((Iw) & (~(IW_B_MASK << IW_B_LSB))) | \
+ (((Val) & IW_B_MASK) << IW_B_LSB))
+#define GET_IW_C(Iw) \
+ (((Iw) >> IW_C_LSB) & IW_C_MASK)
+#define SET_IW_C(Iw, Val) \
+ Iw = (((Iw) & (~(IW_C_MASK << IW_C_LSB))) | \
+ (((Val) & IW_C_MASK) << IW_C_LSB))
+#define GET_IW_CONTROL_REGNUM(Iw) \
+ (((Iw) >> IW_CONTROL_REGNUM_LSB) & IW_CONTROL_REGNUM_MASK)
+#define SET_IW_CONTROL_REGNUM(Iw, Val) \
+ Iw = (((Iw) & (~(IW_CONTROL_REGNUM_MASK << IW_CONTROL_REGNUM_LSB))) | \
+ (((Val) & IW_CONTROL_REGNUM_MASK) << IW_CONTROL_REGNUM_LSB))
+#define GET_IW_CUSTOM_N(Iw) \
+ (((Iw) >> IW_CUSTOM_N_LSB) & IW_CUSTOM_N_MASK)
+#define SET_IW_CUSTOM_N(Iw, Val) \
+ Iw = (((Iw) & (~(IW_CUSTOM_N_MASK << IW_CUSTOM_N_LSB))) | \
+ (((Val) & IW_CUSTOM_N_MASK) << IW_CUSTOM_N_LSB))
+#define GET_IW_CUSTOM_READRA(Iw) \
+ (((Iw) >> IW_CUSTOM_READRA_LSB) & IW_CUSTOM_READRA_MASK)
+#define SET_IW_CUSTOM_READRA(Iw, Val) \
+ Iw = (((Iw) & (~(IW_CUSTOM_READRA_MASK << IW_CUSTOM_READRA_LSB))) | \
+ (((Val) & IW_CUSTOM_READRA_MASK) << IW_CUSTOM_READRA_LSB))
+#define GET_IW_CUSTOM_READRB(Iw) \
+ (((Iw) >> IW_CUSTOM_READRB_LSB) & IW_CUSTOM_READRB_MASK)
+#define SET_IW_CUSTOM_READRB(Iw, Val) \
+ Iw = (((Iw) & (~(IW_CUSTOM_READRB_MASK << IW_CUSTOM_READRB_LSB))) | \
+ (((Val) & IW_CUSTOM_READRB_MASK) << IW_CUSTOM_READRB_LSB))
+#define GET_IW_CUSTOM_WRITERC(Iw) \
+ (((Iw) >> IW_CUSTOM_WRITERC_LSB) & IW_CUSTOM_WRITERC_MASK)
+#define SET_IW_CUSTOM_WRITERC(Iw, Val) \
+ Iw = (((Iw) & (~(IW_CUSTOM_WRITERC_MASK << IW_CUSTOM_WRITERC_LSB))) | \
+ (((Val) & IW_CUSTOM_WRITERC_MASK) << IW_CUSTOM_WRITERC_LSB))
+#define GET_IW_IMM16(Iw) \
+ (((Iw) >> IW_IMM16_LSB) & IW_IMM16_MASK)
+#define SET_IW_IMM16(Iw, Val) \
+ Iw = (((Iw) & (~(IW_IMM16_MASK << IW_IMM16_LSB))) | \
+ (((Val) & IW_IMM16_MASK) << IW_IMM16_LSB))
+#define GET_IW_IMM26(Iw) \
+ (((Iw) >> IW_IMM26_LSB) & IW_IMM26_MASK)
+#define SET_IW_IMM26(Iw, Val) \
+ Iw = (((Iw) & (~(IW_IMM26_MASK << IW_IMM26_LSB))) | \
+ (((Val) & IW_IMM26_MASK) << IW_IMM26_LSB))
+#define GET_IW_MEMSZ(Iw) \
+ (((Iw) >> IW_MEMSZ_LSB) & IW_MEMSZ_MASK)
+#define SET_IW_MEMSZ(Iw, Val) \
+ Iw = (((Iw) & (~(IW_MEMSZ_MASK << IW_MEMSZ_LSB))) | \
+ (((Val) & IW_MEMSZ_MASK) << IW_MEMSZ_LSB))
+#define GET_IW_OP(Iw) \
+ (((Iw) >> IW_OP_LSB) & IW_OP_MASK)
+#define SET_IW_OP(Iw, Val) \
+ Iw = (((Iw) & (~(IW_OP_MASK << IW_OP_LSB))) | \
+ (((Val) & IW_OP_MASK) << IW_OP_LSB))
+#define GET_IW_OPX(Iw) \
+ (((Iw) >> IW_OPX_LSB) & IW_OPX_MASK)
+#define SET_IW_OPX(Iw, Val) \
+ Iw = (((Iw) & (~(IW_OPX_MASK << IW_OPX_LSB))) | \
+ (((Val) & IW_OPX_MASK) << IW_OPX_LSB))
+#define GET_IW_SHIFT_IMM5(Iw) \
+ (((Iw) >> IW_SHIFT_IMM5_LSB) & IW_SHIFT_IMM5_MASK)
+#define SET_IW_SHIFT_IMM5(Iw, Val) \
+ Iw = (((Iw) & (~(IW_SHIFT_IMM5_MASK << IW_SHIFT_IMM5_LSB))) | \
+ (((Val) & IW_SHIFT_IMM5_MASK) << IW_SHIFT_IMM5_LSB))
+#define GET_IW_TRAP_BREAK_IMM5(Iw) \
+ (((Iw) >> IW_TRAP_BREAK_IMM5_LSB) & IW_TRAP_BREAK_IMM5_MASK)
+#define SET_IW_TRAP_BREAK_IMM5(Iw, Val) \
+ Iw = (((Iw) & (~(IW_TRAP_BREAK_IMM5_MASK << IW_TRAP_BREAK_IMM5_LSB))) | \
+ (((Val) & IW_TRAP_BREAK_IMM5_MASK) << IW_TRAP_BREAK_IMM5_LSB))
+
+/* Macros to extract control register fields */
+#define GET_BADADDR_REG_BADDR(Reg) \
+ (((Reg) >> BADADDR_REG_BADDR_LSB) & BADADDR_REG_BADDR_MASK)
+#define SET_BADADDR_REG_BADDR(Reg, Val) \
+ Reg = (((Reg) & (~(BADADDR_REG_BADDR_MASK << BADADDR_REG_BADDR_LSB))) | \
+ (((Val) & BADADDR_REG_BADDR_MASK) << BADADDR_REG_BADDR_LSB))
+#define GET_BSTATUS_REG_MMU(Reg) \
+ (((Reg) >> BSTATUS_REG_MMU_LSB) & BSTATUS_REG_MMU_MASK)
+#define SET_BSTATUS_REG_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(BSTATUS_REG_MMU_MASK << BSTATUS_REG_MMU_LSB))) | \
+ (((Val) & BSTATUS_REG_MMU_MASK) << BSTATUS_REG_MMU_LSB))
+#define GET_BSTATUS_REG_MPU(Reg) \
+ (((Reg) >> BSTATUS_REG_MPU_LSB) & BSTATUS_REG_MPU_MASK)
+#define SET_BSTATUS_REG_MPU(Reg, Val) \
+ Reg = (((Reg) & (~(BSTATUS_REG_MPU_MASK << BSTATUS_REG_MPU_LSB))) | \
+ (((Val) & BSTATUS_REG_MPU_MASK) << BSTATUS_REG_MPU_LSB))
+#define GET_BSTATUS_REG_NO_MMU(Reg) \
+ (((Reg) >> BSTATUS_REG_NO_MMU_LSB) & BSTATUS_REG_NO_MMU_MASK)
+#define SET_BSTATUS_REG_NO_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(BSTATUS_REG_NO_MMU_MASK << BSTATUS_REG_NO_MMU_LSB))) | \
+ (((Val) & BSTATUS_REG_NO_MMU_MASK) << BSTATUS_REG_NO_MMU_LSB))
+#define GET_CONFIG_REG_PE(Reg) \
+ (((Reg) >> CONFIG_REG_PE_LSB) & CONFIG_REG_PE_MASK)
+#define SET_CONFIG_REG_PE(Reg, Val) \
+ Reg = (((Reg) & (~(CONFIG_REG_PE_MASK << CONFIG_REG_PE_LSB))) | \
+ (((Val) & CONFIG_REG_PE_MASK) << CONFIG_REG_PE_LSB))
+#define GET_ESTATUS_REG_MMU(Reg) \
+ (((Reg) >> ESTATUS_REG_MMU_LSB) & ESTATUS_REG_MMU_MASK)
+#define SET_ESTATUS_REG_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(ESTATUS_REG_MMU_MASK << ESTATUS_REG_MMU_LSB))) | \
+ (((Val) & ESTATUS_REG_MMU_MASK) << ESTATUS_REG_MMU_LSB))
+#define GET_ESTATUS_REG_MPU(Reg) \
+ (((Reg) >> ESTATUS_REG_MPU_LSB) & ESTATUS_REG_MPU_MASK)
+#define SET_ESTATUS_REG_MPU(Reg, Val) \
+ Reg = (((Reg) & (~(ESTATUS_REG_MPU_MASK << ESTATUS_REG_MPU_LSB))) | \
+ (((Val) & ESTATUS_REG_MPU_MASK) << ESTATUS_REG_MPU_LSB))
+#define GET_ESTATUS_REG_NO_MMU(Reg) \
+ (((Reg) >> ESTATUS_REG_NO_MMU_LSB) & ESTATUS_REG_NO_MMU_MASK)
+#define SET_ESTATUS_REG_NO_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(ESTATUS_REG_NO_MMU_MASK << ESTATUS_REG_NO_MMU_LSB))) | \
+ (((Val) & ESTATUS_REG_NO_MMU_MASK) << ESTATUS_REG_NO_MMU_LSB))
+#define GET_EXCEPTION_REG_CAUSE(Reg) \
+ (((Reg) >> EXCEPTION_REG_CAUSE_LSB) & EXCEPTION_REG_CAUSE_MASK)
+#define SET_EXCEPTION_REG_CAUSE(Reg, Val) \
+ Reg = (((Reg) & (~(EXCEPTION_REG_CAUSE_MASK << \
+ EXCEPTION_REG_CAUSE_LSB))) | \
+ (((Val) & EXCEPTION_REG_CAUSE_MASK) << EXCEPTION_REG_CAUSE_LSB))
+#define GET_EXCEPTION_REG_MEA(Reg) \
+ (((Reg) >> EXCEPTION_REG_MEA_LSB) & EXCEPTION_REG_MEA_MASK)
+#define SET_EXCEPTION_REG_MEA(Reg, Val) \
+ Reg = (((Reg) & (~(EXCEPTION_REG_MEA_MASK << EXCEPTION_REG_MEA_LSB))) | \
+ (((Val) & EXCEPTION_REG_MEA_MASK) << EXCEPTION_REG_MEA_LSB))
+#define GET_EXCEPTION_REG_MEE(Reg) \
+ (((Reg) >> EXCEPTION_REG_MEE_LSB) & EXCEPTION_REG_MEE_MASK)
+#define SET_EXCEPTION_REG_MEE(Reg, Val) \
+ Reg = (((Reg) & (~(EXCEPTION_REG_MEE_MASK << EXCEPTION_REG_MEE_LSB))) | \
+ (((Val) & EXCEPTION_REG_MEE_MASK) << EXCEPTION_REG_MEE_LSB))
+#define GET_MPUACC_REG_C(Reg) \
+ (((Reg) >> MPUACC_REG_C_LSB) & MPUACC_REG_C_MASK)
+#define SET_MPUACC_REG_C(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_C_MASK << MPUACC_REG_C_LSB))) | \
+ (((Val) & MPUACC_REG_C_MASK) << MPUACC_REG_C_LSB))
+#define GET_MPUACC_REG_LIMIT(Reg) \
+ (((Reg) >> MPUACC_REG_LIMIT_LSB) & MPUACC_REG_LIMIT_MASK)
+#define SET_MPUACC_REG_LIMIT(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_LIMIT_MASK << MPUACC_REG_LIMIT_LSB))) | \
+ (((Val) & MPUACC_REG_LIMIT_MASK) << MPUACC_REG_LIMIT_LSB))
+#define GET_MPUACC_REG_MASK(Reg) \
+ (((Reg) >> MPUACC_REG_MASK_LSB) & MPUACC_REG_MASK_MASK)
+#define SET_MPUACC_REG_MASK(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_MASK_MASK << MPUACC_REG_MASK_LSB))) | \
+ (((Val) & MPUACC_REG_MASK_MASK) << MPUACC_REG_MASK_LSB))
+#define GET_MPUACC_REG_PERM(Reg) \
+ (((Reg) >> MPUACC_REG_PERM_LSB) & MPUACC_REG_PERM_MASK)
+#define SET_MPUACC_REG_PERM(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_PERM_MASK << MPUACC_REG_PERM_LSB))) | \
+ (((Val) & MPUACC_REG_PERM_MASK) << MPUACC_REG_PERM_LSB))
+#define GET_MPUACC_REG_RD(Reg) \
+ (((Reg) >> MPUACC_REG_RD_LSB) & MPUACC_REG_RD_MASK)
+#define SET_MPUACC_REG_RD(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_RD_MASK << MPUACC_REG_RD_LSB))) | \
+ (((Val) & MPUACC_REG_RD_MASK) << MPUACC_REG_RD_LSB))
+#define GET_MPUACC_REG_RSV1(Reg) \
+ (((Reg) >> MPUACC_REG_RSV1_LSB) & MPUACC_REG_RSV1_MASK)
+#define SET_MPUACC_REG_RSV1(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_RSV1_MASK << MPUACC_REG_RSV1_LSB))) | \
+ (((Val) & MPUACC_REG_RSV1_MASK) << MPUACC_REG_RSV1_LSB))
+#define GET_MPUACC_REG_WR(Reg) \
+ (((Reg) >> MPUACC_REG_WR_LSB) & MPUACC_REG_WR_MASK)
+#define SET_MPUACC_REG_WR(Reg, Val) \
+ Reg = (((Reg) & (~(MPUACC_REG_WR_MASK << MPUACC_REG_WR_LSB))) | \
+ (((Val) & MPUACC_REG_WR_MASK) << MPUACC_REG_WR_LSB))
+#define GET_MPUBASE_REG_BASE(Reg) \
+ (((Reg) >> MPUBASE_REG_BASE_LSB) & MPUBASE_REG_BASE_MASK)
+#define SET_MPUBASE_REG_BASE(Reg, Val) \
+ Reg = (((Reg) & (~(MPUBASE_REG_BASE_MASK << MPUBASE_REG_BASE_LSB))) | \
+ (((Val) & MPUBASE_REG_BASE_MASK) << MPUBASE_REG_BASE_LSB))
+#define GET_MPUBASE_REG_D(Reg) \
+ (((Reg) >> MPUBASE_REG_D_LSB) & MPUBASE_REG_D_MASK)
+#define SET_MPUBASE_REG_D(Reg, Val) \
+ Reg = (((Reg) & (~(MPUBASE_REG_D_MASK << MPUBASE_REG_D_LSB))) | \
+ (((Val) & MPUBASE_REG_D_MASK) << MPUBASE_REG_D_LSB))
+#define GET_MPUBASE_REG_INDEX(Reg) \
+ (((Reg) >> MPUBASE_REG_INDEX_LSB) & MPUBASE_REG_INDEX_MASK)
+#define SET_MPUBASE_REG_INDEX(Reg, Val) \
+ Reg = (((Reg) & (~(MPUBASE_REG_INDEX_MASK << MPUBASE_REG_INDEX_LSB))) | \
+ (((Val) & MPUBASE_REG_INDEX_MASK) << MPUBASE_REG_INDEX_LSB))
+#define GET_MPUBASE_REG_RSV1(Reg) \
+ (((Reg) >> MPUBASE_REG_RSV1_LSB) & MPUBASE_REG_RSV1_MASK)
+#define SET_MPUBASE_REG_RSV1(Reg, Val) \
+ Reg = (((Reg) & (~(MPUBASE_REG_RSV1_MASK << MPUBASE_REG_RSV1_LSB))) | \
+ (((Val) & MPUBASE_REG_RSV1_MASK) << MPUBASE_REG_RSV1_LSB))
+#define GET_PTEADDR_REG_PTBASE(Reg) \
+ (((Reg) >> PTEADDR_REG_PTBASE_LSB) & PTEADDR_REG_PTBASE_MASK)
+#define SET_PTEADDR_REG_PTBASE(Reg, Val) \
+ Reg = (((Reg) & (~(PTEADDR_REG_PTBASE_MASK << PTEADDR_REG_PTBASE_LSB))) | \
+ (((Val) & PTEADDR_REG_PTBASE_MASK) << PTEADDR_REG_PTBASE_LSB))
+#define GET_PTEADDR_REG_RSV(Reg) \
+ (((Reg) >> PTEADDR_REG_RSV_LSB) & PTEADDR_REG_RSV_MASK)
+#define SET_PTEADDR_REG_RSV(Reg, Val) \
+ Reg = (((Reg) & (~(PTEADDR_REG_RSV_MASK << PTEADDR_REG_RSV_LSB))) | \
+ (((Val) & PTEADDR_REG_RSV_MASK) << PTEADDR_REG_RSV_LSB))
+#define GET_PTEADDR_REG_VPN(Reg) \
+ (((Reg) >> PTEADDR_REG_VPN_LSB) & PTEADDR_REG_VPN_MASK)
+#define SET_PTEADDR_REG_VPN(Reg, Val) \
+ Reg = (((Reg) & (~(PTEADDR_REG_VPN_MASK << PTEADDR_REG_VPN_LSB))) | \
+ (((Val) & PTEADDR_REG_VPN_MASK) << PTEADDR_REG_VPN_LSB))
+#define GET_SIM_REG_PERF_CNT_CLR(Reg) \
+ (((Reg) >> SIM_REG_PERF_CNT_CLR_LSB) & SIM_REG_PERF_CNT_CLR_MASK)
+#define SET_SIM_REG_PERF_CNT_CLR(Reg, Val) \
+ Reg = (((Reg) & (~(SIM_REG_PERF_CNT_CLR_MASK << \
+ SIM_REG_PERF_CNT_CLR_LSB))) | \
+ (((Val) & SIM_REG_PERF_CNT_CLR_MASK) << SIM_REG_PERF_CNT_CLR_LSB))
+#define GET_SIM_REG_PERF_CNT_EN(Reg) \
+ (((Reg) >> SIM_REG_PERF_CNT_EN_LSB) & SIM_REG_PERF_CNT_EN_MASK)
+#define SET_SIM_REG_PERF_CNT_EN(Reg, Val) \
+ Reg = (((Reg) & (~(SIM_REG_PERF_CNT_EN_MASK << \
+ SIM_REG_PERF_CNT_EN_LSB))) | \
+ (((Val) & SIM_REG_PERF_CNT_EN_MASK) << SIM_REG_PERF_CNT_EN_LSB))
+#define GET_SIM_REG_SHOW_MMU_REGS(Reg) \
+ (((Reg) >> SIM_REG_SHOW_MMU_REGS_LSB) & SIM_REG_SHOW_MMU_REGS_MASK)
+#define SET_SIM_REG_SHOW_MMU_REGS(Reg, Val) \
+ Reg = (((Reg) & (~(SIM_REG_SHOW_MMU_REGS_MASK << \
+ SIM_REG_SHOW_MMU_REGS_LSB))) | \
+ (((Val) & SIM_REG_SHOW_MMU_REGS_MASK) << SIM_REG_SHOW_MMU_REGS_LSB))
+#define GET_SIM_REG_SHOW_TLB(Reg) \
+ (((Reg) >> SIM_REG_SHOW_TLB_LSB) & SIM_REG_SHOW_TLB_MASK)
+#define SET_SIM_REG_SHOW_TLB(Reg, Val) \
+ Reg = (((Reg) & (~(SIM_REG_SHOW_TLB_MASK << SIM_REG_SHOW_TLB_LSB))) | \
+ (((Val) & SIM_REG_SHOW_TLB_MASK) << SIM_REG_SHOW_TLB_LSB))
+#define GET_SIM_REG_STOP(Reg) \
+ (((Reg) >> SIM_REG_STOP_LSB) & SIM_REG_STOP_MASK)
+#define SET_SIM_REG_STOP(Reg, Val) \
+ Reg = (((Reg) & (~(SIM_REG_STOP_MASK << SIM_REG_STOP_LSB))) | \
+ (((Val) & SIM_REG_STOP_MASK) << SIM_REG_STOP_LSB))
+#define GET_STATUS_REG_EH(Reg) \
+ (((Reg) >> STATUS_REG_EH_LSB) & STATUS_REG_EH_MASK)
+#define SET_STATUS_REG_EH(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_EH_MASK << STATUS_REG_EH_LSB))) | \
+ (((Val) & STATUS_REG_EH_MASK) << STATUS_REG_EH_LSB))
+#define GET_STATUS_REG_MMU(Reg) \
+ (((Reg) >> STATUS_REG_MMU_LSB) & STATUS_REG_MMU_MASK)
+#define SET_STATUS_REG_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_MMU_MASK << STATUS_REG_MMU_LSB))) | \
+ (((Val) & STATUS_REG_MMU_MASK) << STATUS_REG_MMU_LSB))
+#define GET_STATUS_REG_MMU_RSV(Reg) \
+ (((Reg) >> STATUS_REG_MMU_RSV_LSB) & STATUS_REG_MMU_RSV_MASK)
+#define SET_STATUS_REG_MMU_RSV(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_MMU_RSV_MASK << STATUS_REG_MMU_RSV_LSB))) | \
+ (((Val) & STATUS_REG_MMU_RSV_MASK) << STATUS_REG_MMU_RSV_LSB))
+#define GET_STATUS_REG_MPU(Reg) \
+ (((Reg) >> STATUS_REG_MPU_LSB) & STATUS_REG_MPU_MASK)
+#define SET_STATUS_REG_MPU(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_MPU_MASK << STATUS_REG_MPU_LSB))) | \
+ (((Val) & STATUS_REG_MPU_MASK) << STATUS_REG_MPU_LSB))
+#define GET_STATUS_REG_MPU_RSV(Reg) \
+ (((Reg) >> STATUS_REG_MPU_RSV_LSB) & STATUS_REG_MPU_RSV_MASK)
+#define SET_STATUS_REG_MPU_RSV(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_MPU_RSV_MASK << STATUS_REG_MPU_RSV_LSB))) | \
+ (((Val) & STATUS_REG_MPU_RSV_MASK) << STATUS_REG_MPU_RSV_LSB))
+#define GET_STATUS_REG_NO_MMU(Reg) \
+ (((Reg) >> STATUS_REG_NO_MMU_LSB) & STATUS_REG_NO_MMU_MASK)
+#define SET_STATUS_REG_NO_MMU(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_NO_MMU_MASK << STATUS_REG_NO_MMU_LSB))) | \
+ (((Val) & STATUS_REG_NO_MMU_MASK) << STATUS_REG_NO_MMU_LSB))
+#define GET_STATUS_REG_NO_MMU_RSV(Reg) \
+ (((Reg) >> STATUS_REG_NO_MMU_RSV_LSB) & STATUS_REG_NO_MMU_RSV_MASK)
+#define SET_STATUS_REG_NO_MMU_RSV(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_NO_MMU_RSV_MASK << \
+ STATUS_REG_NO_MMU_RSV_LSB))) | \
+ (((Val) & STATUS_REG_NO_MMU_RSV_MASK) << STATUS_REG_NO_MMU_RSV_LSB))
+#define GET_STATUS_REG_PIE(Reg) \
+ (((Reg) >> STATUS_REG_PIE_LSB) & STATUS_REG_PIE_MASK)
+#define SET_STATUS_REG_PIE(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_PIE_MASK << STATUS_REG_PIE_LSB))) | \
+ (((Val) & STATUS_REG_PIE_MASK) << STATUS_REG_PIE_LSB))
+#define GET_STATUS_REG_U(Reg) \
+ (((Reg) >> STATUS_REG_U_LSB) & STATUS_REG_U_MASK)
+#define SET_STATUS_REG_U(Reg, Val) \
+ Reg = (((Reg) & (~(STATUS_REG_U_MASK << STATUS_REG_U_LSB))) | \
+ (((Val) & STATUS_REG_U_MASK) << STATUS_REG_U_LSB))
+#define GET_TLBACC_REG_C(Reg) \
+ (((Reg) >> TLBACC_REG_C_LSB) & TLBACC_REG_C_MASK)
+#define SET_TLBACC_REG_C(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_C_MASK << TLBACC_REG_C_LSB))) | \
+ (((Val) & TLBACC_REG_C_MASK) << TLBACC_REG_C_LSB))
+#define GET_TLBACC_REG_G(Reg) \
+ (((Reg) >> TLBACC_REG_G_LSB) & TLBACC_REG_G_MASK)
+#define SET_TLBACC_REG_G(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_G_MASK << TLBACC_REG_G_LSB))) | \
+ (((Val) & TLBACC_REG_G_MASK) << TLBACC_REG_G_LSB))
+#define GET_TLBACC_REG_IG(Reg) \
+ (((Reg) >> TLBACC_REG_IG_LSB) & TLBACC_REG_IG_MASK)
+#define SET_TLBACC_REG_IG(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_IG_MASK << TLBACC_REG_IG_LSB))) | \
+ (((Val) & TLBACC_REG_IG_MASK) << TLBACC_REG_IG_LSB))
+#define GET_TLBACC_REG_PFN(Reg) \
+ (((Reg) >> TLBACC_REG_PFN_LSB) & TLBACC_REG_PFN_MASK)
+#define SET_TLBACC_REG_PFN(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_PFN_MASK << TLBACC_REG_PFN_LSB))) | \
+ (((Val) & TLBACC_REG_PFN_MASK) << TLBACC_REG_PFN_LSB))
+#define GET_TLBACC_REG_R(Reg) \
+ (((Reg) >> TLBACC_REG_R_LSB) & TLBACC_REG_R_MASK)
+#define SET_TLBACC_REG_R(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_R_MASK << TLBACC_REG_R_LSB))) | \
+ (((Val) & TLBACC_REG_R_MASK) << TLBACC_REG_R_LSB))
+#define GET_TLBACC_REG_W(Reg) \
+ (((Reg) >> TLBACC_REG_W_LSB) & TLBACC_REG_W_MASK)
+#define SET_TLBACC_REG_W(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_W_MASK << TLBACC_REG_W_LSB))) | \
+ (((Val) & TLBACC_REG_W_MASK) << TLBACC_REG_W_LSB))
+#define GET_TLBACC_REG_X(Reg) \
+ (((Reg) >> TLBACC_REG_X_LSB) & TLBACC_REG_X_MASK)
+#define SET_TLBACC_REG_X(Reg, Val) \
+ Reg = (((Reg) & (~(TLBACC_REG_X_MASK << TLBACC_REG_X_LSB))) | \
+ (((Val) & TLBACC_REG_X_MASK) << TLBACC_REG_X_LSB))
+#define GET_TLBMISC_REG_BAD(Reg) \
+ (((Reg) >> TLBMISC_REG_BAD_LSB) & TLBMISC_REG_BAD_MASK)
+#define SET_TLBMISC_REG_BAD(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_BAD_MASK << TLBMISC_REG_BAD_LSB))) | \
+ (((Val) & TLBMISC_REG_BAD_MASK) << TLBMISC_REG_BAD_LSB))
+#define GET_TLBMISC_REG_D(Reg) \
+ (((Reg) >> TLBMISC_REG_D_LSB) & TLBMISC_REG_D_MASK)
+#define SET_TLBMISC_REG_D(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_D_MASK << TLBMISC_REG_D_LSB))) | \
+ (((Val) & TLBMISC_REG_D_MASK) << TLBMISC_REG_D_LSB))
+#define GET_TLBMISC_REG_DBL(Reg) \
+ (((Reg) >> TLBMISC_REG_DBL_LSB) & TLBMISC_REG_DBL_MASK)
+#define SET_TLBMISC_REG_DBL(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_DBL_MASK << TLBMISC_REG_DBL_LSB))) | \
+ (((Val) & TLBMISC_REG_DBL_MASK) << TLBMISC_REG_DBL_LSB))
+#define GET_TLBMISC_REG_PERM(Reg) \
+ (((Reg) >> TLBMISC_REG_PERM_LSB) & TLBMISC_REG_PERM_MASK)
+#define SET_TLBMISC_REG_PERM(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_PERM_MASK << TLBMISC_REG_PERM_LSB))) | \
+ (((Val) & TLBMISC_REG_PERM_MASK) << TLBMISC_REG_PERM_LSB))
+#define GET_TLBMISC_REG_PID(Reg) \
+ (((Reg) >> TLBMISC_REG_PID_LSB) & TLBMISC_REG_PID_MASK)
+#define SET_TLBMISC_REG_PID(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_PID_MASK << TLBMISC_REG_PID_LSB))) | \
+ (((Val) & TLBMISC_REG_PID_MASK) << TLBMISC_REG_PID_LSB))
+#define GET_TLBMISC_REG_RD(Reg) \
+ (((Reg) >> TLBMISC_REG_RD_LSB) & TLBMISC_REG_RD_MASK)
+#define SET_TLBMISC_REG_RD(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_RD_MASK << TLBMISC_REG_RD_LSB))) | \
+ (((Val) & TLBMISC_REG_RD_MASK) << TLBMISC_REG_RD_LSB))
+#define GET_TLBMISC_REG_RSV1(Reg) \
+ (((Reg) >> TLBMISC_REG_RSV1_LSB) & TLBMISC_REG_RSV1_MASK)
+#define SET_TLBMISC_REG_RSV1(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_RSV1_MASK << TLBMISC_REG_RSV1_LSB))) | \
+ (((Val) & TLBMISC_REG_RSV1_MASK) << TLBMISC_REG_RSV1_LSB))
+#define GET_TLBMISC_REG_WAY(Reg) \
+ (((Reg) >> TLBMISC_REG_WAY_LSB) & TLBMISC_REG_WAY_MASK)
+#define SET_TLBMISC_REG_WAY(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_WAY_MASK << TLBMISC_REG_WAY_LSB))) | \
+ (((Val) & TLBMISC_REG_WAY_MASK) << TLBMISC_REG_WAY_LSB))
+#define GET_TLBMISC_REG_WE(Reg) \
+ (((Reg) >> TLBMISC_REG_WE_LSB) & TLBMISC_REG_WE_MASK)
+#define SET_TLBMISC_REG_WE(Reg, Val) \
+ Reg = (((Reg) & (~(TLBMISC_REG_WE_MASK << TLBMISC_REG_WE_LSB))) | \
+ (((Val) & TLBMISC_REG_WE_MASK) << TLBMISC_REG_WE_LSB))
+
+/* Macros to extract MMU fields */
+#define GET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Addr) \
+ (((Addr) >> MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB) & \
+ MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK)
+#define SET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK << \
+ MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB))) | \
+ (((Val) & MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK) << \
+ MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB))
+#define GET_MMU_ADDR_BYPASS_TLB(Addr) \
+ (((Addr) >> MMU_ADDR_BYPASS_TLB_LSB) & MMU_ADDR_BYPASS_TLB_MASK)
+#define SET_MMU_ADDR_BYPASS_TLB(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_MASK << \
+ MMU_ADDR_BYPASS_TLB_LSB))) | \
+ (((Val) & MMU_ADDR_BYPASS_TLB_MASK) << MMU_ADDR_BYPASS_TLB_LSB))
+#define GET_MMU_ADDR_BYPASS_TLB_PADDR(Addr) \
+ (((Addr) >> MMU_ADDR_BYPASS_TLB_PADDR_LSB) & MMU_ADDR_BYPASS_TLB_PADDR_MASK)
+#define SET_MMU_ADDR_BYPASS_TLB_PADDR(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_PADDR_MASK << \
+ MMU_ADDR_BYPASS_TLB_PADDR_LSB))) | \
+ (((Val) & MMU_ADDR_BYPASS_TLB_PADDR_MASK) << \
+ MMU_ADDR_BYPASS_TLB_PADDR_LSB))
+#define GET_MMU_ADDR_IO_REGION(Addr) \
+ (((Addr) >> MMU_ADDR_IO_REGION_LSB) & MMU_ADDR_IO_REGION_MASK)
+#define SET_MMU_ADDR_IO_REGION(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_IO_REGION_MASK << \
+ MMU_ADDR_IO_REGION_LSB))) | \
+ (((Val) & MMU_ADDR_IO_REGION_MASK) << MMU_ADDR_IO_REGION_LSB))
+#define GET_MMU_ADDR_KERNEL_MMU_REGION(Addr) \
+ (((Addr) >> MMU_ADDR_KERNEL_MMU_REGION_LSB) & \
+ MMU_ADDR_KERNEL_MMU_REGION_MASK)
+#define SET_MMU_ADDR_KERNEL_MMU_REGION(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_KERNEL_MMU_REGION_MASK << \
+ MMU_ADDR_KERNEL_MMU_REGION_LSB))) | \
+ (((Val) & MMU_ADDR_KERNEL_MMU_REGION_MASK) << \
+ MMU_ADDR_KERNEL_MMU_REGION_LSB))
+#define GET_MMU_ADDR_KERNEL_REGION(Addr) \
+ (((Addr) >> MMU_ADDR_KERNEL_REGION_LSB) & MMU_ADDR_KERNEL_REGION_MASK)
+#define SET_MMU_ADDR_KERNEL_REGION(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_KERNEL_REGION_MASK << \
+ MMU_ADDR_KERNEL_REGION_LSB))) | \
+ (((Val) & MMU_ADDR_KERNEL_REGION_MASK) << MMU_ADDR_KERNEL_REGION_LSB))
+#define GET_MMU_ADDR_PAGE_OFFSET(Addr) \
+ (((Addr) >> MMU_ADDR_PAGE_OFFSET_LSB) & MMU_ADDR_PAGE_OFFSET_MASK)
+#define SET_MMU_ADDR_PAGE_OFFSET(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_PAGE_OFFSET_MASK << \
+ MMU_ADDR_PAGE_OFFSET_LSB))) | \
+ (((Val) & MMU_ADDR_PAGE_OFFSET_MASK) << MMU_ADDR_PAGE_OFFSET_LSB))
+#define GET_MMU_ADDR_PFN(Addr) \
+ (((Addr) >> MMU_ADDR_PFN_LSB) & MMU_ADDR_PFN_MASK)
+#define SET_MMU_ADDR_PFN(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_PFN_MASK << MMU_ADDR_PFN_LSB))) | \
+ (((Val) & MMU_ADDR_PFN_MASK) << MMU_ADDR_PFN_LSB))
+#define GET_MMU_ADDR_USER_REGION(Addr) \
+ (((Addr) >> MMU_ADDR_USER_REGION_LSB) & MMU_ADDR_USER_REGION_MASK)
+#define SET_MMU_ADDR_USER_REGION(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_USER_REGION_MASK << \
+ MMU_ADDR_USER_REGION_LSB))) | \
+ (((Val) & MMU_ADDR_USER_REGION_MASK) << MMU_ADDR_USER_REGION_LSB))
+#define GET_MMU_ADDR_VPN(Addr) \
+ (((Addr) >> MMU_ADDR_VPN_LSB) & MMU_ADDR_VPN_MASK)
+#define SET_MMU_ADDR_VPN(Addr, Val) \
+ Addr = (((Addr) & (~(MMU_ADDR_VPN_MASK << MMU_ADDR_VPN_LSB))) | \
+ (((Val) & MMU_ADDR_VPN_MASK) << MMU_ADDR_VPN_LSB))
+
+/* OP instruction values */
+#define OP_ADDI 4
+#define OP_ANDHI 44
+#define OP_ANDI 12
+#define OP_BEQ 38
+#define OP_BGE 14
+#define OP_BGEU 46
+#define OP_BLT 22
+#define OP_BLTU 54
+#define OP_BNE 30
+#define OP_BR 6
+#define OP_CALL 0
+#define OP_CMPEQI 32
+#define OP_CMPGEI 8
+#define OP_CMPGEUI 40
+#define OP_CMPLTI 16
+#define OP_CMPLTUI 48
+#define OP_CMPNEI 24
+#define OP_CUSTOM 50
+#define OP_FLUSHD 59
+#define OP_FLUSHDA 27
+#define OP_INITD 51
+#define OP_INITDA 19
+#define OP_JMPI 1
+#define OP_LDB 7
+#define OP_LDBIO 39
+#define OP_LDBU 3
+#define OP_LDBUIO 35
+#define OP_LDH 15
+#define OP_LDHIO 47
+#define OP_LDHU 11
+#define OP_LDHUIO 43
+#define OP_LDL 31
+#define OP_LDW 23
+#define OP_LDWIO 55
+#define OP_MULI 36
+#define OP_OPX 58
+#define OP_ORHI 52
+#define OP_ORI 20
+#define OP_STB 5
+#define OP_STBIO 37
+#define OP_STC 29
+#define OP_STH 13
+#define OP_STHIO 45
+#define OP_STW 21
+#define OP_STWIO 53
+#define OP_XORHI 60
+#define OP_XORI 28
+
+/* OPX instruction values */
+#define OPX_ADD 49
+#define OPX_AND 14
+#define OPX_BREAK 52
+#define OPX_BRET 9
+#define OPX_CALLR 29
+#define OPX_CMPEQ 32
+#define OPX_CMPGE 8
+#define OPX_CMPGEU 40
+#define OPX_CMPLT 16
+#define OPX_CMPLTU 48
+#define OPX_CMPNE 24
+#define OPX_CRST 62
+#define OPX_DIV 37
+#define OPX_DIVU 36
+#define OPX_ERET 1
+#define OPX_FLUSHI 12
+#define OPX_FLUSHP 4
+#define OPX_HBREAK 53
+#define OPX_INITI 41
+#define OPX_INTR 61
+#define OPX_JMP 13
+#define OPX_MUL 39
+#define OPX_MULXSS 31
+#define OPX_MULXSU 23
+#define OPX_MULXUU 7
+#define OPX_NEXTPC 28
+#define OPX_NOR 6
+#define OPX_OR 22
+#define OPX_RDCTL 38
+#define OPX_RET 5
+#define OPX_ROL 3
+#define OPX_ROLI 2
+#define OPX_ROR 11
+#define OPX_SLL 19
+#define OPX_SLLI 18
+#define OPX_SRA 59
+#define OPX_SRAI 58
+#define OPX_SRL 27
+#define OPX_SRLI 26
+#define OPX_SUB 57
+#define OPX_SYNC 54
+#define OPX_TRAP 45
+#define OPX_WRCTL 46
+#define OPX_XOR 30
+
+/* Macros to detect sub-opcode instructions */
+#define IS_OPX_INST(Iw) (GET_IW_OP(Iw) == OP_OPX)
+#define IS_CUSTOM_INST(Iw) (GET_IW_OP(Iw) == OP_CUSTOM)
+
+/* Instruction property macros */
+#define IW_PROP_RESERVED_OP(Iw) (0)
+
+#define IW_PROP_RESERVED_OPX(Iw) (0)
+
+#define IW_PROP_RESERVED(Iw) (0)
+
+#define IW_PROP_SUPERVISOR_ONLY(Iw) ( \
+ (op_prop_supervisor_only[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_supervisor_only[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_supervisor_only[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_supervisor_only[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_INITI_FLUSHI(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_INITI) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_FLUSHI) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_FLUSH_PIPE(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_flush_pipe[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_flush_pipe[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_JMP_INDIRECT_NON_TRAP(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_jmp_indirect_non_trap[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_jmp_indirect_non_trap[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_JMP_INDIRECT(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_jmp_indirect[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_jmp_indirect[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_JMP_DIRECT(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_CALL)) || \
+ ((GET_IW_OP((Iw)) == OP_JMPI)) \
+ ) \
+ \
+)
+
+#define IW_PROP_MUL_LSW(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_MULI)) || \
+ ((GET_IW_OPX((Iw)) == OPX_MUL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_MULX(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_mulx[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_mulx[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_MUL(Iw) ( \
+ (op_prop_mul[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_mul[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_mul[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_mul[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_DIV_UNSIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_DIVU) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_DIV_SIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_DIV) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_DIV(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_DIVU) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_DIV) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_UNIMPLEMENTED(Iw) (0)
+
+#define IW_PROP_ILLEGAL(Iw) (0)
+
+#define IW_PROP_IMPLICIT_DST_RETADDR(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_CALL)) \
+ ) \
+ \
+)
+
+#define IW_PROP_IMPLICIT_DST_ERETADDR(Iw) (0)
+
+#define IW_PROP_INTR(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_INTR) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_EXCEPTION(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_TRAP) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BREAK(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_BREAK) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_CRST(Iw) (0)
+
+#define IW_PROP_WR_CTL_REG(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_wr_ctl_reg[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_wr_ctl_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_UNCOND_CTI_NON_BR(Iw) ( \
+ (op_prop_uncond_cti_non_br[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_uncond_cti_non_br[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_uncond_cti_non_br[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_uncond_cti_non_br[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_RETADDR(Iw) ( \
+ (op_prop_retaddr[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_retaddr[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_retaddr[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_retaddr[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SHIFT_LEFT(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_SLLI) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_SLL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_SHIFT_LOGICAL(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_logical[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_logical[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ROT_LEFT(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_ROLI) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_ROL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_SHIFT_ROT_LEFT(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_rot_left[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_rot_left[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SHIFT_RIGHT_LOGICAL(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_SRLI) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_SRL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_SHIFT_RIGHT_ARITH(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_SRAI) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_SRA) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_SHIFT_RIGHT(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_right[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_right[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ROT_RIGHT(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_ROR) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_SHIFT_ROT_RIGHT(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_rot_right[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_rot_right[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SHIFT_ROT(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_rot[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_rot[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SHIFT_ROT_IMM(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_shift_rot_imm[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_shift_rot_imm[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ROTATE(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_rotate[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_rotate[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOGIC_REG(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_logic_reg[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_logic_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOGIC_HI_IMM16(Iw) ( \
+ (op_prop_logic_hi_imm16[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_logic_hi_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOGIC_LO_IMM16(Iw) ( \
+ (op_prop_logic_lo_imm16[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_logic_lo_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOGIC_IMM16(Iw) ( \
+ (op_prop_logic_imm16[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_logic_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOGIC(Iw) ( \
+ (op_prop_logic[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_logic[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_logic[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_logic[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_UNSIGNED_LO_IMM16(Iw) ( \
+ (op_prop_unsigned_lo_imm16[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_unsigned_lo_imm16[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_unsigned_lo_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_unsigned_lo_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ARITH_IMM16(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_ADDI)) || \
+ ((GET_IW_OP((Iw)) == OP_MULI)) \
+ ) \
+ \
+)
+
+#define IW_PROP_CMP_IMM16(Iw) ( \
+ (op_prop_cmp_imm16[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_imm16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_JMPI(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_JMPI)) \
+ ) \
+ \
+)
+
+#define IW_PROP_CMP_IMM16_WITH_CALL_JMPI(Iw) ( \
+ (op_prop_cmp_imm16_with_call_jmpi[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_imm16_with_call_jmpi[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP_REG(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_cmp_reg[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SRC_IMM5(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_src_imm5[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_src_imm5[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP_WITH_LT(Iw) ( \
+ (op_prop_cmp_with_lt[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_cmp_with_lt[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_with_lt[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp_with_lt[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP_WITH_EQ(Iw) ( \
+ (op_prop_cmp_with_eq[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_cmp_with_eq[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_with_eq[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp_with_eq[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP_WITH_GE(Iw) ( \
+ (op_prop_cmp_with_ge[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_cmp_with_ge[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_with_ge[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp_with_ge[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP_WITH_NE(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_CMPNEI)) || \
+ ((GET_IW_OPX((Iw)) == OPX_CMPNE) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_CMP_ALU_SIGNED(Iw) ( \
+ (op_prop_cmp_alu_signed[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_cmp_alu_signed[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp_alu_signed[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp_alu_signed[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_CMP(Iw) ( \
+ (op_prop_cmp[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_cmp[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_cmp[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_cmp[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_BR_WITH_LT(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_BLT)) || \
+ ((GET_IW_OP((Iw)) == OP_BLTU)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BR_WITH_GE(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_BGE)) || \
+ ((GET_IW_OP((Iw)) == OP_BGEU)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BR_WITH_EQ(Iw) ( \
+ (op_prop_br_with_eq[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_br_with_eq[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_BR_WITH_NE(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_BNE)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BR_ALU_SIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_BGE)) || \
+ ((GET_IW_OP((Iw)) == OP_BLT)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BR_COND(Iw) ( \
+ (op_prop_br_cond[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_br_cond[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_BR_UNCOND(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_BR)) \
+ ) \
+ \
+)
+
+#define IW_PROP_BR(Iw) ( \
+ (op_prop_br[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_br[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ALU_SUB(Iw) ( \
+ (op_prop_alu_sub[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_alu_sub[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_alu_sub[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_alu_sub[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_FORCE_XOR(Iw) ( \
+ (op_prop_force_xor[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_force_xor[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_force_xor[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_force_xor[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD8(Iw) ( \
+ (op_prop_load8[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load8[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD16(Iw) ( \
+ (op_prop_load16[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load16[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD32(Iw) ( \
+ (op_prop_load32[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load32[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD_SIGNED(Iw) ( \
+ (op_prop_load_signed[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load_signed[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD_UNSIGNED(Iw) ( \
+ (op_prop_load_unsigned[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load_unsigned[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD(Iw) ( \
+ (op_prop_load[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_DCACHE_MANAGEMENT_NOP(Iw) ( \
+ (op_prop_dcache_management_nop[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_dcache_management_nop[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD_DCACHE_MANAGEMENT(Iw) ( \
+ (op_prop_load_dcache_management[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load_dcache_management[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD_NON_IO(Iw) ( \
+ (op_prop_load_non_io[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load_non_io[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_STORE8(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_STB)) || \
+ ((GET_IW_OP((Iw)) == OP_STBIO)) \
+ ) \
+ \
+)
+
+#define IW_PROP_STORE16(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_STH)) || \
+ ((GET_IW_OP((Iw)) == OP_STHIO)) \
+ ) \
+ \
+)
+
+#define IW_PROP_STORE32(Iw) ( \
+ (op_prop_store32[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_store32[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_STORE(Iw) ( \
+ (op_prop_store[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_store[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_STORE_NON_IO(Iw) ( \
+ (op_prop_store_non_io[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_store_non_io[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_MEM(Iw) ( \
+ (op_prop_mem[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_mem[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_INITD(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_INITD)) \
+ ) \
+ \
+)
+
+#define IW_PROP_INITDA(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_INITDA)) \
+ ) \
+ \
+)
+
+#define IW_PROP_FLUSHD(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_FLUSHD)) \
+ ) \
+ \
+)
+
+#define IW_PROP_FLUSHDA(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
+ ) \
+ \
+)
+
+#define IW_PROP_INITD_FLUSHD(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_INITD)) || \
+ ((GET_IW_OP((Iw)) == OP_FLUSHD)) \
+ ) \
+ \
+)
+
+#define IW_PROP_INITDA_FLUSHDA(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_INITDA)) || \
+ ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
+ ) \
+ \
+)
+
+#define IW_PROP_INITD_INITDA(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_INITD)) || \
+ ((GET_IW_OP((Iw)) == OP_INITDA)) \
+ ) \
+ \
+)
+
+#define IW_PROP_FLUSHD_FLUSHDA(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_FLUSHD)) || \
+ ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
+ ) \
+ \
+)
+
+#define IW_PROP_INITD_FLUSHD_FLUSHDA(Iw) ( \
+ (op_prop_initd_flushd_flushda[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_initd_flushd_flushda[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_DCACHE_MANAGEMENT(Iw) ( \
+ (op_prop_dcache_management[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_dcache_management[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_LOAD_IO(Iw) ( \
+ (op_prop_load_io[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_load_io[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_STORE_IO(Iw) ( \
+ (op_prop_store_io[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_store_io[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_MEM_IO(Iw) ( \
+ (op_prop_mem_io[GET_IW_OP(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_mem_io[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_ARITH(Iw) ( \
+ (op_prop_arith[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_arith[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_arith[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_arith[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_A_NOT_SRC(Iw) ( \
+ ( \
+ ((GET_IW_OP((Iw)) == OP_CALL)) || \
+ ((GET_IW_OP((Iw)) == OP_JMPI)) \
+ ) \
+ || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_READRA(Iw)) \
+)
+
+#define IW_PROP_B_NOT_SRC(Iw) ( \
+ (op_prop_b_not_src[GET_IW_OP(Iw)]) \
+ || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_READRB(Iw)))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_b_not_src[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_IGNORE_DST(Iw) ( \
+ (op_prop_ignore_dst[GET_IW_OP(Iw)]) \
+ || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_WRITERC(Iw)))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_ignore_dst[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SRC2_CHOOSE_IMM(Iw) ( \
+ (op_prop_src2_choose_imm[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_src2_choose_imm[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_src2_choose_imm[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_src2_choose_imm[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_WRCTL_INST(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_WRCTL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_RDCTL_INST(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_RDCTL) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_MUL_SRC1_SIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) || \
+ ((GET_IW_OPX((Iw)) == OPX_MULXSU) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_MUL_SRC2_SIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_MUL_SHIFT_SRC1_SIGNED(Iw) ( \
+ (IS_OPX_INST(Iw) && opx_prop_mul_shift_src1_signed[GET_IW_OPX(Iw)]))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_mul_shift_src1_signed[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_MUL_SHIFT_SRC2_SIGNED(Iw) ( \
+ ( \
+ ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) \
+ ) \
+ \
+)
+
+#define IW_PROP_DONT_DISPLAY_DST_REG(Iw) ( \
+ (op_prop_dont_display_dst_reg[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_dont_display_dst_reg[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_dont_display_dst_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_dont_display_dst_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_DONT_DISPLAY_SRC1_REG(Iw) ( \
+ (op_prop_dont_display_src1_reg[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_dont_display_src1_reg[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_dont_display_src1_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_dont_display_src1_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_DONT_DISPLAY_SRC2_REG(Iw) ( \
+ (op_prop_dont_display_src2_reg[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_dont_display_src2_reg[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_dont_display_src2_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_dont_display_src2_reg[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SRC1_NO_X(Iw) ( \
+ (op_prop_src1_no_x[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_src1_no_x[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_src1_no_x[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_src1_no_x[64];
+#endif /* ALT_ASM_SRC */
+
+#define IW_PROP_SRC2_NO_X(Iw) ( \
+ (op_prop_src2_no_x[GET_IW_OP(Iw)] || \
+ (IS_OPX_INST(Iw) && opx_prop_src2_no_x[GET_IW_OPX(Iw)])))
+
+#ifndef ALT_ASM_SRC
+extern unsigned char op_prop_src2_no_x[64];
+#endif /* ALT_ASM_SRC */
+
+#ifndef ALT_ASM_SRC
+extern unsigned char opx_prop_src2_no_x[64];
+#endif /* ALT_ASM_SRC */
+
+/* Instruction types */
+#define INST_TYPE_OP 0
+#define INST_TYPE_OPX 1
+
+/* Canonical instruction codes independent of encoding */
+#define CALL_INST_CODE 0
+#define JMPI_INST_CODE 1
+#define LDBU_INST_CODE 2
+#define ADDI_INST_CODE 3
+#define STB_INST_CODE 4
+#define BR_INST_CODE 5
+#define LDB_INST_CODE 6
+#define CMPGEI_INST_CODE 7
+#define LDHU_INST_CODE 8
+#define ANDI_INST_CODE 9
+#define STH_INST_CODE 10
+#define BGE_INST_CODE 11
+#define LDH_INST_CODE 12
+#define CMPLTI_INST_CODE 13
+#define INITDA_INST_CODE 14
+#define ORI_INST_CODE 15
+#define STW_INST_CODE 16
+#define BLT_INST_CODE 17
+#define LDW_INST_CODE 18
+#define CMPNEI_INST_CODE 19
+#define FLUSHDA_INST_CODE 20
+#define XORI_INST_CODE 21
+#define STC_INST_CODE 22
+#define BNE_INST_CODE 23
+#define LDL_INST_CODE 24
+#define CMPEQI_INST_CODE 25
+#define LDBUIO_INST_CODE 26
+#define MULI_INST_CODE 27
+#define STBIO_INST_CODE 28
+#define BEQ_INST_CODE 29
+#define LDBIO_INST_CODE 30
+#define CMPGEUI_INST_CODE 31
+#define LDHUIO_INST_CODE 32
+#define ANDHI_INST_CODE 33
+#define STHIO_INST_CODE 34
+#define BGEU_INST_CODE 35
+#define LDHIO_INST_CODE 36
+#define CMPLTUI_INST_CODE 37
+#define CUSTOM_INST_CODE 38
+#define INITD_INST_CODE 39
+#define ORHI_INST_CODE 40
+#define STWIO_INST_CODE 41
+#define BLTU_INST_CODE 42
+#define LDWIO_INST_CODE 43
+#define FLUSHD_INST_CODE 44
+#define XORHI_INST_CODE 45
+#define ERET_INST_CODE 46
+#define ROLI_INST_CODE 47
+#define ROL_INST_CODE 48
+#define FLUSHP_INST_CODE 49
+#define RET_INST_CODE 50
+#define NOR_INST_CODE 51
+#define MULXUU_INST_CODE 52
+#define CMPGE_INST_CODE 53
+#define BRET_INST_CODE 54
+#define ROR_INST_CODE 55
+#define FLUSHI_INST_CODE 56
+#define JMP_INST_CODE 57
+#define AND_INST_CODE 58
+#define CMPLT_INST_CODE 59
+#define SLLI_INST_CODE 60
+#define SLL_INST_CODE 61
+#define OR_INST_CODE 62
+#define MULXSU_INST_CODE 63
+#define CMPNE_INST_CODE 64
+#define SRLI_INST_CODE 65
+#define SRL_INST_CODE 66
+#define NEXTPC_INST_CODE 67
+#define CALLR_INST_CODE 68
+#define XOR_INST_CODE 69
+#define MULXSS_INST_CODE 70
+#define CMPEQ_INST_CODE 71
+#define DIVU_INST_CODE 72
+#define DIV_INST_CODE 73
+#define RDCTL_INST_CODE 74
+#define MUL_INST_CODE 75
+#define CMPGEU_INST_CODE 76
+#define INITI_INST_CODE 77
+#define TRAP_INST_CODE 78
+#define WRCTL_INST_CODE 79
+#define CMPLTU_INST_CODE 80
+#define ADD_INST_CODE 81
+#define BREAK_INST_CODE 82
+#define HBREAK_INST_CODE 83
+#define SYNC_INST_CODE 84
+#define SUB_INST_CODE 85
+#define SRAI_INST_CODE 86
+#define SRA_INST_CODE 87
+#define INTR_INST_CODE 88
+#define CRST_INST_CODE 89
+#define RSV_INST_CODE 90
+#define NUM_NIOS2_INST_CODES 91
+
+#ifndef ALT_ASM_SRC
+/* Instruction information entry */
+typedef struct {
+ const char *name; /* Assembly-language instruction name */
+ int instType; /* INST_TYPE_OP or INST_TYPE_OPX */
+ unsigned opcode; /* Value of instruction word OP/OPX field */
+} Nios2InstInfo;
+
+extern Nios2InstInfo nios2InstInfo[NUM_NIOS2_INST_CODES];
+#endif /* ALT_ASM_SRC */
+
+/* Returns the instruction code given the 32-bit instruction word */
+#define GET_INST_CODE(Iw) \
+ (IS_OPX_INST(Iw) ? opxToInstCode[GET_IW_OPX(Iw)] : \
+ opToInstCode[GET_IW_OP(Iw)])
+
+#ifndef ALT_ASM_SRC
+extern int opToInstCode[64];
+extern int opxToInstCode[64];
+#endif /* ALT_ASM_SRC */
+
+/*
+ * MMU Memory Region Macros
+ */
+#define USER_REGION_MIN_VADDR 0x00000000
+#define USER_REGION_MAX_VADDR 0x7fffffff
+#define KERNEL_MMU_REGION_MIN_VADDR 0x80000000
+#define KERNEL_MMU_REGION_MAX_VADDR 0xbfffffff
+#define KERNEL_REGION_MIN_VADDR 0xc0000000
+#define KERNEL_REGION_MAX_VADDR 0xdfffffff
+#define IO_REGION_MIN_VADDR 0xe0000000
+#define IO_REGION_MAX_VADDR 0xffffffff
+
+#define MMU_PAGE_SIZE (0x1 << (MMU_ADDR_PAGE_OFFSET_SZ))
+
+#define isMmuUserRegion(Vaddr) \
+ (GET_MMU_ADDR_USER_REGION(Vaddr) == MMU_ADDR_USER_REGION)
+#define isMmuKernelMmuRegion(Vaddr) \
+ (GET_MMU_ADDR_KERNEL_MMU_REGION(Vaddr) == MMU_ADDR_KERNEL_MMU_REGION)
+#define isMmuKernelRegion(Vaddr) \
+ (GET_MMU_ADDR_KERNEL_REGION(Vaddr) == MMU_ADDR_KERNEL_REGION)
+#define isMmuIORegion(Vaddr) \
+ (GET_MMU_ADDR_IO_REGION(Vaddr) == MMU_ADDR_IO_REGION)
+
+/* Does this virtual address bypass the TLB? */
+#define vaddrBypassTlb(Vaddr) \
+ (GET_MMU_ADDR_BYPASS_TLB(Vaddr) == MMU_ADDR_BYPASS_TLB)
+
+/* If TLB is bypassed, is the address cacheable or uncachable. */
+#define vaddrBypassTlbCacheable(Vaddr) \
+ (GET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Vaddr) == MMU_ADDR_BYPASS_TLB_CACHEABLE)
+
+/*
+ * Compute physical address for regions that bypass the TLB.
+ * Just need to clear some top bits.
+ */
+#define bypassTlbVaddrToPaddr(Vaddr) \
+ ((Vaddr) & (MMU_ADDR_BYPASS_TLB_PADDR_MASK << \
+ MMU_ADDR_BYPASS_TLB_PADDR_LSB))
+
+/*
+ * Will the physical address fit in the Kernel/IO region virtual address space?
+ */
+#define fitsInKernelRegion(Paddr) \
+ (GET_MMU_ADDR_KERNEL_REGION(Paddr) == 0)
+#define fitsInIORegion(Paddr) \
+ (GET_MMU_ADDR_IO_REGION(Paddr) == 0)
+
+/* Convert a physical address to a Kernel/IO region virtual address. */
+#define paddrToKernelRegionVaddr(Paddr) \
+ ((Paddr) | (MMU_ADDR_KERNEL_REGION << MMU_ADDR_KERNEL_REGION_LSB))
+#define paddrToIORegionVaddr(Paddr) \
+ ((Paddr) | (MMU_ADDR_IO_REGION << MMU_ADDR_IO_REGION_LSB))
+
+/*
+ * Convert a virtual address to a Kernel/IO region virtual address.
+ * Uses bypassTlbVaddrToPaddr to clear top bits.
+ */
+#define vaddrToKernelRegionVaddr(Vaddr) \
+ paddrToKernelRegionVaddr(bypassTlbVaddrToPaddr(Vaddr))
+#define vaddrToIORegionVaddr(Vaddr) \
+ paddrToIORegionVaddr(bypassTlbVaddrToPaddr(Vaddr))
+
+/* Convert between VPN/PFN and virtual/physical addresses. */
+#define vpnToVaddr(Vpn) ((Vpn) << MMU_ADDR_VPN_LSB)
+#define pfnToPaddr(Pfn) ((Pfn) << MMU_ADDR_PFN_LSB)
+#define vaddrToVpn(Vaddr) GET_MMU_ADDR_VPN(Vaddr)
+#define paddrToPfn(Paddr) GET_MMU_ADDR_PFN(Paddr)
+
+/* Bitwise OR with a KERNEL region address to make it an IO region address */
+#define KERNEL_TO_IO_REGION 0x20000000
+
+/* Exception information */
+#ifndef ALT_ASM_SRC
+typedef struct {
+ const char *name;
+ int priority;
+ int subPriority; /* -1 if none */
+ int causeId; /* -1 if none */
+ int recordAddr;
+} ExcInfo;
+
+extern ExcInfo excInfo[NUM_EXC_IDS];
+#endif /* ALT_ASM_SRC */
+
+#endif /* _NIOS2_ISA_H_ */
diff --git a/nios2-opc.c b/nios2-opc.c
new file mode 100644
index 0000000..64e1673
--- /dev/null
+++ b/nios2-opc.c
@@ -0,0 +1,540 @@
+/* nios2-opc.c -- Altera New Jersey opcode list.
+
+ Copyright (C) 2003
+ by Nigel Gray (ngray@altera.com).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include "nios2.h"
+
+/* Register string table */
+
+const struct nios2_reg nios2_builtin_regs[] = {
+ {"zero", 0},
+ {"at", 1}, /* assembler temporary */
+ {"r2", 2},
+ {"r3", 3},
+ {"r4", 4},
+ {"r5", 5},
+ {"r6", 6},
+ {"r7", 7},
+ {"r8", 8},
+ {"r9", 9},
+ {"r10", 10},
+ {"r11", 11},
+ {"r12", 12},
+ {"r13", 13},
+ {"r14", 14},
+ {"r15", 15},
+ {"r16", 16},
+ {"r17", 17},
+ {"r18", 18},
+ {"r19", 19},
+ {"r20", 20},
+ {"r21", 21},
+ {"r22", 22},
+ {"r23", 23},
+ {"et", 24},
+ {"bt", 25},
+ {"gp", 26}, /* global pointer */
+ {"sp", 27}, /* stack pointer */
+ {"fp", 28}, /* frame pointer */
+ {"ea", 29}, /* exception return address */
+ {"ba", 30}, /* breakpoint return address */
+ {"ra", 31}, /* return address */
+
+ /* alternative names for special registers */
+ {"r0", 0},
+ {"r1", 1},
+ {"r24", 24},
+ {"r25", 25},
+ {"r26", 26},
+ {"r27", 27},
+ {"r28", 28},
+ {"r29", 29},
+ {"r30", 30},
+ {"r31", 31},
+
+ /* control register names */
+ {"status", 0},
+ {"estatus", 1},
+ {"bstatus", 2},
+ {"ienable", 3},
+ {"ipending", 4},
+ {"cpuid", 5},
+ {"ctl6", 6},
+ {"exception", 7},
+ {"pteaddr", 8},
+ {"tlbacc", 9},
+ {"tlbmisc", 10},
+ {"fstatus", 11},
+ {"badaddr", 12},
+ {"config", 13},
+ {"mpubase", 14},
+ {"mpuacc", 15},
+ {"ctl16", 16},
+ {"ctl17", 17},
+ {"ctl18", 18},
+ {"ctl19", 19},
+ {"ctl20", 20},
+ {"ctl21", 21},
+ {"ctl22", 22},
+ {"ctl23", 23},
+ {"ctl24", 24},
+ {"ctl25", 25},
+ {"ctl26", 26},
+ {"ctl27", 27},
+ {"ctl28", 28},
+ {"ctl29", 29},
+ {"ctl30", 30},
+ {"ctl31", 31},
+
+ /* alternative names for special control registers */
+ {"ctl0", 0},
+ {"ctl1", 1},
+ {"ctl2", 2},
+ {"ctl3", 3},
+ {"ctl4", 4},
+ {"ctl5", 5},
+ {"ctl7", 7},
+ {"ctl8", 8},
+ {"ctl9", 9},
+ {"ctl10", 10},
+ {"ctl11", 11},
+ {"ctl12", 12},
+ {"ctl13", 13},
+ {"ctl14", 14},
+ {"ctl15", 15},
+
+
+ /* coprocessor register names */
+ {"c0", 0},
+ {"c1", 1},
+ {"c2", 2},
+ {"c3", 3},
+ {"c4", 4},
+ {"c5", 5},
+ {"c6", 6},
+ {"c7", 7},
+ {"c8", 8},
+ {"c9", 9},
+ {"c10", 10},
+ {"c11", 11},
+ {"c12", 12},
+ {"c13", 13},
+ {"c14", 14},
+ {"c15", 15},
+ {"c16", 16},
+ {"c17", 17},
+ {"c18", 18},
+ {"c19", 19},
+ {"c20", 20},
+ {"c21", 21},
+ {"c22", 22},
+ {"c23", 23},
+ {"c24", 24},
+ {"c25", 25},
+ {"c26", 26},
+ {"c27", 27},
+ {"c28", 28},
+ {"c29", 29},
+ {"c30", 30},
+ {"c31", 31},
+};
+
+#define NIOS2_NUM_REGS \
+ ((sizeof(nios2_builtin_regs)) / (sizeof(nios2_builtin_regs[0])))
+const int nios2_num_builtin_regs = NIOS2_NUM_REGS;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct nios2_reg *nios2_regs = (struct nios2_reg *) nios2_builtin_regs;
+int nios2_num_regs = NIOS2_NUM_REGS;
+#undef NIOS2_NUM_REGS
+
+/* overflow message string templates */
+
+const char *overflow_msgs[] = {
+ "call target address 0x%08x out of range 0x%08x to 0x%08x",
+ "branch offset %d out of range %d to %d",
+ "%s offset %d out of range %d to %d",
+ "immediate value %d out of range %d to %d",
+ "immediate value %u out of range %u to %u",
+ "immediate value %u out of range %u to %u",
+ "custom instruction opcode %u out of range %u to %u",
+};
+
+
+
+/*------------------------------------------------------------------------------
+ This is the opcode table used by the New Jersey GNU as, disassembler and GDB
+ ----------------------------------------------------------------------------*/
+
+/*
+ The following letters can appear in the args field of the nios2_opcode
+ structure:
+
+ c - a 5-bit control register index or break opcode
+ d - a 5-bit destination register index
+ s - a 5-bit left source register index
+ t - a 5-bit right source register index
+ i - a 16-bit signed immediate
+ u - a 16-bit unsigned immediate
+
+ j - a 5-bit unsigned immediate
+ k - a 6-bit unsigned immediate
+ l - an 8-bit unsigned immediate
+ m - a 26-bit unsigned immediate
+*/
+
+/* *INDENT-OFF* */
+/* FIXME: Re-format for GNU standards */
+const struct nios2_opcode nios2_builtin_opcodes[] = {
+ /* name, args, args_test, num_args, match, mask, pinfo */
+ {"add", "d,s,t", "d,s,t,E", 3, OP_MATCH_ADD,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"addi", "t,s,i", "t,s,i,E", 3, OP_MATCH_ADDI,
+ OP_MASK_IOP, NIOS2_INSN_ADDI,
+ signed_immed16_overflow },
+ {"subi", "t,s,i", "t,s,i,E", 3, OP_MATCH_ADDI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO,
+ signed_immed16_overflow },
+ {"and", "d,s,t", "d,s,t,E", 3, OP_MATCH_AND,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"andhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ANDHI,
+ OP_MASK_IOP, 0,
+ unsigned_immed16_overflow },
+ {"andi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ANDI,
+ OP_MASK_IOP, NIOS2_INSN_ANDI,
+ unsigned_immed16_overflow },
+ {"beq", "s,t,o", "s,t,o,E", 3, OP_MATCH_BEQ,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bge", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGE,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bgeu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGEU,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bgt", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLT,
+ OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bgtu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLTU,
+ OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"ble", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGE,
+ OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bleu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGEU,
+ OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"blt", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLT,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bltu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLTU,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"bne", "s,t,o", "s,t,o,E", 3, OP_MATCH_BNE,
+ OP_MASK_IOP, NIOS2_INSN_CBRANCH,
+ branch_target_overflow },
+ {"br", "o", "o,E", 1, OP_MATCH_BR,
+ OP_MASK_IOP, NIOS2_INSN_UBRANCH,
+ branch_target_overflow },
+ {"break", "b", "b,E", 1, OP_MATCH_BREAK,
+ OP_MASK_BREAK, 0,
+ no_overflow },
+ {"bret", "", "E", 0, OP_MATCH_BRET,
+ OP_MASK, 0,
+ no_overflow },
+ {"flushd", "i(s)", "i(s)E", 2, OP_MATCH_FLUSHD,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"flushda", "i(s)", "i(s)E", 2, OP_MATCH_FLUSHDA,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"flushi", "s", "s,E", 1, OP_MATCH_FLUSHI,
+ OP_MASK_FLUSHI, 0,
+ no_overflow },
+ {"flushp", "", "E", 0, OP_MATCH_FLUSHP,
+ OP_MASK, 0,
+ no_overflow },
+ {"initd", "i(s)", "i(s)E", 2, OP_MATCH_INITD,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"initda", "i(s)", "i(s)E", 2, OP_MATCH_INITDA,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"initi", "s", "s,E", 1, OP_MATCH_INITI,
+ OP_MASK_INITI, 0,
+ no_overflow },
+ {"call", "m", "m,E", 1, OP_MATCH_CALL,
+ OP_MASK_IOP, NIOS2_INSN_CALL,
+ call_target_overflow },
+ {"callr", "s", "s,E", 1, OP_MATCH_CALLR,
+ OP_MASK_CALLR, 0,
+ no_overflow },
+ {"cmpeq", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPEQ,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmpeqi", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPEQI,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"cmpge", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGE,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmpgei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPGEI,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"cmpgeu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGEU,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmpgeui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPGEUI,
+ OP_MASK_IOP, 0,
+ unsigned_immed16_overflow },
+ {"cmpgt", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLT,
+ OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
+ no_overflow },
+ {"cmpgti", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPGEI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO,
+ signed_immed16_overflow },
+ {"cmpgtu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLTU,
+ OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
+ no_overflow },
+ {"cmpgtui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPGEUI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO,
+ unsigned_immed16_overflow },
+ {"cmple", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGE,
+ OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
+ no_overflow },
+ {"cmplei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPLTI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO,
+ signed_immed16_overflow },
+ {"cmpleu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGEU,
+ OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
+ no_overflow },
+ {"cmpleui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPLTUI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO,
+ unsigned_immed16_overflow },
+ {"cmplt", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLT,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmplti", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPLTI,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"cmpltu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLTU,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmpltui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPLTUI,
+ OP_MASK_IOP, 0,
+ unsigned_immed16_overflow },
+ {"cmpne", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPNE,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"cmpnei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPNEI,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"div", "d,s,t", "d,s,t,E", 3, OP_MATCH_DIV,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"divu", "d,s,t", "d,s,t,E", 3, OP_MATCH_DIVU,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"jmp", "s", "s,E", 1, OP_MATCH_JMP,
+ OP_MASK_JMP, 0,
+ no_overflow },
+ {"jmpi", "m", "m,E", 1, OP_MATCH_JMPI,
+ OP_MASK_IOP, 0,
+ no_overflow },
+ {"ldb", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDB,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldbio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldbu", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBU,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldbuio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBUIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldh", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDH,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldhio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldhu", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHU,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldhuio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHUIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldl", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDL,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldw", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDW,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"ldwio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDWIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"mov", "d,s", "d,s,E", 2, OP_MATCH_ADD,
+ OP_MASK_RRT|OP_MASK_ROPX|OP_MASK_ROP, NIOS2_INSN_MACRO_MOV,
+ no_overflow },
+ {"movhi", "t,u", "t,u,E", 2, OP_MATCH_ORHI,
+ OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
+ unsigned_immed16_overflow },
+ {"movui", "t,u", "t,u,E", 2, OP_MATCH_ORI,
+ OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
+ unsigned_immed16_overflow },
+ {"movi", "t,i", "t,i,E", 2, OP_MATCH_ADDI,
+ OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
+ signed_immed16_overflow },
+ /* movia expands to two instructions so there is no mask or match */
+ {"movia", "t,o", "t,o,E", 2, OP_MATCH_ORHI,
+ OP_MASK_IOP, NIOS2_INSN_MACRO_MOVIA,
+ no_overflow },
+ {"mul", "d,s,t", "d,s,t,E", 3, OP_MATCH_MUL,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"muli", "t,s,i", "t,s,i,E", 3, OP_MATCH_MULI,
+ OP_MASK_IOP, 0,
+ signed_immed16_overflow },
+ {"mulxss", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXSS,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"mulxsu", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXSU,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"mulxuu", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXUU,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"nextpc", "d", "d,E", 1, OP_MATCH_NEXTPC,
+ OP_MASK_NEXTPC, 0,
+ no_overflow },
+ {"nop", "", "E", 0, OP_MATCH_ADD,
+ OP_MASK, NIOS2_INSN_MACRO_MOV,
+ no_overflow },
+ {"nor", "d,s,t", "d,s,t,E", 3, OP_MATCH_NOR,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"or", "d,s,t", "d,s,t,E", 3, OP_MATCH_OR,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"orhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ORHI,
+ OP_MASK_IOP, 0,
+ unsigned_immed16_overflow },
+ {"ori", "t,s,u", "t,s,u,E", 3, OP_MATCH_ORI,
+ OP_MASK_IOP, NIOS2_INSN_ORI,
+ unsigned_immed16_overflow },
+ {"rdctl", "d,c", "d,c,E", 2, OP_MATCH_RDCTL,
+ OP_MASK_RDCTL, 0,
+ no_overflow },
+ {"ret", "", "E", 0, OP_MATCH_RET,
+ OP_MASK, 0,
+ no_overflow },
+ {"rol", "d,s,t", "d,s,t,E", 3, OP_MATCH_ROL,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"roli", "d,s,j", "d,s,j,E", 3, OP_MATCH_ROLI,
+ OP_MASK_ROLI, 0,
+ unsigned_immed5_overflow },
+ {"ror", "d,s,t", "d,s,t,E", 3, OP_MATCH_ROR,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"sll", "d,s,t", "d,s,t,E", 3, OP_MATCH_SLL,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"slli", "d,s,j", "d,s,j,E", 3, OP_MATCH_SLLI,
+ OP_MASK_SLLI, 0,
+ unsigned_immed5_overflow },
+ {"sra", "d,s,t", "d,s,t,E", 3, OP_MATCH_SRA,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"srai", "d,s,j", "d,s,j,E", 3, OP_MATCH_SRAI,
+ OP_MASK_SRAI, 0,
+ unsigned_immed5_overflow },
+ {"srl", "d,s,t", "d,s,t,E", 3, OP_MATCH_SRL,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"srli", "d,s,j", "d,s,j,E", 3, OP_MATCH_SRLI,
+ OP_MASK_SRLI, 0,
+ unsigned_immed5_overflow },
+ {"stb", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STB,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"stbio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STBIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"stc", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STC,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"sth", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STH,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"sthio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STHIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"stw", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STW,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"stwio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STWIO,
+ OP_MASK_IOP, 0,
+ address_offset_overflow },
+ {"sub", "d,s,t", "d,s,t,E", 3, OP_MATCH_SUB,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"sync", "", "E", 0, OP_MATCH_SYNC,
+ OP_MASK_SYNC, 0,
+ no_overflow },
+ {"trap", "", "E", 0, OP_MATCH_TRAP,
+ OP_MASK_TRAP, 0,
+ no_overflow },
+ {"eret", "", "E", 0, OP_MATCH_ERET,
+ OP_MASK, 0,
+ no_overflow },
+ {"custom", "l,d,s,t", "l,d,s,t,E", 4, OP_MATCH_CUSTOM,
+ OP_MASK_ROP, 0,
+ custom_opcode_overflow },
+ {"wrctl", "c,s", "c,s,E", 2, OP_MATCH_WRCTL,
+ OP_MASK_WRCTL, 0,
+ no_overflow },
+ {"xor", "d,s,t", "d,s,t,E", 3, OP_MATCH_XOR,
+ OP_MASK_ROPX | OP_MASK_ROP, 0,
+ no_overflow },
+ {"xorhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_XORHI,
+ OP_MASK_IOP, 0,
+ unsigned_immed16_overflow },
+ {"xori", "t,s,u", "t,s,u,E", 3, OP_MATCH_XORI,
+ OP_MASK_IOP, NIOS2_INSN_XORI,
+ unsigned_immed16_overflow }
+};
+/* *INDENT-ON* */
+
+#define NIOS2_NUM_OPCODES \
+ ((sizeof(nios2_builtin_opcodes)) / (sizeof(nios2_builtin_opcodes[0])))
+const int bfd_nios2_num_builtin_opcodes = NIOS2_NUM_OPCODES;
+
+/* const removed from the following to allow for dynamic extensions to the
+ * built-in instruction set. */
+struct nios2_opcode *nios2_opcodes =
+ (struct nios2_opcode *) nios2_builtin_opcodes;
+int bfd_nios2_num_opcodes = NIOS2_NUM_OPCODES;
+#undef NIOS2_NUM_OPCODES
diff --git a/nios2.h b/nios2.h
new file mode 100644
index 0000000..ff60b77
--- /dev/null
+++ b/nios2.h
@@ -0,0 +1,366 @@
+/* nios2.h. Altera New Jersey opcode list for GAS, the GNU assembler.
+
+ Copyright (C) 2003
+ by Nigel Gray (ngray@altera.com).
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _NIOS2_H_
+#define _NIOS2_H_
+
+
+/****************************************************************************
+ * This file contains structures, bit masks and shift counts used
+ * by the GNU toolchain to define the New Jersey instruction set and
+ * access various opcode fields.
+ ****************************************************************************/
+
+enum overflow_type {
+ call_target_overflow = 0,
+ branch_target_overflow,
+ address_offset_overflow,
+ signed_immed16_overflow,
+ unsigned_immed16_overflow,
+ unsigned_immed5_overflow,
+ custom_opcode_overflow,
+ no_overflow
+};
+
+/*---------------------------------------------------------------------------
+ This structure holds information for a particular instruction
+ ---------------------------------------------------------------------------*/
+
+/* match When assembling, this
+ opcode is modified by the arguments to produce the actual opcode
+ that is used. If pinfo is INSN_MACRO, then this is 0. */
+
+/* mask If pinfo is not INSN_MACRO, then this is a bit mask for the
+ relevant portions of the opcode when disassembling. If the
+ actual opcode anded with the match field equals the opcode field,
+ then we have found the correct instruction. If pinfo is
+ INSN_MACRO, then this field is the macro identifier. */
+
+/* For a macro, this is INSN_MACRO. Otherwise, it is a collection
+ of bits describing the instruction, notably any relevant hazard
+ information. */
+
+struct nios2_opcode {
+ const char *name; /* The name of the instruction. */
+ const char *args; /* A string describing the arguments
+ for this instruction. */
+ const char *args_test; /* Like args, but with an extra argument
+ for the expected opcode */
+ unsigned long num_args; /* the number of arguments the instruction
+ takes */
+ unsigned long match; /* The basic opcode for the instruction. */
+ unsigned long mask; /* mask for the opcode field of the
+ instruction */
+ unsigned long pinfo; /* is this a real instruction or
+ instruction macro */
+ enum overflow_type overflow_msg; /* msg template used to generate
+ informative message when fixup
+ overflows */
+};
+
+/* This value is used in the nios2_opcode.pinfo field to indicate that the
+ instruction is a macro or pseudo-op. This requires special treatment by
+ the assembler, and is used by the disassembler to determine whether to
+ check for a nop */
+#define NIOS2_INSN_MACRO 0x80000000
+#define NIOS2_INSN_MACRO_MOV 0x80000001
+#define NIOS2_INSN_MACRO_MOVI 0x80000002
+#define NIOS2_INSN_MACRO_MOVIA 0x80000004
+
+#define NIOS2_INSN_RELAXABLE 0x40000000
+#define NIOS2_INSN_UBRANCH 0x00000010
+#define NIOS2_INSN_CBRANCH 0x00000020
+#define NIOS2_INSN_CALL 0x00000040
+
+#define NIOS2_INSN_ADDI 0x00000080
+#define NIOS2_INSN_ANDI 0x00000100
+#define NIOS2_INSN_ORI 0x00000200
+#define NIOS2_INSN_XORI 0x00000400
+
+
+
+/* Associates a register name ($6) with a 5-bit index (eg 6) */
+struct nios2_reg {
+ const char *name;
+ const int index;
+};
+
+
+/* -------------------------------------------------------------------------
+ Bitfield masks for New Jersey instructions
+ -------------------------------------------------------------------------*/
+
+/* These are bit masks and shift counts to use to access the various
+ fields of an instruction. */
+
+/* Macros for getting and setting an instruction field */
+#define GET_INSN_FIELD(X, i) (((i) & OP_MASK_##X) >> OP_SH_##X)
+#define SET_INSN_FIELD(X, i, j) ((i) = ((i) & ~(OP_MASK_##X)) | \
+ ((j) << OP_SH_##X))
+
+
+/*
+ We include the auto-generated file nios2-isa.h and define the mask
+ and shifts below in terms of those in nios2-isa.h. This ensures
+ that the binutils and hardware are always in sync
+*/
+
+#include "nios2-isa.h"
+
+#define OP_MASK_OP (IW_OP_MASK << IW_OP_LSB)
+#define OP_SH_OP IW_OP_LSB
+
+
+/* Masks and shifts for I-type instructions */
+
+#define OP_MASK_IOP (IW_OP_MASK << IW_OP_LSB)
+#define OP_SH_IOP IW_OP_LSB
+
+#define OP_MASK_IMM16 (IW_IMM16_MASK << IW_IMM16_LSB)
+#define OP_SH_IMM16 IW_IMM16_LSB
+
+#define OP_MASK_IRD (IW_B_MASK << IW_B_LSB)
+#define OP_SH_IRD IW_B_LSB
+
+#define OP_MASK_IRT (IW_B_MASK << IW_B_LSB)
+#define OP_SH_IRT IW_B_LSB
+
+#define OP_MASK_IRS (IW_A_MASK << IW_A_LSB)
+#define OP_SH_IRS IW_A_LSB
+
+/* Masks and shifts for R-type instructions */
+
+#define OP_MASK_ROP (IW_OP_MASK << IW_OP_LSB)
+#define OP_SH_ROP IW_OP_LSB
+
+#define OP_MASK_ROPX (IW_OPX_MASK << IW_OPX_LSB)
+#define OP_SH_ROPX IW_OPX_LSB
+
+#define OP_MASK_RRD (IW_C_MASK << IW_C_LSB)
+#define OP_SH_RRD IW_C_LSB
+
+#define OP_MASK_RRT (IW_B_MASK << IW_B_LSB)
+#define OP_SH_RRT IW_B_LSB
+
+#define OP_MASK_RRS (IW_A_MASK << IW_A_LSB)
+#define OP_SH_RRS IW_A_LSB
+
+/* Masks and shifts for J-type instructions */
+
+#define OP_MASK_JOP (IW_OP_MASK << IW_OP_LSB)
+#define OP_SH_JOP IW_OP_LSB
+
+#define OP_MASK_IMM26 (IW_IMM26_MASK << IW_IMM26_LSB)
+#define OP_SH_IMM26 IW_IMM26_LSB
+
+/* Masks and shifts for CTL instructions */
+
+#define OP_MASK_RCTL 0x000007c0
+#define OP_SH_RCTL 6
+
+/* break instruction imm5 field */
+#define OP_MASK_TRAP_IMM5 0x000007c0
+#define OP_SH_TRAP_IMM5 6
+
+/* instruction imm5 field */
+#define OP_MASK_IMM5 (IW_SHIFT_IMM5_MASK << IW_SHIFT_IMM5_LSB)
+#define OP_SH_IMM5 IW_SHIFT_IMM5_LSB
+
+/* cache operation fields (type j,i(s)) */
+#define OP_MASK_CACHE_OPX (IW_B_MASK << IW_B_LSB)
+#define OP_SH_CACHE_OPX IW_B_LSB
+#define OP_MASK_CACHE_RRS (IW_A_MASK << IW_A_LSB)
+#define OP_SH_CACHE_RRS IW_A_LSB
+
+/* custom instruction masks */
+#define OP_MASK_CUSTOM_A 0x00010000
+#define OP_SH_CUSTOM_A 16
+
+#define OP_MASK_CUSTOM_B 0x00008000
+#define OP_SH_CUSTOM_B 15
+
+#define OP_MASK_CUSTOM_C 0x00004000
+#define OP_SH_CUSTOM_C 14
+
+#define OP_MASK_CUSTOM_N 0x00003fc0
+#define OP_SH_CUSTOM_N 6
+#define OP_MAX_CUSTOM_N 255
+
+/*
+ The following macros define the opcode matches for each
+ instruction
+ code & OP_MASK_INST == OP_MATCH_INST
+ */
+
+/* OP instruction matches */
+#define OP_MATCH_ADDI OP_ADDI
+#define OP_MATCH_ANDHI OP_ANDHI
+#define OP_MATCH_ANDI OP_ANDI
+#define OP_MATCH_BEQ OP_BEQ
+#define OP_MATCH_BGE OP_BGE
+#define OP_MATCH_BGEU OP_BGEU
+#define OP_MATCH_BLT OP_BLT
+#define OP_MATCH_BLTU OP_BLTU
+#define OP_MATCH_BNE OP_BNE
+#define OP_MATCH_BR OP_BR
+#define OP_MATCH_FLUSHD OP_FLUSHD
+#define OP_MATCH_FLUSHDA OP_FLUSHDA
+#define OP_MATCH_INITD OP_INITD
+#define OP_MATCH_INITDA OP_INITDA
+#define OP_MATCH_CALL OP_CALL
+#define OP_MATCH_CMPEQI OP_CMPEQI
+#define OP_MATCH_CMPGEI OP_CMPGEI
+#define OP_MATCH_CMPGEUI OP_CMPGEUI
+#define OP_MATCH_CMPLTI OP_CMPLTI
+#define OP_MATCH_CMPLTUI OP_CMPLTUI
+#define OP_MATCH_CMPNEI OP_CMPNEI
+#define OP_MATCH_JMPI OP_JMPI
+#define OP_MATCH_LDB OP_LDB
+#define OP_MATCH_LDBIO OP_LDBIO
+#define OP_MATCH_LDBU OP_LDBU
+#define OP_MATCH_LDBUIO OP_LDBUIO
+#define OP_MATCH_LDH OP_LDH
+#define OP_MATCH_LDHIO OP_LDHIO
+#define OP_MATCH_LDHU OP_LDHU
+#define OP_MATCH_LDHUIO OP_LDHUIO
+#define OP_MATCH_LDL OP_LDL
+#define OP_MATCH_LDW OP_LDW
+#define OP_MATCH_LDWIO OP_LDWIO
+#define OP_MATCH_MULI OP_MULI
+#define OP_MATCH_OPX OP_OPX
+#define OP_MATCH_ORHI OP_ORHI
+#define OP_MATCH_ORI OP_ORI
+#define OP_MATCH_STB OP_STB
+#define OP_MATCH_STBIO OP_STBIO
+#define OP_MATCH_STC OP_STC
+#define OP_MATCH_STH OP_STH
+#define OP_MATCH_STHIO OP_STHIO
+#define OP_MATCH_STW OP_STW
+#define OP_MATCH_STWIO OP_STWIO
+#define OP_MATCH_CUSTOM OP_CUSTOM
+#define OP_MATCH_XORHI OP_XORHI
+#define OP_MATCH_XORI OP_XORI
+#define OP_MATCH_OPX OP_OPX
+
+
+
+/* OPX instruction values */
+#define OP_MATCH_ADD ((OPX_ADD << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_AND ((OPX_AND << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_BREAK ((0x1e << 17) | (OPX_BREAK << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_BRET ((0xf0000000) | (OPX_BRET << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CALLR ((0x1f << 17) | (OPX_CALLR << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPEQ ((OPX_CMPEQ << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPGE ((OPX_CMPGE << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPGEU ((OPX_CMPGEU << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPLT ((OPX_CMPLT << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPLTU ((OPX_CMPLTU << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_CMPNE ((OPX_CMPNE << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_DIV ((OPX_DIV << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_DIVU ((OPX_DIVU << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_JMP ((OPX_JMP << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_MUL ((OPX_MUL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_MULXSS ((OPX_MULXSS << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_MULXSU ((OPX_MULXSU << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_MULXUU ((OPX_MULXUU << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_NEXTPC ((OPX_NEXTPC << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_NOR ((OPX_NOR << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_OR ((OPX_OR << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_RDCTL ((OPX_RDCTL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_RET ((0xf8000000) | (OPX_RET << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_ROL ((OPX_ROL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_ROLI ((OPX_ROLI << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_ROR ((OPX_ROR << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SLL ((OPX_SLL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SLLI ((OPX_SLLI << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SRA ((OPX_SRA << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SRAI ((OPX_SRAI << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SRL ((OPX_SRL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SRLI ((OPX_SRLI << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SUB ((OPX_SUB << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_SYNC ((OPX_SYNC << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_TRAP ((0x1d << 17) | (OPX_TRAP << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_ERET ((0xe8000000) | (OPX_ERET << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_WRCTL ((OPX_WRCTL << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_XOR ((OPX_XOR << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_FLUSHI ((OPX_FLUSHI << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_FLUSHP ((OPX_FLUSHP << IW_OPX_LSB) | (OP_OPX))
+#define OP_MATCH_INITI ((OPX_INITI << IW_OPX_LSB) | (OP_OPX))
+
+/*
+ Some unusual op masks
+*/
+#define OP_MASK_BREAK ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_RRD | \
+ OP_MASK_ROPX | OP_MASK_OP) & 0xfffff03f)
+#define OP_MASK_CALLR ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_JMP ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_SYNC ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_TRAP ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_RRD | \
+ OP_MASK_ROPX | OP_MASK_OP) & 0xfffff83f)
+#define OP_MASK_WRCTL ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_NEXTPC ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_FLUSHI ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_INITI ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
+
+#define OP_MASK_ROLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_SLLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_SRAI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_SRLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+#define OP_MASK_RDCTL ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
+
+#ifndef OP_MASK
+#define OP_MASK 0xffffffff
+#endif
+
+/* These are the data structures we use to hold the instruction information */
+
+extern const struct nios2_opcode nios2_builtin_opcodes[];
+extern const int bfd_nios2_num_builtin_opcodes;
+extern struct nios2_opcode *nios2_opcodes;
+extern int bfd_nios2_num_opcodes;
+
+/* These are the data structures used to hold the register information */
+extern const struct nios2_reg nios2_builtin_regs[];
+extern struct nios2_reg *nios2_regs;
+extern const int nios2_num_builtin_regs;
+extern int nios2_num_regs;
+
+/* Machine-independent macro for number of opcodes */
+
+#define NUMOPCODES bfd_nios2_num_opcodes
+#define NUMREGISTERS nios2_num_regs;
+
+/* these are used in disassembly to get the correct register names */
+#define NUMREGNAMES 32
+#define NUMCTLREGNAMES 32
+#define CTLREGBASE 42
+#define COPROCREGBASE 83
+#define NUMCOPROCREGNAMES 32
+
+
+/* this is made extern so that the assembler can use it to find out
+ what instruction caused an error */
+extern const struct nios2_opcode *nios2_find_opcode_hash(unsigned long);
+
+/* overflow message strings used in the assembler */
+extern const char *overflow_msgs[];
+
+#endif /* _NIOS2_H */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB crwulff
@ 2012-09-11 19:58 ` Blue Swirl
0 siblings, 0 replies; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 19:58 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> Makefile.objs | 2 +
> nios2-dis.c | 417 +++++++++++
> nios2-isa.h | 2150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> nios2-opc.c | 540 +++++++++++++++
> nios2.h | 366 ++++++++++
> 5 files changed, 3475 insertions(+)
> create mode 100644 nios2-dis.c
> create mode 100644 nios2-isa.h
> create mode 100644 nios2-opc.c
> create mode 100644 nios2.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 4412757..16cbe8b 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -133,6 +133,8 @@ libdis-$(CONFIG_IA64_DIS) += ia64-dis.o
> libdis-$(CONFIG_M68K_DIS) += m68k-dis.o
> libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o
> libdis-$(CONFIG_MIPS_DIS) += mips-dis.o
> +libdis-$(CONFIG_NIOS2_DIS) += nios2-dis.o
> +libdis-$(CONFIG_NIOS2_DIS) += nios2-opc.o
> libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
> libdis-$(CONFIG_S390_DIS) += s390-dis.o
> libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
> diff --git a/nios2-dis.c b/nios2-dis.c
> new file mode 100644
> index 0000000..09c79eb
> --- /dev/null
> +++ b/nios2-dis.c
> @@ -0,0 +1,417 @@
> +/* nios2-dis.c -- Altera New Jersey disassemble routines.
> +
> + Copyright (C) 2003
> + by Nigel Gray (ngray@altera.com).
> +
> +This file is part of GDB, GAS, and the GNU binutils.
> +
> +GDB, GAS, and the GNU binutils are free software; you can redistribute
> +them and/or modify them under the terms of the GNU General Public
> +License as published by the Free Software Foundation; either version
> +1, or (at your option) any later version.
> +
> +GDB, GAS, and the GNU binutils are distributed in the hope that they
> +will be useful, but WITHOUT ANY WARRANTY; without even the implied
> +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> +the GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this file; see the file COPYING. If not, write to the Free
> +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
Current address of FSF is:
51 Franklin Street, Fifth Floor
Boston, MA 02110-1301
USA
However, please use the recommended web version like other files.
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +#include <string.h>
> +#include "dis-asm.h"
> +#include "nios2.h"
> +
> +#ifndef FALSE
> +#define FALSE 0
> +#endif
> +#ifndef TRUE
> +#define TRUE 1
> +#endif
> +
> +/* length of New Jersey instruction in bytes */
> +#define INSNLEN 4
> +
> +/* helper function prototypes */
> +static int nios2_disassemble(bfd_vma, unsigned long, disassemble_info *);
> +static void nios2_init_opcode_hash(void);
> +
> +
> +static int nios2_print_insn_arg(const char *argptr, unsigned long opcode,
> + bfd_vma address, disassemble_info *info);
> +
> +
> +/* print_insn_nios2 is the main disassemble function for New Jersey.
> + The function diassembler(abfd) (source in disassemble.c) returns a
> + pointer to this either print_insn_big_nios2 or
> + print_insn_little_nios2, which in turn call this function, when the
> + bfd machine type is New Jersey. print_insn_nios2 reads the
> + instruction word at the address given, and prints the disassembled
> + instruction on the stream info->stream using info->fprintf_func. */
> +
> +int
> +print_insn_nios2(bfd_vma address, disassemble_info *info)
> +{
> + /* buffer into which the instruction bytes are written */
> + bfd_byte buffer[INSNLEN];
> + /* used to indicate return status from function calls */
> + int status;
> +
> + assert(info != NULL);
> +
> + status = (*info->read_memory_func) (address, buffer, INSNLEN, info);
> + if (status == 0) {
> + unsigned long insn;
> + insn = (unsigned long) bfd_getl32(buffer);
> + status = nios2_disassemble(address, insn, info);
> + } else {
> + (*info->memory_error_func) (status, address, info);
> + status = -1;
> + }
> + return status;
> +}
> +
> +/* Data structures used by the opcode hash table */
> +
> +typedef struct _nios2_opcode_hash {
> + const struct nios2_opcode *opcode;
> + struct _nios2_opcode_hash *next;
> +} nios2_opcode_hash;
> +
> +static bfd_boolean nios2_hash_init;
> +static nios2_opcode_hash *nios2_hash[(OP_MASK_OP) + 1];
> +
> +/* separate hash table for pseudo-ops */
> +static nios2_opcode_hash *nios2_ps_hash[(OP_MASK_OP) + 1];
> +
> +/* Function to initialize the opcode hash table */
> +
> +void
> +nios2_init_opcode_hash(void)
> +{
> + unsigned int i;
> + register const struct nios2_opcode *op;
> + nios2_opcode_hash *tmp_hash;
> +
> + for (i = 0; i <= OP_MASK_OP; ++i) {
> + nios2_hash[0] = NULL;
> + }
> + for (i = 0; i <= OP_MASK_OP; i++) {
> + for (op = nios2_opcodes; op < &nios2_opcodes[NUMOPCODES]; op++) {
> + if ((op->pinfo & NIOS2_INSN_MACRO) == NIOS2_INSN_MACRO) {
> + if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP) &&
> + (op->pinfo &
> + (NIOS2_INSN_MACRO_MOV | NIOS2_INSN_MACRO_MOVI) &
> + 0x7fffffff) != 0) {
> + tmp_hash = nios2_ps_hash[i];
> + if (tmp_hash == NULL) {
> + tmp_hash =
> + (nios2_opcode_hash *)
> + malloc(sizeof(nios2_opcode_hash));
> + nios2_ps_hash[i] = tmp_hash;
> + } else {
> + while (tmp_hash->next != NULL) {
> + tmp_hash = tmp_hash->next;
> + }
> + tmp_hash->next =
> + (nios2_opcode_hash *)
> + malloc(sizeof(nios2_opcode_hash));
> + tmp_hash = tmp_hash->next;
> + }
> + if (tmp_hash == NULL) {
> + fprintf(stderr,
> + "error allocating memory...broken disassembler\n");
> + abort();
> + }
> + tmp_hash->opcode = op;
> + tmp_hash->next = NULL;
> + }
> + } else if (i == ((op->match >> OP_SH_OP) & OP_MASK_OP)) {
> + tmp_hash = nios2_hash[i];
> + if (tmp_hash == NULL) {
> + tmp_hash =
> + (nios2_opcode_hash *)malloc(sizeof(nios2_opcode_hash));
> + nios2_hash[i] = tmp_hash;
> + } else {
> + while (tmp_hash->next != NULL) {
> + tmp_hash = tmp_hash->next;
> + }
> + tmp_hash->next =
> + (nios2_opcode_hash *)malloc(sizeof(nios2_opcode_hash));
> + tmp_hash = tmp_hash->next;
> + }
> + if (tmp_hash == NULL) {
> + fprintf(stderr,
> + "error allocating memory...broken disassembler\n");
> + abort();
> + }
> + tmp_hash->opcode = op;
> + tmp_hash->next = NULL;
> + }
> + }
> + }
> + nios2_hash_init = 1;
> +#ifdef DEBUG_HASHTABLE
> + for (i = 0; i <= OP_MASK_OP; ++i) {
> + printf("index: 0x%02X ops: ", i);
> + tmp_hash = nios2_hash[i];
> + if (tmp_hash != NULL) {
> + while (tmp_hash != NULL) {
> + printf("%s ", tmp_hash->opcode->name);
> + tmp_hash = tmp_hash->next;
> + }
> + }
> + printf("\n");
> + }
> +
> + for (i = 0; i <= OP_MASK_OP; ++i) {
> + printf("index: 0x%02X ops: ", i);
> + tmp_hash = nios2_ps_hash[i];
> + if (tmp_hash != NULL) {
> + while (tmp_hash != NULL) {
> + printf("%s ", tmp_hash->opcode->name);
> + tmp_hash = tmp_hash->next;
> + }
> + }
> + printf("\n");
> + }
> +#endif
> +}
> +
> +/* Function which returns a pointer to an nios2_opcode struct for
> + a given instruction opcode, or NULL if there is an error */
> +
> +const struct nios2_opcode *
> +nios2_find_opcode_hash(unsigned long opcode)
> +{
> + nios2_opcode_hash *entry;
> +
> + /* Build a hash table to shorten the search time. */
> + if (!nios2_hash_init) {
> + nios2_init_opcode_hash();
> + }
> +
> + /* first look in the pseudo-op hashtable */
> + entry = nios2_ps_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
> +
> + /* look for a match and if we get one, this is the instruction we decode */
> + while (entry != NULL) {
> + if ((entry->opcode->match) == (opcode & entry->opcode->mask)) {
> + return entry->opcode;
> + } else {
> + entry = entry->next;
> + }
> + }
> +
> + /* if we haven't yet returned, then we need to look in the main
> + hashtable */
> + entry = nios2_hash[(opcode >> OP_SH_OP) & OP_MASK_OP];
> +
> + if (entry == NULL) {
> + return NULL;
> + }
> +
> + while (entry != NULL) {
> + if ((entry->opcode->match) == (opcode & entry->opcode->mask)) {
> + return entry->opcode;
> + } else {
> + entry = entry->next;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +/* nios2_disassemble does all the work of disassembling a New Jersey
> + instruction opcode */
> +
> +int
> +nios2_disassemble(bfd_vma address, unsigned long opcode,
> + disassemble_info *info)
> +{
> + const struct nios2_opcode *op;
> + const char *argstr;
> +
> + info->bytes_per_line = INSNLEN;
> + info->bytes_per_chunk = INSNLEN;
> + info->display_endian = info->endian;
> + info->insn_info_valid = 1;
> + info->branch_delay_insns = 0;
> + info->data_size = 0;
> + info->insn_type = dis_nonbranch;
> + info->target = 0;
> + info->target2 = 0;
> +
> + (*info->fprintf_func) (info->stream, "%08x %08x ",
> + (int)address, (int)opcode);
> +
> + /* Find the major opcode and use this to disassemble
> + the instruction and its arguments */
> + op = nios2_find_opcode_hash(opcode);
> +
> + if (op != NULL) {
> + bfd_boolean is_nop = FALSE;
> + if (op->pinfo == NIOS2_INSN_MACRO_MOV) {
> + /* check for mov r0, r0 and if it is
> + change to nop */
> + int dst, src;
> + dst = GET_INSN_FIELD(RRD, opcode);
> + src = GET_INSN_FIELD(RRS, opcode);
> + if (dst == 0 && src == 0) {
> + (*info->fprintf_func) (info->stream, "nop");
> + is_nop = TRUE;
> + } else {
> + (*info->fprintf_func) (info->stream, "%s", op->name);
> + }
> + } else {
> + (*info->fprintf_func) (info->stream, "%s", op->name);
> + }
> +
> + if (!is_nop) {
> + argstr = op->args;
> + if (argstr != NULL && *argstr != '\0') {
> + (*info->fprintf_func) (info->stream, "\t");
> + while (*argstr != '\0') {
> + nios2_print_insn_arg(argstr, opcode, address, info);
> + ++argstr;
> + }
> + }
> + }
> + } else {
> + /* Handle undefined instructions. */
> + info->insn_type = dis_noninsn;
> + (*info->fprintf_func) (info->stream, "0x%x", (int)opcode);
> + }
> + /* this tells the caller how far to advance the program counter */
> + return INSNLEN;
> +}
> +
> +/* The function nios2_print_insn_arg uses the character pointed
> + to by argptr to determine how it print the next token or separator
> + character in the arguments to an instruction */
> +int
> +nios2_print_insn_arg(const char *argptr,
> + unsigned long opcode, bfd_vma address,
> + disassemble_info *info)
> +{
> + unsigned long i = 0;
> + unsigned long reg_base;
> +
> + assert(argptr != NULL);
> + assert(info != NULL);
> +
> + switch (*argptr) {
> + case ',':
> + case '(':
> + case ')':
> + (*info->fprintf_func) (info->stream, "%c", *argptr);
> + break;
> + case 'd':
> + i = GET_INSN_FIELD(RRD, opcode);
> +
> + if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
> + && GET_INSN_FIELD(CUSTOM_C, opcode) == 0) {
> + reg_base = COPROCREGBASE;
> + } else {
> + reg_base = 0;
> + }
> +
> + if (i < NUMREGNAMES) {
> + (*info->fprintf_func) (info->stream, "%s",
> + nios2_regs[i + reg_base].name);
> + } else {
> + (*info->fprintf_func) (info->stream, "unknown");
> + }
> + break;
> + case 's':
> + i = GET_INSN_FIELD(RRS, opcode);
> +
> + if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
> + && GET_INSN_FIELD(CUSTOM_A, opcode) == 0) {
> + reg_base = COPROCREGBASE;
> + } else {
> + reg_base = 0;
> + }
> +
> + if (i < NUMREGNAMES) {
> + (*info->fprintf_func) (info->stream, "%s",
> + nios2_regs[i + reg_base].name);
> + } else {
> + (*info->fprintf_func) (info->stream, "unknown");
> + }
> + break;
> + case 't':
> + i = GET_INSN_FIELD(RRT, opcode);
> +
> + if (GET_INSN_FIELD(OP, opcode) == OP_MATCH_CUSTOM
> + && GET_INSN_FIELD(CUSTOM_B, opcode) == 0) {
> + reg_base = COPROCREGBASE;
> + } else {
> + reg_base = 0;
> + }
> +
> + if (i < NUMREGNAMES) {
> + (*info->fprintf_func) (info->stream, "%s",
> + nios2_regs[i + reg_base].name);
> + } else {
> + (*info->fprintf_func) (info->stream, "unknown");
> + }
> + break;
> + case 'i':
> + /* 16-bit signed immediate */
> + i = (signed) (GET_INSN_FIELD(IMM16, opcode) << 16) >> 16;
> + (*info->fprintf_func) (info->stream, "%d", (int)i);
> + break;
> + case 'u':
> + /* 16-bit unsigned immediate */
> + i = GET_INSN_FIELD(IMM16, opcode);
> + (*info->fprintf_func) (info->stream, "%d", (int)i);
> + break;
> + case 'o':
> + /* 16-bit signed immediate address offset */
> + i = (signed) (GET_INSN_FIELD(IMM16, opcode) << 16) >> 16;
> + address = address + 4 + i; /* NG changed to byte offset 1/9/03 */
> + (*info->print_address_func) (address, info);
> + break;
> + case 'p':
> + /* 5-bit unsigned immediate */
> + i = GET_INSN_FIELD(CACHE_OPX, opcode);
> + (*info->fprintf_func) (info->stream, "%d", (int)i);
> + break;
> + case 'j':
> + /* 5-bit unsigned immediate */
> + i = GET_INSN_FIELD(IMM5, opcode);
> + (*info->fprintf_func) (info->stream, "%d", (int)i);
> + break;
> + case 'l':
> + /* 8-bit unsigned immediate */
> + /* FIXME - not yet implemented */
> + i = GET_INSN_FIELD(CUSTOM_N, opcode);
> + (*info->fprintf_func) (info->stream, "%u", (int)i);
> + break;
> + case 'm':
> + /* 26-bit unsigned immediate */
> + i = GET_INSN_FIELD(IMM26, opcode);
> + /* this translates to an address because its only used in
> + call instructions */
> + address = (address & 0xf0000000) | (i << 2);
> + (*info->print_address_func) (address, info);
> + break;
> + case 'c':
> + i = GET_INSN_FIELD(IMM5, opcode); /* ctrl register index */
> + (*info->fprintf_func) (info->stream, "%s",
> + nios2_regs[CTLREGBASE + i].name);
> + break;
> + case 'b':
> + i = GET_INSN_FIELD(IMM5, opcode);
> + (*info->fprintf_func) (info->stream, "%d", (int)i);
> + break;
> + default:
> + (*info->fprintf_func) (info->stream, "unknown");
> + break;
> + }
> + return 0;
> +}
> diff --git a/nios2-isa.h b/nios2-isa.h
> new file mode 100644
> index 0000000..125124c
> --- /dev/null
> +++ b/nios2-isa.h
The three files should be concatenated like it is done for other targets.
> @@ -0,0 +1,2150 @@
> +/*
> + * This file defines Nios II instruction set constants.
> + * To include it in assembly code (.S file), define ALT_ASM_SRC
> + * before including this file.
> + *
> + * This file is automatically generated by gen_isa.pl - do not edit
> + */
> +
> +#ifndef _NIOS2_ISA_H_
> +#define _NIOS2_ISA_H_
> +
> +/* OP instruction opcode values (index is OP field) */
> +#define NUM_OP_INSTS 64
> +
> +#ifndef ALT_ASM_SRC
> +extern const char *op_names[NUM_OP_INSTS];
> +#endif /* ALT_ASM_SRC */
> +
> +/* OPX instruction opcode values (index is OPX field) */
> +#define NUM_OPX_INSTS 64
> +
> +#ifndef ALT_ASM_SRC
> +extern const char *opx_names[NUM_OPX_INSTS];
> +#endif /* ALT_ASM_SRC */
> +
> +/* Constants for instruction fields and ISA */
> +#define BREAK_INST_EXC_ID 7
> +#define CPU_RESET_CAUSE_ID 1
> +#define CPU_RESET_EXC_ID 1
> +#define DIV_ERROR_CAUSE_ID 8
> +#define DIV_ERROR_EXC_ID 10
> +#define EMPTY_SLAVE_DATA_ACCESS_ERROR_CAUSE_ID 30
> +#define EMPTY_SLAVE_DATA_ACCESS_ERROR_EXC_ID 22
> +#define EMPTY_SLAVE_INST_ACCESS_ERROR_CAUSE_ID 29
> +#define EMPTY_SLAVE_INST_ACCESS_ERROR_EXC_ID 21
> +#define HBREAK_EXC_ID 2
> +#define ILLEGAL_INST_CAUSE_ID 5
> +#define ILLEGAL_INST_EXC_ID 6
> +#define MAX_CAUSE_ID 31
> +#define MISALIGNED_DATA_ADDR_CAUSE_ID 6
> +#define MISALIGNED_DATA_ADDR_EXC_ID 8
> +#define MISALIGNED_TARGET_PC_CAUSE_ID 7
> +#define MISALIGNED_TARGET_PC_EXC_ID 9
> +#define MPU_DATA_REGION_VIOLATION_CAUSE_ID 17
> +#define MPU_DATA_REGION_VIOLATION_EXC_ID 20
> +#define MPU_INST_REGION_VIOLATION_CAUSE_ID 16
> +#define MPU_INST_REGION_VIOLATION_EXC_ID 19
> +#define NIOS2_DISPLAY_INST_TRACE 1
> +#define NIOS2_DISPLAY_MEM_TRAFFIC 2
> +#define NONE_EXC_ID 0
> +#define NORM_INTR_CAUSE_ID 2
> +#define NORM_INTR_EXC_ID 3
> +#define NUM_EXC_IDS 24
> +#define READONLY_SLAVE_DATA_ACCESS_ERROR_CAUSE_ID 31
> +#define READONLY_SLAVE_DATA_ACCESS_ERROR_EXC_ID 23
> +#define RECORD_DATA_ADDR 2
> +#define RECORD_NOTHING 0
> +#define RECORD_TARGET_PCB 1
> +#define RESET_CAUSE_ID 0
> +#define SUPERVISOR_DATA_ADDR_CAUSE_ID 11
> +#define SUPERVISOR_DATA_ADDR_EXC_ID 13
> +#define SUPERVISOR_INST_ADDR_CAUSE_ID 9
> +#define SUPERVISOR_INST_ADDR_EXC_ID 11
> +#define SUPERVISOR_INST_CAUSE_ID 10
> +#define SUPERVISOR_INST_EXC_ID 12
> +#define TLB_DATA_MISS_EXC_ID 15
> +#define TLB_INST_MISS_EXC_ID 14
> +#define TLB_MISS_CAUSE_ID 12
> +#define TLB_R_PERM_CAUSE_ID 14
> +#define TLB_R_PERM_EXC_ID 17
> +#define TLB_W_PERM_CAUSE_ID 15
> +#define TLB_W_PERM_EXC_ID 18
> +#define TLB_X_PERM_CAUSE_ID 13
> +#define TLB_X_PERM_EXC_ID 16
> +#define TRAP_INST_CAUSE_ID 3
> +#define TRAP_INST_EXC_ID 4
> +#define UNIMP_INST_CAUSE_ID 4
> +#define UNIMP_INST_EXC_ID 5
> +#define AT_REGNUM 1
> +#define BADADDR_REG_BADDR_LSB 0
> +#define BADADDR_REG_BADDR_MSB 31
> +#define BADADDR_REG_BADDR_SZ 32
> +#define BADADDR_REG_BADDR_MASK 0xffffffff
> +#define BADADDR_REG_LSB 0
> +#define BADADDR_REG_MSB 31
> +#define BADADDR_REG_REGNUM 12
> +#define BADADDR_REG_SZ 32
> +#define BADADDR_REG_MASK 0xffffffff
> +#define BRETADDR_REGNUM 30
> +#define BSTATUS_REG_LSB 0
> +#define BSTATUS_REG_MMU_LSB 0
> +#define BSTATUS_REG_MMU_MSB 2
> +#define BSTATUS_REG_MMU_SZ 3
> +#define BSTATUS_REG_MMU_MASK 0x7
> +#define BSTATUS_REG_MPU_LSB 0
> +#define BSTATUS_REG_MPU_MSB 1
> +#define BSTATUS_REG_MPU_SZ 2
> +#define BSTATUS_REG_MPU_MASK 0x3
> +#define BSTATUS_REG_MSB 2
> +#define BSTATUS_REG_NO_MMU_LSB 0
> +#define BSTATUS_REG_NO_MMU_MSB 0
> +#define BSTATUS_REG_NO_MMU_SZ 1
> +#define BSTATUS_REG_NO_MMU_MASK 0x1
> +#define BSTATUS_REG_REGNUM 2
> +#define BSTATUS_REG_SZ 3
> +#define BSTATUS_REG_MASK 0x7
> +#define BT_REGNUM 25
> +#define CACHE_MAX_BYTES 65536
> +#define CACHE_MAX_LINE_BYTES 32
> +#define CACHE_MIN_LINE_BYTES 4
> +#define COMPARE_OP_EQ 0x0
> +#define COMPARE_OP_GE 0x1
> +#define COMPARE_OP_LSB 3
> +#define COMPARE_OP_LT 0x2
> +#define COMPARE_OP_MSB 4
> +#define COMPARE_OP_NE 0x3
> +#define COMPARE_OP_SZ 2
> +#define COMPARE_OP_MASK 0x3
> +#define CONFIG_REG_LSB 0
> +#define CONFIG_REG_MSB 0
> +#define CONFIG_REG_PE_LSB 0
> +#define CONFIG_REG_PE_MSB 0
> +#define CONFIG_REG_PE_SZ 1
> +#define CONFIG_REG_PE_MASK 0x1
> +#define CONFIG_REG_REGNUM 13
> +#define CONFIG_REG_SZ 1
> +#define CONFIG_REG_MASK 0x1
> +#define CPUID_REG_LSB 0
> +#define CPUID_REG_MSB 31
> +#define CPUID_REG_REGNUM 5
> +#define CPUID_REG_SZ 32
> +#define CPUID_REG_MASK 0xffffffff
> +#define DATAPATH_LOG2_SZ 5
> +#define DATAPATH_LOG2_MASK 0x1f
> +#define DATAPATH_LSB 0
> +#define DATAPATH_MSB 31
> +#define DATAPATH_SZ 32
> +#define DATAPATH_MASK 0xffffffff
> +#define EMPTY_CRST_IW 127034
> +#define EMPTY_HBREAK_IW 4040762
> +#define EMPTY_INTR_IW 3926074
> +#define EMPTY_NOP_IW 100410
> +#define EMPTY_RET_IW 4160759866
> +#define ERETADDR_REGNUM 29
> +#define ESTATUS_REG_LSB 0
> +#define ESTATUS_REG_MMU_LSB 0
> +#define ESTATUS_REG_MMU_MSB 2
> +#define ESTATUS_REG_MMU_SZ 3
> +#define ESTATUS_REG_MMU_MASK 0x7
> +#define ESTATUS_REG_MPU_LSB 0
> +#define ESTATUS_REG_MPU_MSB 1
> +#define ESTATUS_REG_MPU_SZ 2
> +#define ESTATUS_REG_MPU_MASK 0x3
> +#define ESTATUS_REG_MSB 2
> +#define ESTATUS_REG_NO_MMU_LSB 0
> +#define ESTATUS_REG_NO_MMU_MSB 0
> +#define ESTATUS_REG_NO_MMU_SZ 1
> +#define ESTATUS_REG_NO_MMU_MASK 0x1
> +#define ESTATUS_REG_REGNUM 1
> +#define ESTATUS_REG_SZ 3
> +#define ESTATUS_REG_MASK 0x7
> +#define ET_REGNUM 24
> +#define EXCEPTION_REG_CAUSE_LSB 2
> +#define EXCEPTION_REG_CAUSE_MSB 6
> +#define EXCEPTION_REG_CAUSE_SZ 5
> +#define EXCEPTION_REG_CAUSE_MASK 0x1f
> +#define EXCEPTION_REG_LSB 0
> +#define EXCEPTION_REG_MEA_LSB 0
> +#define EXCEPTION_REG_MEA_MSB 0
> +#define EXCEPTION_REG_MEA_SZ 1
> +#define EXCEPTION_REG_MEA_MASK 0x1
> +#define EXCEPTION_REG_MEE_LSB 1
> +#define EXCEPTION_REG_MEE_MSB 1
> +#define EXCEPTION_REG_MEE_SZ 1
> +#define EXCEPTION_REG_MEE_MASK 0x1
> +#define EXCEPTION_REG_MSB 6
> +#define EXCEPTION_REG_REGNUM 7
> +#define EXCEPTION_REG_SZ 7
> +#define EXCEPTION_REG_MASK 0x7f
> +#define FP_REGNUM 28
> +#define FSTATUS_REG_REGNUM 11
> +#define GP_REGNUM 26
> +#define IENABLE_REG_LSB 0
> +#define IENABLE_REG_MSB 31
> +#define IENABLE_REG_REGNUM 3
> +#define IENABLE_REG_SZ 32
> +#define IENABLE_REG_MASK 0xffffffff
> +#define IPENDING_REG_LSB 0
> +#define IPENDING_REG_MSB 31
> +#define IPENDING_REG_REGNUM 4
> +#define IPENDING_REG_SZ 32
> +#define IPENDING_REG_MASK 0xffffffff
> +#define IW_A_LSB 27
> +#define IW_A_MSB 31
> +#define IW_A_SZ 5
> +#define IW_A_MASK 0x1f
> +#define IW_B_LSB 22
> +#define IW_B_MSB 26
> +#define IW_B_SZ 5
> +#define IW_B_MASK 0x1f
> +#define IW_C_LSB 17
> +#define IW_C_MSB 21
> +#define IW_C_SZ 5
> +#define IW_C_MASK 0x1f
> +#define IW_CONTROL_REGNUM_BASE 0
> +#define IW_CONTROL_REGNUM_LSB 6
> +#define IW_CONTROL_REGNUM_MSB 9
> +#define IW_CONTROL_REGNUM_SZ 4
> +#define IW_CONTROL_REGNUM_MASK 0xf
> +#define IW_CUSTOM_N_LSB 6
> +#define IW_CUSTOM_N_MSB 13
> +#define IW_CUSTOM_N_SZ 8
> +#define IW_CUSTOM_N_MASK 0xff
> +#define IW_CUSTOM_READRA_LSB 16
> +#define IW_CUSTOM_READRA_MSB 16
> +#define IW_CUSTOM_READRA_SZ 1
> +#define IW_CUSTOM_READRA_MASK 0x1
> +#define IW_CUSTOM_READRB_LSB 15
> +#define IW_CUSTOM_READRB_MSB 15
> +#define IW_CUSTOM_READRB_SZ 1
> +#define IW_CUSTOM_READRB_MASK 0x1
> +#define IW_CUSTOM_WRITERC_LSB 14
> +#define IW_CUSTOM_WRITERC_MSB 14
> +#define IW_CUSTOM_WRITERC_SZ 1
> +#define IW_CUSTOM_WRITERC_MASK 0x1
> +#define IW_IMM16_LSB 6
> +#define IW_IMM16_MSB 21
> +#define IW_IMM16_SZ 16
> +#define IW_IMM16_MASK 0xffff
> +#define IW_IMM26_LSB 6
> +#define IW_IMM26_MSB 31
> +#define IW_IMM26_SZ 26
> +#define IW_IMM26_MASK 0x3ffffff
> +#define IW_MEMSZ_BYTE 0x0
> +#define IW_MEMSZ_HWORD 0x1
> +#define IW_MEMSZ_LSB 3
> +#define IW_MEMSZ_MSB 4
> +#define IW_MEMSZ_SZ 2
> +#define IW_MEMSZ_MASK 0x3
> +#define IW_MEMSZ_WORD_MSB 0x1
> +#define IW_OP_LSB 0
> +#define IW_OP_MSB 5
> +#define IW_OP_SZ 6
> +#define IW_OP_MASK 0x3f
> +#define IW_OPX_LSB 11
> +#define IW_OPX_MSB 16
> +#define IW_OPX_SZ 6
> +#define IW_OPX_MASK 0x3f
> +#define IW_SHIFT_IMM5_LSB 6
> +#define IW_SHIFT_IMM5_MSB 10
> +#define IW_SHIFT_IMM5_SZ 5
> +#define IW_SHIFT_IMM5_MASK 0x1f
> +#define IW_SZ 32
> +#define IW_MASK 0xffffffff
> +#define IW_TRAP_BREAK_IMM5_LSB 6
> +#define IW_TRAP_BREAK_IMM5_MSB 10
> +#define IW_TRAP_BREAK_IMM5_SZ 5
> +#define IW_TRAP_BREAK_IMM5_MASK 0x1f
> +#define JMP_CALLR_VS_RET_IS_RET 0
> +#define JMP_CALLR_VS_RET_OPX_BIT 3
> +#define LOGIC_OP_AND 0x1
> +#define LOGIC_OP_LSB 3
> +#define LOGIC_OP_MSB 4
> +#define LOGIC_OP_NOR 0x0
> +#define LOGIC_OP_OR 0x2
> +#define LOGIC_OP_SZ 2
> +#define LOGIC_OP_MASK 0x3
> +#define LOGIC_OP_XOR 0x3
> +#define MMU_ADDR_BYPASS_TLB 0x3
> +#define MMU_ADDR_BYPASS_TLB_CACHEABLE 0x0
> +#define MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB 29
> +#define MMU_ADDR_BYPASS_TLB_CACHEABLE_MSB 29
> +#define MMU_ADDR_BYPASS_TLB_CACHEABLE_SZ 1
> +#define MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK 0x1
> +#define MMU_ADDR_BYPASS_TLB_LSB 30
> +#define MMU_ADDR_BYPASS_TLB_MSB 31
> +#define MMU_ADDR_BYPASS_TLB_PADDR_LSB 0
> +#define MMU_ADDR_BYPASS_TLB_PADDR_MSB 28
> +#define MMU_ADDR_BYPASS_TLB_PADDR_SZ 29
> +#define MMU_ADDR_BYPASS_TLB_PADDR_MASK 0x1fffffff
> +#define MMU_ADDR_BYPASS_TLB_SZ 2
> +#define MMU_ADDR_BYPASS_TLB_MASK 0x3
> +#define MMU_ADDR_IO_REGION 0x7
> +#define MMU_ADDR_IO_REGION_LSB 29
> +#define MMU_ADDR_IO_REGION_MSB 31
> +#define MMU_ADDR_IO_REGION_SZ 3
> +#define MMU_ADDR_IO_REGION_MASK 0x7
> +#define MMU_ADDR_IO_REGION_VPN 0xe0000
> +#define MMU_ADDR_KERNEL_MMU_REGION 0x2
> +#define MMU_ADDR_KERNEL_MMU_REGION_LSB 30
> +#define MMU_ADDR_KERNEL_MMU_REGION_MSB 31
> +#define MMU_ADDR_KERNEL_MMU_REGION_SZ 2
> +#define MMU_ADDR_KERNEL_MMU_REGION_MASK 0x3
> +#define MMU_ADDR_KERNEL_REGION 0x6
> +#define MMU_ADDR_KERNEL_REGION_INT 6
> +#define MMU_ADDR_KERNEL_REGION_LSB 29
> +#define MMU_ADDR_KERNEL_REGION_MSB 31
> +#define MMU_ADDR_KERNEL_REGION_SZ 3
> +#define MMU_ADDR_KERNEL_REGION_MASK 0x7
> +#define MMU_ADDR_PAGE_OFFSET_LSB 0
> +#define MMU_ADDR_PAGE_OFFSET_MSB 11
> +#define MMU_ADDR_PAGE_OFFSET_SZ 12
> +#define MMU_ADDR_PAGE_OFFSET_MASK 0xfff
> +#define MMU_ADDR_PFN_LSB 12
> +#define MMU_ADDR_PFN_MSB 31
> +#define MMU_ADDR_PFN_SZ 20
> +#define MMU_ADDR_PFN_MASK 0xfffff
> +#define MMU_ADDR_USER_REGION 0x0
> +#define MMU_ADDR_USER_REGION_LSB 31
> +#define MMU_ADDR_USER_REGION_MSB 31
> +#define MMU_ADDR_USER_REGION_SZ 1
> +#define MMU_ADDR_USER_REGION_MASK 0x1
> +#define MMU_ADDR_VPN_LSB 12
> +#define MMU_ADDR_VPN_MSB 31
> +#define MMU_ADDR_VPN_SZ 20
> +#define MMU_ADDR_VPN_MASK 0xfffff
> +#define MPU_DATA_PERM_SUPER_NONE_USER_NONE 0
> +#define MPU_DATA_PERM_SUPER_RD_USER_NONE 1
> +#define MPU_DATA_PERM_SUPER_RD_USER_RD 2
> +#define MPU_DATA_PERM_SUPER_RW_USER_NONE 4
> +#define MPU_DATA_PERM_SUPER_RW_USER_RD 5
> +#define MPU_DATA_PERM_SUPER_RW_USER_RW 6
> +#define MPU_DATA_PERM_SZ 3
> +#define MPU_DATA_PERM_MASK 0x7
> +#define MPU_INST_PERM_SUPER_EXEC_USER_EXEC 2
> +#define MPU_INST_PERM_SUPER_EXEC_USER_NONE 1
> +#define MPU_INST_PERM_SUPER_NONE_USER_NONE 0
> +#define MPU_INST_PERM_SZ 2
> +#define MPU_INST_PERM_MASK 0x3
> +#define MPU_MAX_REGION_SIZE_LOG2 20
> +#define MPU_MAX_REGIONS 32
> +#define MPU_MIN_REGION_SIZE_LOG2 6
> +#define MPU_MIN_REGIONS 1
> +#define MPUACC_REG_C_LSB 5
> +#define MPUACC_REG_C_MSB 5
> +#define MPUACC_REG_C_SZ 1
> +#define MPUACC_REG_C_MASK 0x1
> +#define MPUACC_REG_LIMIT_LSB 6
> +#define MPUACC_REG_LIMIT_MSB 31
> +#define MPUACC_REG_LIMIT_SZ 26
> +#define MPUACC_REG_LIMIT_MASK 0x3ffffff
> +#define MPUACC_REG_LSB 0
> +#define MPUACC_REG_MASK_LSB 6
> +#define MPUACC_REG_MASK_MSB 30
> +#define MPUACC_REG_MASK_SZ 25
> +#define MPUACC_REG_MASK_MASK 0x1ffffff
> +#define MPUACC_REG_MSB 31
> +#define MPUACC_REG_PERM_LSB 2
> +#define MPUACC_REG_PERM_MSB 4
> +#define MPUACC_REG_PERM_SZ 3
> +#define MPUACC_REG_PERM_MASK 0x7
> +#define MPUACC_REG_RD_LSB 1
> +#define MPUACC_REG_RD_MSB 1
> +#define MPUACC_REG_RD_SZ 1
> +#define MPUACC_REG_RD_MASK 0x1
> +#define MPUACC_REG_REGNUM 15
> +#define MPUACC_REG_RSV1_LSB 31
> +#define MPUACC_REG_RSV1_MSB 31
> +#define MPUACC_REG_RSV1_SZ 1
> +#define MPUACC_REG_RSV1_MASK 0x1
> +#define MPUACC_REG_SZ 32
> +#define MPUACC_REG_MASK 0xffffffff
> +#define MPUACC_REG_WR_LSB 0
> +#define MPUACC_REG_WR_MSB 0
> +#define MPUACC_REG_WR_SZ 1
> +#define MPUACC_REG_WR_MASK 0x1
> +#define MPUBASE_REG_BASE_LSB 6
> +#define MPUBASE_REG_BASE_MSB 30
> +#define MPUBASE_REG_BASE_SZ 25
> +#define MPUBASE_REG_BASE_MASK 0x1ffffff
> +#define MPUBASE_REG_D_LSB 0
> +#define MPUBASE_REG_D_MSB 0
> +#define MPUBASE_REG_D_SZ 1
> +#define MPUBASE_REG_D_MASK 0x1
> +#define MPUBASE_REG_INDEX_LSB 1
> +#define MPUBASE_REG_INDEX_MSB 5
> +#define MPUBASE_REG_INDEX_SZ 5
> +#define MPUBASE_REG_INDEX_MASK 0x1f
> +#define MPUBASE_REG_LSB 0
> +#define MPUBASE_REG_MSB 31
> +#define MPUBASE_REG_REGNUM 14
> +#define MPUBASE_REG_RSV1_LSB 31
> +#define MPUBASE_REG_RSV1_MSB 31
> +#define MPUBASE_REG_RSV1_SZ 1
> +#define MPUBASE_REG_RSV1_MASK 0x1
> +#define MPUBASE_REG_SZ 32
> +#define MPUBASE_REG_MASK 0xffffffff
> +#define PTEADDR_REG_LSB 0
> +#define PTEADDR_REG_MSB 31
> +#define PTEADDR_REG_PTBASE_LSB 22
> +#define PTEADDR_REG_PTBASE_MSB 31
> +#define PTEADDR_REG_PTBASE_SZ 10
> +#define PTEADDR_REG_PTBASE_MASK 0x3ff
> +#define PTEADDR_REG_REGNUM 8
> +#define PTEADDR_REG_RSV_LSB 0
> +#define PTEADDR_REG_RSV_MSB 1
> +#define PTEADDR_REG_RSV_SZ 2
> +#define PTEADDR_REG_RSV_MASK 0x3
> +#define PTEADDR_REG_SZ 32
> +#define PTEADDR_REG_MASK 0xffffffff
> +#define PTEADDR_REG_VPN_LSB 2
> +#define PTEADDR_REG_VPN_MSB 21
> +#define PTEADDR_REG_VPN_SZ 20
> +#define PTEADDR_REG_VPN_MASK 0xfffff
> +#define REGNUM_SZ 5
> +#define REGNUM_MASK 0x1f
> +#define RETADDR_REGNUM 31
> +#define RF_ADDR_SZ 5
> +#define RF_ADDR_MASK 0x1f
> +#define RF_NUM_REG 32
> +#define SIM_REG_LSB 0
> +#define SIM_REG_MSB 2
> +#define SIM_REG_PERF_CNT_CLR_LSB 2
> +#define SIM_REG_PERF_CNT_CLR_MSB 2
> +#define SIM_REG_PERF_CNT_CLR_SZ 1
> +#define SIM_REG_PERF_CNT_CLR_MASK 0x1
> +#define SIM_REG_PERF_CNT_EN_LSB 1
> +#define SIM_REG_PERF_CNT_EN_MSB 1
> +#define SIM_REG_PERF_CNT_EN_SZ 1
> +#define SIM_REG_PERF_CNT_EN_MASK 0x1
> +#define SIM_REG_REGNUM 6
> +#define SIM_REG_SHOW_MMU_REGS_LSB 4
> +#define SIM_REG_SHOW_MMU_REGS_MSB 4
> +#define SIM_REG_SHOW_MMU_REGS_SZ 1
> +#define SIM_REG_SHOW_MMU_REGS_MASK 0x1
> +#define SIM_REG_SHOW_TLB_LSB 3
> +#define SIM_REG_SHOW_TLB_MSB 3
> +#define SIM_REG_SHOW_TLB_SZ 1
> +#define SIM_REG_SHOW_TLB_MASK 0x1
> +#define SIM_REG_STOP_LSB 0
> +#define SIM_REG_STOP_MSB 0
> +#define SIM_REG_STOP_SZ 1
> +#define SIM_REG_STOP_MASK 0x1
> +#define SIM_REG_SZ 3
> +#define SIM_REG_MASK 0x7
> +#define SP_REGNUM 27
> +#define STATUS_REG_EH_LSB 2
> +#define STATUS_REG_EH_MSB 2
> +#define STATUS_REG_EH_SZ 1
> +#define STATUS_REG_EH_MASK 0x1
> +#define STATUS_REG_LSB 0
> +#define STATUS_REG_MMU_LSB 0
> +#define STATUS_REG_MMU_MSB 2
> +#define STATUS_REG_MMU_RSV_LSB 3
> +#define STATUS_REG_MMU_RSV_MSB 31
> +#define STATUS_REG_MMU_RSV_SZ 29
> +#define STATUS_REG_MMU_RSV_MASK 0x1fffffff
> +#define STATUS_REG_MMU_SZ 3
> +#define STATUS_REG_MMU_MASK 0x7
> +#define STATUS_REG_MPU_LSB 0
> +#define STATUS_REG_MPU_MSB 1
> +#define STATUS_REG_MPU_RSV_LSB 2
> +#define STATUS_REG_MPU_RSV_MSB 31
> +#define STATUS_REG_MPU_RSV_SZ 30
> +#define STATUS_REG_MPU_RSV_MASK 0x3fffffff
> +#define STATUS_REG_MPU_SZ 2
> +#define STATUS_REG_MPU_MASK 0x3
> +#define STATUS_REG_MSB 2
> +#define STATUS_REG_NO_MMU_LSB 0
> +#define STATUS_REG_NO_MMU_MSB 0
> +#define STATUS_REG_NO_MMU_RSV_LSB 1
> +#define STATUS_REG_NO_MMU_RSV_MSB 31
> +#define STATUS_REG_NO_MMU_RSV_SZ 31
> +#define STATUS_REG_NO_MMU_RSV_MASK 0x7fffffff
> +#define STATUS_REG_NO_MMU_SZ 1
> +#define STATUS_REG_NO_MMU_MASK 0x1
> +#define STATUS_REG_PIE_LSB 0
> +#define STATUS_REG_PIE_MSB 0
> +#define STATUS_REG_PIE_SZ 1
> +#define STATUS_REG_PIE_MASK 0x1
> +#define STATUS_REG_REGNUM 0
> +#define STATUS_REG_SZ 3
> +#define STATUS_REG_MASK 0x7
> +#define STATUS_REG_U_LSB 1
> +#define STATUS_REG_U_MSB 1
> +#define STATUS_REG_U_SZ 1
> +#define STATUS_REG_U_MASK 0x1
> +#define TLB_MAX_ENTRIES 1024
> +#define TLB_MAX_LINES 128
> +#define TLB_MAX_PID_SZ 14
> +#define TLB_MAX_PID_MASK 0x3fff
> +#define TLB_MAX_PTR_SZ 10
> +#define TLB_MAX_PTR_MASK 0x3ff
> +#define TLB_MAX_WAYS 16
> +#define TLB_MIN_PID_SZ 8
> +#define TLB_MIN_PID_MASK 0xff
> +#define TLB_MIN_PTR_SZ 7
> +#define TLB_MIN_PTR_MASK 0x7f
> +#define TLB_MIN_WAYS 8
> +#define TLBACC_REG_C_LSB 24
> +#define TLBACC_REG_C_MSB 24
> +#define TLBACC_REG_C_SZ 1
> +#define TLBACC_REG_C_MASK 0x1
> +#define TLBACC_REG_G_LSB 20
> +#define TLBACC_REG_G_MSB 20
> +#define TLBACC_REG_G_SZ 1
> +#define TLBACC_REG_G_MASK 0x1
> +#define TLBACC_REG_IG_LSB 25
> +#define TLBACC_REG_IG_MSB 31
> +#define TLBACC_REG_IG_SZ 7
> +#define TLBACC_REG_IG_MASK 0x7f
> +#define TLBACC_REG_LSB 0
> +#define TLBACC_REG_MSB 24
> +#define TLBACC_REG_PFN_LSB 0
> +#define TLBACC_REG_PFN_MSB 19
> +#define TLBACC_REG_PFN_SZ 20
> +#define TLBACC_REG_PFN_MASK 0xfffff
> +#define TLBACC_REG_R_LSB 23
> +#define TLBACC_REG_R_MSB 23
> +#define TLBACC_REG_R_SZ 1
> +#define TLBACC_REG_R_MASK 0x1
> +#define TLBACC_REG_REGNUM 9
> +#define TLBACC_REG_SZ 25
> +#define TLBACC_REG_MASK 0x1ffffff
> +#define TLBACC_REG_W_LSB 22
> +#define TLBACC_REG_W_MSB 22
> +#define TLBACC_REG_W_SZ 1
> +#define TLBACC_REG_W_MASK 0x1
> +#define TLBACC_REG_X_LSB 21
> +#define TLBACC_REG_X_MSB 21
> +#define TLBACC_REG_X_SZ 1
> +#define TLBACC_REG_X_MASK 0x1
> +#define TLBMISC_REG_BAD_LSB 2
> +#define TLBMISC_REG_BAD_MSB 2
> +#define TLBMISC_REG_BAD_SZ 1
> +#define TLBMISC_REG_BAD_MASK 0x1
> +#define TLBMISC_REG_D_LSB 0
> +#define TLBMISC_REG_D_MSB 0
> +#define TLBMISC_REG_D_SZ 1
> +#define TLBMISC_REG_D_MASK 0x1
> +#define TLBMISC_REG_DBL_LSB 3
> +#define TLBMISC_REG_DBL_MSB 3
> +#define TLBMISC_REG_DBL_SZ 1
> +#define TLBMISC_REG_DBL_MASK 0x1
> +#define TLBMISC_REG_LSB 0
> +#define TLBMISC_REG_MSB 23
> +#define TLBMISC_REG_PERM_LSB 1
> +#define TLBMISC_REG_PERM_MSB 1
> +#define TLBMISC_REG_PERM_SZ 1
> +#define TLBMISC_REG_PERM_MASK 0x1
> +#define TLBMISC_REG_PID_LSB 4
> +#define TLBMISC_REG_PID_MSB 17
> +#define TLBMISC_REG_PID_SZ 14
> +#define TLBMISC_REG_PID_MASK 0x3fff
> +#define TLBMISC_REG_RD_LSB 19
> +#define TLBMISC_REG_RD_MSB 19
> +#define TLBMISC_REG_RD_SZ 1
> +#define TLBMISC_REG_RD_MASK 0x1
> +#define TLBMISC_REG_REGNUM 10
> +#define TLBMISC_REG_RSV1_LSB 24
> +#define TLBMISC_REG_RSV1_MSB 31
> +#define TLBMISC_REG_RSV1_SZ 8
> +#define TLBMISC_REG_RSV1_MASK 0xff
> +#define TLBMISC_REG_SZ 24
> +#define TLBMISC_REG_MASK 0xffffff
> +#define TLBMISC_REG_WAY_LSB 20
> +#define TLBMISC_REG_WAY_MSB 23
> +#define TLBMISC_REG_WAY_SZ 4
> +#define TLBMISC_REG_WAY_MASK 0xf
> +#define TLBMISC_REG_WE_LSB 18
> +#define TLBMISC_REG_WE_MSB 18
> +#define TLBMISC_REG_WE_SZ 1
> +#define TLBMISC_REG_WE_MASK 0x1
> +
> +/* Macros to extract instruction fields */
> +#define GET_IW_A(Iw) \
> + (((Iw) >> IW_A_LSB) & IW_A_MASK)
> +#define SET_IW_A(Iw, Val) \
> + Iw = (((Iw) & (~(IW_A_MASK << IW_A_LSB))) | \
> + (((Val) & IW_A_MASK) << IW_A_LSB))
> +#define GET_IW_B(Iw) \
> + (((Iw) >> IW_B_LSB) & IW_B_MASK)
> +#define SET_IW_B(Iw, Val) \
> + Iw = (((Iw) & (~(IW_B_MASK << IW_B_LSB))) | \
> + (((Val) & IW_B_MASK) << IW_B_LSB))
> +#define GET_IW_C(Iw) \
> + (((Iw) >> IW_C_LSB) & IW_C_MASK)
> +#define SET_IW_C(Iw, Val) \
> + Iw = (((Iw) & (~(IW_C_MASK << IW_C_LSB))) | \
> + (((Val) & IW_C_MASK) << IW_C_LSB))
> +#define GET_IW_CONTROL_REGNUM(Iw) \
> + (((Iw) >> IW_CONTROL_REGNUM_LSB) & IW_CONTROL_REGNUM_MASK)
> +#define SET_IW_CONTROL_REGNUM(Iw, Val) \
> + Iw = (((Iw) & (~(IW_CONTROL_REGNUM_MASK << IW_CONTROL_REGNUM_LSB))) | \
> + (((Val) & IW_CONTROL_REGNUM_MASK) << IW_CONTROL_REGNUM_LSB))
> +#define GET_IW_CUSTOM_N(Iw) \
> + (((Iw) >> IW_CUSTOM_N_LSB) & IW_CUSTOM_N_MASK)
> +#define SET_IW_CUSTOM_N(Iw, Val) \
> + Iw = (((Iw) & (~(IW_CUSTOM_N_MASK << IW_CUSTOM_N_LSB))) | \
> + (((Val) & IW_CUSTOM_N_MASK) << IW_CUSTOM_N_LSB))
> +#define GET_IW_CUSTOM_READRA(Iw) \
> + (((Iw) >> IW_CUSTOM_READRA_LSB) & IW_CUSTOM_READRA_MASK)
> +#define SET_IW_CUSTOM_READRA(Iw, Val) \
> + Iw = (((Iw) & (~(IW_CUSTOM_READRA_MASK << IW_CUSTOM_READRA_LSB))) | \
> + (((Val) & IW_CUSTOM_READRA_MASK) << IW_CUSTOM_READRA_LSB))
> +#define GET_IW_CUSTOM_READRB(Iw) \
> + (((Iw) >> IW_CUSTOM_READRB_LSB) & IW_CUSTOM_READRB_MASK)
> +#define SET_IW_CUSTOM_READRB(Iw, Val) \
> + Iw = (((Iw) & (~(IW_CUSTOM_READRB_MASK << IW_CUSTOM_READRB_LSB))) | \
> + (((Val) & IW_CUSTOM_READRB_MASK) << IW_CUSTOM_READRB_LSB))
> +#define GET_IW_CUSTOM_WRITERC(Iw) \
> + (((Iw) >> IW_CUSTOM_WRITERC_LSB) & IW_CUSTOM_WRITERC_MASK)
> +#define SET_IW_CUSTOM_WRITERC(Iw, Val) \
> + Iw = (((Iw) & (~(IW_CUSTOM_WRITERC_MASK << IW_CUSTOM_WRITERC_LSB))) | \
> + (((Val) & IW_CUSTOM_WRITERC_MASK) << IW_CUSTOM_WRITERC_LSB))
> +#define GET_IW_IMM16(Iw) \
> + (((Iw) >> IW_IMM16_LSB) & IW_IMM16_MASK)
> +#define SET_IW_IMM16(Iw, Val) \
> + Iw = (((Iw) & (~(IW_IMM16_MASK << IW_IMM16_LSB))) | \
> + (((Val) & IW_IMM16_MASK) << IW_IMM16_LSB))
> +#define GET_IW_IMM26(Iw) \
> + (((Iw) >> IW_IMM26_LSB) & IW_IMM26_MASK)
> +#define SET_IW_IMM26(Iw, Val) \
> + Iw = (((Iw) & (~(IW_IMM26_MASK << IW_IMM26_LSB))) | \
> + (((Val) & IW_IMM26_MASK) << IW_IMM26_LSB))
> +#define GET_IW_MEMSZ(Iw) \
> + (((Iw) >> IW_MEMSZ_LSB) & IW_MEMSZ_MASK)
> +#define SET_IW_MEMSZ(Iw, Val) \
> + Iw = (((Iw) & (~(IW_MEMSZ_MASK << IW_MEMSZ_LSB))) | \
> + (((Val) & IW_MEMSZ_MASK) << IW_MEMSZ_LSB))
> +#define GET_IW_OP(Iw) \
> + (((Iw) >> IW_OP_LSB) & IW_OP_MASK)
> +#define SET_IW_OP(Iw, Val) \
> + Iw = (((Iw) & (~(IW_OP_MASK << IW_OP_LSB))) | \
> + (((Val) & IW_OP_MASK) << IW_OP_LSB))
> +#define GET_IW_OPX(Iw) \
> + (((Iw) >> IW_OPX_LSB) & IW_OPX_MASK)
> +#define SET_IW_OPX(Iw, Val) \
> + Iw = (((Iw) & (~(IW_OPX_MASK << IW_OPX_LSB))) | \
> + (((Val) & IW_OPX_MASK) << IW_OPX_LSB))
> +#define GET_IW_SHIFT_IMM5(Iw) \
> + (((Iw) >> IW_SHIFT_IMM5_LSB) & IW_SHIFT_IMM5_MASK)
> +#define SET_IW_SHIFT_IMM5(Iw, Val) \
> + Iw = (((Iw) & (~(IW_SHIFT_IMM5_MASK << IW_SHIFT_IMM5_LSB))) | \
> + (((Val) & IW_SHIFT_IMM5_MASK) << IW_SHIFT_IMM5_LSB))
> +#define GET_IW_TRAP_BREAK_IMM5(Iw) \
> + (((Iw) >> IW_TRAP_BREAK_IMM5_LSB) & IW_TRAP_BREAK_IMM5_MASK)
> +#define SET_IW_TRAP_BREAK_IMM5(Iw, Val) \
> + Iw = (((Iw) & (~(IW_TRAP_BREAK_IMM5_MASK << IW_TRAP_BREAK_IMM5_LSB))) | \
> + (((Val) & IW_TRAP_BREAK_IMM5_MASK) << IW_TRAP_BREAK_IMM5_LSB))
> +
> +/* Macros to extract control register fields */
> +#define GET_BADADDR_REG_BADDR(Reg) \
> + (((Reg) >> BADADDR_REG_BADDR_LSB) & BADADDR_REG_BADDR_MASK)
> +#define SET_BADADDR_REG_BADDR(Reg, Val) \
> + Reg = (((Reg) & (~(BADADDR_REG_BADDR_MASK << BADADDR_REG_BADDR_LSB))) | \
> + (((Val) & BADADDR_REG_BADDR_MASK) << BADADDR_REG_BADDR_LSB))
> +#define GET_BSTATUS_REG_MMU(Reg) \
> + (((Reg) >> BSTATUS_REG_MMU_LSB) & BSTATUS_REG_MMU_MASK)
> +#define SET_BSTATUS_REG_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(BSTATUS_REG_MMU_MASK << BSTATUS_REG_MMU_LSB))) | \
> + (((Val) & BSTATUS_REG_MMU_MASK) << BSTATUS_REG_MMU_LSB))
> +#define GET_BSTATUS_REG_MPU(Reg) \
> + (((Reg) >> BSTATUS_REG_MPU_LSB) & BSTATUS_REG_MPU_MASK)
> +#define SET_BSTATUS_REG_MPU(Reg, Val) \
> + Reg = (((Reg) & (~(BSTATUS_REG_MPU_MASK << BSTATUS_REG_MPU_LSB))) | \
> + (((Val) & BSTATUS_REG_MPU_MASK) << BSTATUS_REG_MPU_LSB))
> +#define GET_BSTATUS_REG_NO_MMU(Reg) \
> + (((Reg) >> BSTATUS_REG_NO_MMU_LSB) & BSTATUS_REG_NO_MMU_MASK)
> +#define SET_BSTATUS_REG_NO_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(BSTATUS_REG_NO_MMU_MASK << BSTATUS_REG_NO_MMU_LSB))) | \
> + (((Val) & BSTATUS_REG_NO_MMU_MASK) << BSTATUS_REG_NO_MMU_LSB))
> +#define GET_CONFIG_REG_PE(Reg) \
> + (((Reg) >> CONFIG_REG_PE_LSB) & CONFIG_REG_PE_MASK)
> +#define SET_CONFIG_REG_PE(Reg, Val) \
> + Reg = (((Reg) & (~(CONFIG_REG_PE_MASK << CONFIG_REG_PE_LSB))) | \
> + (((Val) & CONFIG_REG_PE_MASK) << CONFIG_REG_PE_LSB))
> +#define GET_ESTATUS_REG_MMU(Reg) \
> + (((Reg) >> ESTATUS_REG_MMU_LSB) & ESTATUS_REG_MMU_MASK)
> +#define SET_ESTATUS_REG_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(ESTATUS_REG_MMU_MASK << ESTATUS_REG_MMU_LSB))) | \
> + (((Val) & ESTATUS_REG_MMU_MASK) << ESTATUS_REG_MMU_LSB))
> +#define GET_ESTATUS_REG_MPU(Reg) \
> + (((Reg) >> ESTATUS_REG_MPU_LSB) & ESTATUS_REG_MPU_MASK)
> +#define SET_ESTATUS_REG_MPU(Reg, Val) \
> + Reg = (((Reg) & (~(ESTATUS_REG_MPU_MASK << ESTATUS_REG_MPU_LSB))) | \
> + (((Val) & ESTATUS_REG_MPU_MASK) << ESTATUS_REG_MPU_LSB))
> +#define GET_ESTATUS_REG_NO_MMU(Reg) \
> + (((Reg) >> ESTATUS_REG_NO_MMU_LSB) & ESTATUS_REG_NO_MMU_MASK)
> +#define SET_ESTATUS_REG_NO_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(ESTATUS_REG_NO_MMU_MASK << ESTATUS_REG_NO_MMU_LSB))) | \
> + (((Val) & ESTATUS_REG_NO_MMU_MASK) << ESTATUS_REG_NO_MMU_LSB))
> +#define GET_EXCEPTION_REG_CAUSE(Reg) \
> + (((Reg) >> EXCEPTION_REG_CAUSE_LSB) & EXCEPTION_REG_CAUSE_MASK)
> +#define SET_EXCEPTION_REG_CAUSE(Reg, Val) \
> + Reg = (((Reg) & (~(EXCEPTION_REG_CAUSE_MASK << \
> + EXCEPTION_REG_CAUSE_LSB))) | \
> + (((Val) & EXCEPTION_REG_CAUSE_MASK) << EXCEPTION_REG_CAUSE_LSB))
> +#define GET_EXCEPTION_REG_MEA(Reg) \
> + (((Reg) >> EXCEPTION_REG_MEA_LSB) & EXCEPTION_REG_MEA_MASK)
> +#define SET_EXCEPTION_REG_MEA(Reg, Val) \
> + Reg = (((Reg) & (~(EXCEPTION_REG_MEA_MASK << EXCEPTION_REG_MEA_LSB))) | \
> + (((Val) & EXCEPTION_REG_MEA_MASK) << EXCEPTION_REG_MEA_LSB))
> +#define GET_EXCEPTION_REG_MEE(Reg) \
> + (((Reg) >> EXCEPTION_REG_MEE_LSB) & EXCEPTION_REG_MEE_MASK)
> +#define SET_EXCEPTION_REG_MEE(Reg, Val) \
> + Reg = (((Reg) & (~(EXCEPTION_REG_MEE_MASK << EXCEPTION_REG_MEE_LSB))) | \
> + (((Val) & EXCEPTION_REG_MEE_MASK) << EXCEPTION_REG_MEE_LSB))
> +#define GET_MPUACC_REG_C(Reg) \
> + (((Reg) >> MPUACC_REG_C_LSB) & MPUACC_REG_C_MASK)
> +#define SET_MPUACC_REG_C(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_C_MASK << MPUACC_REG_C_LSB))) | \
> + (((Val) & MPUACC_REG_C_MASK) << MPUACC_REG_C_LSB))
> +#define GET_MPUACC_REG_LIMIT(Reg) \
> + (((Reg) >> MPUACC_REG_LIMIT_LSB) & MPUACC_REG_LIMIT_MASK)
> +#define SET_MPUACC_REG_LIMIT(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_LIMIT_MASK << MPUACC_REG_LIMIT_LSB))) | \
> + (((Val) & MPUACC_REG_LIMIT_MASK) << MPUACC_REG_LIMIT_LSB))
> +#define GET_MPUACC_REG_MASK(Reg) \
> + (((Reg) >> MPUACC_REG_MASK_LSB) & MPUACC_REG_MASK_MASK)
> +#define SET_MPUACC_REG_MASK(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_MASK_MASK << MPUACC_REG_MASK_LSB))) | \
> + (((Val) & MPUACC_REG_MASK_MASK) << MPUACC_REG_MASK_LSB))
> +#define GET_MPUACC_REG_PERM(Reg) \
> + (((Reg) >> MPUACC_REG_PERM_LSB) & MPUACC_REG_PERM_MASK)
> +#define SET_MPUACC_REG_PERM(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_PERM_MASK << MPUACC_REG_PERM_LSB))) | \
> + (((Val) & MPUACC_REG_PERM_MASK) << MPUACC_REG_PERM_LSB))
> +#define GET_MPUACC_REG_RD(Reg) \
> + (((Reg) >> MPUACC_REG_RD_LSB) & MPUACC_REG_RD_MASK)
> +#define SET_MPUACC_REG_RD(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_RD_MASK << MPUACC_REG_RD_LSB))) | \
> + (((Val) & MPUACC_REG_RD_MASK) << MPUACC_REG_RD_LSB))
> +#define GET_MPUACC_REG_RSV1(Reg) \
> + (((Reg) >> MPUACC_REG_RSV1_LSB) & MPUACC_REG_RSV1_MASK)
> +#define SET_MPUACC_REG_RSV1(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_RSV1_MASK << MPUACC_REG_RSV1_LSB))) | \
> + (((Val) & MPUACC_REG_RSV1_MASK) << MPUACC_REG_RSV1_LSB))
> +#define GET_MPUACC_REG_WR(Reg) \
> + (((Reg) >> MPUACC_REG_WR_LSB) & MPUACC_REG_WR_MASK)
> +#define SET_MPUACC_REG_WR(Reg, Val) \
> + Reg = (((Reg) & (~(MPUACC_REG_WR_MASK << MPUACC_REG_WR_LSB))) | \
> + (((Val) & MPUACC_REG_WR_MASK) << MPUACC_REG_WR_LSB))
> +#define GET_MPUBASE_REG_BASE(Reg) \
> + (((Reg) >> MPUBASE_REG_BASE_LSB) & MPUBASE_REG_BASE_MASK)
> +#define SET_MPUBASE_REG_BASE(Reg, Val) \
> + Reg = (((Reg) & (~(MPUBASE_REG_BASE_MASK << MPUBASE_REG_BASE_LSB))) | \
> + (((Val) & MPUBASE_REG_BASE_MASK) << MPUBASE_REG_BASE_LSB))
> +#define GET_MPUBASE_REG_D(Reg) \
> + (((Reg) >> MPUBASE_REG_D_LSB) & MPUBASE_REG_D_MASK)
> +#define SET_MPUBASE_REG_D(Reg, Val) \
> + Reg = (((Reg) & (~(MPUBASE_REG_D_MASK << MPUBASE_REG_D_LSB))) | \
> + (((Val) & MPUBASE_REG_D_MASK) << MPUBASE_REG_D_LSB))
> +#define GET_MPUBASE_REG_INDEX(Reg) \
> + (((Reg) >> MPUBASE_REG_INDEX_LSB) & MPUBASE_REG_INDEX_MASK)
> +#define SET_MPUBASE_REG_INDEX(Reg, Val) \
> + Reg = (((Reg) & (~(MPUBASE_REG_INDEX_MASK << MPUBASE_REG_INDEX_LSB))) | \
> + (((Val) & MPUBASE_REG_INDEX_MASK) << MPUBASE_REG_INDEX_LSB))
> +#define GET_MPUBASE_REG_RSV1(Reg) \
> + (((Reg) >> MPUBASE_REG_RSV1_LSB) & MPUBASE_REG_RSV1_MASK)
> +#define SET_MPUBASE_REG_RSV1(Reg, Val) \
> + Reg = (((Reg) & (~(MPUBASE_REG_RSV1_MASK << MPUBASE_REG_RSV1_LSB))) | \
> + (((Val) & MPUBASE_REG_RSV1_MASK) << MPUBASE_REG_RSV1_LSB))
> +#define GET_PTEADDR_REG_PTBASE(Reg) \
> + (((Reg) >> PTEADDR_REG_PTBASE_LSB) & PTEADDR_REG_PTBASE_MASK)
> +#define SET_PTEADDR_REG_PTBASE(Reg, Val) \
> + Reg = (((Reg) & (~(PTEADDR_REG_PTBASE_MASK << PTEADDR_REG_PTBASE_LSB))) | \
> + (((Val) & PTEADDR_REG_PTBASE_MASK) << PTEADDR_REG_PTBASE_LSB))
> +#define GET_PTEADDR_REG_RSV(Reg) \
> + (((Reg) >> PTEADDR_REG_RSV_LSB) & PTEADDR_REG_RSV_MASK)
> +#define SET_PTEADDR_REG_RSV(Reg, Val) \
> + Reg = (((Reg) & (~(PTEADDR_REG_RSV_MASK << PTEADDR_REG_RSV_LSB))) | \
> + (((Val) & PTEADDR_REG_RSV_MASK) << PTEADDR_REG_RSV_LSB))
> +#define GET_PTEADDR_REG_VPN(Reg) \
> + (((Reg) >> PTEADDR_REG_VPN_LSB) & PTEADDR_REG_VPN_MASK)
> +#define SET_PTEADDR_REG_VPN(Reg, Val) \
> + Reg = (((Reg) & (~(PTEADDR_REG_VPN_MASK << PTEADDR_REG_VPN_LSB))) | \
> + (((Val) & PTEADDR_REG_VPN_MASK) << PTEADDR_REG_VPN_LSB))
> +#define GET_SIM_REG_PERF_CNT_CLR(Reg) \
> + (((Reg) >> SIM_REG_PERF_CNT_CLR_LSB) & SIM_REG_PERF_CNT_CLR_MASK)
> +#define SET_SIM_REG_PERF_CNT_CLR(Reg, Val) \
> + Reg = (((Reg) & (~(SIM_REG_PERF_CNT_CLR_MASK << \
> + SIM_REG_PERF_CNT_CLR_LSB))) | \
> + (((Val) & SIM_REG_PERF_CNT_CLR_MASK) << SIM_REG_PERF_CNT_CLR_LSB))
> +#define GET_SIM_REG_PERF_CNT_EN(Reg) \
> + (((Reg) >> SIM_REG_PERF_CNT_EN_LSB) & SIM_REG_PERF_CNT_EN_MASK)
> +#define SET_SIM_REG_PERF_CNT_EN(Reg, Val) \
> + Reg = (((Reg) & (~(SIM_REG_PERF_CNT_EN_MASK << \
> + SIM_REG_PERF_CNT_EN_LSB))) | \
> + (((Val) & SIM_REG_PERF_CNT_EN_MASK) << SIM_REG_PERF_CNT_EN_LSB))
> +#define GET_SIM_REG_SHOW_MMU_REGS(Reg) \
> + (((Reg) >> SIM_REG_SHOW_MMU_REGS_LSB) & SIM_REG_SHOW_MMU_REGS_MASK)
> +#define SET_SIM_REG_SHOW_MMU_REGS(Reg, Val) \
> + Reg = (((Reg) & (~(SIM_REG_SHOW_MMU_REGS_MASK << \
> + SIM_REG_SHOW_MMU_REGS_LSB))) | \
> + (((Val) & SIM_REG_SHOW_MMU_REGS_MASK) << SIM_REG_SHOW_MMU_REGS_LSB))
> +#define GET_SIM_REG_SHOW_TLB(Reg) \
> + (((Reg) >> SIM_REG_SHOW_TLB_LSB) & SIM_REG_SHOW_TLB_MASK)
> +#define SET_SIM_REG_SHOW_TLB(Reg, Val) \
> + Reg = (((Reg) & (~(SIM_REG_SHOW_TLB_MASK << SIM_REG_SHOW_TLB_LSB))) | \
> + (((Val) & SIM_REG_SHOW_TLB_MASK) << SIM_REG_SHOW_TLB_LSB))
> +#define GET_SIM_REG_STOP(Reg) \
> + (((Reg) >> SIM_REG_STOP_LSB) & SIM_REG_STOP_MASK)
> +#define SET_SIM_REG_STOP(Reg, Val) \
> + Reg = (((Reg) & (~(SIM_REG_STOP_MASK << SIM_REG_STOP_LSB))) | \
> + (((Val) & SIM_REG_STOP_MASK) << SIM_REG_STOP_LSB))
> +#define GET_STATUS_REG_EH(Reg) \
> + (((Reg) >> STATUS_REG_EH_LSB) & STATUS_REG_EH_MASK)
> +#define SET_STATUS_REG_EH(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_EH_MASK << STATUS_REG_EH_LSB))) | \
> + (((Val) & STATUS_REG_EH_MASK) << STATUS_REG_EH_LSB))
> +#define GET_STATUS_REG_MMU(Reg) \
> + (((Reg) >> STATUS_REG_MMU_LSB) & STATUS_REG_MMU_MASK)
> +#define SET_STATUS_REG_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_MMU_MASK << STATUS_REG_MMU_LSB))) | \
> + (((Val) & STATUS_REG_MMU_MASK) << STATUS_REG_MMU_LSB))
> +#define GET_STATUS_REG_MMU_RSV(Reg) \
> + (((Reg) >> STATUS_REG_MMU_RSV_LSB) & STATUS_REG_MMU_RSV_MASK)
> +#define SET_STATUS_REG_MMU_RSV(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_MMU_RSV_MASK << STATUS_REG_MMU_RSV_LSB))) | \
> + (((Val) & STATUS_REG_MMU_RSV_MASK) << STATUS_REG_MMU_RSV_LSB))
> +#define GET_STATUS_REG_MPU(Reg) \
> + (((Reg) >> STATUS_REG_MPU_LSB) & STATUS_REG_MPU_MASK)
> +#define SET_STATUS_REG_MPU(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_MPU_MASK << STATUS_REG_MPU_LSB))) | \
> + (((Val) & STATUS_REG_MPU_MASK) << STATUS_REG_MPU_LSB))
> +#define GET_STATUS_REG_MPU_RSV(Reg) \
> + (((Reg) >> STATUS_REG_MPU_RSV_LSB) & STATUS_REG_MPU_RSV_MASK)
> +#define SET_STATUS_REG_MPU_RSV(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_MPU_RSV_MASK << STATUS_REG_MPU_RSV_LSB))) | \
> + (((Val) & STATUS_REG_MPU_RSV_MASK) << STATUS_REG_MPU_RSV_LSB))
> +#define GET_STATUS_REG_NO_MMU(Reg) \
> + (((Reg) >> STATUS_REG_NO_MMU_LSB) & STATUS_REG_NO_MMU_MASK)
> +#define SET_STATUS_REG_NO_MMU(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_NO_MMU_MASK << STATUS_REG_NO_MMU_LSB))) | \
> + (((Val) & STATUS_REG_NO_MMU_MASK) << STATUS_REG_NO_MMU_LSB))
> +#define GET_STATUS_REG_NO_MMU_RSV(Reg) \
> + (((Reg) >> STATUS_REG_NO_MMU_RSV_LSB) & STATUS_REG_NO_MMU_RSV_MASK)
> +#define SET_STATUS_REG_NO_MMU_RSV(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_NO_MMU_RSV_MASK << \
> + STATUS_REG_NO_MMU_RSV_LSB))) | \
> + (((Val) & STATUS_REG_NO_MMU_RSV_MASK) << STATUS_REG_NO_MMU_RSV_LSB))
> +#define GET_STATUS_REG_PIE(Reg) \
> + (((Reg) >> STATUS_REG_PIE_LSB) & STATUS_REG_PIE_MASK)
> +#define SET_STATUS_REG_PIE(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_PIE_MASK << STATUS_REG_PIE_LSB))) | \
> + (((Val) & STATUS_REG_PIE_MASK) << STATUS_REG_PIE_LSB))
> +#define GET_STATUS_REG_U(Reg) \
> + (((Reg) >> STATUS_REG_U_LSB) & STATUS_REG_U_MASK)
> +#define SET_STATUS_REG_U(Reg, Val) \
> + Reg = (((Reg) & (~(STATUS_REG_U_MASK << STATUS_REG_U_LSB))) | \
> + (((Val) & STATUS_REG_U_MASK) << STATUS_REG_U_LSB))
> +#define GET_TLBACC_REG_C(Reg) \
> + (((Reg) >> TLBACC_REG_C_LSB) & TLBACC_REG_C_MASK)
> +#define SET_TLBACC_REG_C(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_C_MASK << TLBACC_REG_C_LSB))) | \
> + (((Val) & TLBACC_REG_C_MASK) << TLBACC_REG_C_LSB))
> +#define GET_TLBACC_REG_G(Reg) \
> + (((Reg) >> TLBACC_REG_G_LSB) & TLBACC_REG_G_MASK)
> +#define SET_TLBACC_REG_G(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_G_MASK << TLBACC_REG_G_LSB))) | \
> + (((Val) & TLBACC_REG_G_MASK) << TLBACC_REG_G_LSB))
> +#define GET_TLBACC_REG_IG(Reg) \
> + (((Reg) >> TLBACC_REG_IG_LSB) & TLBACC_REG_IG_MASK)
> +#define SET_TLBACC_REG_IG(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_IG_MASK << TLBACC_REG_IG_LSB))) | \
> + (((Val) & TLBACC_REG_IG_MASK) << TLBACC_REG_IG_LSB))
> +#define GET_TLBACC_REG_PFN(Reg) \
> + (((Reg) >> TLBACC_REG_PFN_LSB) & TLBACC_REG_PFN_MASK)
> +#define SET_TLBACC_REG_PFN(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_PFN_MASK << TLBACC_REG_PFN_LSB))) | \
> + (((Val) & TLBACC_REG_PFN_MASK) << TLBACC_REG_PFN_LSB))
> +#define GET_TLBACC_REG_R(Reg) \
> + (((Reg) >> TLBACC_REG_R_LSB) & TLBACC_REG_R_MASK)
> +#define SET_TLBACC_REG_R(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_R_MASK << TLBACC_REG_R_LSB))) | \
> + (((Val) & TLBACC_REG_R_MASK) << TLBACC_REG_R_LSB))
> +#define GET_TLBACC_REG_W(Reg) \
> + (((Reg) >> TLBACC_REG_W_LSB) & TLBACC_REG_W_MASK)
> +#define SET_TLBACC_REG_W(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_W_MASK << TLBACC_REG_W_LSB))) | \
> + (((Val) & TLBACC_REG_W_MASK) << TLBACC_REG_W_LSB))
> +#define GET_TLBACC_REG_X(Reg) \
> + (((Reg) >> TLBACC_REG_X_LSB) & TLBACC_REG_X_MASK)
> +#define SET_TLBACC_REG_X(Reg, Val) \
> + Reg = (((Reg) & (~(TLBACC_REG_X_MASK << TLBACC_REG_X_LSB))) | \
> + (((Val) & TLBACC_REG_X_MASK) << TLBACC_REG_X_LSB))
> +#define GET_TLBMISC_REG_BAD(Reg) \
> + (((Reg) >> TLBMISC_REG_BAD_LSB) & TLBMISC_REG_BAD_MASK)
> +#define SET_TLBMISC_REG_BAD(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_BAD_MASK << TLBMISC_REG_BAD_LSB))) | \
> + (((Val) & TLBMISC_REG_BAD_MASK) << TLBMISC_REG_BAD_LSB))
> +#define GET_TLBMISC_REG_D(Reg) \
> + (((Reg) >> TLBMISC_REG_D_LSB) & TLBMISC_REG_D_MASK)
> +#define SET_TLBMISC_REG_D(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_D_MASK << TLBMISC_REG_D_LSB))) | \
> + (((Val) & TLBMISC_REG_D_MASK) << TLBMISC_REG_D_LSB))
> +#define GET_TLBMISC_REG_DBL(Reg) \
> + (((Reg) >> TLBMISC_REG_DBL_LSB) & TLBMISC_REG_DBL_MASK)
> +#define SET_TLBMISC_REG_DBL(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_DBL_MASK << TLBMISC_REG_DBL_LSB))) | \
> + (((Val) & TLBMISC_REG_DBL_MASK) << TLBMISC_REG_DBL_LSB))
> +#define GET_TLBMISC_REG_PERM(Reg) \
> + (((Reg) >> TLBMISC_REG_PERM_LSB) & TLBMISC_REG_PERM_MASK)
> +#define SET_TLBMISC_REG_PERM(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_PERM_MASK << TLBMISC_REG_PERM_LSB))) | \
> + (((Val) & TLBMISC_REG_PERM_MASK) << TLBMISC_REG_PERM_LSB))
> +#define GET_TLBMISC_REG_PID(Reg) \
> + (((Reg) >> TLBMISC_REG_PID_LSB) & TLBMISC_REG_PID_MASK)
> +#define SET_TLBMISC_REG_PID(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_PID_MASK << TLBMISC_REG_PID_LSB))) | \
> + (((Val) & TLBMISC_REG_PID_MASK) << TLBMISC_REG_PID_LSB))
> +#define GET_TLBMISC_REG_RD(Reg) \
> + (((Reg) >> TLBMISC_REG_RD_LSB) & TLBMISC_REG_RD_MASK)
> +#define SET_TLBMISC_REG_RD(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_RD_MASK << TLBMISC_REG_RD_LSB))) | \
> + (((Val) & TLBMISC_REG_RD_MASK) << TLBMISC_REG_RD_LSB))
> +#define GET_TLBMISC_REG_RSV1(Reg) \
> + (((Reg) >> TLBMISC_REG_RSV1_LSB) & TLBMISC_REG_RSV1_MASK)
> +#define SET_TLBMISC_REG_RSV1(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_RSV1_MASK << TLBMISC_REG_RSV1_LSB))) | \
> + (((Val) & TLBMISC_REG_RSV1_MASK) << TLBMISC_REG_RSV1_LSB))
> +#define GET_TLBMISC_REG_WAY(Reg) \
> + (((Reg) >> TLBMISC_REG_WAY_LSB) & TLBMISC_REG_WAY_MASK)
> +#define SET_TLBMISC_REG_WAY(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_WAY_MASK << TLBMISC_REG_WAY_LSB))) | \
> + (((Val) & TLBMISC_REG_WAY_MASK) << TLBMISC_REG_WAY_LSB))
> +#define GET_TLBMISC_REG_WE(Reg) \
> + (((Reg) >> TLBMISC_REG_WE_LSB) & TLBMISC_REG_WE_MASK)
> +#define SET_TLBMISC_REG_WE(Reg, Val) \
> + Reg = (((Reg) & (~(TLBMISC_REG_WE_MASK << TLBMISC_REG_WE_LSB))) | \
> + (((Val) & TLBMISC_REG_WE_MASK) << TLBMISC_REG_WE_LSB))
> +
> +/* Macros to extract MMU fields */
> +#define GET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Addr) \
> + (((Addr) >> MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB) & \
> + MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK)
> +#define SET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK << \
> + MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB))) | \
> + (((Val) & MMU_ADDR_BYPASS_TLB_CACHEABLE_MASK) << \
> + MMU_ADDR_BYPASS_TLB_CACHEABLE_LSB))
> +#define GET_MMU_ADDR_BYPASS_TLB(Addr) \
> + (((Addr) >> MMU_ADDR_BYPASS_TLB_LSB) & MMU_ADDR_BYPASS_TLB_MASK)
> +#define SET_MMU_ADDR_BYPASS_TLB(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_MASK << \
> + MMU_ADDR_BYPASS_TLB_LSB))) | \
> + (((Val) & MMU_ADDR_BYPASS_TLB_MASK) << MMU_ADDR_BYPASS_TLB_LSB))
> +#define GET_MMU_ADDR_BYPASS_TLB_PADDR(Addr) \
> + (((Addr) >> MMU_ADDR_BYPASS_TLB_PADDR_LSB) & MMU_ADDR_BYPASS_TLB_PADDR_MASK)
> +#define SET_MMU_ADDR_BYPASS_TLB_PADDR(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_BYPASS_TLB_PADDR_MASK << \
> + MMU_ADDR_BYPASS_TLB_PADDR_LSB))) | \
> + (((Val) & MMU_ADDR_BYPASS_TLB_PADDR_MASK) << \
> + MMU_ADDR_BYPASS_TLB_PADDR_LSB))
> +#define GET_MMU_ADDR_IO_REGION(Addr) \
> + (((Addr) >> MMU_ADDR_IO_REGION_LSB) & MMU_ADDR_IO_REGION_MASK)
> +#define SET_MMU_ADDR_IO_REGION(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_IO_REGION_MASK << \
> + MMU_ADDR_IO_REGION_LSB))) | \
> + (((Val) & MMU_ADDR_IO_REGION_MASK) << MMU_ADDR_IO_REGION_LSB))
> +#define GET_MMU_ADDR_KERNEL_MMU_REGION(Addr) \
> + (((Addr) >> MMU_ADDR_KERNEL_MMU_REGION_LSB) & \
> + MMU_ADDR_KERNEL_MMU_REGION_MASK)
> +#define SET_MMU_ADDR_KERNEL_MMU_REGION(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_KERNEL_MMU_REGION_MASK << \
> + MMU_ADDR_KERNEL_MMU_REGION_LSB))) | \
> + (((Val) & MMU_ADDR_KERNEL_MMU_REGION_MASK) << \
> + MMU_ADDR_KERNEL_MMU_REGION_LSB))
> +#define GET_MMU_ADDR_KERNEL_REGION(Addr) \
> + (((Addr) >> MMU_ADDR_KERNEL_REGION_LSB) & MMU_ADDR_KERNEL_REGION_MASK)
> +#define SET_MMU_ADDR_KERNEL_REGION(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_KERNEL_REGION_MASK << \
> + MMU_ADDR_KERNEL_REGION_LSB))) | \
> + (((Val) & MMU_ADDR_KERNEL_REGION_MASK) << MMU_ADDR_KERNEL_REGION_LSB))
> +#define GET_MMU_ADDR_PAGE_OFFSET(Addr) \
> + (((Addr) >> MMU_ADDR_PAGE_OFFSET_LSB) & MMU_ADDR_PAGE_OFFSET_MASK)
> +#define SET_MMU_ADDR_PAGE_OFFSET(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_PAGE_OFFSET_MASK << \
> + MMU_ADDR_PAGE_OFFSET_LSB))) | \
> + (((Val) & MMU_ADDR_PAGE_OFFSET_MASK) << MMU_ADDR_PAGE_OFFSET_LSB))
> +#define GET_MMU_ADDR_PFN(Addr) \
> + (((Addr) >> MMU_ADDR_PFN_LSB) & MMU_ADDR_PFN_MASK)
> +#define SET_MMU_ADDR_PFN(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_PFN_MASK << MMU_ADDR_PFN_LSB))) | \
> + (((Val) & MMU_ADDR_PFN_MASK) << MMU_ADDR_PFN_LSB))
> +#define GET_MMU_ADDR_USER_REGION(Addr) \
> + (((Addr) >> MMU_ADDR_USER_REGION_LSB) & MMU_ADDR_USER_REGION_MASK)
> +#define SET_MMU_ADDR_USER_REGION(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_USER_REGION_MASK << \
> + MMU_ADDR_USER_REGION_LSB))) | \
> + (((Val) & MMU_ADDR_USER_REGION_MASK) << MMU_ADDR_USER_REGION_LSB))
> +#define GET_MMU_ADDR_VPN(Addr) \
> + (((Addr) >> MMU_ADDR_VPN_LSB) & MMU_ADDR_VPN_MASK)
> +#define SET_MMU_ADDR_VPN(Addr, Val) \
> + Addr = (((Addr) & (~(MMU_ADDR_VPN_MASK << MMU_ADDR_VPN_LSB))) | \
> + (((Val) & MMU_ADDR_VPN_MASK) << MMU_ADDR_VPN_LSB))
> +
> +/* OP instruction values */
> +#define OP_ADDI 4
> +#define OP_ANDHI 44
> +#define OP_ANDI 12
> +#define OP_BEQ 38
> +#define OP_BGE 14
> +#define OP_BGEU 46
> +#define OP_BLT 22
> +#define OP_BLTU 54
> +#define OP_BNE 30
> +#define OP_BR 6
> +#define OP_CALL 0
> +#define OP_CMPEQI 32
> +#define OP_CMPGEI 8
> +#define OP_CMPGEUI 40
> +#define OP_CMPLTI 16
> +#define OP_CMPLTUI 48
> +#define OP_CMPNEI 24
> +#define OP_CUSTOM 50
> +#define OP_FLUSHD 59
> +#define OP_FLUSHDA 27
> +#define OP_INITD 51
> +#define OP_INITDA 19
> +#define OP_JMPI 1
> +#define OP_LDB 7
> +#define OP_LDBIO 39
> +#define OP_LDBU 3
> +#define OP_LDBUIO 35
> +#define OP_LDH 15
> +#define OP_LDHIO 47
> +#define OP_LDHU 11
> +#define OP_LDHUIO 43
> +#define OP_LDL 31
> +#define OP_LDW 23
> +#define OP_LDWIO 55
> +#define OP_MULI 36
> +#define OP_OPX 58
> +#define OP_ORHI 52
> +#define OP_ORI 20
> +#define OP_STB 5
> +#define OP_STBIO 37
> +#define OP_STC 29
> +#define OP_STH 13
> +#define OP_STHIO 45
> +#define OP_STW 21
> +#define OP_STWIO 53
> +#define OP_XORHI 60
> +#define OP_XORI 28
> +
> +/* OPX instruction values */
> +#define OPX_ADD 49
> +#define OPX_AND 14
> +#define OPX_BREAK 52
> +#define OPX_BRET 9
> +#define OPX_CALLR 29
> +#define OPX_CMPEQ 32
> +#define OPX_CMPGE 8
> +#define OPX_CMPGEU 40
> +#define OPX_CMPLT 16
> +#define OPX_CMPLTU 48
> +#define OPX_CMPNE 24
> +#define OPX_CRST 62
> +#define OPX_DIV 37
> +#define OPX_DIVU 36
> +#define OPX_ERET 1
> +#define OPX_FLUSHI 12
> +#define OPX_FLUSHP 4
> +#define OPX_HBREAK 53
> +#define OPX_INITI 41
> +#define OPX_INTR 61
> +#define OPX_JMP 13
> +#define OPX_MUL 39
> +#define OPX_MULXSS 31
> +#define OPX_MULXSU 23
> +#define OPX_MULXUU 7
> +#define OPX_NEXTPC 28
> +#define OPX_NOR 6
> +#define OPX_OR 22
> +#define OPX_RDCTL 38
> +#define OPX_RET 5
> +#define OPX_ROL 3
> +#define OPX_ROLI 2
> +#define OPX_ROR 11
> +#define OPX_SLL 19
> +#define OPX_SLLI 18
> +#define OPX_SRA 59
> +#define OPX_SRAI 58
> +#define OPX_SRL 27
> +#define OPX_SRLI 26
> +#define OPX_SUB 57
> +#define OPX_SYNC 54
> +#define OPX_TRAP 45
> +#define OPX_WRCTL 46
> +#define OPX_XOR 30
> +
> +/* Macros to detect sub-opcode instructions */
> +#define IS_OPX_INST(Iw) (GET_IW_OP(Iw) == OP_OPX)
> +#define IS_CUSTOM_INST(Iw) (GET_IW_OP(Iw) == OP_CUSTOM)
> +
> +/* Instruction property macros */
> +#define IW_PROP_RESERVED_OP(Iw) (0)
> +
> +#define IW_PROP_RESERVED_OPX(Iw) (0)
> +
> +#define IW_PROP_RESERVED(Iw) (0)
> +
> +#define IW_PROP_SUPERVISOR_ONLY(Iw) ( \
> + (op_prop_supervisor_only[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_supervisor_only[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_supervisor_only[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_supervisor_only[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_INITI_FLUSHI(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_INITI) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_FLUSHI) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_FLUSH_PIPE(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_flush_pipe[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_flush_pipe[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_JMP_INDIRECT_NON_TRAP(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_jmp_indirect_non_trap[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_jmp_indirect_non_trap[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_JMP_INDIRECT(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_jmp_indirect[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_jmp_indirect[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_JMP_DIRECT(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_CALL)) || \
> + ((GET_IW_OP((Iw)) == OP_JMPI)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_MUL_LSW(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_MULI)) || \
> + ((GET_IW_OPX((Iw)) == OPX_MUL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_MULX(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_mulx[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_mulx[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_MUL(Iw) ( \
> + (op_prop_mul[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_mul[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_mul[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_mul[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_DIV_UNSIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_DIVU) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_DIV_SIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_DIV) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_DIV(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_DIVU) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_DIV) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_UNIMPLEMENTED(Iw) (0)
> +
> +#define IW_PROP_ILLEGAL(Iw) (0)
> +
> +#define IW_PROP_IMPLICIT_DST_RETADDR(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_CALL)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_IMPLICIT_DST_ERETADDR(Iw) (0)
> +
> +#define IW_PROP_INTR(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_INTR) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_EXCEPTION(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_TRAP) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BREAK(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_BREAK) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_CRST(Iw) (0)
> +
> +#define IW_PROP_WR_CTL_REG(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_wr_ctl_reg[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_wr_ctl_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_UNCOND_CTI_NON_BR(Iw) ( \
> + (op_prop_uncond_cti_non_br[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_uncond_cti_non_br[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_uncond_cti_non_br[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_uncond_cti_non_br[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_RETADDR(Iw) ( \
> + (op_prop_retaddr[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_retaddr[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_retaddr[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_retaddr[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SHIFT_LEFT(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_SLLI) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_SLL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_SHIFT_LOGICAL(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_logical[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_logical[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ROT_LEFT(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_ROLI) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_ROL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_SHIFT_ROT_LEFT(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_rot_left[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_rot_left[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SHIFT_RIGHT_LOGICAL(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_SRLI) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_SRL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_SHIFT_RIGHT_ARITH(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_SRAI) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_SRA) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_SHIFT_RIGHT(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_right[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_right[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ROT_RIGHT(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_ROR) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_SHIFT_ROT_RIGHT(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_rot_right[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_rot_right[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SHIFT_ROT(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_rot[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_rot[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SHIFT_ROT_IMM(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_shift_rot_imm[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_shift_rot_imm[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ROTATE(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_rotate[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_rotate[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOGIC_REG(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_logic_reg[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_logic_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOGIC_HI_IMM16(Iw) ( \
> + (op_prop_logic_hi_imm16[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_logic_hi_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOGIC_LO_IMM16(Iw) ( \
> + (op_prop_logic_lo_imm16[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_logic_lo_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOGIC_IMM16(Iw) ( \
> + (op_prop_logic_imm16[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_logic_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOGIC(Iw) ( \
> + (op_prop_logic[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_logic[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_logic[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_logic[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_UNSIGNED_LO_IMM16(Iw) ( \
> + (op_prop_unsigned_lo_imm16[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_unsigned_lo_imm16[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_unsigned_lo_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_unsigned_lo_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ARITH_IMM16(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_ADDI)) || \
> + ((GET_IW_OP((Iw)) == OP_MULI)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_CMP_IMM16(Iw) ( \
> + (op_prop_cmp_imm16[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_imm16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_JMPI(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_JMPI)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_CMP_IMM16_WITH_CALL_JMPI(Iw) ( \
> + (op_prop_cmp_imm16_with_call_jmpi[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_imm16_with_call_jmpi[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP_REG(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_cmp_reg[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SRC_IMM5(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_src_imm5[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_src_imm5[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP_WITH_LT(Iw) ( \
> + (op_prop_cmp_with_lt[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_cmp_with_lt[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_with_lt[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp_with_lt[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP_WITH_EQ(Iw) ( \
> + (op_prop_cmp_with_eq[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_cmp_with_eq[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_with_eq[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp_with_eq[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP_WITH_GE(Iw) ( \
> + (op_prop_cmp_with_ge[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_cmp_with_ge[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_with_ge[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp_with_ge[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP_WITH_NE(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_CMPNEI)) || \
> + ((GET_IW_OPX((Iw)) == OPX_CMPNE) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_CMP_ALU_SIGNED(Iw) ( \
> + (op_prop_cmp_alu_signed[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_cmp_alu_signed[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp_alu_signed[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp_alu_signed[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_CMP(Iw) ( \
> + (op_prop_cmp[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_cmp[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_cmp[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_cmp[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_BR_WITH_LT(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_BLT)) || \
> + ((GET_IW_OP((Iw)) == OP_BLTU)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BR_WITH_GE(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_BGE)) || \
> + ((GET_IW_OP((Iw)) == OP_BGEU)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BR_WITH_EQ(Iw) ( \
> + (op_prop_br_with_eq[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_br_with_eq[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_BR_WITH_NE(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_BNE)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BR_ALU_SIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_BGE)) || \
> + ((GET_IW_OP((Iw)) == OP_BLT)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BR_COND(Iw) ( \
> + (op_prop_br_cond[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_br_cond[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_BR_UNCOND(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_BR)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_BR(Iw) ( \
> + (op_prop_br[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_br[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ALU_SUB(Iw) ( \
> + (op_prop_alu_sub[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_alu_sub[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_alu_sub[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_alu_sub[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_FORCE_XOR(Iw) ( \
> + (op_prop_force_xor[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_force_xor[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_force_xor[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_force_xor[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD8(Iw) ( \
> + (op_prop_load8[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load8[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD16(Iw) ( \
> + (op_prop_load16[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load16[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD32(Iw) ( \
> + (op_prop_load32[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load32[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD_SIGNED(Iw) ( \
> + (op_prop_load_signed[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load_signed[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD_UNSIGNED(Iw) ( \
> + (op_prop_load_unsigned[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load_unsigned[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD(Iw) ( \
> + (op_prop_load[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_DCACHE_MANAGEMENT_NOP(Iw) ( \
> + (op_prop_dcache_management_nop[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_dcache_management_nop[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD_DCACHE_MANAGEMENT(Iw) ( \
> + (op_prop_load_dcache_management[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load_dcache_management[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD_NON_IO(Iw) ( \
> + (op_prop_load_non_io[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load_non_io[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_STORE8(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_STB)) || \
> + ((GET_IW_OP((Iw)) == OP_STBIO)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_STORE16(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_STH)) || \
> + ((GET_IW_OP((Iw)) == OP_STHIO)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_STORE32(Iw) ( \
> + (op_prop_store32[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_store32[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_STORE(Iw) ( \
> + (op_prop_store[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_store[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_STORE_NON_IO(Iw) ( \
> + (op_prop_store_non_io[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_store_non_io[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_MEM(Iw) ( \
> + (op_prop_mem[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_mem[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_INITD(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_INITD)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_INITDA(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_INITDA)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_FLUSHD(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_FLUSHD)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_FLUSHDA(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_INITD_FLUSHD(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_INITD)) || \
> + ((GET_IW_OP((Iw)) == OP_FLUSHD)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_INITDA_FLUSHDA(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_INITDA)) || \
> + ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_INITD_INITDA(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_INITD)) || \
> + ((GET_IW_OP((Iw)) == OP_INITDA)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_FLUSHD_FLUSHDA(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_FLUSHD)) || \
> + ((GET_IW_OP((Iw)) == OP_FLUSHDA)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_INITD_FLUSHD_FLUSHDA(Iw) ( \
> + (op_prop_initd_flushd_flushda[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_initd_flushd_flushda[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_DCACHE_MANAGEMENT(Iw) ( \
> + (op_prop_dcache_management[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_dcache_management[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_LOAD_IO(Iw) ( \
> + (op_prop_load_io[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_load_io[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_STORE_IO(Iw) ( \
> + (op_prop_store_io[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_store_io[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_MEM_IO(Iw) ( \
> + (op_prop_mem_io[GET_IW_OP(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_mem_io[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_ARITH(Iw) ( \
> + (op_prop_arith[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_arith[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_arith[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_arith[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_A_NOT_SRC(Iw) ( \
> + ( \
> + ((GET_IW_OP((Iw)) == OP_CALL)) || \
> + ((GET_IW_OP((Iw)) == OP_JMPI)) \
> + ) \
> + || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_READRA(Iw)) \
> +)
> +
> +#define IW_PROP_B_NOT_SRC(Iw) ( \
> + (op_prop_b_not_src[GET_IW_OP(Iw)]) \
> + || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_READRB(Iw)))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_b_not_src[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_IGNORE_DST(Iw) ( \
> + (op_prop_ignore_dst[GET_IW_OP(Iw)]) \
> + || (IS_CUSTOM_INST(Iw) && !GET_IW_CUSTOM_WRITERC(Iw)))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_ignore_dst[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SRC2_CHOOSE_IMM(Iw) ( \
> + (op_prop_src2_choose_imm[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_src2_choose_imm[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_src2_choose_imm[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_src2_choose_imm[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_WRCTL_INST(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_WRCTL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_RDCTL_INST(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_RDCTL) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_MUL_SRC1_SIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) || \
> + ((GET_IW_OPX((Iw)) == OPX_MULXSU) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_MUL_SRC2_SIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_MUL_SHIFT_SRC1_SIGNED(Iw) ( \
> + (IS_OPX_INST(Iw) && opx_prop_mul_shift_src1_signed[GET_IW_OPX(Iw)]))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_mul_shift_src1_signed[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_MUL_SHIFT_SRC2_SIGNED(Iw) ( \
> + ( \
> + ((GET_IW_OPX((Iw)) == OPX_MULXSS) && IS_OPX_INST(Iw)) \
> + ) \
> + \
> +)
> +
> +#define IW_PROP_DONT_DISPLAY_DST_REG(Iw) ( \
> + (op_prop_dont_display_dst_reg[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_dont_display_dst_reg[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_dont_display_dst_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_dont_display_dst_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_DONT_DISPLAY_SRC1_REG(Iw) ( \
> + (op_prop_dont_display_src1_reg[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_dont_display_src1_reg[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_dont_display_src1_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_dont_display_src1_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_DONT_DISPLAY_SRC2_REG(Iw) ( \
> + (op_prop_dont_display_src2_reg[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_dont_display_src2_reg[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_dont_display_src2_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_dont_display_src2_reg[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SRC1_NO_X(Iw) ( \
> + (op_prop_src1_no_x[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_src1_no_x[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_src1_no_x[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_src1_no_x[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#define IW_PROP_SRC2_NO_X(Iw) ( \
> + (op_prop_src2_no_x[GET_IW_OP(Iw)] || \
> + (IS_OPX_INST(Iw) && opx_prop_src2_no_x[GET_IW_OPX(Iw)])))
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char op_prop_src2_no_x[64];
> +#endif /* ALT_ASM_SRC */
> +
> +#ifndef ALT_ASM_SRC
> +extern unsigned char opx_prop_src2_no_x[64];
> +#endif /* ALT_ASM_SRC */
> +
> +/* Instruction types */
> +#define INST_TYPE_OP 0
> +#define INST_TYPE_OPX 1
> +
> +/* Canonical instruction codes independent of encoding */
> +#define CALL_INST_CODE 0
> +#define JMPI_INST_CODE 1
> +#define LDBU_INST_CODE 2
> +#define ADDI_INST_CODE 3
> +#define STB_INST_CODE 4
> +#define BR_INST_CODE 5
> +#define LDB_INST_CODE 6
> +#define CMPGEI_INST_CODE 7
> +#define LDHU_INST_CODE 8
> +#define ANDI_INST_CODE 9
> +#define STH_INST_CODE 10
> +#define BGE_INST_CODE 11
> +#define LDH_INST_CODE 12
> +#define CMPLTI_INST_CODE 13
> +#define INITDA_INST_CODE 14
> +#define ORI_INST_CODE 15
> +#define STW_INST_CODE 16
> +#define BLT_INST_CODE 17
> +#define LDW_INST_CODE 18
> +#define CMPNEI_INST_CODE 19
> +#define FLUSHDA_INST_CODE 20
> +#define XORI_INST_CODE 21
> +#define STC_INST_CODE 22
> +#define BNE_INST_CODE 23
> +#define LDL_INST_CODE 24
> +#define CMPEQI_INST_CODE 25
> +#define LDBUIO_INST_CODE 26
> +#define MULI_INST_CODE 27
> +#define STBIO_INST_CODE 28
> +#define BEQ_INST_CODE 29
> +#define LDBIO_INST_CODE 30
> +#define CMPGEUI_INST_CODE 31
> +#define LDHUIO_INST_CODE 32
> +#define ANDHI_INST_CODE 33
> +#define STHIO_INST_CODE 34
> +#define BGEU_INST_CODE 35
> +#define LDHIO_INST_CODE 36
> +#define CMPLTUI_INST_CODE 37
> +#define CUSTOM_INST_CODE 38
> +#define INITD_INST_CODE 39
> +#define ORHI_INST_CODE 40
> +#define STWIO_INST_CODE 41
> +#define BLTU_INST_CODE 42
> +#define LDWIO_INST_CODE 43
> +#define FLUSHD_INST_CODE 44
> +#define XORHI_INST_CODE 45
> +#define ERET_INST_CODE 46
> +#define ROLI_INST_CODE 47
> +#define ROL_INST_CODE 48
> +#define FLUSHP_INST_CODE 49
> +#define RET_INST_CODE 50
> +#define NOR_INST_CODE 51
> +#define MULXUU_INST_CODE 52
> +#define CMPGE_INST_CODE 53
> +#define BRET_INST_CODE 54
> +#define ROR_INST_CODE 55
> +#define FLUSHI_INST_CODE 56
> +#define JMP_INST_CODE 57
> +#define AND_INST_CODE 58
> +#define CMPLT_INST_CODE 59
> +#define SLLI_INST_CODE 60
> +#define SLL_INST_CODE 61
> +#define OR_INST_CODE 62
> +#define MULXSU_INST_CODE 63
> +#define CMPNE_INST_CODE 64
> +#define SRLI_INST_CODE 65
> +#define SRL_INST_CODE 66
> +#define NEXTPC_INST_CODE 67
> +#define CALLR_INST_CODE 68
> +#define XOR_INST_CODE 69
> +#define MULXSS_INST_CODE 70
> +#define CMPEQ_INST_CODE 71
> +#define DIVU_INST_CODE 72
> +#define DIV_INST_CODE 73
> +#define RDCTL_INST_CODE 74
> +#define MUL_INST_CODE 75
> +#define CMPGEU_INST_CODE 76
> +#define INITI_INST_CODE 77
> +#define TRAP_INST_CODE 78
> +#define WRCTL_INST_CODE 79
> +#define CMPLTU_INST_CODE 80
> +#define ADD_INST_CODE 81
> +#define BREAK_INST_CODE 82
> +#define HBREAK_INST_CODE 83
> +#define SYNC_INST_CODE 84
> +#define SUB_INST_CODE 85
> +#define SRAI_INST_CODE 86
> +#define SRA_INST_CODE 87
> +#define INTR_INST_CODE 88
> +#define CRST_INST_CODE 89
> +#define RSV_INST_CODE 90
> +#define NUM_NIOS2_INST_CODES 91
> +
> +#ifndef ALT_ASM_SRC
> +/* Instruction information entry */
> +typedef struct {
> + const char *name; /* Assembly-language instruction name */
> + int instType; /* INST_TYPE_OP or INST_TYPE_OPX */
> + unsigned opcode; /* Value of instruction word OP/OPX field */
> +} Nios2InstInfo;
> +
> +extern Nios2InstInfo nios2InstInfo[NUM_NIOS2_INST_CODES];
> +#endif /* ALT_ASM_SRC */
> +
> +/* Returns the instruction code given the 32-bit instruction word */
> +#define GET_INST_CODE(Iw) \
> + (IS_OPX_INST(Iw) ? opxToInstCode[GET_IW_OPX(Iw)] : \
> + opToInstCode[GET_IW_OP(Iw)])
> +
> +#ifndef ALT_ASM_SRC
> +extern int opToInstCode[64];
> +extern int opxToInstCode[64];
> +#endif /* ALT_ASM_SRC */
> +
> +/*
> + * MMU Memory Region Macros
> + */
> +#define USER_REGION_MIN_VADDR 0x00000000
> +#define USER_REGION_MAX_VADDR 0x7fffffff
> +#define KERNEL_MMU_REGION_MIN_VADDR 0x80000000
> +#define KERNEL_MMU_REGION_MAX_VADDR 0xbfffffff
> +#define KERNEL_REGION_MIN_VADDR 0xc0000000
> +#define KERNEL_REGION_MAX_VADDR 0xdfffffff
> +#define IO_REGION_MIN_VADDR 0xe0000000
> +#define IO_REGION_MAX_VADDR 0xffffffff
> +
> +#define MMU_PAGE_SIZE (0x1 << (MMU_ADDR_PAGE_OFFSET_SZ))
> +
> +#define isMmuUserRegion(Vaddr) \
> + (GET_MMU_ADDR_USER_REGION(Vaddr) == MMU_ADDR_USER_REGION)
> +#define isMmuKernelMmuRegion(Vaddr) \
> + (GET_MMU_ADDR_KERNEL_MMU_REGION(Vaddr) == MMU_ADDR_KERNEL_MMU_REGION)
> +#define isMmuKernelRegion(Vaddr) \
> + (GET_MMU_ADDR_KERNEL_REGION(Vaddr) == MMU_ADDR_KERNEL_REGION)
> +#define isMmuIORegion(Vaddr) \
> + (GET_MMU_ADDR_IO_REGION(Vaddr) == MMU_ADDR_IO_REGION)
> +
> +/* Does this virtual address bypass the TLB? */
> +#define vaddrBypassTlb(Vaddr) \
> + (GET_MMU_ADDR_BYPASS_TLB(Vaddr) == MMU_ADDR_BYPASS_TLB)
> +
> +/* If TLB is bypassed, is the address cacheable or uncachable. */
> +#define vaddrBypassTlbCacheable(Vaddr) \
> + (GET_MMU_ADDR_BYPASS_TLB_CACHEABLE(Vaddr) == MMU_ADDR_BYPASS_TLB_CACHEABLE)
> +
> +/*
> + * Compute physical address for regions that bypass the TLB.
> + * Just need to clear some top bits.
> + */
> +#define bypassTlbVaddrToPaddr(Vaddr) \
> + ((Vaddr) & (MMU_ADDR_BYPASS_TLB_PADDR_MASK << \
> + MMU_ADDR_BYPASS_TLB_PADDR_LSB))
> +
> +/*
> + * Will the physical address fit in the Kernel/IO region virtual address space?
> + */
> +#define fitsInKernelRegion(Paddr) \
> + (GET_MMU_ADDR_KERNEL_REGION(Paddr) == 0)
> +#define fitsInIORegion(Paddr) \
> + (GET_MMU_ADDR_IO_REGION(Paddr) == 0)
> +
> +/* Convert a physical address to a Kernel/IO region virtual address. */
> +#define paddrToKernelRegionVaddr(Paddr) \
> + ((Paddr) | (MMU_ADDR_KERNEL_REGION << MMU_ADDR_KERNEL_REGION_LSB))
> +#define paddrToIORegionVaddr(Paddr) \
> + ((Paddr) | (MMU_ADDR_IO_REGION << MMU_ADDR_IO_REGION_LSB))
> +
> +/*
> + * Convert a virtual address to a Kernel/IO region virtual address.
> + * Uses bypassTlbVaddrToPaddr to clear top bits.
> + */
> +#define vaddrToKernelRegionVaddr(Vaddr) \
> + paddrToKernelRegionVaddr(bypassTlbVaddrToPaddr(Vaddr))
> +#define vaddrToIORegionVaddr(Vaddr) \
> + paddrToIORegionVaddr(bypassTlbVaddrToPaddr(Vaddr))
> +
> +/* Convert between VPN/PFN and virtual/physical addresses. */
> +#define vpnToVaddr(Vpn) ((Vpn) << MMU_ADDR_VPN_LSB)
> +#define pfnToPaddr(Pfn) ((Pfn) << MMU_ADDR_PFN_LSB)
> +#define vaddrToVpn(Vaddr) GET_MMU_ADDR_VPN(Vaddr)
> +#define paddrToPfn(Paddr) GET_MMU_ADDR_PFN(Paddr)
> +
> +/* Bitwise OR with a KERNEL region address to make it an IO region address */
> +#define KERNEL_TO_IO_REGION 0x20000000
> +
> +/* Exception information */
> +#ifndef ALT_ASM_SRC
> +typedef struct {
> + const char *name;
> + int priority;
> + int subPriority; /* -1 if none */
> + int causeId; /* -1 if none */
> + int recordAddr;
> +} ExcInfo;
> +
> +extern ExcInfo excInfo[NUM_EXC_IDS];
> +#endif /* ALT_ASM_SRC */
> +
> +#endif /* _NIOS2_ISA_H_ */
> diff --git a/nios2-opc.c b/nios2-opc.c
> new file mode 100644
> index 0000000..64e1673
> --- /dev/null
> +++ b/nios2-opc.c
> @@ -0,0 +1,540 @@
> +/* nios2-opc.c -- Altera New Jersey opcode list.
> +
> + Copyright (C) 2003
> + by Nigel Gray (ngray@altera.com).
> +
> +This file is part of GDB, GAS, and the GNU binutils.
> +
> +GDB, GAS, and the GNU binutils are free software; you can redistribute
> +them and/or modify them under the terms of the GNU General Public
> +License as published by the Free Software Foundation; either version
> +1, or (at your option) any later version.
> +
> +GDB, GAS, and the GNU binutils are distributed in the hope that they
> +will be useful, but WITHOUT ANY WARRANTY; without even the implied
> +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> +the GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this file; see the file COPYING. If not, write to the Free
> +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
> +
> +#include <stdio.h>
> +#include "nios2.h"
> +
> +/* Register string table */
> +
> +const struct nios2_reg nios2_builtin_regs[] = {
> + {"zero", 0},
> + {"at", 1}, /* assembler temporary */
> + {"r2", 2},
> + {"r3", 3},
> + {"r4", 4},
> + {"r5", 5},
> + {"r6", 6},
> + {"r7", 7},
> + {"r8", 8},
> + {"r9", 9},
> + {"r10", 10},
> + {"r11", 11},
> + {"r12", 12},
> + {"r13", 13},
> + {"r14", 14},
> + {"r15", 15},
> + {"r16", 16},
> + {"r17", 17},
> + {"r18", 18},
> + {"r19", 19},
> + {"r20", 20},
> + {"r21", 21},
> + {"r22", 22},
> + {"r23", 23},
> + {"et", 24},
> + {"bt", 25},
> + {"gp", 26}, /* global pointer */
> + {"sp", 27}, /* stack pointer */
> + {"fp", 28}, /* frame pointer */
> + {"ea", 29}, /* exception return address */
> + {"ba", 30}, /* breakpoint return address */
> + {"ra", 31}, /* return address */
> +
> + /* alternative names for special registers */
> + {"r0", 0},
> + {"r1", 1},
> + {"r24", 24},
> + {"r25", 25},
> + {"r26", 26},
> + {"r27", 27},
> + {"r28", 28},
> + {"r29", 29},
> + {"r30", 30},
> + {"r31", 31},
> +
> + /* control register names */
> + {"status", 0},
> + {"estatus", 1},
> + {"bstatus", 2},
> + {"ienable", 3},
> + {"ipending", 4},
> + {"cpuid", 5},
> + {"ctl6", 6},
> + {"exception", 7},
> + {"pteaddr", 8},
> + {"tlbacc", 9},
> + {"tlbmisc", 10},
> + {"fstatus", 11},
> + {"badaddr", 12},
> + {"config", 13},
> + {"mpubase", 14},
> + {"mpuacc", 15},
> + {"ctl16", 16},
> + {"ctl17", 17},
> + {"ctl18", 18},
> + {"ctl19", 19},
> + {"ctl20", 20},
> + {"ctl21", 21},
> + {"ctl22", 22},
> + {"ctl23", 23},
> + {"ctl24", 24},
> + {"ctl25", 25},
> + {"ctl26", 26},
> + {"ctl27", 27},
> + {"ctl28", 28},
> + {"ctl29", 29},
> + {"ctl30", 30},
> + {"ctl31", 31},
> +
> + /* alternative names for special control registers */
> + {"ctl0", 0},
> + {"ctl1", 1},
> + {"ctl2", 2},
> + {"ctl3", 3},
> + {"ctl4", 4},
> + {"ctl5", 5},
> + {"ctl7", 7},
> + {"ctl8", 8},
> + {"ctl9", 9},
> + {"ctl10", 10},
> + {"ctl11", 11},
> + {"ctl12", 12},
> + {"ctl13", 13},
> + {"ctl14", 14},
> + {"ctl15", 15},
> +
> +
> + /* coprocessor register names */
> + {"c0", 0},
> + {"c1", 1},
> + {"c2", 2},
> + {"c3", 3},
> + {"c4", 4},
> + {"c5", 5},
> + {"c6", 6},
> + {"c7", 7},
> + {"c8", 8},
> + {"c9", 9},
> + {"c10", 10},
> + {"c11", 11},
> + {"c12", 12},
> + {"c13", 13},
> + {"c14", 14},
> + {"c15", 15},
> + {"c16", 16},
> + {"c17", 17},
> + {"c18", 18},
> + {"c19", 19},
> + {"c20", 20},
> + {"c21", 21},
> + {"c22", 22},
> + {"c23", 23},
> + {"c24", 24},
> + {"c25", 25},
> + {"c26", 26},
> + {"c27", 27},
> + {"c28", 28},
> + {"c29", 29},
> + {"c30", 30},
> + {"c31", 31},
> +};
> +
> +#define NIOS2_NUM_REGS \
> + ((sizeof(nios2_builtin_regs)) / (sizeof(nios2_builtin_regs[0])))
> +const int nios2_num_builtin_regs = NIOS2_NUM_REGS;
> +
> +/* const removed from the following to allow for dynamic extensions to the
> + * built-in instruction set. */
> +struct nios2_reg *nios2_regs = (struct nios2_reg *) nios2_builtin_regs;
> +int nios2_num_regs = NIOS2_NUM_REGS;
> +#undef NIOS2_NUM_REGS
> +
> +/* overflow message string templates */
> +
> +const char *overflow_msgs[] = {
> + "call target address 0x%08x out of range 0x%08x to 0x%08x",
> + "branch offset %d out of range %d to %d",
> + "%s offset %d out of range %d to %d",
> + "immediate value %d out of range %d to %d",
> + "immediate value %u out of range %u to %u",
> + "immediate value %u out of range %u to %u",
> + "custom instruction opcode %u out of range %u to %u",
> +};
> +
> +
> +
> +/*------------------------------------------------------------------------------
> + This is the opcode table used by the New Jersey GNU as, disassembler and GDB
> + ----------------------------------------------------------------------------*/
> +
> +/*
> + The following letters can appear in the args field of the nios2_opcode
> + structure:
> +
> + c - a 5-bit control register index or break opcode
> + d - a 5-bit destination register index
> + s - a 5-bit left source register index
> + t - a 5-bit right source register index
> + i - a 16-bit signed immediate
> + u - a 16-bit unsigned immediate
> +
> + j - a 5-bit unsigned immediate
> + k - a 6-bit unsigned immediate
> + l - an 8-bit unsigned immediate
> + m - a 26-bit unsigned immediate
> +*/
> +
> +/* *INDENT-OFF* */
> +/* FIXME: Re-format for GNU standards */
> +const struct nios2_opcode nios2_builtin_opcodes[] = {
> + /* name, args, args_test, num_args, match, mask, pinfo */
> + {"add", "d,s,t", "d,s,t,E", 3, OP_MATCH_ADD,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"addi", "t,s,i", "t,s,i,E", 3, OP_MATCH_ADDI,
> + OP_MASK_IOP, NIOS2_INSN_ADDI,
> + signed_immed16_overflow },
> + {"subi", "t,s,i", "t,s,i,E", 3, OP_MATCH_ADDI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO,
> + signed_immed16_overflow },
> + {"and", "d,s,t", "d,s,t,E", 3, OP_MATCH_AND,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"andhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ANDHI,
> + OP_MASK_IOP, 0,
> + unsigned_immed16_overflow },
> + {"andi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ANDI,
> + OP_MASK_IOP, NIOS2_INSN_ANDI,
> + unsigned_immed16_overflow },
> + {"beq", "s,t,o", "s,t,o,E", 3, OP_MATCH_BEQ,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bge", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGE,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bgeu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGEU,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bgt", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLT,
> + OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bgtu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLTU,
> + OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"ble", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGE,
> + OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bleu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BGEU,
> + OP_MASK_IOP, NIOS2_INSN_MACRO|NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"blt", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLT,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bltu", "s,t,o", "s,t,o,E", 3, OP_MATCH_BLTU,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"bne", "s,t,o", "s,t,o,E", 3, OP_MATCH_BNE,
> + OP_MASK_IOP, NIOS2_INSN_CBRANCH,
> + branch_target_overflow },
> + {"br", "o", "o,E", 1, OP_MATCH_BR,
> + OP_MASK_IOP, NIOS2_INSN_UBRANCH,
> + branch_target_overflow },
> + {"break", "b", "b,E", 1, OP_MATCH_BREAK,
> + OP_MASK_BREAK, 0,
> + no_overflow },
> + {"bret", "", "E", 0, OP_MATCH_BRET,
> + OP_MASK, 0,
> + no_overflow },
> + {"flushd", "i(s)", "i(s)E", 2, OP_MATCH_FLUSHD,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"flushda", "i(s)", "i(s)E", 2, OP_MATCH_FLUSHDA,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"flushi", "s", "s,E", 1, OP_MATCH_FLUSHI,
> + OP_MASK_FLUSHI, 0,
> + no_overflow },
> + {"flushp", "", "E", 0, OP_MATCH_FLUSHP,
> + OP_MASK, 0,
> + no_overflow },
> + {"initd", "i(s)", "i(s)E", 2, OP_MATCH_INITD,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"initda", "i(s)", "i(s)E", 2, OP_MATCH_INITDA,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"initi", "s", "s,E", 1, OP_MATCH_INITI,
> + OP_MASK_INITI, 0,
> + no_overflow },
> + {"call", "m", "m,E", 1, OP_MATCH_CALL,
> + OP_MASK_IOP, NIOS2_INSN_CALL,
> + call_target_overflow },
> + {"callr", "s", "s,E", 1, OP_MATCH_CALLR,
> + OP_MASK_CALLR, 0,
> + no_overflow },
> + {"cmpeq", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPEQ,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmpeqi", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPEQI,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"cmpge", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGE,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmpgei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPGEI,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"cmpgeu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGEU,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmpgeui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPGEUI,
> + OP_MASK_IOP, 0,
> + unsigned_immed16_overflow },
> + {"cmpgt", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLT,
> + OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
> + no_overflow },
> + {"cmpgti", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPGEI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO,
> + signed_immed16_overflow },
> + {"cmpgtu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLTU,
> + OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
> + no_overflow },
> + {"cmpgtui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPGEUI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO,
> + unsigned_immed16_overflow },
> + {"cmple", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGE,
> + OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
> + no_overflow },
> + {"cmplei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPLTI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO,
> + signed_immed16_overflow },
> + {"cmpleu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPGEU,
> + OP_MASK_ROPX | OP_MASK_ROP, NIOS2_INSN_MACRO,
> + no_overflow },
> + {"cmpleui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPLTUI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO,
> + unsigned_immed16_overflow },
> + {"cmplt", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLT,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmplti", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPLTI,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"cmpltu", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPLTU,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmpltui", "t,s,u", "t,s,u,E", 3, OP_MATCH_CMPLTUI,
> + OP_MASK_IOP, 0,
> + unsigned_immed16_overflow },
> + {"cmpne", "d,s,t", "d,s,t,E", 3, OP_MATCH_CMPNE,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"cmpnei", "t,s,i", "t,s,i,E", 3, OP_MATCH_CMPNEI,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"div", "d,s,t", "d,s,t,E", 3, OP_MATCH_DIV,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"divu", "d,s,t", "d,s,t,E", 3, OP_MATCH_DIVU,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"jmp", "s", "s,E", 1, OP_MATCH_JMP,
> + OP_MASK_JMP, 0,
> + no_overflow },
> + {"jmpi", "m", "m,E", 1, OP_MATCH_JMPI,
> + OP_MASK_IOP, 0,
> + no_overflow },
> + {"ldb", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDB,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldbio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldbu", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBU,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldbuio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDBUIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldh", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDH,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldhio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldhu", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHU,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldhuio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDHUIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldl", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDL,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldw", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDW,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"ldwio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_LDWIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"mov", "d,s", "d,s,E", 2, OP_MATCH_ADD,
> + OP_MASK_RRT|OP_MASK_ROPX|OP_MASK_ROP, NIOS2_INSN_MACRO_MOV,
> + no_overflow },
> + {"movhi", "t,u", "t,u,E", 2, OP_MATCH_ORHI,
> + OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
> + unsigned_immed16_overflow },
> + {"movui", "t,u", "t,u,E", 2, OP_MATCH_ORI,
> + OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
> + unsigned_immed16_overflow },
> + {"movi", "t,i", "t,i,E", 2, OP_MATCH_ADDI,
> + OP_MASK_IRS|OP_MASK_IOP, NIOS2_INSN_MACRO_MOVI,
> + signed_immed16_overflow },
> + /* movia expands to two instructions so there is no mask or match */
> + {"movia", "t,o", "t,o,E", 2, OP_MATCH_ORHI,
> + OP_MASK_IOP, NIOS2_INSN_MACRO_MOVIA,
> + no_overflow },
> + {"mul", "d,s,t", "d,s,t,E", 3, OP_MATCH_MUL,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"muli", "t,s,i", "t,s,i,E", 3, OP_MATCH_MULI,
> + OP_MASK_IOP, 0,
> + signed_immed16_overflow },
> + {"mulxss", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXSS,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"mulxsu", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXSU,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"mulxuu", "d,s,t", "d,s,t,E", 3, OP_MATCH_MULXUU,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"nextpc", "d", "d,E", 1, OP_MATCH_NEXTPC,
> + OP_MASK_NEXTPC, 0,
> + no_overflow },
> + {"nop", "", "E", 0, OP_MATCH_ADD,
> + OP_MASK, NIOS2_INSN_MACRO_MOV,
> + no_overflow },
> + {"nor", "d,s,t", "d,s,t,E", 3, OP_MATCH_NOR,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"or", "d,s,t", "d,s,t,E", 3, OP_MATCH_OR,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"orhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_ORHI,
> + OP_MASK_IOP, 0,
> + unsigned_immed16_overflow },
> + {"ori", "t,s,u", "t,s,u,E", 3, OP_MATCH_ORI,
> + OP_MASK_IOP, NIOS2_INSN_ORI,
> + unsigned_immed16_overflow },
> + {"rdctl", "d,c", "d,c,E", 2, OP_MATCH_RDCTL,
> + OP_MASK_RDCTL, 0,
> + no_overflow },
> + {"ret", "", "E", 0, OP_MATCH_RET,
> + OP_MASK, 0,
> + no_overflow },
> + {"rol", "d,s,t", "d,s,t,E", 3, OP_MATCH_ROL,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"roli", "d,s,j", "d,s,j,E", 3, OP_MATCH_ROLI,
> + OP_MASK_ROLI, 0,
> + unsigned_immed5_overflow },
> + {"ror", "d,s,t", "d,s,t,E", 3, OP_MATCH_ROR,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"sll", "d,s,t", "d,s,t,E", 3, OP_MATCH_SLL,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"slli", "d,s,j", "d,s,j,E", 3, OP_MATCH_SLLI,
> + OP_MASK_SLLI, 0,
> + unsigned_immed5_overflow },
> + {"sra", "d,s,t", "d,s,t,E", 3, OP_MATCH_SRA,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"srai", "d,s,j", "d,s,j,E", 3, OP_MATCH_SRAI,
> + OP_MASK_SRAI, 0,
> + unsigned_immed5_overflow },
> + {"srl", "d,s,t", "d,s,t,E", 3, OP_MATCH_SRL,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"srli", "d,s,j", "d,s,j,E", 3, OP_MATCH_SRLI,
> + OP_MASK_SRLI, 0,
> + unsigned_immed5_overflow },
> + {"stb", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STB,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"stbio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STBIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"stc", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STC,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"sth", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STH,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"sthio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STHIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"stw", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STW,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"stwio", "t,i(s)", "t,i(s)E", 3, OP_MATCH_STWIO,
> + OP_MASK_IOP, 0,
> + address_offset_overflow },
> + {"sub", "d,s,t", "d,s,t,E", 3, OP_MATCH_SUB,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"sync", "", "E", 0, OP_MATCH_SYNC,
> + OP_MASK_SYNC, 0,
> + no_overflow },
> + {"trap", "", "E", 0, OP_MATCH_TRAP,
> + OP_MASK_TRAP, 0,
> + no_overflow },
> + {"eret", "", "E", 0, OP_MATCH_ERET,
> + OP_MASK, 0,
> + no_overflow },
> + {"custom", "l,d,s,t", "l,d,s,t,E", 4, OP_MATCH_CUSTOM,
> + OP_MASK_ROP, 0,
> + custom_opcode_overflow },
> + {"wrctl", "c,s", "c,s,E", 2, OP_MATCH_WRCTL,
> + OP_MASK_WRCTL, 0,
> + no_overflow },
> + {"xor", "d,s,t", "d,s,t,E", 3, OP_MATCH_XOR,
> + OP_MASK_ROPX | OP_MASK_ROP, 0,
> + no_overflow },
> + {"xorhi", "t,s,u", "t,s,u,E", 3, OP_MATCH_XORHI,
> + OP_MASK_IOP, 0,
> + unsigned_immed16_overflow },
> + {"xori", "t,s,u", "t,s,u,E", 3, OP_MATCH_XORI,
> + OP_MASK_IOP, NIOS2_INSN_XORI,
> + unsigned_immed16_overflow }
> +};
> +/* *INDENT-ON* */
> +
> +#define NIOS2_NUM_OPCODES \
> + ((sizeof(nios2_builtin_opcodes)) / (sizeof(nios2_builtin_opcodes[0])))
> +const int bfd_nios2_num_builtin_opcodes = NIOS2_NUM_OPCODES;
> +
> +/* const removed from the following to allow for dynamic extensions to the
> + * built-in instruction set. */
> +struct nios2_opcode *nios2_opcodes =
> + (struct nios2_opcode *) nios2_builtin_opcodes;
> +int bfd_nios2_num_opcodes = NIOS2_NUM_OPCODES;
> +#undef NIOS2_NUM_OPCODES
> diff --git a/nios2.h b/nios2.h
> new file mode 100644
> index 0000000..ff60b77
> --- /dev/null
> +++ b/nios2.h
> @@ -0,0 +1,366 @@
> +/* nios2.h. Altera New Jersey opcode list for GAS, the GNU assembler.
> +
> + Copyright (C) 2003
> + by Nigel Gray (ngray@altera.com).
> +
> +This file is part of GDB, GAS, and the GNU binutils.
> +
> +GDB, GAS, and the GNU binutils are free software; you can redistribute
> +them and/or modify them under the terms of the GNU General Public
> +License as published by the Free Software Foundation; either version
> +1, or (at your option) any later version.
> +
> +GDB, GAS, and the GNU binutils are distributed in the hope that they
> +will be useful, but WITHOUT ANY WARRANTY; without even the implied
> +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
> +the GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this file; see the file COPYING. If not, write to the Free
> +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
> +
> +#ifndef _NIOS2_H_
> +#define _NIOS2_H_
> +
> +
> +/****************************************************************************
> + * This file contains structures, bit masks and shift counts used
> + * by the GNU toolchain to define the New Jersey instruction set and
> + * access various opcode fields.
> + ****************************************************************************/
> +
> +enum overflow_type {
> + call_target_overflow = 0,
> + branch_target_overflow,
> + address_offset_overflow,
> + signed_immed16_overflow,
> + unsigned_immed16_overflow,
> + unsigned_immed5_overflow,
> + custom_opcode_overflow,
> + no_overflow
> +};
> +
> +/*---------------------------------------------------------------------------
> + This structure holds information for a particular instruction
> + ---------------------------------------------------------------------------*/
> +
> +/* match When assembling, this
> + opcode is modified by the arguments to produce the actual opcode
> + that is used. If pinfo is INSN_MACRO, then this is 0. */
> +
> +/* mask If pinfo is not INSN_MACRO, then this is a bit mask for the
> + relevant portions of the opcode when disassembling. If the
> + actual opcode anded with the match field equals the opcode field,
> + then we have found the correct instruction. If pinfo is
> + INSN_MACRO, then this field is the macro identifier. */
> +
> +/* For a macro, this is INSN_MACRO. Otherwise, it is a collection
> + of bits describing the instruction, notably any relevant hazard
> + information. */
> +
> +struct nios2_opcode {
> + const char *name; /* The name of the instruction. */
> + const char *args; /* A string describing the arguments
> + for this instruction. */
> + const char *args_test; /* Like args, but with an extra argument
> + for the expected opcode */
> + unsigned long num_args; /* the number of arguments the instruction
> + takes */
> + unsigned long match; /* The basic opcode for the instruction. */
> + unsigned long mask; /* mask for the opcode field of the
> + instruction */
> + unsigned long pinfo; /* is this a real instruction or
> + instruction macro */
> + enum overflow_type overflow_msg; /* msg template used to generate
> + informative message when fixup
> + overflows */
> +};
> +
> +/* This value is used in the nios2_opcode.pinfo field to indicate that the
> + instruction is a macro or pseudo-op. This requires special treatment by
> + the assembler, and is used by the disassembler to determine whether to
> + check for a nop */
> +#define NIOS2_INSN_MACRO 0x80000000
> +#define NIOS2_INSN_MACRO_MOV 0x80000001
> +#define NIOS2_INSN_MACRO_MOVI 0x80000002
> +#define NIOS2_INSN_MACRO_MOVIA 0x80000004
> +
> +#define NIOS2_INSN_RELAXABLE 0x40000000
> +#define NIOS2_INSN_UBRANCH 0x00000010
> +#define NIOS2_INSN_CBRANCH 0x00000020
> +#define NIOS2_INSN_CALL 0x00000040
> +
> +#define NIOS2_INSN_ADDI 0x00000080
> +#define NIOS2_INSN_ANDI 0x00000100
> +#define NIOS2_INSN_ORI 0x00000200
> +#define NIOS2_INSN_XORI 0x00000400
> +
> +
> +
> +/* Associates a register name ($6) with a 5-bit index (eg 6) */
> +struct nios2_reg {
> + const char *name;
> + const int index;
> +};
> +
> +
> +/* -------------------------------------------------------------------------
> + Bitfield masks for New Jersey instructions
> + -------------------------------------------------------------------------*/
> +
> +/* These are bit masks and shift counts to use to access the various
> + fields of an instruction. */
> +
> +/* Macros for getting and setting an instruction field */
> +#define GET_INSN_FIELD(X, i) (((i) & OP_MASK_##X) >> OP_SH_##X)
> +#define SET_INSN_FIELD(X, i, j) ((i) = ((i) & ~(OP_MASK_##X)) | \
> + ((j) << OP_SH_##X))
> +
> +
> +/*
> + We include the auto-generated file nios2-isa.h and define the mask
> + and shifts below in terms of those in nios2-isa.h. This ensures
> + that the binutils and hardware are always in sync
> +*/
> +
> +#include "nios2-isa.h"
> +
> +#define OP_MASK_OP (IW_OP_MASK << IW_OP_LSB)
> +#define OP_SH_OP IW_OP_LSB
> +
> +
> +/* Masks and shifts for I-type instructions */
> +
> +#define OP_MASK_IOP (IW_OP_MASK << IW_OP_LSB)
> +#define OP_SH_IOP IW_OP_LSB
> +
> +#define OP_MASK_IMM16 (IW_IMM16_MASK << IW_IMM16_LSB)
> +#define OP_SH_IMM16 IW_IMM16_LSB
> +
> +#define OP_MASK_IRD (IW_B_MASK << IW_B_LSB)
> +#define OP_SH_IRD IW_B_LSB
> +
> +#define OP_MASK_IRT (IW_B_MASK << IW_B_LSB)
> +#define OP_SH_IRT IW_B_LSB
> +
> +#define OP_MASK_IRS (IW_A_MASK << IW_A_LSB)
> +#define OP_SH_IRS IW_A_LSB
> +
> +/* Masks and shifts for R-type instructions */
> +
> +#define OP_MASK_ROP (IW_OP_MASK << IW_OP_LSB)
> +#define OP_SH_ROP IW_OP_LSB
> +
> +#define OP_MASK_ROPX (IW_OPX_MASK << IW_OPX_LSB)
> +#define OP_SH_ROPX IW_OPX_LSB
> +
> +#define OP_MASK_RRD (IW_C_MASK << IW_C_LSB)
> +#define OP_SH_RRD IW_C_LSB
> +
> +#define OP_MASK_RRT (IW_B_MASK << IW_B_LSB)
> +#define OP_SH_RRT IW_B_LSB
> +
> +#define OP_MASK_RRS (IW_A_MASK << IW_A_LSB)
> +#define OP_SH_RRS IW_A_LSB
> +
> +/* Masks and shifts for J-type instructions */
> +
> +#define OP_MASK_JOP (IW_OP_MASK << IW_OP_LSB)
> +#define OP_SH_JOP IW_OP_LSB
> +
> +#define OP_MASK_IMM26 (IW_IMM26_MASK << IW_IMM26_LSB)
> +#define OP_SH_IMM26 IW_IMM26_LSB
> +
> +/* Masks and shifts for CTL instructions */
> +
> +#define OP_MASK_RCTL 0x000007c0
> +#define OP_SH_RCTL 6
> +
> +/* break instruction imm5 field */
> +#define OP_MASK_TRAP_IMM5 0x000007c0
> +#define OP_SH_TRAP_IMM5 6
> +
> +/* instruction imm5 field */
> +#define OP_MASK_IMM5 (IW_SHIFT_IMM5_MASK << IW_SHIFT_IMM5_LSB)
> +#define OP_SH_IMM5 IW_SHIFT_IMM5_LSB
> +
> +/* cache operation fields (type j,i(s)) */
> +#define OP_MASK_CACHE_OPX (IW_B_MASK << IW_B_LSB)
> +#define OP_SH_CACHE_OPX IW_B_LSB
> +#define OP_MASK_CACHE_RRS (IW_A_MASK << IW_A_LSB)
> +#define OP_SH_CACHE_RRS IW_A_LSB
> +
> +/* custom instruction masks */
> +#define OP_MASK_CUSTOM_A 0x00010000
> +#define OP_SH_CUSTOM_A 16
> +
> +#define OP_MASK_CUSTOM_B 0x00008000
> +#define OP_SH_CUSTOM_B 15
> +
> +#define OP_MASK_CUSTOM_C 0x00004000
> +#define OP_SH_CUSTOM_C 14
> +
> +#define OP_MASK_CUSTOM_N 0x00003fc0
> +#define OP_SH_CUSTOM_N 6
> +#define OP_MAX_CUSTOM_N 255
> +
> +/*
> + The following macros define the opcode matches for each
> + instruction
> + code & OP_MASK_INST == OP_MATCH_INST
> + */
> +
> +/* OP instruction matches */
> +#define OP_MATCH_ADDI OP_ADDI
> +#define OP_MATCH_ANDHI OP_ANDHI
> +#define OP_MATCH_ANDI OP_ANDI
> +#define OP_MATCH_BEQ OP_BEQ
> +#define OP_MATCH_BGE OP_BGE
> +#define OP_MATCH_BGEU OP_BGEU
> +#define OP_MATCH_BLT OP_BLT
> +#define OP_MATCH_BLTU OP_BLTU
> +#define OP_MATCH_BNE OP_BNE
> +#define OP_MATCH_BR OP_BR
> +#define OP_MATCH_FLUSHD OP_FLUSHD
> +#define OP_MATCH_FLUSHDA OP_FLUSHDA
> +#define OP_MATCH_INITD OP_INITD
> +#define OP_MATCH_INITDA OP_INITDA
> +#define OP_MATCH_CALL OP_CALL
> +#define OP_MATCH_CMPEQI OP_CMPEQI
> +#define OP_MATCH_CMPGEI OP_CMPGEI
> +#define OP_MATCH_CMPGEUI OP_CMPGEUI
> +#define OP_MATCH_CMPLTI OP_CMPLTI
> +#define OP_MATCH_CMPLTUI OP_CMPLTUI
> +#define OP_MATCH_CMPNEI OP_CMPNEI
> +#define OP_MATCH_JMPI OP_JMPI
> +#define OP_MATCH_LDB OP_LDB
> +#define OP_MATCH_LDBIO OP_LDBIO
> +#define OP_MATCH_LDBU OP_LDBU
> +#define OP_MATCH_LDBUIO OP_LDBUIO
> +#define OP_MATCH_LDH OP_LDH
> +#define OP_MATCH_LDHIO OP_LDHIO
> +#define OP_MATCH_LDHU OP_LDHU
> +#define OP_MATCH_LDHUIO OP_LDHUIO
> +#define OP_MATCH_LDL OP_LDL
> +#define OP_MATCH_LDW OP_LDW
> +#define OP_MATCH_LDWIO OP_LDWIO
> +#define OP_MATCH_MULI OP_MULI
> +#define OP_MATCH_OPX OP_OPX
> +#define OP_MATCH_ORHI OP_ORHI
> +#define OP_MATCH_ORI OP_ORI
> +#define OP_MATCH_STB OP_STB
> +#define OP_MATCH_STBIO OP_STBIO
> +#define OP_MATCH_STC OP_STC
> +#define OP_MATCH_STH OP_STH
> +#define OP_MATCH_STHIO OP_STHIO
> +#define OP_MATCH_STW OP_STW
> +#define OP_MATCH_STWIO OP_STWIO
> +#define OP_MATCH_CUSTOM OP_CUSTOM
> +#define OP_MATCH_XORHI OP_XORHI
> +#define OP_MATCH_XORI OP_XORI
> +#define OP_MATCH_OPX OP_OPX
> +
> +
> +
> +/* OPX instruction values */
> +#define OP_MATCH_ADD ((OPX_ADD << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_AND ((OPX_AND << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_BREAK ((0x1e << 17) | (OPX_BREAK << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_BRET ((0xf0000000) | (OPX_BRET << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CALLR ((0x1f << 17) | (OPX_CALLR << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPEQ ((OPX_CMPEQ << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPGE ((OPX_CMPGE << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPGEU ((OPX_CMPGEU << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPLT ((OPX_CMPLT << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPLTU ((OPX_CMPLTU << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_CMPNE ((OPX_CMPNE << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_DIV ((OPX_DIV << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_DIVU ((OPX_DIVU << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_JMP ((OPX_JMP << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_MUL ((OPX_MUL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_MULXSS ((OPX_MULXSS << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_MULXSU ((OPX_MULXSU << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_MULXUU ((OPX_MULXUU << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_NEXTPC ((OPX_NEXTPC << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_NOR ((OPX_NOR << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_OR ((OPX_OR << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_RDCTL ((OPX_RDCTL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_RET ((0xf8000000) | (OPX_RET << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_ROL ((OPX_ROL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_ROLI ((OPX_ROLI << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_ROR ((OPX_ROR << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SLL ((OPX_SLL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SLLI ((OPX_SLLI << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SRA ((OPX_SRA << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SRAI ((OPX_SRAI << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SRL ((OPX_SRL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SRLI ((OPX_SRLI << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SUB ((OPX_SUB << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_SYNC ((OPX_SYNC << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_TRAP ((0x1d << 17) | (OPX_TRAP << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_ERET ((0xe8000000) | (OPX_ERET << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_WRCTL ((OPX_WRCTL << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_XOR ((OPX_XOR << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_FLUSHI ((OPX_FLUSHI << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_FLUSHP ((OPX_FLUSHP << IW_OPX_LSB) | (OP_OPX))
> +#define OP_MATCH_INITI ((OPX_INITI << IW_OPX_LSB) | (OP_OPX))
> +
> +/*
> + Some unusual op masks
> +*/
> +#define OP_MASK_BREAK ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_RRD | \
> + OP_MASK_ROPX | OP_MASK_OP) & 0xfffff03f)
> +#define OP_MASK_CALLR ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_JMP ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_SYNC ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_TRAP ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_RRD | \
> + OP_MASK_ROPX | OP_MASK_OP) & 0xfffff83f)
> +#define OP_MASK_WRCTL ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_NEXTPC ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_FLUSHI ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_INITI ((OP_MASK_RRT | OP_MASK_RRD | OP_MASK_ROPX | OP_MASK_OP))
> +
> +#define OP_MASK_ROLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_SLLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_SRAI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_SRLI ((OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +#define OP_MASK_RDCTL ((OP_MASK_RRS | OP_MASK_RRT | OP_MASK_ROPX | OP_MASK_OP))
> +
> +#ifndef OP_MASK
> +#define OP_MASK 0xffffffff
> +#endif
> +
> +/* These are the data structures we use to hold the instruction information */
> +
> +extern const struct nios2_opcode nios2_builtin_opcodes[];
> +extern const int bfd_nios2_num_builtin_opcodes;
> +extern struct nios2_opcode *nios2_opcodes;
> +extern int bfd_nios2_num_opcodes;
> +
> +/* These are the data structures used to hold the register information */
> +extern const struct nios2_reg nios2_builtin_regs[];
> +extern struct nios2_reg *nios2_regs;
> +extern const int nios2_num_builtin_regs;
> +extern int nios2_num_regs;
> +
> +/* Machine-independent macro for number of opcodes */
> +
> +#define NUMOPCODES bfd_nios2_num_opcodes
> +#define NUMREGISTERS nios2_num_regs;
> +
> +/* these are used in disassembly to get the correct register names */
> +#define NUMREGNAMES 32
> +#define NUMCTLREGNAMES 32
> +#define CTLREGBASE 42
> +#define COPROCREGBASE 83
> +#define NUMCOPROCREGNAMES 32
> +
> +
> +/* this is made extern so that the assembler can use it to find out
> + what instruction caused an error */
> +extern const struct nios2_opcode *nios2_find_opcode_hash(unsigned long);
> +
> +/* overflow message strings used in the assembler */
> +extern const char *overflow_msgs[];
> +
> +#endif /* _NIOS2_H */
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
2012-09-10 0:19 ` [Qemu-devel] [PATCH 1/9] NiosII: Add support for the Altera NiosII soft-core CPU crwulff
2012-09-10 0:20 ` [Qemu-devel] [PATCH 2/9] NiosII: Disassembly of NiosII instructions ported from GDB crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-11 19:53 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices crwulff
` (6 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
hw/Makefile.objs | 5 ++
hw/altera.h | 34 ++++++++
hw/altera_timer.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++
hw/altera_uart.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/altera_vic.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++
hw/nios2.h | 46 +++++++++++
hw/nios2_pic_cpu.c | 48 ++++++++++++
7 files changed, 744 insertions(+)
create mode 100644 hw/altera.h
create mode 100644 hw/altera_timer.c
create mode 100644 hw/altera_uart.c
create mode 100644 hw/altera_vic.c
create mode 100644 hw/nios2.h
create mode 100644 hw/nios2_pic_cpu.c
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 6dfebd2..59dd2d5 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -67,6 +67,11 @@ hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
hw-obj-$(CONFIG_XILINX_AXI) += stream.o
+# Altera devices
+hw-obj-$(CONFIG_ALTERA) += altera_vic.o
+hw-obj-$(CONFIG_ALTERA) += altera_uart.o
+hw-obj-$(CONFIG_ALTERA) += altera_timer.o
+
# PKUnity SoC devices
hw-obj-$(CONFIG_PUV3) += puv3_intc.o
hw-obj-$(CONFIG_PUV3) += puv3_ost.o
diff --git a/hw/altera.h b/hw/altera.h
new file mode 100644
index 0000000..b25366e
--- /dev/null
+++ b/hw/altera.h
@@ -0,0 +1,34 @@
+/*
+ * Altera Nios II device instantiation header.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+/* timer */
+static inline DeviceState *altera_timer_create(target_phys_addr_t base,
+ qemu_irq irq, int freq)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "altera,timer");
+ qdev_prop_set_uint32(dev, "frequency", freq);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
diff --git a/hw/altera_timer.c b/hw/altera_timer.c
new file mode 100644
index 0000000..e490da5
--- /dev/null
+++ b/hw/altera_timer.c
@@ -0,0 +1,198 @@
+/*
+ * QEMU model of the Altera timer.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "ptimer.h"
+
+#define R_STATUS 0
+#define R_CONTROL 1
+#define R_PERIODL 2
+#define R_PERIODH 3
+#define R_SNAPL 4
+#define R_SNAPH 5
+#define R_MAX 6
+
+#define STATUS_TO 0x0001
+#define STATUS_RUN 0x0002
+
+#define CONTROL_ITO 0x0001
+#define CONTROL_CONT 0x0002
+#define CONTROL_START 0x0004
+#define CONTROL_STOP 0x0008
+
+struct altera_timer {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq irq;
+ uint32_t freq_hz;
+ QEMUBH *bh;
+ ptimer_state *ptimer;
+ uint32_t regs[R_MAX];
+};
+
+static uint64_t timer_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct altera_timer *t = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ addr &= 0x7;
+ switch (addr) {
+ case R_STATUS:
+ r = t->regs[R_STATUS];
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(t->regs)) {
+ r = t->regs[addr];
+ }
+ break;
+ }
+
+ qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+
+ return r;
+}
+
+static void timer_start(struct altera_timer *t)
+{
+ ptimer_stop(t->ptimer);
+ ptimer_set_count(t->ptimer, (t->regs[R_PERIODH]<<16) | t->regs[R_PERIODL]);
+ ptimer_run(t->ptimer, 1);
+}
+
+static void timer_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct altera_timer *t = opaque;
+ uint32_t value = val64;
+ uint32_t count = 0;
+
+ addr >>= 2;
+ addr &= 0x7;
+ switch (addr) {
+ case R_STATUS:
+ /* Writing zero clears the timeout */
+ t->regs[R_STATUS] &= ~STATUS_TO;
+ break;
+
+ case R_CONTROL:
+ t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT);
+ if ((value & CONTROL_START) &&
+ ((t->regs[R_STATUS] & STATUS_RUN) == 0)) {
+ timer_start(t);
+ }
+ if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) {
+ ptimer_stop(t->ptimer);
+ }
+ break;
+
+ case R_PERIODL:
+ case R_PERIODH:
+ t->regs[addr] = value & 0xFFFF;
+ if (t->regs[R_STATUS] & STATUS_RUN) {
+ timer_start(t);
+ }
+ break;
+
+ case R_SNAPL:
+ case R_SNAPH:
+ count = ptimer_get_count(t->ptimer);
+ t->regs[R_SNAPL] = count & 0xFFFF;
+ t->regs[R_SNAPH] = (count>>16) & 0xFFFF;
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(t->regs)) {
+ t->regs[addr] = value;
+ }
+ break;
+ }
+
+ qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+}
+
+static const MemoryRegionOps timer_ops = {
+ .read = timer_read,
+ .write = timer_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static void timer_hit(void *opaque)
+{
+ struct altera_timer *t = opaque;
+ t->regs[R_STATUS] |= STATUS_TO;
+
+ if (t->regs[R_CONTROL] & CONTROL_CONT) {
+ timer_start(t);
+ }
+ qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
+}
+
+static int altera_timer_init(SysBusDevice *dev)
+{
+ struct altera_timer *t = FROM_SYSBUS(typeof(*t), dev);
+
+ sysbus_init_irq(dev, &t->irq);
+
+ t->bh = qemu_bh_new(timer_hit, t);
+ t->ptimer = ptimer_init(t->bh);
+ ptimer_set_freq(t->ptimer, t->freq_hz);
+
+ memory_region_init_io(&t->mmio, &timer_ops, t, "altera,timer",
+ R_MAX * sizeof(uint32_t));
+ sysbus_init_mmio(dev, &t->mmio);
+ return 0;
+}
+
+static Property altera_timer_properties[] = {
+ DEFINE_PROP_UINT32("frequency", struct altera_timer, freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = altera_timer_init;
+ dc->props = altera_timer_properties;
+}
+
+static TypeInfo altera_timer_info = {
+ .name = "altera,timer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct altera_timer),
+ .class_init = altera_timer_class_init,
+};
+
+static void altera_timer_register(void)
+{
+ type_register_static(&altera_timer_info);
+}
+
+type_init(altera_timer_register)
+
diff --git a/hw/altera_uart.c b/hw/altera_uart.c
new file mode 100644
index 0000000..e32651e
--- /dev/null
+++ b/hw/altera_uart.c
@@ -0,0 +1,218 @@
+/*
+ * QEMU model of the Altera uart.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "qemu-char.h"
+
+#define R_RXDATA 0
+#define R_TXDATA 1
+#define R_STATUS 2
+#define R_CONTROL 3
+#define R_DIVISOR 4
+#define R_ENDOFPACKET 5
+#define R_MAX 6
+
+#define STATUS_PE 0x0001
+#define STATUS_FE 0x0002
+#define STATUS_BRK 0x0004
+#define STATUS_ROE 0x0008
+#define STATUS_TOE 0x0010
+#define STATUS_TMT 0x0020
+#define STATUS_TRDY 0x0040
+#define STATUS_RRDY 0x0080
+#define STATUS_E 0x0100
+#define STATUS_DTCS 0x0400
+#define STATUS_CTS 0x0800
+#define STATUS_EOP 0x1000
+
+#define CONTROL_IPE 0x0001
+#define CONTROL_IFE 0x0002
+#define CONTROL_IBRK 0x0004
+#define CONTROL_IROE 0x0008
+#define CONTROL_ITOE 0x0010
+#define CONTROL_ITMT 0x0020
+#define CONTROL_ITRDY 0x0040
+#define CONTROL_IRRDY 0x0080
+#define CONTROL_IE 0x0100
+#define CONTROL_TBRK 0x0200
+#define CONTROL_IDTCS 0x0400
+#define CONTROL_RTS 0x0800
+#define CONTROL_IEOP 0x1000
+
+struct altera_uart {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ CharDriverState *chr;
+ qemu_irq irq;
+
+ uint32_t regs[R_MAX];
+};
+
+static void uart_update_irq(struct altera_uart *s)
+{
+ unsigned int irq;
+
+ irq = (s->regs[R_STATUS] & s->regs[R_CONTROL] &
+ (STATUS_PE | STATUS_FE | STATUS_BRK | STATUS_ROE | STATUS_TOE |
+ STATUS_TMT | STATUS_TRDY | STATUS_RRDY | STATUS_E | STATUS_DTCS));
+ irq = (irq == 0) ? 0 : 1;
+ qemu_set_irq(s->irq, irq);
+}
+
+static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct altera_uart *s = opaque;
+ uint32_t r = 0;
+ addr >>= 2;
+ addr &= 0x7;
+ switch (addr) {
+ case R_RXDATA:
+ r = s->regs[R_RXDATA];
+ s->regs[R_STATUS] &= ~STATUS_RRDY;
+ uart_update_irq(s);
+ break;
+
+ case R_STATUS:
+ r = s->regs[R_STATUS];
+ s->regs[R_STATUS] &= ~(STATUS_PE | STATUS_FE | STATUS_BRK |
+ STATUS_ROE | STATUS_TOE | STATUS_E |
+ STATUS_DTCS);
+ uart_update_irq(s);
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(s->regs)) {
+ r = s->regs[addr];
+ }
+ break;
+ }
+
+ return r;
+}
+
+static void uart_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct altera_uart *s = opaque;
+ uint32_t value = val64;
+ unsigned char ch = value;
+
+ addr >>= 2;
+ addr &= 0x7;
+
+ switch (addr) {
+ case R_TXDATA:
+ if (s->chr) {
+ qemu_chr_fe_write(s->chr, &ch, 1);
+ }
+
+ s->regs[addr] = value;
+ break;
+
+ case R_RXDATA:
+ case R_STATUS:
+ /* No writeable bits */
+ break;
+
+ default:
+ s->regs[addr] = value;
+ break;
+ }
+ uart_update_irq(s);
+}
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+ struct altera_uart *s = opaque;
+
+ s->regs[R_RXDATA] = *buf;
+ s->regs[R_STATUS] |= STATUS_RRDY;
+
+ uart_update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+ struct altera_uart *s = opaque;
+ return ((s->regs[R_STATUS] & STATUS_RRDY) == 0);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4
+ }
+};
+
+static int altera_uart_init(SysBusDevice *dev)
+{
+ struct altera_uart *s = FROM_SYSBUS(typeof(*s), dev);
+
+ s->regs[R_STATUS] = STATUS_TMT | STATUS_TRDY; /* Always ready to tx */
+
+ sysbus_init_irq(dev, &s->irq);
+
+ memory_region_init_io(&s->mmio, &uart_ops, s,
+ "altera,uart", R_MAX * sizeof(uint32_t));
+ sysbus_init_mmio(dev, &s->mmio);
+
+ s->chr = qemu_char_get_next_serial();
+ if (s->chr) {
+ qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
+ }
+
+ return 0;
+}
+
+static Property altera_uart_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_uart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = altera_uart_init;
+ dc->props = altera_uart_properties;
+}
+
+static TypeInfo altera_uart_info = {
+ .name = "altera,uart",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct altera_uart),
+ .class_init = altera_uart_class_init,
+};
+
+static void altera_uart_register(void)
+{
+ type_register_static(&altera_uart_info);
+}
+
+type_init(altera_uart_register)
+
diff --git a/hw/altera_vic.c b/hw/altera_vic.c
new file mode 100644
index 0000000..43a2b68
--- /dev/null
+++ b/hw/altera_vic.c
@@ -0,0 +1,195 @@
+/*
+ * QEMU Altera Vectored Interrupt Controller.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+
+#define R_INT_CONFIG_0 0
+#define R_INT_ENABLE 32
+#define R_INT_ENABLE_SET 33
+#define R_INT_ENABLE_CLR 34
+#define R_INT_PENDING 35
+#define R_INT_RAW_STATUS 36
+#define R_SW_INTERRUPT 37
+#define R_SW_INTERRUPT_SET 38
+#define R_SW_INTERRUPT_CLR 39
+#define R_VIC_CONFIG 40
+#define R_VIC_STATUS 41
+#define R_VEC_TBL_BASE 42
+#define R_VEC_TBL_ADDR 43
+#define R_MAX 44
+
+struct altera_vic {
+ SysBusDevice busdev;
+ MemoryRegion mmio;
+ qemu_irq parent_irq;
+
+ /* Runtime control registers. */
+ uint32_t regs[R_MAX];
+};
+
+static void update_irq(struct altera_vic *pv)
+{
+ uint32_t i;
+ pv->regs[R_INT_PENDING] = (pv->regs[R_INT_RAW_STATUS] |
+ pv->regs[R_SW_INTERRUPT]) &
+ pv->regs[R_INT_ENABLE];
+
+ for (i = 0; i < 32; i++) {
+ if (pv->regs[R_INT_PENDING] & (1 << i)) {
+ break;
+ }
+ }
+ if (i == 32) {
+ pv->regs[R_VEC_TBL_ADDR] = 0;
+ pv->regs[R_VIC_STATUS] = 0;
+ qemu_irq_lower(pv->parent_irq);
+ } else {
+ pv->regs[R_VEC_TBL_ADDR] = pv->regs[R_VEC_TBL_BASE] +
+ i*(4 << (pv->regs[R_VIC_CONFIG] & 7));
+ pv->regs[R_VIC_STATUS] = 0x80000000 | i;
+ qemu_irq_raise(pv->parent_irq);
+ }
+}
+
+static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct altera_vic *pv = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+ if (addr < R_MAX) {
+ r = pv->regs[addr];
+ }
+
+ return r;
+}
+
+static void pic_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct altera_vic *pv = opaque;
+ uint32_t value = val64;
+
+ addr >>= 2;
+ if (addr < R_INT_ENABLE) {
+ /* R_INT_CONFIG_XX */
+ pv->regs[addr] = value & 0x00001FFF;
+ } else {
+ switch (addr) {
+ case R_INT_PENDING:
+ case R_INT_RAW_STATUS:
+ case R_VIC_STATUS:
+ case R_VEC_TBL_ADDR:
+ /* read only */
+ break;
+
+ case R_INT_ENABLE_SET:
+ pv->regs[R_INT_ENABLE] |= value;
+ break;
+
+ case R_SW_INTERRUPT_SET:
+ pv->regs[R_SW_INTERRUPT] |= value;
+ break;
+
+ case R_INT_ENABLE_CLR:
+ pv->regs[R_INT_ENABLE] &= ~value;
+ break;
+
+ case R_SW_INTERRUPT_CLR:
+ pv->regs[R_SW_INTERRUPT] &= ~value;
+ break;
+
+ case R_VIC_CONFIG:
+ pv->regs[addr] = value & 0x0000000F;
+ break;
+
+ default:
+ if (addr < ARRAY_SIZE(pv->regs)) {
+ pv->regs[addr] = value;
+ }
+ break;
+ }
+ }
+ update_irq(pv);
+}
+
+static const MemoryRegionOps pic_ops = {
+ .read = pic_read,
+ .write = pic_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ struct altera_vic *pv = opaque;
+
+ pv->regs[R_INT_RAW_STATUS] &= ~(1 << irq);
+ pv->regs[R_INT_RAW_STATUS] |= level << irq;
+
+ update_irq(pv);
+}
+
+static int altera_vic_init(SysBusDevice *dev)
+{
+ struct altera_vic *pv = FROM_SYSBUS(typeof(*pv), dev);
+
+ qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+ sysbus_init_irq(dev, &pv->parent_irq);
+
+ memset(pv->regs, 0, sizeof(uint32_t) * R_MAX);
+ memory_region_init_io(&pv->mmio, &pic_ops, pv,
+ "altera,vic", R_MAX * sizeof(uint32_t));
+ sysbus_init_mmio(dev, &pv->mmio);
+ return 0;
+}
+
+static Property altera_vic_properties[] = {
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_vic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = altera_vic_init;
+ dc->props = altera_vic_properties;
+}
+
+static TypeInfo altera_vic_info = {
+ .name = "altera,vic",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct altera_vic),
+ .class_init = altera_vic_class_init,
+};
+
+static void altera_vic_register(void)
+{
+ type_register_static(&altera_vic_info);
+}
+
+type_init(altera_vic_register)
+
diff --git a/hw/nios2.h b/hw/nios2.h
new file mode 100644
index 0000000..a4af154
--- /dev/null
+++ b/hw/nios2.h
@@ -0,0 +1,46 @@
+/*
+ * Altera Nios II CPU interrupt controllers
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+
+qemu_irq *nios2_pic_init_cpu(CPUNios2State *env);
+
+static inline DeviceState *
+altera_vic_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "altera,vic");
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+static inline DeviceState *
+altera_iic_create(qemu_irq irq, int kind_of_intr)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "altera,iic");
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
new file mode 100644
index 0000000..c89b4ae
--- /dev/null
+++ b/hw/nios2_pic_cpu.c
@@ -0,0 +1,48 @@
+/*
+ * QEMU Altera Nios II CPU interrupt wrapper logic.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "hw.h"
+#include "pc.h"
+#include "nios2.h"
+
+void pic_info(Monitor *mon)
+{
+}
+
+void irq_info(Monitor *mon)
+{
+}
+
+static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
+{
+ CPUNios2State *env = (CPUNios2State *)opaque;
+ int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+ if (level) {
+ cpu_interrupt(env, type);
+ } else {
+ cpu_reset_interrupt(env, type);
+ }
+}
+
+qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
+{
+ return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
+}
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII crwulff
@ 2012-09-11 19:53 ` Blue Swirl
2012-09-15 15:06 ` Andreas Färber
0 siblings, 1 reply; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 19:53 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> hw/Makefile.objs | 5 ++
> hw/altera.h | 34 ++++++++
> hw/altera_timer.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++
> hw/altera_uart.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> hw/altera_vic.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++
> hw/nios2.h | 46 +++++++++++
> hw/nios2_pic_cpu.c | 48 ++++++++++++
> 7 files changed, 744 insertions(+)
> create mode 100644 hw/altera.h
> create mode 100644 hw/altera_timer.c
> create mode 100644 hw/altera_uart.c
> create mode 100644 hw/altera_vic.c
> create mode 100644 hw/nios2.h
> create mode 100644 hw/nios2_pic_cpu.c
>
> diff --git a/hw/Makefile.objs b/hw/Makefile.objs
> index 6dfebd2..59dd2d5 100644
> --- a/hw/Makefile.objs
> +++ b/hw/Makefile.objs
> @@ -67,6 +67,11 @@ hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
> hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o
> hw-obj-$(CONFIG_XILINX_AXI) += stream.o
>
> +# Altera devices
> +hw-obj-$(CONFIG_ALTERA) += altera_vic.o
> +hw-obj-$(CONFIG_ALTERA) += altera_uart.o
> +hw-obj-$(CONFIG_ALTERA) += altera_timer.o
> +
> # PKUnity SoC devices
> hw-obj-$(CONFIG_PUV3) += puv3_intc.o
> hw-obj-$(CONFIG_PUV3) += puv3_ost.o
> diff --git a/hw/altera.h b/hw/altera.h
> new file mode 100644
> index 0000000..b25366e
> --- /dev/null
> +++ b/hw/altera.h
> @@ -0,0 +1,34 @@
> +/*
> + * Altera Nios II device instantiation header.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +/* timer */
> +static inline DeviceState *altera_timer_create(target_phys_addr_t base,
> + qemu_irq irq, int freq)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "altera,timer");
> + qdev_prop_set_uint32(dev, "frequency", freq);
> + qdev_init_nofail(dev);
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> + return dev;
> +}
> +
> diff --git a/hw/altera_timer.c b/hw/altera_timer.c
> new file mode 100644
> index 0000000..e490da5
> --- /dev/null
> +++ b/hw/altera_timer.c
> @@ -0,0 +1,198 @@
> +/*
> + * QEMU model of the Altera timer.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +#include "ptimer.h"
> +
> +#define R_STATUS 0
> +#define R_CONTROL 1
> +#define R_PERIODL 2
> +#define R_PERIODH 3
> +#define R_SNAPL 4
> +#define R_SNAPH 5
> +#define R_MAX 6
> +
> +#define STATUS_TO 0x0001
> +#define STATUS_RUN 0x0002
> +
> +#define CONTROL_ITO 0x0001
> +#define CONTROL_CONT 0x0002
> +#define CONTROL_START 0x0004
> +#define CONTROL_STOP 0x0008
> +
> +struct altera_timer {
CamelCase
> + SysBusDevice busdev;
> + MemoryRegion mmio;
> + qemu_irq irq;
> + uint32_t freq_hz;
> + QEMUBH *bh;
> + ptimer_state *ptimer;
> + uint32_t regs[R_MAX];
> +};
> +
> +static uint64_t timer_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct altera_timer *t = opaque;
> + uint32_t r = 0;
uint32_t does not match uint64_t for function return value.
> +
> + addr >>= 2;
> + addr &= 0x7;
> + switch (addr) {
> + case R_STATUS:
> + r = t->regs[R_STATUS];
> + break;
> +
> + default:
> + if (addr < ARRAY_SIZE(t->regs)) {
> + r = t->regs[addr];
> + }
> + break;
> + }
> +
> + qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
It would be better to set the IRQ only if the state has changed.
> +
> + return r;
> +}
> +
> +static void timer_start(struct altera_timer *t)
> +{
> + ptimer_stop(t->ptimer);
> + ptimer_set_count(t->ptimer, (t->regs[R_PERIODH]<<16) | t->regs[R_PERIODL]);
> + ptimer_run(t->ptimer, 1);
> +}
> +
> +static void timer_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct altera_timer *t = opaque;
> + uint32_t value = val64;
> + uint32_t count = 0;
> +
> + addr >>= 2;
> + addr &= 0x7;
> + switch (addr) {
> + case R_STATUS:
> + /* Writing zero clears the timeout */
> + t->regs[R_STATUS] &= ~STATUS_TO;
> + break;
> +
> + case R_CONTROL:
> + t->regs[R_CONTROL] = value & (CONTROL_ITO | CONTROL_CONT);
> + if ((value & CONTROL_START) &&
> + ((t->regs[R_STATUS] & STATUS_RUN) == 0)) {
> + timer_start(t);
> + }
> + if ((value & CONTROL_STOP) && (t->regs[R_STATUS] & STATUS_RUN)) {
> + ptimer_stop(t->ptimer);
> + }
> + break;
> +
> + case R_PERIODL:
> + case R_PERIODH:
> + t->regs[addr] = value & 0xFFFF;
> + if (t->regs[R_STATUS] & STATUS_RUN) {
> + timer_start(t);
> + }
> + break;
> +
> + case R_SNAPL:
> + case R_SNAPH:
> + count = ptimer_get_count(t->ptimer);
> + t->regs[R_SNAPL] = count & 0xFFFF;
> + t->regs[R_SNAPH] = (count>>16) & 0xFFFF;
> + break;
> +
> + default:
> + if (addr < ARRAY_SIZE(t->regs)) {
> + t->regs[addr] = value;
> + }
> + break;
> + }
> +
> + qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
> +}
> +
> +static const MemoryRegionOps timer_ops = {
> + .read = timer_read,
> + .write = timer_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
Native endian cases should be rare, you should fix it to LE or BE.
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4
> + }
> +};
> +
> +static void timer_hit(void *opaque)
> +{
> + struct altera_timer *t = opaque;
> + t->regs[R_STATUS] |= STATUS_TO;
> +
> + if (t->regs[R_CONTROL] & CONTROL_CONT) {
> + timer_start(t);
> + }
> + qemu_set_irq(t->irq, t->regs[R_STATUS] & t->regs[R_CONTROL] & CONTROL_ITO);
> +}
> +
> +static int altera_timer_init(SysBusDevice *dev)
> +{
> + struct altera_timer *t = FROM_SYSBUS(typeof(*t), dev);
> +
> + sysbus_init_irq(dev, &t->irq);
> +
> + t->bh = qemu_bh_new(timer_hit, t);
> + t->ptimer = ptimer_init(t->bh);
> + ptimer_set_freq(t->ptimer, t->freq_hz);
> +
> + memory_region_init_io(&t->mmio, &timer_ops, t, "altera,timer",
> + R_MAX * sizeof(uint32_t));
> + sysbus_init_mmio(dev, &t->mmio);
> + return 0;
> +}
> +
> +static Property altera_timer_properties[] = {
> + DEFINE_PROP_UINT32("frequency", struct altera_timer, freq_hz, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_timer_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = altera_timer_init;
> + dc->props = altera_timer_properties;
> +}
> +
> +static TypeInfo altera_timer_info = {
const
> + .name = "altera,timer",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct altera_timer),
> + .class_init = altera_timer_class_init,
> +};
> +
> +static void altera_timer_register(void)
> +{
> + type_register_static(&altera_timer_info);
> +}
> +
> +type_init(altera_timer_register)
> +
newline
> diff --git a/hw/altera_uart.c b/hw/altera_uart.c
> new file mode 100644
> index 0000000..e32651e
> --- /dev/null
> +++ b/hw/altera_uart.c
> @@ -0,0 +1,218 @@
> +/*
> + * QEMU model of the Altera uart.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "qemu-char.h"
> +
> +#define R_RXDATA 0
> +#define R_TXDATA 1
> +#define R_STATUS 2
> +#define R_CONTROL 3
> +#define R_DIVISOR 4
> +#define R_ENDOFPACKET 5
> +#define R_MAX 6
> +
> +#define STATUS_PE 0x0001
> +#define STATUS_FE 0x0002
> +#define STATUS_BRK 0x0004
> +#define STATUS_ROE 0x0008
> +#define STATUS_TOE 0x0010
> +#define STATUS_TMT 0x0020
> +#define STATUS_TRDY 0x0040
> +#define STATUS_RRDY 0x0080
> +#define STATUS_E 0x0100
> +#define STATUS_DTCS 0x0400
> +#define STATUS_CTS 0x0800
> +#define STATUS_EOP 0x1000
> +
> +#define CONTROL_IPE 0x0001
> +#define CONTROL_IFE 0x0002
> +#define CONTROL_IBRK 0x0004
> +#define CONTROL_IROE 0x0008
> +#define CONTROL_ITOE 0x0010
> +#define CONTROL_ITMT 0x0020
> +#define CONTROL_ITRDY 0x0040
> +#define CONTROL_IRRDY 0x0080
> +#define CONTROL_IE 0x0100
> +#define CONTROL_TBRK 0x0200
> +#define CONTROL_IDTCS 0x0400
> +#define CONTROL_RTS 0x0800
> +#define CONTROL_IEOP 0x1000
> +
> +struct altera_uart {
> + SysBusDevice busdev;
> + MemoryRegion mmio;
> + CharDriverState *chr;
> + qemu_irq irq;
> +
> + uint32_t regs[R_MAX];
> +};
> +
> +static void uart_update_irq(struct altera_uart *s)
> +{
> + unsigned int irq;
> +
> + irq = (s->regs[R_STATUS] & s->regs[R_CONTROL] &
> + (STATUS_PE | STATUS_FE | STATUS_BRK | STATUS_ROE | STATUS_TOE |
> + STATUS_TMT | STATUS_TRDY | STATUS_RRDY | STATUS_E | STATUS_DTCS));
> + irq = (irq == 0) ? 0 : 1;
> + qemu_set_irq(s->irq, irq);
> +}
> +
> +static uint64_t uart_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct altera_uart *s = opaque;
> + uint32_t r = 0;
> + addr >>= 2;
> + addr &= 0x7;
> + switch (addr) {
> + case R_RXDATA:
> + r = s->regs[R_RXDATA];
> + s->regs[R_STATUS] &= ~STATUS_RRDY;
> + uart_update_irq(s);
> + break;
> +
> + case R_STATUS:
> + r = s->regs[R_STATUS];
> + s->regs[R_STATUS] &= ~(STATUS_PE | STATUS_FE | STATUS_BRK |
> + STATUS_ROE | STATUS_TOE | STATUS_E |
> + STATUS_DTCS);
> + uart_update_irq(s);
> + break;
> +
> + default:
> + if (addr < ARRAY_SIZE(s->regs)) {
> + r = s->regs[addr];
> + }
> + break;
> + }
> +
> + return r;
> +}
> +
> +static void uart_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct altera_uart *s = opaque;
> + uint32_t value = val64;
> + unsigned char ch = value;
> +
> + addr >>= 2;
> + addr &= 0x7;
> +
> + switch (addr) {
> + case R_TXDATA:
> + if (s->chr) {
> + qemu_chr_fe_write(s->chr, &ch, 1);
> + }
> +
> + s->regs[addr] = value;
> + break;
> +
> + case R_RXDATA:
> + case R_STATUS:
> + /* No writeable bits */
> + break;
> +
> + default:
> + s->regs[addr] = value;
> + break;
> + }
> + uart_update_irq(s);
> +}
> +
> +static void uart_rx(void *opaque, const uint8_t *buf, int size)
> +{
> + struct altera_uart *s = opaque;
> +
> + s->regs[R_RXDATA] = *buf;
> + s->regs[R_STATUS] |= STATUS_RRDY;
> +
> + uart_update_irq(s);
> +}
> +
> +static int uart_can_rx(void *opaque)
> +{
> + struct altera_uart *s = opaque;
> + return ((s->regs[R_STATUS] & STATUS_RRDY) == 0);
> +}
> +
> +static void uart_event(void *opaque, int event)
> +{
> +}
> +
> +static const MemoryRegionOps uart_ops = {
> + .read = uart_read,
> + .write = uart_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 1,
> + .max_access_size = 4
> + }
> +};
> +
> +static int altera_uart_init(SysBusDevice *dev)
> +{
> + struct altera_uart *s = FROM_SYSBUS(typeof(*s), dev);
> +
> + s->regs[R_STATUS] = STATUS_TMT | STATUS_TRDY; /* Always ready to tx */
> +
> + sysbus_init_irq(dev, &s->irq);
> +
> + memory_region_init_io(&s->mmio, &uart_ops, s,
> + "altera,uart", R_MAX * sizeof(uint32_t));
> + sysbus_init_mmio(dev, &s->mmio);
> +
> + s->chr = qemu_char_get_next_serial();
> + if (s->chr) {
> + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s);
> + }
> +
> + return 0;
> +}
> +
> +static Property altera_uart_properties[] = {
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_uart_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = altera_uart_init;
> + dc->props = altera_uart_properties;
> +}
> +
> +static TypeInfo altera_uart_info = {
> + .name = "altera,uart",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct altera_uart),
> + .class_init = altera_uart_class_init,
> +};
> +
> +static void altera_uart_register(void)
> +{
> + type_register_static(&altera_uart_info);
> +}
> +
> +type_init(altera_uart_register)
> +
> diff --git a/hw/altera_vic.c b/hw/altera_vic.c
> new file mode 100644
> index 0000000..43a2b68
> --- /dev/null
> +++ b/hw/altera_vic.c
> @@ -0,0 +1,195 @@
> +/*
> + * QEMU Altera Vectored Interrupt Controller.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +
> +#define R_INT_CONFIG_0 0
> +#define R_INT_ENABLE 32
> +#define R_INT_ENABLE_SET 33
> +#define R_INT_ENABLE_CLR 34
> +#define R_INT_PENDING 35
> +#define R_INT_RAW_STATUS 36
> +#define R_SW_INTERRUPT 37
> +#define R_SW_INTERRUPT_SET 38
> +#define R_SW_INTERRUPT_CLR 39
> +#define R_VIC_CONFIG 40
> +#define R_VIC_STATUS 41
> +#define R_VEC_TBL_BASE 42
> +#define R_VEC_TBL_ADDR 43
> +#define R_MAX 44
> +
> +struct altera_vic {
> + SysBusDevice busdev;
> + MemoryRegion mmio;
> + qemu_irq parent_irq;
> +
> + /* Runtime control registers. */
> + uint32_t regs[R_MAX];
> +};
> +
> +static void update_irq(struct altera_vic *pv)
> +{
> + uint32_t i;
> + pv->regs[R_INT_PENDING] = (pv->regs[R_INT_RAW_STATUS] |
> + pv->regs[R_SW_INTERRUPT]) &
> + pv->regs[R_INT_ENABLE];
> +
> + for (i = 0; i < 32; i++) {
> + if (pv->regs[R_INT_PENDING] & (1 << i)) {
> + break;
> + }
> + }
> + if (i == 32) {
> + pv->regs[R_VEC_TBL_ADDR] = 0;
> + pv->regs[R_VIC_STATUS] = 0;
> + qemu_irq_lower(pv->parent_irq);
> + } else {
> + pv->regs[R_VEC_TBL_ADDR] = pv->regs[R_VEC_TBL_BASE] +
> + i*(4 << (pv->regs[R_VIC_CONFIG] & 7));
Spaces around '*'.
> + pv->regs[R_VIC_STATUS] = 0x80000000 | i;
> + qemu_irq_raise(pv->parent_irq);
> + }
> +}
> +
> +static uint64_t pic_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct altera_vic *pv = opaque;
> + uint32_t r = 0;
> +
> + addr >>= 2;
> + if (addr < R_MAX) {
> + r = pv->regs[addr];
> + }
> +
> + return r;
> +}
> +
> +static void pic_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct altera_vic *pv = opaque;
> + uint32_t value = val64;
> +
> + addr >>= 2;
> + if (addr < R_INT_ENABLE) {
> + /* R_INT_CONFIG_XX */
> + pv->regs[addr] = value & 0x00001FFF;
> + } else {
> + switch (addr) {
> + case R_INT_PENDING:
> + case R_INT_RAW_STATUS:
> + case R_VIC_STATUS:
> + case R_VEC_TBL_ADDR:
> + /* read only */
> + break;
> +
> + case R_INT_ENABLE_SET:
> + pv->regs[R_INT_ENABLE] |= value;
> + break;
> +
> + case R_SW_INTERRUPT_SET:
> + pv->regs[R_SW_INTERRUPT] |= value;
> + break;
> +
> + case R_INT_ENABLE_CLR:
> + pv->regs[R_INT_ENABLE] &= ~value;
> + break;
> +
> + case R_SW_INTERRUPT_CLR:
> + pv->regs[R_SW_INTERRUPT] &= ~value;
> + break;
> +
> + case R_VIC_CONFIG:
> + pv->regs[addr] = value & 0x0000000F;
> + break;
> +
> + default:
> + if (addr < ARRAY_SIZE(pv->regs)) {
> + pv->regs[addr] = value;
> + }
> + break;
> + }
> + }
> + update_irq(pv);
> +}
> +
> +static const MemoryRegionOps pic_ops = {
> + .read = pic_read,
> + .write = pic_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> + struct altera_vic *pv = opaque;
> +
> + pv->regs[R_INT_RAW_STATUS] &= ~(1 << irq);
> + pv->regs[R_INT_RAW_STATUS] |= level << irq;
> +
> + update_irq(pv);
> +}
> +
> +static int altera_vic_init(SysBusDevice *dev)
> +{
> + struct altera_vic *pv = FROM_SYSBUS(typeof(*pv), dev);
> +
> + qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> + sysbus_init_irq(dev, &pv->parent_irq);
> +
> + memset(pv->regs, 0, sizeof(uint32_t) * R_MAX);
> + memory_region_init_io(&pv->mmio, &pic_ops, pv,
> + "altera,vic", R_MAX * sizeof(uint32_t));
> + sysbus_init_mmio(dev, &pv->mmio);
> + return 0;
> +}
> +
> +static Property altera_vic_properties[] = {
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_vic_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = altera_vic_init;
> + dc->props = altera_vic_properties;
> +}
> +
> +static TypeInfo altera_vic_info = {
> + .name = "altera,vic",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct altera_vic),
> + .class_init = altera_vic_class_init,
> +};
> +
> +static void altera_vic_register(void)
> +{
> + type_register_static(&altera_vic_info);
> +}
> +
> +type_init(altera_vic_register)
> +
> diff --git a/hw/nios2.h b/hw/nios2.h
> new file mode 100644
> index 0000000..a4af154
> --- /dev/null
> +++ b/hw/nios2.h
> @@ -0,0 +1,46 @@
> +/*
> + * Altera Nios II CPU interrupt controllers
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
Missing duplicate include protection #ifdeffery.
> +#include "sysbus.h"
> +
> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env);
> +
> +static inline DeviceState *
> +altera_vic_create(target_phys_addr_t base, qemu_irq irq, int kind_of_intr)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "altera,vic");
> + qdev_init_nofail(dev);
> + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> + return dev;
> +}
> +
> +static inline DeviceState *
> +altera_iic_create(qemu_irq irq, int kind_of_intr)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "altera,iic");
> + qdev_init_nofail(dev);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> + return dev;
> +}
> diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
> new file mode 100644
> index 0000000..c89b4ae
> --- /dev/null
> +++ b/hw/nios2_pic_cpu.c
> @@ -0,0 +1,48 @@
> +/*
> + * QEMU Altera Nios II CPU interrupt wrapper logic.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "hw.h"
> +#include "pc.h"
> +#include "nios2.h"
> +
> +void pic_info(Monitor *mon)
> +{
> +}
> +
> +void irq_info(Monitor *mon)
> +{
> +}
> +
> +static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
> +{
> + CPUNios2State *env = (CPUNios2State *)opaque;
Useless cast in C.
> + int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
> +
> + if (level) {
> + cpu_interrupt(env, type);
> + } else {
> + cpu_reset_interrupt(env, type);
> + }
> +}
> +
> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
> +{
> + return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
> +}
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII.
2012-09-11 19:53 ` Blue Swirl
@ 2012-09-15 15:06 ` Andreas Färber
0 siblings, 0 replies; 31+ messages in thread
From: Andreas Färber @ 2012-09-15 15:06 UTC (permalink / raw)
To: crwulff; +Cc: Blue Swirl, qemu-devel
Am 11.09.2012 21:53, schrieb Blue Swirl:
> On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
>> diff --git a/hw/nios2_pic_cpu.c b/hw/nios2_pic_cpu.c
>> new file mode 100644
>> index 0000000..c89b4ae
>> --- /dev/null
>> +++ b/hw/nios2_pic_cpu.c
>> @@ -0,0 +1,48 @@
>> +/*
>> + * QEMU Altera Nios II CPU interrupt wrapper logic.
>> + *
>> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, see
>> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
>> + */
>> +
>> +#include "hw.h"
>> +#include "pc.h"
Why?
>> +#include "nios2.h"
>> +
>> +void pic_info(Monitor *mon)
>> +{
>> +}
>> +
>> +void irq_info(Monitor *mon)
>> +{
>> +}
Thought these stubs were no longer necessary...
>> +
>> +static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
>> +{
>> + CPUNios2State *env = (CPUNios2State *)opaque;
>
> Useless cast in C.
Please use Nios2CPU so that we can more easily make cpu_interrupt() and
cpu_reset_interrupt() take a CPUState argument in the future.
Please also split this patch up per device and always cc the appropriate
maintainers to facilitate review (e.g.,
--cc-cmd="scripts/get_maintainer.pl --nogit-fallback").
Regards,
Andreas
>
>> + int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
>> +
>> + if (level) {
>> + cpu_interrupt(env, type);
>> + } else {
>> + cpu_reset_interrupt(env, type);
>> + }
>> +}
>> +
>> +qemu_irq *nios2_pic_init_cpu(CPUNios2State *env)
>> +{
>> + return qemu_allocate_irqs(nios2_pic_cpu_handler, env, 2);
>> +}
--
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (2 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 3/9] Altera: Add support for Altera devices required to boot linux on NiosII crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-11 20:22 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children crwulff
` (5 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
hw/Makefile.objs | 7 +
hw/labx_audio_depacketizer.c | 409 ++++++++++++++++++++++++++++
hw/labx_audio_packetizer.c | 397 +++++++++++++++++++++++++++
hw/labx_devices.h | 103 +++++++
hw/labx_dma.c | 241 +++++++++++++++++
hw/labx_ethernet.c | 615 ++++++++++++++++++++++++++++++++++++++++++
hw/labx_ptp.c | 291 ++++++++++++++++++++
7 files changed, 2063 insertions(+)
create mode 100644 hw/labx_audio_depacketizer.c
create mode 100644 hw/labx_audio_packetizer.c
create mode 100644 hw/labx_devices.h
create mode 100644 hw/labx_dma.c
create mode 100644 hw/labx_ethernet.c
create mode 100644 hw/labx_ptp.c
diff --git a/hw/Makefile.objs b/hw/Makefile.objs
index 59dd2d5..ebbeb16 100644
--- a/hw/Makefile.objs
+++ b/hw/Makefile.objs
@@ -72,6 +72,13 @@ hw-obj-$(CONFIG_ALTERA) += altera_vic.o
hw-obj-$(CONFIG_ALTERA) += altera_uart.o
hw-obj-$(CONFIG_ALTERA) += altera_timer.o
+# Lab X devices
+hw-obj-$(CONFIG_LABX) += labx_audio_packetizer.o
+hw-obj-$(CONFIG_LABX) += labx_audio_depacketizer.o
+hw-obj-$(CONFIG_LABX) += labx_dma.o
+hw-obj-$(CONFIG_LABX) += labx_ethernet.o
+hw-obj-$(CONFIG_LABX) += labx_ptp.o
+
# PKUnity SoC devices
hw-obj-$(CONFIG_PUV3) += puv3_intc.o
hw-obj-$(CONFIG_PUV3) += puv3_ost.o
diff --git a/hw/labx_audio_depacketizer.c b/hw/labx_audio_depacketizer.c
new file mode 100644
index 0000000..5da3f47
--- /dev/null
+++ b/hw/labx_audio_depacketizer.c
@@ -0,0 +1,409 @@
+
+/*
+ * QEMU model of the LabX audio depacketizer.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "labx_devices.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+struct clock_domain_info {
+ uint32_t tsInterval;
+};
+
+typedef struct audio_depacketizer {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_depacketizer;
+ MemoryRegion mmio_clock_domain;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t clockDomains;
+ uint32_t cacheDataWords;
+ uint32_t paramWords;
+ uint32_t microcodeWords;
+ uint32_t maxStreamSlots;
+ uint32_t maxStreams;
+ uint32_t hasDMA;
+ uint32_t matchArch;
+
+ /* IRQ */
+ qemu_irq irq;
+
+ /* Values set by drivers */
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+
+ /* Clock domain information */
+ struct clock_domain_info *clockDomainInfo;
+
+ /* Attached DMA (if hasDMA > 0) */
+ DeviceState *dma;
+} depacketizer_t;
+
+/*
+ * Depacketizer registers
+ */
+static uint64_t depacketizer_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* vector bar */
+ break;
+
+ case 0x02: /* id select 0 */
+ break;
+
+ case 0x03: /* id select 1 */
+ break;
+
+ case 0x04: /* id select 2 */
+ break;
+
+ case 0x05: /* id select 3 */
+ break;
+
+ case 0x06: /* id config data */
+ break;
+
+ case 0x08: /* irq mask */
+ break;
+
+ case 0x09: /* irq flags */
+ break;
+
+ case 0x0A: /* sync */
+ break;
+
+ case 0x0B: /* relocate */
+ break;
+
+ case 0x0C: /* stream status 0 */
+ break;
+
+ case 0x0D: /* stream status 1 */
+ break;
+
+ case 0x0E: /* stream status 2 */
+ break;
+
+ case 0x0F: /* stream status 3 */
+ break;
+
+ case 0xFD: /* capabilities a */
+ retval = (p->maxStreamSlots & 0x7F);
+ break;
+
+ case 0xFE: /* capabilities b */
+ retval = ((p->matchArch & 0xFF) << 24) |
+ ((p->maxStreams & 0xFF) << 16) |
+ ((p->clockDomains & 0xFF) << 8) |
+ ((min_bits(p->paramWords-1) & 0x0F) << 4) |
+ ((min_bits(p->microcodeWords-1) & 0x0F));
+ break;
+
+ case 0xFF: /* revision */
+ retval = 0x00000014;
+ break;
+
+ default:
+ printf("labx-audio-depacketizer: Read of unknown register %08X\n",
+ addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void depacketizer_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*depacketizer_t *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* vector bar */
+ break;
+
+ case 0x02: /* id select 0 */
+ break;
+
+ case 0x03: /* id select 1 */
+ break;
+
+ case 0x04: /* id select 2 */
+ break;
+
+ case 0x05: /* id select 3 */
+ break;
+
+ case 0x06: /* id config data */
+ break;
+
+ case 0x08: /* irq mask */
+ break;
+
+ case 0x09: /* irq flags */
+ break;
+
+ case 0x0A: /* sync */
+ break;
+
+ case 0x0B: /* relocate */
+ break;
+
+ case 0x0C: /* stream status 0 */
+ break;
+
+ case 0x0D: /* stream status 1 */
+ break;
+
+ case 0x0E: /* stream status 2 */
+ break;
+
+ case 0x0F: /* stream status 3 */
+ break;
+
+ case 0xFD: /* capabilities a */
+ break;
+
+ case 0xFE: /* capabilities b */
+ break;
+
+ case 0xFF: /* revision */
+ break;
+
+ default:
+ printf("labx-audio-depacketizer: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps depacketizer_regs_ops = {
+ .read = depacketizer_regs_read,
+ .write = depacketizer_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Clock domain registers
+ */
+static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ uint32_t retval = 0;
+ int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x10) {
+ case 0x00: /* recovery index */
+ break;
+
+ case 0x01: /* ts interval */
+ retval = p->clockDomainInfo[domain].tsInterval;
+ break;
+
+ case 0x08: /* DAC offset */
+ break;
+
+ case 0x09: /* DAC P coeff */
+ break;
+
+ case 0x0A: /* lock count */
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ depacketizer_t *p = opaque;
+ uint32_t value = val64;
+ int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x10) {
+ case 0x00: /* recovery index */
+ break;
+
+ case 0x01: /* ts interval */
+ p->clockDomainInfo[domain].tsInterval = value;
+ break;
+
+ case 0x08: /* DAC offset */
+ break;
+
+ case 0x09: /* DAC P coeff */
+ break;
+
+ case 0x0A: /* lock count */
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps clock_domain_regs_ops = {
+ .read = clock_domain_regs_read,
+ .write = clock_domain_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ depacketizer_t *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ depacketizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_audio_depacketizer_init(SysBusDevice *dev)
+{
+ depacketizer_t *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+ p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
+ p->clockDomains);
+
+ /* Set up the IRQ */
+ sysbus_init_irq(dev, &p->irq);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_depacketizer, &depacketizer_regs_ops, p,
+ "labx,audio-depacketizer-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
+ "labx,audio-depacketizer-cd-regs",
+ 0x10 * 4 * p->clockDomains);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,audio-depacketizer-microcode",
+ 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_depacketizer);
+ sysbus_init_mmio(dev, &p->mmio_clock_domain);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress +
+ (2 << (min_bits(p->microcodeWords-1)+2)));
+
+ if (p->hasDMA) {
+ p->dma = labx_dma_create(p->baseAddress +
+ (4 << (min_bits(p->microcodeWords-1)+2)),
+ 1024);
+ }
+
+ return 0;
+}
+
+static Property labx_audio_depacketizer_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", depacketizer_t, baseAddress, 0),
+ DEFINE_PROP_UINT32("clockDomains", depacketizer_t, clockDomains, 1),
+ DEFINE_PROP_UINT32("cacheDataWords", depacketizer_t, cacheDataWords, 1024),
+ DEFINE_PROP_UINT32("paramWords", depacketizer_t, paramWords, 1024),
+ DEFINE_PROP_UINT32("microcodeWords", depacketizer_t, microcodeWords, 1024),
+ DEFINE_PROP_UINT32("maxStreamSlots", depacketizer_t, maxStreamSlots, 32),
+ DEFINE_PROP_UINT32("maxStreams", depacketizer_t, maxStreams, 128),
+ DEFINE_PROP_UINT32("hasDMA", depacketizer_t, hasDMA, 1),
+ DEFINE_PROP_UINT32("matchArch", depacketizer_t, matchArch, 255),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_audio_depacketizer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_audio_depacketizer_init;
+ dc->props = labx_audio_depacketizer_properties;
+}
+
+static TypeInfo labx_audio_depacketizer_info = {
+ .name = "labx,audio-depacketizer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(depacketizer_t),
+ .class_init = labx_audio_depacketizer_class_init,
+};
+
+static void labx_audio_depacketizer_register(void)
+{
+ type_register_static(&labx_audio_depacketizer_info);
+}
+
+type_init(labx_audio_depacketizer_register)
+
diff --git a/hw/labx_audio_packetizer.c b/hw/labx_audio_packetizer.c
new file mode 100644
index 0000000..120cce0
--- /dev/null
+++ b/hw/labx_audio_packetizer.c
@@ -0,0 +1,397 @@
+
+/*
+ * QEMU model of the LabX audio packetizer.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+struct clock_domain_info {
+ uint32_t tsInterval;
+ uint32_t domainEnabled;
+};
+
+typedef struct audio_packetizer {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_packetizer;
+ MemoryRegion mmio_clock_domain;
+ MemoryRegion mmio_template;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t clockDomains;
+ uint32_t cacheDataWords;
+ uint32_t templateWords;
+ uint32_t microcodeWords;
+ uint32_t shaperFractionBits;
+ uint32_t maxStreamSlots;
+ uint32_t dualOutput;
+
+ /* IRQ */
+ qemu_irq irq;
+
+ /* Values set by drivers */
+ uint32_t tsOffset;
+ uint32_t sendSlope;
+ uint32_t idleSlope;
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+
+ /* Template buffer */
+ uint32_t *templateRam;
+
+ /* Clock domain information */
+ struct clock_domain_info *clockDomainInfo;
+} packetizer_t;
+
+/*
+ * Packetizer registers
+ */
+static uint64_t packetizer_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* start vector */
+ break;
+
+ case 0x02: /* ts offset */
+ break;
+
+ case 0x03: /* irq mask */
+ break;
+
+ case 0x04: /* irq flags */
+ break;
+
+ case 0x05: /* sync reg */
+ break;
+
+ case 0x06: /* send slope */
+ break;
+
+ case 0x07: /* idle slope */
+ break;
+
+ case 0xFD: /* capabilities a */
+ retval = (p->maxStreamSlots & 0x7F) | ((p->dualOutput) ? 0x80 : 0x00);
+ break;
+
+ case 0xFE: /* capabilities b */
+ retval = ((p->shaperFractionBits & 0x7F) << 24) |
+ ((p->clockDomains & 0xFF) << 16) |
+ ((min_bits(p->templateWords-1) & 0xFF) << 8) |
+ ((min_bits(p->microcodeWords-1) & 0xFF));
+ break;
+
+ case 0xFF: /* revision */
+ retval = 0x00000013;
+ break;
+
+ default:
+ printf("labx-audio-packetizer: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void packetizer_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0xFF) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* start vector */
+ break;
+
+ case 0x02: /* ts offset */
+ p->tsOffset = value;
+ break;
+
+ case 0x03: /* irq mask */
+ break;
+
+ case 0x04: /* irq flags */
+ break;
+
+ case 0x05: /* sync reg */
+ break;
+
+ case 0x06: /* send slope */
+ p->sendSlope = value;
+ break;
+
+ case 0x07: /* idle slope */
+ p->idleSlope = value;
+ break;
+
+ case 0xFD: /* capabilities a */
+ break;
+
+ case 0xFE: /* capabilities b */
+ break;
+
+ case 0xFF: /* revision */
+ break;
+
+ default:
+ printf("labx-audio-packetizer: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps packetizer_regs_ops = {
+ .read = packetizer_regs_read,
+ .write = packetizer_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Clock domain registers
+ */
+static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ uint32_t retval = 0;
+ int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x01) {
+ case 0x00: /* ts interval */
+ retval = p->clockDomainInfo[domain].tsInterval;
+ break;
+
+ case 0x01: /* domain enable */
+ retval = p->clockDomainInfo[domain].domainEnabled;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
+
+ switch ((addr>>2)&0x01) {
+ case 0x00: /* ts interval */
+ p->clockDomainInfo[domain].tsInterval = value;
+ break;
+
+ case 0x01: /* domain enable */
+ p->clockDomainInfo[domain].domainEnabled = value;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const MemoryRegionOps clock_domain_regs_ops = {
+ .read = clock_domain_regs_read,
+ .write = clock_domain_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Template RAM
+ */
+static uint64_t template_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ return p->templateRam[RAM_INDEX(addr, p->templateWords)];
+}
+
+static void template_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->templateRam[RAM_INDEX(addr, p->templateWords)] = value;
+}
+
+static const MemoryRegionOps template_ram_ops = {
+ .read = template_ram_read,
+ .write = template_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ packetizer_t *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ packetizer_t *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static int labx_audio_packetizer_init(SysBusDevice *dev)
+{
+ packetizer_t *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->tsOffset = 0x00000000;
+ p->sendSlope = 0x00000000;
+ p->idleSlope = 0x00000000;
+ p->templateRam = g_malloc0(p->templateWords*4);
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+ p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
+ p->clockDomains);
+
+ /* Set up the IRQ */
+ sysbus_init_irq(dev, &p->irq);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_packetizer, &packetizer_regs_ops, p,
+ "labx,audio-packetizer-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
+ "labx,audio-packetizer-cd-regs",
+ 2 * 4 * p->clockDomains);
+ memory_region_init_io(&p->mmio_template, &template_ram_ops, p,
+ "labx,audio-packetizer-template",
+ 4 * p->templateWords);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,audio-packetizer-microcode",
+ 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_packetizer);
+ sysbus_init_mmio(dev, &p->mmio_clock_domain);
+ sysbus_init_mmio(dev, &p->mmio_template);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress +
+ (2 << (min_bits(p->microcodeWords-1)+2)));
+ sysbus_mmio_map(dev, 3, p->baseAddress +
+ (3 << (min_bits(p->microcodeWords-1)+2)));
+
+ return 0;
+}
+
+static Property labx_audio_packetizer_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", packetizer_t, baseAddress,
+ 0),
+ DEFINE_PROP_UINT32("clockDomains", packetizer_t, clockDomains,
+ 1),
+ DEFINE_PROP_UINT32("cacheDataWords", packetizer_t, cacheDataWords,
+ 1024),
+ DEFINE_PROP_UINT32("templateWords", packetizer_t, templateWords,
+ 1024),
+ DEFINE_PROP_UINT32("microcodeWords", packetizer_t, microcodeWords,
+ 1024),
+ DEFINE_PROP_UINT32("shaperFractionBits", packetizer_t, shaperFractionBits,
+ 16),
+ DEFINE_PROP_UINT32("maxStreamSlots", packetizer_t, maxStreamSlots,
+ 32),
+ DEFINE_PROP_UINT32("dualOutput", packetizer_t, dualOutput,
+ 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_audio_packetizer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_audio_packetizer_init;
+ dc->props = labx_audio_packetizer_properties;
+}
+
+static TypeInfo labx_audio_packetizer_info = {
+ .name = "labx,audio-packetizer",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(packetizer_t),
+ .class_init = labx_audio_packetizer_class_init,
+};
+
+static void labx_audio_packetizer_register(void)
+{
+ type_register_static(&labx_audio_packetizer_info);
+}
+
+type_init(labx_audio_packetizer_register)
+
diff --git a/hw/labx_devices.h b/hw/labx_devices.h
new file mode 100644
index 0000000..317341e
--- /dev/null
+++ b/hw/labx_devices.h
@@ -0,0 +1,103 @@
+/*
+ * Lab X device types header.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include <net.h>
+
+/* Audio packetizer */
+static inline DeviceState *
+labx_audio_packetizer_create(target_phys_addr_t base, qemu_irq irq,
+ int clockDomains, int cacheDataWords)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,audio-packetizer");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
+ qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* Audio depacketizer */
+static inline DeviceState *
+labx_audio_depacketizer_create(target_phys_addr_t base, qemu_irq irq,
+ int clockDomains, int cacheDataWords, int hasDMA)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,audio-depacketizer");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
+ qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
+ qdev_prop_set_uint32(dev, "hasDMA", hasDMA);
+ qdev_init_nofail(dev);
+ sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
+ return dev;
+}
+
+/* DMA */
+static inline DeviceState *
+labx_dma_create(target_phys_addr_t base, int microcodeWords)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,dma");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_prop_set_uint32(dev, "microcodeWords", microcodeWords);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
+/* Ethernet */
+static inline DeviceState *
+labx_ethernet_create(NICInfo *nd, target_phys_addr_t base, qemu_irq hostIrq,
+ qemu_irq fifoIrq, qemu_irq phyIrq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ qemu_check_nic_model(nd, "labx-ethernet");
+
+ dev = qdev_create(NULL, "labx,ethernet");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_set_nic_properties(dev, nd);
+ qdev_init_nofail(dev);
+
+ s = sysbus_from_qdev(dev);
+ sysbus_connect_irq(s, 0, hostIrq);
+ sysbus_connect_irq(s, 1, fifoIrq);
+ sysbus_connect_irq(s, 2, phyIrq);
+
+ return dev;
+}
+
+/* PTP */
+static inline DeviceState *
+labx_ptp_create(target_phys_addr_t base)
+{
+ DeviceState *dev;
+
+ dev = qdev_create(NULL, "labx,ptp");
+ qdev_prop_set_uint32(dev, "baseAddress", base);
+ qdev_init_nofail(dev);
+ return dev;
+}
+
diff --git a/hw/labx_dma.c b/hw/labx_dma.c
new file mode 100644
index 0000000..9d8058c
--- /dev/null
+++ b/hw/labx_dma.c
@@ -0,0 +1,241 @@
+
+/*
+ * QEMU model of the LabX DMA Engine.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+
+struct labx_dma {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_dma;
+ MemoryRegion mmio_microcode;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+ uint32_t paramWords;
+ uint32_t microcodeWords;
+ uint32_t numIndexRegs;
+ uint32_t numChannels;
+ uint32_t numAlus;
+
+ /* Values set by drivers */
+
+ /* Microcode buffer */
+ uint32_t *microcodeRam;
+};
+
+/*
+ * DMA registers
+ */
+static uint64_t dma_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_dma *p = opaque;
+
+ uint32_t retval = 0;
+
+ if ((addr>>2) & 0x80) {
+ /* vector */
+ } else {
+ switch ((addr>>2) & 0x7F) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* channel enable */
+ break;
+
+ case 0x02: /* channel start */
+ break;
+
+ case 0x03: /* channel irq enable */
+ break;
+
+ case 0x04: /* channel irq */
+ break;
+
+ case 0x05: /* sync */
+ break;
+
+ case 0x7E: /* capabilities */
+ retval = ((p->numIndexRegs & 0x0F) << 12) |
+ ((p->numChannels & 0x03) << 10) |
+ ((p->numAlus & 0x03) << 8) |
+ ((min_bits(p->paramWords-1) & 0x0F) << 4) |
+ ((min_bits(p->microcodeWords-1) & 0x0F));
+ break;
+
+ case 0x7F: /* revision */
+ retval = 0x00000011;
+ break;
+
+ default:
+ printf("labx-dma: Read of unknown register %08X\n", addr);
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static void dma_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_dma *p = opaque; */
+ uint32_t value = val64;
+
+ if ((addr>>2) & 0x80) {
+ /* vector */
+ } else {
+ switch ((addr>>2) & 0x7F) {
+ case 0x00: /* control */
+ break;
+
+ case 0x01: /* channel enable */
+ break;
+
+ case 0x02: /* channel start */
+ break;
+
+ case 0x03: /* channel irq enable */
+ break;
+
+ case 0x04: /* channel irq */
+ break;
+
+ case 0x05: /* sync */
+ break;
+
+ case 0x7E: /* capabilities */
+ break;
+
+ case 0x7F: /* revision */
+ break;
+
+ default:
+ printf("labx-dma: Write of unknown register "
+ "%08X = %08X\n", addr, value);
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps dma_regs_ops = {
+ .read = dma_regs_read,
+ .write = dma_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Microcode RAM
+ */
+static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_dma *p = opaque;
+
+ return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
+}
+
+static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_dma *p = opaque;
+ uint32_t value = val64;
+
+ p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
+}
+
+static const MemoryRegionOps microcode_ram_ops = {
+ .read = microcode_ram_read,
+ .write = microcode_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_dma_init(SysBusDevice *dev)
+{
+ struct labx_dma *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->microcodeRam = g_malloc0(p->microcodeWords*4);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_dma, &dma_regs_ops, p,
+ "labx,dma-regs", 0x100 * 4);
+ memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
+ "labx,dma-microcode", 4 * p->microcodeWords);
+
+ sysbus_init_mmio(dev, &p->mmio_dma);
+ sysbus_init_mmio(dev, &p->mmio_microcode);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress +
+ (1 << (min_bits(p->microcodeWords-1)+2)));
+
+ return 0;
+}
+
+static Property labx_dma_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_dma, baseAddress, 0),
+ DEFINE_PROP_UINT32("paramWords", struct labx_dma, paramWords, 1024),
+ DEFINE_PROP_UINT32("microcodeWords", struct labx_dma, microcodeWords, 1024),
+ DEFINE_PROP_UINT32("numIndexRegs", struct labx_dma, numIndexRegs, 4),
+ DEFINE_PROP_UINT32("numChannels", struct labx_dma, numChannels, 1),
+ DEFINE_PROP_UINT32("numAlus", struct labx_dma, numAlus, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_dma_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_dma_init;
+ dc->props = labx_dma_properties;
+}
+
+static TypeInfo labx_dma_info = {
+ .name = "labx,dma",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_dma),
+ .class_init = labx_dma_class_init,
+};
+
+static void labx_dma_register(void)
+{
+ type_register_static(&labx_dma_info);
+}
+
+type_init(labx_dma_register)
+
diff --git a/hw/labx_ethernet.c b/hw/labx_ethernet.c
new file mode 100644
index 0000000..c47c91b
--- /dev/null
+++ b/hw/labx_ethernet.c
@@ -0,0 +1,615 @@
+
+/*
+ * QEMU model of the LabX legacy ethernet core.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+#include "net.h"
+
+#define FIFO_RAM_BYTES 2048
+#define LENGTH_FIFO_WORDS 16
+
+struct labx_ethernet {
+ SysBusDevice busdev;
+ qemu_irq hostIrq;
+ qemu_irq fifoIrq;
+ qemu_irq phyIrq;
+ NICState *nic;
+ NICConf conf;
+
+ MemoryRegion mmio_ethernet;
+ MemoryRegion mmio_mac;
+ MemoryRegion mmio_fifo;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+
+ /* Values set by drivers */
+ uint32_t hostRegs[0x10];
+ uint32_t fifoRegs[0x10];
+
+ /* Tx buffers */
+ uint32_t *txBuffer;
+ uint32_t txPushIndex;
+ uint32_t txPopIndex;
+
+ uint32_t *txLengthBuffer;
+ uint32_t txLengthPushIndex;
+ uint32_t txLengthPopIndex;
+
+ /* Rx buffers */
+ uint32_t *rxBuffer;
+ uint32_t rxPushIndex;
+ uint32_t rxPopIndex;
+
+ uint32_t *rxLengthBuffer;
+ uint32_t rxLengthPushIndex;
+ uint32_t rxLengthPopIndex;
+};
+
+/*
+ * Legacy ethernet registers
+ */
+static void update_host_irq(struct labx_ethernet *p)
+{
+ if ((p->hostRegs[0x03] & p->hostRegs[2]) != 0) {
+ qemu_irq_raise(p->hostIrq);
+ } else {
+ qemu_irq_lower(p->hostIrq);
+ }
+}
+
+static void mdio_xfer(struct labx_ethernet *p, int readWrite,
+ int phyAddr, int regAddr)
+{
+ printf("MDIO %s: addr=%d, reg=%d\n", (readWrite) ? "READ" : "WRITE",
+ phyAddr, regAddr);
+ if (readWrite) {
+ /* TODO: PHY info */
+ p->hostRegs[0x01] = 0x0000FFFF;
+ }
+ p->hostRegs[0x03] |= 1;
+ update_host_irq(p);
+}
+
+static uint64_t ethernet_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* mdio control */
+ case 0x01: /* mdio data */
+ case 0x02: /* irq mask */
+ case 0x03: /* irq flags */
+ case 0x04: /* vlan mask */
+ case 0x05: /* filter select */
+ retval = p->hostRegs[(addr>>2) & 0x0F];
+ break;
+
+ case 0x06: /* filter control */
+ retval = 0x20000000;
+ break;
+
+ case 0x0F: /* revision */
+ retval = 0x00000C13;
+ break;
+
+ case 0x07: /* filter load */
+ retval = p->hostRegs[(addr>>2) & 0x0F];
+ break;
+
+ case 0x08: /* bad packet */
+ retval = 0;
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void ethernet_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* mdio control */
+ p->hostRegs[0x00] = (value & 0x000007FF);
+ mdio_xfer(p, (value >> 10) & 1, (value >> 5) & 0x1F, value & 0x1F);
+ break;
+
+ case 0x01: /* mdio data */
+ p->hostRegs[0x01] = (value & 0x0000FFFF);
+ break;
+
+ case 0x02: /* irq mask */
+ p->hostRegs[0x02] = (value & 0x00000003);
+ update_host_irq(p);
+ break;
+
+ case 0x03: /* irq flags */
+ p->hostRegs[0x03] &= ~(value & 0x00000003);
+ update_host_irq(p);
+ break;
+
+ case 0x04: /* vlan mask */
+ break;
+
+ case 0x05: /* filter select */
+ break;
+
+ case 0x06: /* filter control */
+ break;
+
+ case 0x07: /* filter load */
+ break;
+
+ case 0x08: /* bad packet */
+ break;
+
+ case 0x0F: /* revision */
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps ethernet_regs_ops = {
+ .read = ethernet_regs_read,
+ .write = ethernet_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * MAC registers
+ */
+static uint64_t mac_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ /*struct labx_ethernet *p = opaque; */
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x01: /* host rx config */
+ break;
+
+ case 0x02: /* host tx config */
+ break;
+
+ case 0x04: /* host speed config */
+ break;
+
+ case 0x05: /* host mdio config */
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown mac register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void mac_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_ethernet *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x01: /* host rx config */
+ break;
+
+ case 0x02: /* host tx config */
+ break;
+
+ case 0x04: /* host speed config */
+ break;
+
+ case 0x05: /* host mdio config */
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown mac register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps mac_regs_ops = {
+ .read = mac_regs_read,
+ .write = mac_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * FIFO registers
+ */
+
+#define FIFO_INT_STATUS_ADDRESS 0x0
+#define FIFO_INT_ENABLE_ADDRESS 0x1
+# define FIFO_INT_RPURE 0x80000000
+# define FIFO_INT_RPORE 0x40000000
+# define FIFO_INT_RPUE 0x20000000
+# define FIFO_INT_TPOE 0x10000000
+# define FIFO_INT_TC 0x08000000
+# define FIFO_INT_RC 0x04000000
+# define FIFO_INT_MASK 0xFC000000
+#define FIFO_TX_RESET_ADDRESS 0x2
+# define FIFO_RESET_MAGIC 0xA5
+#define FIFO_TX_VACANCY_ADDRESS 0x3
+#define FIFO_TX_DATA_ADDRESS 0x4
+#define FIFO_TX_LENGTH_ADDRESS 0x5
+#define FIFO_RX_RESET_ADDRESS 0x6
+#define FIFO_RX_OCCUPANCY_ADDRESS 0x7
+#define FIFO_RX_DATA_ADDRESS 0x8
+#define FIFO_RX_LENGTH_ADDRESS 0x9
+
+static void update_fifo_irq(struct labx_ethernet *p)
+{
+ if ((p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &
+ p->fifoRegs[FIFO_INT_ENABLE_ADDRESS]) != 0) {
+ qemu_irq_raise(p->fifoIrq);
+ } else {
+ qemu_irq_lower(p->fifoIrq);
+ }
+}
+
+static void send_packet(struct labx_ethernet *p)
+{
+ while (p->txLengthPopIndex != p->txLengthPushIndex) {
+ int i;
+ uint32_t packetBuf[512];
+
+ int length = p->txLengthBuffer[p->txLengthPopIndex];
+ p->txLengthPopIndex = (p->txLengthPopIndex + 1) % LENGTH_FIFO_WORDS;
+
+ for (i = 0; i < ((length+3)/4); i++) {
+ packetBuf[i] = be32_to_cpu(p->txBuffer[p->txPopIndex]);
+ p->txPopIndex = (p->txPopIndex + 1) % (FIFO_RAM_BYTES/4);
+ }
+
+ qemu_send_packet(&p->nic->nc, (void *)packetBuf, length);
+ }
+
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TC;
+ update_fifo_irq(p);
+}
+
+static uint64_t fifo_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case FIFO_INT_STATUS_ADDRESS:
+ case FIFO_INT_ENABLE_ADDRESS:
+ case FIFO_TX_RESET_ADDRESS:
+ retval = p->fifoRegs[(addr>>2) & 0x0F];
+ break;
+
+ case FIFO_TX_VACANCY_ADDRESS:
+ retval = (p->txPopIndex - p->txPushIndex) - 1;
+ if ((int32_t)retval < 0) {
+ retval += (FIFO_RAM_BYTES/4);
+ }
+
+ if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) {
+ /* Full length fifo */
+ retval = 0;
+ }
+ break;
+
+ case FIFO_TX_DATA_ADDRESS:
+ case FIFO_TX_LENGTH_ADDRESS:
+ case FIFO_RX_RESET_ADDRESS:
+ retval = p->fifoRegs[(addr>>2) & 0x0F];
+ break;
+
+ case FIFO_RX_OCCUPANCY_ADDRESS:
+ retval = p->rxPushIndex - p->rxPopIndex;
+ if ((int32_t)retval < 0) {
+ retval += (FIFO_RAM_BYTES/4);
+ }
+ break;
+
+ case FIFO_RX_DATA_ADDRESS:
+ retval = p->rxBuffer[p->rxPopIndex];
+ if (p->rxPopIndex != p->rxPushIndex) {
+ p->rxPopIndex = (p->rxPopIndex+1) % (FIFO_RAM_BYTES/4);
+ } else {
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
+ update_fifo_irq(p);
+ }
+ break;
+
+ case FIFO_RX_LENGTH_ADDRESS:
+ retval = p->rxLengthBuffer[p->rxLengthPopIndex];
+ if (p->rxLengthPopIndex != p->rxLengthPushIndex) {
+ p->rxLengthPopIndex = (p->rxLengthPopIndex+1) % LENGTH_FIFO_WORDS;
+ } else {
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
+ update_fifo_irq(p);
+ }
+ break;
+
+ default:
+ printf("labx-ethernet: Read of unknown fifo register %08X\n", addr);
+ break;
+ }
+
+ /* printf("FIFO REG READ %08X (%d) = %08X\n",
+ addr, (addr>>2) & 0x0F, retval); */
+
+ return retval;
+}
+
+static void fifo_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ethernet *p = opaque;
+ uint32_t value = val64;
+
+ /* printf("FIFO REG WRITE %08X (%d) = %08X\n",
+ addr, (addr>>2) & 0x0F, value); */
+
+ switch ((addr>>2) & 0x0F) {
+ case FIFO_INT_STATUS_ADDRESS:
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &= ~(value & FIFO_INT_MASK);
+ update_fifo_irq(p);
+ break;
+
+ case FIFO_INT_ENABLE_ADDRESS:
+ p->fifoRegs[FIFO_INT_ENABLE_ADDRESS] = (value & FIFO_INT_MASK);
+ update_fifo_irq(p);
+ break;
+
+ case FIFO_TX_RESET_ADDRESS:
+ if (value == FIFO_RESET_MAGIC) {
+ p->txPushIndex = 0;
+ p->txPopIndex = 0;
+ p->txLengthPushIndex = 0;
+ p->txLengthPopIndex = 0;
+ }
+ break;
+
+ case FIFO_TX_VACANCY_ADDRESS:
+ break;
+
+ case FIFO_TX_DATA_ADDRESS:
+ if ((((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) ||
+ (((p->txPushIndex + 1) % (FIFO_RAM_BYTES/4)) == p->txPopIndex)) {
+ /* Full length fifo or data fifo */
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
+ update_fifo_irq(p);
+ } else {
+ /* Push back the data */
+ p->txBuffer[p->txPushIndex] = value;
+ p->txPushIndex = (p->txPushIndex + 1) % (FIFO_RAM_BYTES/4);
+ }
+ break;
+
+ case FIFO_TX_LENGTH_ADDRESS:
+ if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
+ p->txLengthPopIndex) {
+ /* Full length fifo */
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
+ update_fifo_irq(p);
+ } else {
+ /* Push back the length */
+ p->txLengthBuffer[p->txLengthPushIndex] = value;
+ p->txLengthPushIndex = (p->txLengthPushIndex + 1) %
+ LENGTH_FIFO_WORDS;
+ send_packet(p);
+ }
+ break;
+
+ case FIFO_RX_RESET_ADDRESS:
+ if (value == FIFO_RESET_MAGIC) {
+ p->rxPushIndex = 0;
+ p->rxPopIndex = 0;
+ p->rxLengthPushIndex = 0;
+ p->rxLengthPopIndex = 0;
+ }
+ break;
+
+ case FIFO_RX_OCCUPANCY_ADDRESS:
+ break;
+
+ case FIFO_RX_DATA_ADDRESS:
+ break;
+
+ case FIFO_RX_LENGTH_ADDRESS:
+ break;
+
+ default:
+ printf("labx-ethernet: Write of unknown fifo register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps fifo_regs_ops = {
+ .read = fifo_regs_read,
+ .write = fifo_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int eth_can_rx(NetClientState *nc)
+{
+ /*struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque; */
+
+ return 1;
+}
+
+static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ struct labx_ethernet *p = DO_UPCAST(NICState, nc, nc)->opaque;
+ int i;
+ const uint32_t *wbuf = (const uint32_t *)buf;
+ int rxPushIndexStart = p->rxPushIndex;
+
+ for (i = 0; i < ((size+3)/4); i++) {
+ p->rxBuffer[p->rxPushIndex] = cpu_to_be32(wbuf[i]);
+ p->rxPushIndex = (p->rxPushIndex + 1) % (FIFO_RAM_BYTES/4);
+ if (p->rxPushIndex == p->rxPopIndex) {
+ /* Packet didn't fit */
+ p->rxPushIndex = rxPushIndexStart;
+ return -1;
+ }
+ }
+
+ if ((p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS == p->rxLengthPopIndex) {
+ /* Length didn't fit */
+ p->rxPushIndex = rxPushIndexStart;
+ return -1;
+ }
+
+ p->rxLengthBuffer[p->rxLengthPushIndex] = size;
+ p->rxLengthPushIndex = (p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS;
+
+ p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RC;
+ update_fifo_irq(p);
+
+ return size;
+}
+
+static void eth_cleanup(NetClientState *nc)
+{
+ struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque;
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_labx_ethernet_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .size = sizeof(NICState),
+ .can_receive = eth_can_rx,
+ .receive = eth_rx,
+ .cleanup = eth_cleanup,
+};
+
+static int labx_ethernet_init(SysBusDevice *dev)
+{
+ struct labx_ethernet *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->txBuffer = g_malloc0(FIFO_RAM_BYTES);
+ p->txLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
+ p->rxBuffer = g_malloc0(FIFO_RAM_BYTES);
+ p->rxLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
+
+ p->txPushIndex = 0;
+ p->txPopIndex = 0;
+ p->txLengthPushIndex = 0;
+ p->txLengthPopIndex = 0;
+ p->rxPushIndex = 0;
+ p->rxPopIndex = 0;
+ p->rxLengthPushIndex = 0;
+ p->rxLengthPopIndex = 0;
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_ethernet, ðernet_regs_ops, p,
+ "labx,ethernet-regs", 0x10 * 4);
+ memory_region_init_io(&p->mmio_mac, &mac_regs_ops, p,
+ "labx,ethernet-mac-regs", 0x10 * 4);
+ memory_region_init_io(&p->mmio_fifo, &fifo_regs_ops, p,
+ "labx,ethernet-fifo-regs", 0x10 * 4);
+
+ sysbus_init_mmio(dev, &p->mmio_ethernet);
+ sysbus_init_mmio(dev, &p->mmio_mac);
+ sysbus_init_mmio(dev, &p->mmio_fifo);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress + (1 << (10+2)));
+ sysbus_mmio_map(dev, 2, p->baseAddress + (2 << (10+2)));
+
+ /* Initialize the irqs */
+ sysbus_init_irq(dev, &p->hostIrq);
+ sysbus_init_irq(dev, &p->fifoIrq);
+ sysbus_init_irq(dev, &p->phyIrq);
+
+ /* Set up the NIC */
+ qemu_macaddr_default_if_unset(&p->conf.macaddr);
+ p->nic = qemu_new_nic(&net_labx_ethernet_info, &p->conf,
+ object_get_typename(OBJECT(p)), dev->qdev.id, p);
+ qemu_format_nic_info_str(&p->nic->nc, p->conf.macaddr.a);
+ return 0;
+}
+
+static Property labx_ethernet_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_ethernet, baseAddress, 0),
+ DEFINE_NIC_PROPERTIES(struct labx_ethernet, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_ethernet_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_ethernet_init;
+ dc->props = labx_ethernet_properties;
+}
+
+static TypeInfo labx_ethernet_info = {
+ .name = "labx,ethernet",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_ethernet),
+ .class_init = labx_ethernet_class_init,
+};
+
+static void labx_ethernet_register(void)
+{
+ type_register_static(&labx_ethernet_info);
+}
+
+type_init(labx_ethernet_register)
+
diff --git a/hw/labx_ptp.c b/hw/labx_ptp.c
new file mode 100644
index 0000000..68d4b54
--- /dev/null
+++ b/hw/labx_ptp.c
@@ -0,0 +1,291 @@
+
+/*
+ * QEMU model of the LabX PTP.
+ *
+ * Copyright (c) 2010 Lab X Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "sysemu.h"
+
+#define min_bits qemu_fls
+#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
+
+#define PTP_MAX_PACKETS 8
+#define PTP_MAX_PACKET_BYTES 256
+#define PTP_RAM_BYTES (PTP_MAX_PACKETS*PTP_MAX_PACKET_BYTES)
+#define PTP_HOST_RAM_WORDS (PTP_RAM_BYTES/4)
+
+struct labx_ptp {
+ SysBusDevice busdev;
+
+ MemoryRegion mmio_ptp;
+ MemoryRegion mmio_tx;
+ MemoryRegion mmio_rx;
+
+ /* Device Configuration */
+ uint32_t baseAddress;
+
+ /* Values set by drivers */
+
+ /* Tx buffers */
+ uint32_t *txRam;
+
+ /* Rx buffers */
+ uint32_t *rxRam;
+};
+
+/*
+ * PTP registers
+ */
+static uint64_t ptp_regs_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ /*struct labx_ptp *p = opaque; */
+
+ uint32_t retval = 0;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* rx */
+ break;
+
+ case 0x01: /* tx */
+ break;
+
+ case 0x02: /* irq mask */
+ break;
+
+ case 0x03: /* irq flags */
+ break;
+
+ case 0x04: /* rtc increment */
+ break;
+
+ case 0x05: /* seconds high */
+ break;
+
+ case 0x06: /* seconds low */
+ break;
+
+ case 0x07: /* nanoseconds */
+ break;
+
+ case 0x08: /* timer */
+ break;
+
+ case 0x09: /* local seconds high */
+ break;
+
+ case 0x0A: /* local seconds low */
+ break;
+
+ case 0x0B: /* local nanoseconds */
+ break;
+
+ case 0x0F: /* revision */
+ retval = 0x00000111; /* Report 1 port, revision 1.1 */
+ break;
+
+ default:
+ printf("labx-ptp: Read of unknown register %08X\n", addr);
+ break;
+ }
+
+ return retval;
+}
+
+static void ptp_regs_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ /*struct labx_ptp *p = opaque; */
+ uint32_t value = val64;
+
+ switch ((addr>>2) & 0x0F) {
+ case 0x00: /* rx */
+ break;
+
+ case 0x01: /* tx */
+ break;
+
+ case 0x02: /* irq mask */
+ break;
+
+ case 0x03: /* irq flags */
+ break;
+
+ case 0x04: /* rtc increment */
+ break;
+
+ case 0x05: /* seconds high */
+ break;
+
+ case 0x06: /* seconds low */
+ break;
+
+ case 0x07: /* nanoseconds */
+ break;
+
+ case 0x08: /* timer */
+ break;
+
+ case 0x09: /* local seconds high */
+ break;
+
+ case 0x0A: /* local seconds low */
+ break;
+
+ case 0x0B: /* local nanoseconds */
+ break;
+
+ case 0x0F: /* revision */
+ break;
+
+ default:
+ printf("labx-ptp: Write of unknown register %08X = %08X\n",
+ addr, value);
+ break;
+ }
+}
+
+static const MemoryRegionOps ptp_regs_ops = {
+ .read = ptp_regs_read,
+ .write = ptp_regs_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Tx Ram
+ */
+static uint64_t tx_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+
+ return p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
+}
+
+static void tx_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+ uint32_t value = val64;
+
+ p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
+}
+
+static const MemoryRegionOps tx_ram_ops = {
+ .read = tx_ram_read,
+ .write = tx_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+/*
+ * Rx Ram
+ */
+static uint64_t rx_ram_read(void *opaque, target_phys_addr_t addr,
+ unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+
+ return p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
+}
+
+static void rx_ram_write(void *opaque, target_phys_addr_t addr,
+ uint64_t val64, unsigned int size)
+{
+ struct labx_ptp *p = opaque;
+ uint32_t value = val64;
+
+ p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
+}
+
+static const MemoryRegionOps rx_ram_ops = {
+ .read = rx_ram_read,
+ .write = rx_ram_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+
+static int labx_ptp_init(SysBusDevice *dev)
+{
+ struct labx_ptp *p = FROM_SYSBUS(typeof(*p), dev);
+
+ /* Initialize defaults */
+ p->txRam = g_malloc0(PTP_RAM_BYTES);
+ p->rxRam = g_malloc0(PTP_RAM_BYTES);
+
+ /* Set up memory regions */
+ memory_region_init_io(&p->mmio_ptp, &ptp_regs_ops, p, "labx,ptp-regs",
+ 0x100 * 4);
+ memory_region_init_io(&p->mmio_tx, &tx_ram_ops, p, "labx,ptp-tx",
+ PTP_RAM_BYTES);
+ memory_region_init_io(&p->mmio_rx, &rx_ram_ops, p, "labx,ptp-rx",
+ PTP_RAM_BYTES);
+
+ sysbus_init_mmio(dev, &p->mmio_ptp);
+ sysbus_init_mmio(dev, &p->mmio_tx);
+ sysbus_init_mmio(dev, &p->mmio_rx);
+
+ sysbus_mmio_map(dev, 0, p->baseAddress);
+ sysbus_mmio_map(dev, 1, p->baseAddress + (1 << min_bits(PTP_RAM_BYTES-1)));
+ sysbus_mmio_map(dev, 2, p->baseAddress + (2 << min_bits(PTP_RAM_BYTES-1)));
+
+ return 0;
+}
+
+static Property labx_ptp_properties[] = {
+ DEFINE_PROP_UINT32("baseAddress", struct labx_ptp, baseAddress, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void labx_ptp_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = labx_ptp_init;
+ dc->props = labx_ptp_properties;
+}
+
+static TypeInfo labx_ptp_info = {
+ .name = "labx,ptp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(struct labx_ptp),
+ .class_init = labx_ptp_class_init,
+};
+
+static void labx_ptp_register(void)
+{
+ type_register_static(&labx_ptp_info);
+}
+
+type_init(labx_ptp_register)
+
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices crwulff
@ 2012-09-11 20:22 ` Blue Swirl
0 siblings, 0 replies; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 20:22 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
Same comments as to other patches. Pointers to chip set documentation
would be nice.
> ---
> hw/Makefile.objs | 7 +
> hw/labx_audio_depacketizer.c | 409 ++++++++++++++++++++++++++++
> hw/labx_audio_packetizer.c | 397 +++++++++++++++++++++++++++
> hw/labx_devices.h | 103 +++++++
> hw/labx_dma.c | 241 +++++++++++++++++
> hw/labx_ethernet.c | 615 ++++++++++++++++++++++++++++++++++++++++++
> hw/labx_ptp.c | 291 ++++++++++++++++++++
> 7 files changed, 2063 insertions(+)
> create mode 100644 hw/labx_audio_depacketizer.c
> create mode 100644 hw/labx_audio_packetizer.c
> create mode 100644 hw/labx_devices.h
> create mode 100644 hw/labx_dma.c
> create mode 100644 hw/labx_ethernet.c
> create mode 100644 hw/labx_ptp.c
>
> diff --git a/hw/Makefile.objs b/hw/Makefile.objs
> index 59dd2d5..ebbeb16 100644
> --- a/hw/Makefile.objs
> +++ b/hw/Makefile.objs
> @@ -72,6 +72,13 @@ hw-obj-$(CONFIG_ALTERA) += altera_vic.o
> hw-obj-$(CONFIG_ALTERA) += altera_uart.o
> hw-obj-$(CONFIG_ALTERA) += altera_timer.o
>
> +# Lab X devices
> +hw-obj-$(CONFIG_LABX) += labx_audio_packetizer.o
> +hw-obj-$(CONFIG_LABX) += labx_audio_depacketizer.o
> +hw-obj-$(CONFIG_LABX) += labx_dma.o
> +hw-obj-$(CONFIG_LABX) += labx_ethernet.o
> +hw-obj-$(CONFIG_LABX) += labx_ptp.o
> +
> # PKUnity SoC devices
> hw-obj-$(CONFIG_PUV3) += puv3_intc.o
> hw-obj-$(CONFIG_PUV3) += puv3_ost.o
> diff --git a/hw/labx_audio_depacketizer.c b/hw/labx_audio_depacketizer.c
> new file mode 100644
> index 0000000..5da3f47
> --- /dev/null
> +++ b/hw/labx_audio_depacketizer.c
> @@ -0,0 +1,409 @@
> +
> +/*
> + * QEMU model of the LabX audio depacketizer.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +#include "labx_devices.h"
> +
> +#define min_bits qemu_fls
> +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
> +
> +struct clock_domain_info {
> + uint32_t tsInterval;
> +};
> +
> +typedef struct audio_depacketizer {
> + SysBusDevice busdev;
> +
> + MemoryRegion mmio_depacketizer;
> + MemoryRegion mmio_clock_domain;
> + MemoryRegion mmio_microcode;
> +
> + /* Device Configuration */
> + uint32_t baseAddress;
> + uint32_t clockDomains;
> + uint32_t cacheDataWords;
> + uint32_t paramWords;
> + uint32_t microcodeWords;
> + uint32_t maxStreamSlots;
> + uint32_t maxStreams;
> + uint32_t hasDMA;
> + uint32_t matchArch;
> +
> + /* IRQ */
> + qemu_irq irq;
> +
> + /* Values set by drivers */
> +
> + /* Microcode buffer */
> + uint32_t *microcodeRam;
> +
> + /* Clock domain information */
> + struct clock_domain_info *clockDomainInfo;
> +
> + /* Attached DMA (if hasDMA > 0) */
> + DeviceState *dma;
> +} depacketizer_t;
> +
> +/*
> + * Depacketizer registers
> + */
> +static uint64_t depacketizer_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + depacketizer_t *p = opaque;
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0xFF) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* vector bar */
> + break;
> +
> + case 0x02: /* id select 0 */
> + break;
> +
> + case 0x03: /* id select 1 */
> + break;
> +
> + case 0x04: /* id select 2 */
> + break;
> +
> + case 0x05: /* id select 3 */
> + break;
> +
> + case 0x06: /* id config data */
> + break;
> +
> + case 0x08: /* irq mask */
> + break;
> +
> + case 0x09: /* irq flags */
> + break;
> +
> + case 0x0A: /* sync */
> + break;
> +
> + case 0x0B: /* relocate */
> + break;
> +
> + case 0x0C: /* stream status 0 */
> + break;
> +
> + case 0x0D: /* stream status 1 */
> + break;
> +
> + case 0x0E: /* stream status 2 */
> + break;
> +
> + case 0x0F: /* stream status 3 */
> + break;
> +
> + case 0xFD: /* capabilities a */
> + retval = (p->maxStreamSlots & 0x7F);
> + break;
> +
> + case 0xFE: /* capabilities b */
> + retval = ((p->matchArch & 0xFF) << 24) |
> + ((p->maxStreams & 0xFF) << 16) |
> + ((p->clockDomains & 0xFF) << 8) |
> + ((min_bits(p->paramWords-1) & 0x0F) << 4) |
> + ((min_bits(p->microcodeWords-1) & 0x0F));
> + break;
> +
> + case 0xFF: /* revision */
> + retval = 0x00000014;
> + break;
> +
> + default:
> + printf("labx-audio-depacketizer: Read of unknown register %08X\n",
> + addr);
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void depacketizer_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + /*depacketizer_t *p = opaque; */
> + uint32_t value = val64;
> +
> + switch ((addr>>2) & 0xFF) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* vector bar */
> + break;
> +
> + case 0x02: /* id select 0 */
> + break;
> +
> + case 0x03: /* id select 1 */
> + break;
> +
> + case 0x04: /* id select 2 */
> + break;
> +
> + case 0x05: /* id select 3 */
> + break;
> +
> + case 0x06: /* id config data */
> + break;
> +
> + case 0x08: /* irq mask */
> + break;
> +
> + case 0x09: /* irq flags */
> + break;
> +
> + case 0x0A: /* sync */
> + break;
> +
> + case 0x0B: /* relocate */
> + break;
> +
> + case 0x0C: /* stream status 0 */
> + break;
> +
> + case 0x0D: /* stream status 1 */
> + break;
> +
> + case 0x0E: /* stream status 2 */
> + break;
> +
> + case 0x0F: /* stream status 3 */
> + break;
> +
> + case 0xFD: /* capabilities a */
> + break;
> +
> + case 0xFE: /* capabilities b */
> + break;
> +
> + case 0xFF: /* revision */
> + break;
> +
> + default:
> + printf("labx-audio-depacketizer: Write of unknown register "
> + "%08X = %08X\n", addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps depacketizer_regs_ops = {
> + .read = depacketizer_regs_read,
> + .write = depacketizer_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Clock domain registers
> + */
> +static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + depacketizer_t *p = opaque;
> +
> + uint32_t retval = 0;
> + int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
> +
> + switch ((addr>>2)&0x10) {
> + case 0x00: /* recovery index */
> + break;
> +
> + case 0x01: /* ts interval */
> + retval = p->clockDomainInfo[domain].tsInterval;
> + break;
> +
> + case 0x08: /* DAC offset */
> + break;
> +
> + case 0x09: /* DAC P coeff */
> + break;
> +
> + case 0x0A: /* lock count */
> + break;
> +
> + default:
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + depacketizer_t *p = opaque;
> + uint32_t value = val64;
> + int domain = (addr>>6) & ((1<<min_bits(p->clockDomains-1))-1);
> +
> + switch ((addr>>2)&0x10) {
> + case 0x00: /* recovery index */
> + break;
> +
> + case 0x01: /* ts interval */
> + p->clockDomainInfo[domain].tsInterval = value;
> + break;
> +
> + case 0x08: /* DAC offset */
> + break;
> +
> + case 0x09: /* DAC P coeff */
> + break;
> +
> + case 0x0A: /* lock count */
> + break;
> +
> + default:
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps clock_domain_regs_ops = {
> + .read = clock_domain_regs_read,
> + .write = clock_domain_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Microcode RAM
> + */
> +static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + depacketizer_t *p = opaque;
> +
> + return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
> +}
> +
> +static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + depacketizer_t *p = opaque;
> + uint32_t value = val64;
> +
> + p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
> +}
> +
> +static const MemoryRegionOps microcode_ram_ops = {
> + .read = microcode_ram_read,
> + .write = microcode_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +static int labx_audio_depacketizer_init(SysBusDevice *dev)
> +{
> + depacketizer_t *p = FROM_SYSBUS(typeof(*p), dev);
> +
> + /* Initialize defaults */
> + p->microcodeRam = g_malloc0(p->microcodeWords*4);
> + p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
> + p->clockDomains);
> +
> + /* Set up the IRQ */
> + sysbus_init_irq(dev, &p->irq);
> +
> + /* Set up memory regions */
> + memory_region_init_io(&p->mmio_depacketizer, &depacketizer_regs_ops, p,
> + "labx,audio-depacketizer-regs",
> + 0x100 * 4);
> + memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
> + "labx,audio-depacketizer-cd-regs",
> + 0x10 * 4 * p->clockDomains);
> + memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
> + "labx,audio-depacketizer-microcode",
> + 4 * p->microcodeWords);
> +
> + sysbus_init_mmio(dev, &p->mmio_depacketizer);
> + sysbus_init_mmio(dev, &p->mmio_clock_domain);
> + sysbus_init_mmio(dev, &p->mmio_microcode);
> +
> + sysbus_mmio_map(dev, 0, p->baseAddress);
> + sysbus_mmio_map(dev, 1, p->baseAddress +
> + (1 << (min_bits(p->microcodeWords-1)+2)));
> + sysbus_mmio_map(dev, 2, p->baseAddress +
> + (2 << (min_bits(p->microcodeWords-1)+2)));
> +
> + if (p->hasDMA) {
> + p->dma = labx_dma_create(p->baseAddress +
> + (4 << (min_bits(p->microcodeWords-1)+2)),
> + 1024);
> + }
> +
> + return 0;
> +}
> +
> +static Property labx_audio_depacketizer_properties[] = {
> + DEFINE_PROP_UINT32("baseAddress", depacketizer_t, baseAddress, 0),
> + DEFINE_PROP_UINT32("clockDomains", depacketizer_t, clockDomains, 1),
> + DEFINE_PROP_UINT32("cacheDataWords", depacketizer_t, cacheDataWords, 1024),
> + DEFINE_PROP_UINT32("paramWords", depacketizer_t, paramWords, 1024),
> + DEFINE_PROP_UINT32("microcodeWords", depacketizer_t, microcodeWords, 1024),
> + DEFINE_PROP_UINT32("maxStreamSlots", depacketizer_t, maxStreamSlots, 32),
> + DEFINE_PROP_UINT32("maxStreams", depacketizer_t, maxStreams, 128),
> + DEFINE_PROP_UINT32("hasDMA", depacketizer_t, hasDMA, 1),
> + DEFINE_PROP_UINT32("matchArch", depacketizer_t, matchArch, 255),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void labx_audio_depacketizer_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = labx_audio_depacketizer_init;
> + dc->props = labx_audio_depacketizer_properties;
> +}
> +
> +static TypeInfo labx_audio_depacketizer_info = {
> + .name = "labx,audio-depacketizer",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(depacketizer_t),
> + .class_init = labx_audio_depacketizer_class_init,
> +};
> +
> +static void labx_audio_depacketizer_register(void)
> +{
> + type_register_static(&labx_audio_depacketizer_info);
> +}
> +
> +type_init(labx_audio_depacketizer_register)
> +
> diff --git a/hw/labx_audio_packetizer.c b/hw/labx_audio_packetizer.c
> new file mode 100644
> index 0000000..120cce0
> --- /dev/null
> +++ b/hw/labx_audio_packetizer.c
> @@ -0,0 +1,397 @@
> +
> +/*
> + * QEMU model of the LabX audio packetizer.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +
> +#define min_bits qemu_fls
> +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
> +
> +struct clock_domain_info {
> + uint32_t tsInterval;
> + uint32_t domainEnabled;
> +};
> +
> +typedef struct audio_packetizer {
> + SysBusDevice busdev;
> +
> + MemoryRegion mmio_packetizer;
> + MemoryRegion mmio_clock_domain;
> + MemoryRegion mmio_template;
> + MemoryRegion mmio_microcode;
> +
> + /* Device Configuration */
> + uint32_t baseAddress;
> + uint32_t clockDomains;
> + uint32_t cacheDataWords;
> + uint32_t templateWords;
> + uint32_t microcodeWords;
> + uint32_t shaperFractionBits;
> + uint32_t maxStreamSlots;
> + uint32_t dualOutput;
> +
> + /* IRQ */
> + qemu_irq irq;
> +
> + /* Values set by drivers */
> + uint32_t tsOffset;
> + uint32_t sendSlope;
> + uint32_t idleSlope;
> +
> + /* Microcode buffer */
> + uint32_t *microcodeRam;
> +
> + /* Template buffer */
> + uint32_t *templateRam;
> +
> + /* Clock domain information */
> + struct clock_domain_info *clockDomainInfo;
> +} packetizer_t;
> +
> +/*
> + * Packetizer registers
> + */
> +static uint64_t packetizer_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + packetizer_t *p = opaque;
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0xFF) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* start vector */
> + break;
> +
> + case 0x02: /* ts offset */
> + break;
> +
> + case 0x03: /* irq mask */
> + break;
> +
> + case 0x04: /* irq flags */
> + break;
> +
> + case 0x05: /* sync reg */
> + break;
> +
> + case 0x06: /* send slope */
> + break;
> +
> + case 0x07: /* idle slope */
> + break;
> +
> + case 0xFD: /* capabilities a */
> + retval = (p->maxStreamSlots & 0x7F) | ((p->dualOutput) ? 0x80 : 0x00);
> + break;
> +
> + case 0xFE: /* capabilities b */
> + retval = ((p->shaperFractionBits & 0x7F) << 24) |
> + ((p->clockDomains & 0xFF) << 16) |
> + ((min_bits(p->templateWords-1) & 0xFF) << 8) |
> + ((min_bits(p->microcodeWords-1) & 0xFF));
> + break;
> +
> + case 0xFF: /* revision */
> + retval = 0x00000013;
> + break;
> +
> + default:
> + printf("labx-audio-packetizer: Read of unknown register %08X\n", addr);
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void packetizer_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + packetizer_t *p = opaque;
> + uint32_t value = val64;
> +
> + switch ((addr>>2) & 0xFF) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* start vector */
> + break;
> +
> + case 0x02: /* ts offset */
> + p->tsOffset = value;
> + break;
> +
> + case 0x03: /* irq mask */
> + break;
> +
> + case 0x04: /* irq flags */
> + break;
> +
> + case 0x05: /* sync reg */
> + break;
> +
> + case 0x06: /* send slope */
> + p->sendSlope = value;
> + break;
> +
> + case 0x07: /* idle slope */
> + p->idleSlope = value;
> + break;
> +
> + case 0xFD: /* capabilities a */
> + break;
> +
> + case 0xFE: /* capabilities b */
> + break;
> +
> + case 0xFF: /* revision */
> + break;
> +
> + default:
> + printf("labx-audio-packetizer: Write of unknown register "
> + "%08X = %08X\n", addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps packetizer_regs_ops = {
> + .read = packetizer_regs_read,
> + .write = packetizer_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Clock domain registers
> + */
> +static uint64_t clock_domain_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + packetizer_t *p = opaque;
> +
> + uint32_t retval = 0;
> + int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
> +
> + switch ((addr>>2)&0x01) {
> + case 0x00: /* ts interval */
> + retval = p->clockDomainInfo[domain].tsInterval;
> + break;
> +
> + case 0x01: /* domain enable */
> + retval = p->clockDomainInfo[domain].domainEnabled;
> + break;
> +
> + default:
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void clock_domain_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + packetizer_t *p = opaque;
> + uint32_t value = val64;
> +
> + int domain = (addr>>3) & ((1<<min_bits(p->clockDomains-1))-1);
> +
> + switch ((addr>>2)&0x01) {
> + case 0x00: /* ts interval */
> + p->clockDomainInfo[domain].tsInterval = value;
> + break;
> +
> + case 0x01: /* domain enable */
> + p->clockDomainInfo[domain].domainEnabled = value;
> + break;
> +
> + default:
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps clock_domain_regs_ops = {
> + .read = clock_domain_regs_read,
> + .write = clock_domain_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Template RAM
> + */
> +static uint64_t template_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + packetizer_t *p = opaque;
> +
> + return p->templateRam[RAM_INDEX(addr, p->templateWords)];
> +}
> +
> +static void template_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + packetizer_t *p = opaque;
> + uint32_t value = val64;
> +
> + p->templateRam[RAM_INDEX(addr, p->templateWords)] = value;
> +}
> +
> +static const MemoryRegionOps template_ram_ops = {
> + .read = template_ram_read,
> + .write = template_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Microcode RAM
> + */
> +static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + packetizer_t *p = opaque;
> +
> + return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
> +}
> +
> +static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + packetizer_t *p = opaque;
> + uint32_t value = val64;
> +
> + p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
> +}
> +
> +static const MemoryRegionOps microcode_ram_ops = {
> + .read = microcode_ram_read,
> + .write = microcode_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +static int labx_audio_packetizer_init(SysBusDevice *dev)
> +{
> + packetizer_t *p = FROM_SYSBUS(typeof(*p), dev);
> +
> + /* Initialize defaults */
> + p->tsOffset = 0x00000000;
> + p->sendSlope = 0x00000000;
> + p->idleSlope = 0x00000000;
> + p->templateRam = g_malloc0(p->templateWords*4);
> + p->microcodeRam = g_malloc0(p->microcodeWords*4);
> + p->clockDomainInfo = g_malloc0(sizeof(struct clock_domain_info) *
> + p->clockDomains);
> +
> + /* Set up the IRQ */
> + sysbus_init_irq(dev, &p->irq);
> +
> + /* Set up memory regions */
> + memory_region_init_io(&p->mmio_packetizer, &packetizer_regs_ops, p,
> + "labx,audio-packetizer-regs",
> + 0x100 * 4);
> + memory_region_init_io(&p->mmio_clock_domain, &clock_domain_regs_ops, p,
> + "labx,audio-packetizer-cd-regs",
> + 2 * 4 * p->clockDomains);
> + memory_region_init_io(&p->mmio_template, &template_ram_ops, p,
> + "labx,audio-packetizer-template",
> + 4 * p->templateWords);
> + memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
> + "labx,audio-packetizer-microcode",
> + 4 * p->microcodeWords);
> +
> + sysbus_init_mmio(dev, &p->mmio_packetizer);
> + sysbus_init_mmio(dev, &p->mmio_clock_domain);
> + sysbus_init_mmio(dev, &p->mmio_template);
> + sysbus_init_mmio(dev, &p->mmio_microcode);
> +
> + sysbus_mmio_map(dev, 0, p->baseAddress);
> + sysbus_mmio_map(dev, 1, p->baseAddress +
> + (1 << (min_bits(p->microcodeWords-1)+2)));
> + sysbus_mmio_map(dev, 2, p->baseAddress +
> + (2 << (min_bits(p->microcodeWords-1)+2)));
> + sysbus_mmio_map(dev, 3, p->baseAddress +
> + (3 << (min_bits(p->microcodeWords-1)+2)));
> +
> + return 0;
> +}
> +
> +static Property labx_audio_packetizer_properties[] = {
> + DEFINE_PROP_UINT32("baseAddress", packetizer_t, baseAddress,
> + 0),
> + DEFINE_PROP_UINT32("clockDomains", packetizer_t, clockDomains,
> + 1),
> + DEFINE_PROP_UINT32("cacheDataWords", packetizer_t, cacheDataWords,
> + 1024),
> + DEFINE_PROP_UINT32("templateWords", packetizer_t, templateWords,
> + 1024),
> + DEFINE_PROP_UINT32("microcodeWords", packetizer_t, microcodeWords,
> + 1024),
> + DEFINE_PROP_UINT32("shaperFractionBits", packetizer_t, shaperFractionBits,
> + 16),
> + DEFINE_PROP_UINT32("maxStreamSlots", packetizer_t, maxStreamSlots,
> + 32),
> + DEFINE_PROP_UINT32("dualOutput", packetizer_t, dualOutput,
> + 1),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void labx_audio_packetizer_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = labx_audio_packetizer_init;
> + dc->props = labx_audio_packetizer_properties;
> +}
> +
> +static TypeInfo labx_audio_packetizer_info = {
> + .name = "labx,audio-packetizer",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(packetizer_t),
> + .class_init = labx_audio_packetizer_class_init,
> +};
> +
> +static void labx_audio_packetizer_register(void)
> +{
> + type_register_static(&labx_audio_packetizer_info);
> +}
> +
> +type_init(labx_audio_packetizer_register)
> +
> diff --git a/hw/labx_devices.h b/hw/labx_devices.h
> new file mode 100644
> index 0000000..317341e
> --- /dev/null
> +++ b/hw/labx_devices.h
> @@ -0,0 +1,103 @@
> +/*
> + * Lab X device types header.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include <net.h>
> +
> +/* Audio packetizer */
> +static inline DeviceState *
> +labx_audio_packetizer_create(target_phys_addr_t base, qemu_irq irq,
> + int clockDomains, int cacheDataWords)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "labx,audio-packetizer");
> + qdev_prop_set_uint32(dev, "baseAddress", base);
> + qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
> + qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
> + qdev_init_nofail(dev);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> + return dev;
> +}
> +
> +/* Audio depacketizer */
> +static inline DeviceState *
> +labx_audio_depacketizer_create(target_phys_addr_t base, qemu_irq irq,
> + int clockDomains, int cacheDataWords, int hasDMA)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "labx,audio-depacketizer");
> + qdev_prop_set_uint32(dev, "baseAddress", base);
> + qdev_prop_set_uint32(dev, "clockDomains", clockDomains);
> + qdev_prop_set_uint32(dev, "cacheDataWords", cacheDataWords);
> + qdev_prop_set_uint32(dev, "hasDMA", hasDMA);
> + qdev_init_nofail(dev);
> + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
> + return dev;
> +}
> +
> +/* DMA */
> +static inline DeviceState *
> +labx_dma_create(target_phys_addr_t base, int microcodeWords)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "labx,dma");
> + qdev_prop_set_uint32(dev, "baseAddress", base);
> + qdev_prop_set_uint32(dev, "microcodeWords", microcodeWords);
> + qdev_init_nofail(dev);
> + return dev;
> +}
> +
> +/* Ethernet */
> +static inline DeviceState *
> +labx_ethernet_create(NICInfo *nd, target_phys_addr_t base, qemu_irq hostIrq,
> + qemu_irq fifoIrq, qemu_irq phyIrq)
> +{
> + DeviceState *dev;
> + SysBusDevice *s;
> +
> + qemu_check_nic_model(nd, "labx-ethernet");
> +
> + dev = qdev_create(NULL, "labx,ethernet");
> + qdev_prop_set_uint32(dev, "baseAddress", base);
> + qdev_set_nic_properties(dev, nd);
> + qdev_init_nofail(dev);
> +
> + s = sysbus_from_qdev(dev);
> + sysbus_connect_irq(s, 0, hostIrq);
> + sysbus_connect_irq(s, 1, fifoIrq);
> + sysbus_connect_irq(s, 2, phyIrq);
> +
> + return dev;
> +}
> +
> +/* PTP */
> +static inline DeviceState *
> +labx_ptp_create(target_phys_addr_t base)
> +{
> + DeviceState *dev;
> +
> + dev = qdev_create(NULL, "labx,ptp");
> + qdev_prop_set_uint32(dev, "baseAddress", base);
> + qdev_init_nofail(dev);
> + return dev;
> +}
> +
> diff --git a/hw/labx_dma.c b/hw/labx_dma.c
> new file mode 100644
> index 0000000..9d8058c
> --- /dev/null
> +++ b/hw/labx_dma.c
> @@ -0,0 +1,241 @@
> +
> +/*
> + * QEMU model of the LabX DMA Engine.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +
> +#define min_bits qemu_fls
> +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
> +
> +
> +struct labx_dma {
> + SysBusDevice busdev;
> +
> + MemoryRegion mmio_dma;
> + MemoryRegion mmio_microcode;
> +
> + /* Device Configuration */
> + uint32_t baseAddress;
> + uint32_t paramWords;
> + uint32_t microcodeWords;
> + uint32_t numIndexRegs;
> + uint32_t numChannels;
> + uint32_t numAlus;
> +
> + /* Values set by drivers */
> +
> + /* Microcode buffer */
> + uint32_t *microcodeRam;
> +};
> +
> +/*
> + * DMA registers
> + */
> +static uint64_t dma_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_dma *p = opaque;
> +
> + uint32_t retval = 0;
> +
> + if ((addr>>2) & 0x80) {
> + /* vector */
> + } else {
> + switch ((addr>>2) & 0x7F) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* channel enable */
> + break;
> +
> + case 0x02: /* channel start */
> + break;
> +
> + case 0x03: /* channel irq enable */
> + break;
> +
> + case 0x04: /* channel irq */
> + break;
> +
> + case 0x05: /* sync */
> + break;
> +
> + case 0x7E: /* capabilities */
> + retval = ((p->numIndexRegs & 0x0F) << 12) |
> + ((p->numChannels & 0x03) << 10) |
> + ((p->numAlus & 0x03) << 8) |
> + ((min_bits(p->paramWords-1) & 0x0F) << 4) |
> + ((min_bits(p->microcodeWords-1) & 0x0F));
> + break;
> +
> + case 0x7F: /* revision */
> + retval = 0x00000011;
> + break;
> +
> + default:
> + printf("labx-dma: Read of unknown register %08X\n", addr);
> + break;
> + }
> + }
> +
> + return retval;
> +}
> +
> +static void dma_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + /*struct labx_dma *p = opaque; */
> + uint32_t value = val64;
> +
> + if ((addr>>2) & 0x80) {
> + /* vector */
> + } else {
> + switch ((addr>>2) & 0x7F) {
> + case 0x00: /* control */
> + break;
> +
> + case 0x01: /* channel enable */
> + break;
> +
> + case 0x02: /* channel start */
> + break;
> +
> + case 0x03: /* channel irq enable */
> + break;
> +
> + case 0x04: /* channel irq */
> + break;
> +
> + case 0x05: /* sync */
> + break;
> +
> + case 0x7E: /* capabilities */
> + break;
> +
> + case 0x7F: /* revision */
> + break;
> +
> + default:
> + printf("labx-dma: Write of unknown register "
> + "%08X = %08X\n", addr, value);
> + break;
> + }
> + }
> +}
> +
> +static const MemoryRegionOps dma_regs_ops = {
> + .read = dma_regs_read,
> + .write = dma_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Microcode RAM
> + */
> +static uint64_t microcode_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_dma *p = opaque;
> +
> + return p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)];
> +}
> +
> +static void microcode_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct labx_dma *p = opaque;
> + uint32_t value = val64;
> +
> + p->microcodeRam[RAM_INDEX(addr, p->microcodeWords)] = value;
> +}
> +
> +static const MemoryRegionOps microcode_ram_ops = {
> + .read = microcode_ram_read,
> + .write = microcode_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +static int labx_dma_init(SysBusDevice *dev)
> +{
> + struct labx_dma *p = FROM_SYSBUS(typeof(*p), dev);
> +
> + /* Initialize defaults */
> + p->microcodeRam = g_malloc0(p->microcodeWords*4);
> +
> + /* Set up memory regions */
> + memory_region_init_io(&p->mmio_dma, &dma_regs_ops, p,
> + "labx,dma-regs", 0x100 * 4);
> + memory_region_init_io(&p->mmio_microcode, µcode_ram_ops, p,
> + "labx,dma-microcode", 4 * p->microcodeWords);
> +
> + sysbus_init_mmio(dev, &p->mmio_dma);
> + sysbus_init_mmio(dev, &p->mmio_microcode);
> +
> + sysbus_mmio_map(dev, 0, p->baseAddress);
> + sysbus_mmio_map(dev, 1, p->baseAddress +
> + (1 << (min_bits(p->microcodeWords-1)+2)));
> +
> + return 0;
> +}
> +
> +static Property labx_dma_properties[] = {
> + DEFINE_PROP_UINT32("baseAddress", struct labx_dma, baseAddress, 0),
> + DEFINE_PROP_UINT32("paramWords", struct labx_dma, paramWords, 1024),
> + DEFINE_PROP_UINT32("microcodeWords", struct labx_dma, microcodeWords, 1024),
> + DEFINE_PROP_UINT32("numIndexRegs", struct labx_dma, numIndexRegs, 4),
> + DEFINE_PROP_UINT32("numChannels", struct labx_dma, numChannels, 1),
> + DEFINE_PROP_UINT32("numAlus", struct labx_dma, numAlus, 1),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void labx_dma_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = labx_dma_init;
> + dc->props = labx_dma_properties;
> +}
> +
> +static TypeInfo labx_dma_info = {
> + .name = "labx,dma",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct labx_dma),
> + .class_init = labx_dma_class_init,
> +};
> +
> +static void labx_dma_register(void)
> +{
> + type_register_static(&labx_dma_info);
> +}
> +
> +type_init(labx_dma_register)
> +
> diff --git a/hw/labx_ethernet.c b/hw/labx_ethernet.c
> new file mode 100644
> index 0000000..c47c91b
> --- /dev/null
> +++ b/hw/labx_ethernet.c
> @@ -0,0 +1,615 @@
> +
> +/*
> + * QEMU model of the LabX legacy ethernet core.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +#include "net.h"
> +
> +#define FIFO_RAM_BYTES 2048
> +#define LENGTH_FIFO_WORDS 16
> +
> +struct labx_ethernet {
> + SysBusDevice busdev;
> + qemu_irq hostIrq;
> + qemu_irq fifoIrq;
> + qemu_irq phyIrq;
> + NICState *nic;
> + NICConf conf;
> +
> + MemoryRegion mmio_ethernet;
> + MemoryRegion mmio_mac;
> + MemoryRegion mmio_fifo;
> +
> + /* Device Configuration */
> + uint32_t baseAddress;
> +
> + /* Values set by drivers */
> + uint32_t hostRegs[0x10];
> + uint32_t fifoRegs[0x10];
> +
> + /* Tx buffers */
> + uint32_t *txBuffer;
> + uint32_t txPushIndex;
> + uint32_t txPopIndex;
> +
> + uint32_t *txLengthBuffer;
> + uint32_t txLengthPushIndex;
> + uint32_t txLengthPopIndex;
> +
> + /* Rx buffers */
> + uint32_t *rxBuffer;
> + uint32_t rxPushIndex;
> + uint32_t rxPopIndex;
> +
> + uint32_t *rxLengthBuffer;
> + uint32_t rxLengthPushIndex;
> + uint32_t rxLengthPopIndex;
> +};
> +
> +/*
> + * Legacy ethernet registers
> + */
> +static void update_host_irq(struct labx_ethernet *p)
> +{
> + if ((p->hostRegs[0x03] & p->hostRegs[2]) != 0) {
> + qemu_irq_raise(p->hostIrq);
> + } else {
> + qemu_irq_lower(p->hostIrq);
> + }
> +}
> +
> +static void mdio_xfer(struct labx_ethernet *p, int readWrite,
> + int phyAddr, int regAddr)
> +{
> + printf("MDIO %s: addr=%d, reg=%d\n", (readWrite) ? "READ" : "WRITE",
> + phyAddr, regAddr);
> + if (readWrite) {
> + /* TODO: PHY info */
> + p->hostRegs[0x01] = 0x0000FFFF;
> + }
> + p->hostRegs[0x03] |= 1;
> + update_host_irq(p);
> +}
> +
> +static uint64_t ethernet_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_ethernet *p = opaque;
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x00: /* mdio control */
> + case 0x01: /* mdio data */
> + case 0x02: /* irq mask */
> + case 0x03: /* irq flags */
> + case 0x04: /* vlan mask */
> + case 0x05: /* filter select */
> + retval = p->hostRegs[(addr>>2) & 0x0F];
> + break;
> +
> + case 0x06: /* filter control */
> + retval = 0x20000000;
> + break;
> +
> + case 0x0F: /* revision */
> + retval = 0x00000C13;
> + break;
> +
> + case 0x07: /* filter load */
> + retval = p->hostRegs[(addr>>2) & 0x0F];
> + break;
> +
> + case 0x08: /* bad packet */
> + retval = 0;
> + break;
> +
> + default:
> + printf("labx-ethernet: Read of unknown register %08X\n", addr);
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void ethernet_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct labx_ethernet *p = opaque;
> + uint32_t value = val64;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x00: /* mdio control */
> + p->hostRegs[0x00] = (value & 0x000007FF);
> + mdio_xfer(p, (value >> 10) & 1, (value >> 5) & 0x1F, value & 0x1F);
> + break;
> +
> + case 0x01: /* mdio data */
> + p->hostRegs[0x01] = (value & 0x0000FFFF);
> + break;
> +
> + case 0x02: /* irq mask */
> + p->hostRegs[0x02] = (value & 0x00000003);
> + update_host_irq(p);
> + break;
> +
> + case 0x03: /* irq flags */
> + p->hostRegs[0x03] &= ~(value & 0x00000003);
> + update_host_irq(p);
> + break;
> +
> + case 0x04: /* vlan mask */
> + break;
> +
> + case 0x05: /* filter select */
> + break;
> +
> + case 0x06: /* filter control */
> + break;
> +
> + case 0x07: /* filter load */
> + break;
> +
> + case 0x08: /* bad packet */
> + break;
> +
> + case 0x0F: /* revision */
> + break;
> +
> + default:
> + printf("labx-ethernet: Write of unknown register %08X = %08X\n",
> + addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps ethernet_regs_ops = {
> + .read = ethernet_regs_read,
> + .write = ethernet_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * MAC registers
> + */
> +static uint64_t mac_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + /*struct labx_ethernet *p = opaque; */
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x01: /* host rx config */
> + break;
> +
> + case 0x02: /* host tx config */
> + break;
> +
> + case 0x04: /* host speed config */
> + break;
> +
> + case 0x05: /* host mdio config */
> + break;
> +
> + default:
> + printf("labx-ethernet: Read of unknown mac register %08X\n", addr);
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void mac_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + /*struct labx_ethernet *p = opaque; */
> + uint32_t value = val64;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x01: /* host rx config */
> + break;
> +
> + case 0x02: /* host tx config */
> + break;
> +
> + case 0x04: /* host speed config */
> + break;
> +
> + case 0x05: /* host mdio config */
> + break;
> +
> + default:
> + printf("labx-ethernet: Write of unknown mac register %08X = %08X\n",
> + addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps mac_regs_ops = {
> + .read = mac_regs_read,
> + .write = mac_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * FIFO registers
> + */
> +
> +#define FIFO_INT_STATUS_ADDRESS 0x0
> +#define FIFO_INT_ENABLE_ADDRESS 0x1
> +# define FIFO_INT_RPURE 0x80000000
> +# define FIFO_INT_RPORE 0x40000000
> +# define FIFO_INT_RPUE 0x20000000
> +# define FIFO_INT_TPOE 0x10000000
> +# define FIFO_INT_TC 0x08000000
> +# define FIFO_INT_RC 0x04000000
> +# define FIFO_INT_MASK 0xFC000000
> +#define FIFO_TX_RESET_ADDRESS 0x2
> +# define FIFO_RESET_MAGIC 0xA5
> +#define FIFO_TX_VACANCY_ADDRESS 0x3
> +#define FIFO_TX_DATA_ADDRESS 0x4
> +#define FIFO_TX_LENGTH_ADDRESS 0x5
> +#define FIFO_RX_RESET_ADDRESS 0x6
> +#define FIFO_RX_OCCUPANCY_ADDRESS 0x7
> +#define FIFO_RX_DATA_ADDRESS 0x8
> +#define FIFO_RX_LENGTH_ADDRESS 0x9
> +
> +static void update_fifo_irq(struct labx_ethernet *p)
> +{
> + if ((p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &
> + p->fifoRegs[FIFO_INT_ENABLE_ADDRESS]) != 0) {
> + qemu_irq_raise(p->fifoIrq);
> + } else {
> + qemu_irq_lower(p->fifoIrq);
> + }
> +}
> +
> +static void send_packet(struct labx_ethernet *p)
> +{
> + while (p->txLengthPopIndex != p->txLengthPushIndex) {
> + int i;
> + uint32_t packetBuf[512];
> +
> + int length = p->txLengthBuffer[p->txLengthPopIndex];
> + p->txLengthPopIndex = (p->txLengthPopIndex + 1) % LENGTH_FIFO_WORDS;
> +
> + for (i = 0; i < ((length+3)/4); i++) {
> + packetBuf[i] = be32_to_cpu(p->txBuffer[p->txPopIndex]);
> + p->txPopIndex = (p->txPopIndex + 1) % (FIFO_RAM_BYTES/4);
> + }
> +
> + qemu_send_packet(&p->nic->nc, (void *)packetBuf, length);
> + }
> +
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TC;
> + update_fifo_irq(p);
> +}
> +
> +static uint64_t fifo_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_ethernet *p = opaque;
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0x0F) {
> + case FIFO_INT_STATUS_ADDRESS:
> + case FIFO_INT_ENABLE_ADDRESS:
> + case FIFO_TX_RESET_ADDRESS:
> + retval = p->fifoRegs[(addr>>2) & 0x0F];
> + break;
> +
> + case FIFO_TX_VACANCY_ADDRESS:
> + retval = (p->txPopIndex - p->txPushIndex) - 1;
> + if ((int32_t)retval < 0) {
> + retval += (FIFO_RAM_BYTES/4);
> + }
> +
> + if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
> + p->txLengthPopIndex) {
> + /* Full length fifo */
> + retval = 0;
> + }
> + break;
> +
> + case FIFO_TX_DATA_ADDRESS:
> + case FIFO_TX_LENGTH_ADDRESS:
> + case FIFO_RX_RESET_ADDRESS:
> + retval = p->fifoRegs[(addr>>2) & 0x0F];
> + break;
> +
> + case FIFO_RX_OCCUPANCY_ADDRESS:
> + retval = p->rxPushIndex - p->rxPopIndex;
> + if ((int32_t)retval < 0) {
> + retval += (FIFO_RAM_BYTES/4);
> + }
> + break;
> +
> + case FIFO_RX_DATA_ADDRESS:
> + retval = p->rxBuffer[p->rxPopIndex];
> + if (p->rxPopIndex != p->rxPushIndex) {
> + p->rxPopIndex = (p->rxPopIndex+1) % (FIFO_RAM_BYTES/4);
> + } else {
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
> + update_fifo_irq(p);
> + }
> + break;
> +
> + case FIFO_RX_LENGTH_ADDRESS:
> + retval = p->rxLengthBuffer[p->rxLengthPopIndex];
> + if (p->rxLengthPopIndex != p->rxLengthPushIndex) {
> + p->rxLengthPopIndex = (p->rxLengthPopIndex+1) % LENGTH_FIFO_WORDS;
> + } else {
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RPURE;
> + update_fifo_irq(p);
> + }
> + break;
> +
> + default:
> + printf("labx-ethernet: Read of unknown fifo register %08X\n", addr);
> + break;
> + }
> +
> + /* printf("FIFO REG READ %08X (%d) = %08X\n",
> + addr, (addr>>2) & 0x0F, retval); */
> +
> + return retval;
> +}
> +
> +static void fifo_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct labx_ethernet *p = opaque;
> + uint32_t value = val64;
> +
> + /* printf("FIFO REG WRITE %08X (%d) = %08X\n",
> + addr, (addr>>2) & 0x0F, value); */
> +
> + switch ((addr>>2) & 0x0F) {
> + case FIFO_INT_STATUS_ADDRESS:
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] &= ~(value & FIFO_INT_MASK);
> + update_fifo_irq(p);
> + break;
> +
> + case FIFO_INT_ENABLE_ADDRESS:
> + p->fifoRegs[FIFO_INT_ENABLE_ADDRESS] = (value & FIFO_INT_MASK);
> + update_fifo_irq(p);
> + break;
> +
> + case FIFO_TX_RESET_ADDRESS:
> + if (value == FIFO_RESET_MAGIC) {
> + p->txPushIndex = 0;
> + p->txPopIndex = 0;
> + p->txLengthPushIndex = 0;
> + p->txLengthPopIndex = 0;
> + }
> + break;
> +
> + case FIFO_TX_VACANCY_ADDRESS:
> + break;
> +
> + case FIFO_TX_DATA_ADDRESS:
> + if ((((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
> + p->txLengthPopIndex) ||
> + (((p->txPushIndex + 1) % (FIFO_RAM_BYTES/4)) == p->txPopIndex)) {
> + /* Full length fifo or data fifo */
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
> + update_fifo_irq(p);
> + } else {
> + /* Push back the data */
> + p->txBuffer[p->txPushIndex] = value;
> + p->txPushIndex = (p->txPushIndex + 1) % (FIFO_RAM_BYTES/4);
> + }
> + break;
> +
> + case FIFO_TX_LENGTH_ADDRESS:
> + if (((p->txLengthPushIndex + 1) % LENGTH_FIFO_WORDS) ==
> + p->txLengthPopIndex) {
> + /* Full length fifo */
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_TPOE;
> + update_fifo_irq(p);
> + } else {
> + /* Push back the length */
> + p->txLengthBuffer[p->txLengthPushIndex] = value;
> + p->txLengthPushIndex = (p->txLengthPushIndex + 1) %
> + LENGTH_FIFO_WORDS;
> + send_packet(p);
> + }
> + break;
> +
> + case FIFO_RX_RESET_ADDRESS:
> + if (value == FIFO_RESET_MAGIC) {
> + p->rxPushIndex = 0;
> + p->rxPopIndex = 0;
> + p->rxLengthPushIndex = 0;
> + p->rxLengthPopIndex = 0;
> + }
> + break;
> +
> + case FIFO_RX_OCCUPANCY_ADDRESS:
> + break;
> +
> + case FIFO_RX_DATA_ADDRESS:
> + break;
> +
> + case FIFO_RX_LENGTH_ADDRESS:
> + break;
> +
> + default:
> + printf("labx-ethernet: Write of unknown fifo register %08X = %08X\n",
> + addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps fifo_regs_ops = {
> + .read = fifo_regs_read,
> + .write = fifo_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +static int eth_can_rx(NetClientState *nc)
> +{
> + /*struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque; */
> +
> + return 1;
> +}
> +
> +static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
> +{
> + struct labx_ethernet *p = DO_UPCAST(NICState, nc, nc)->opaque;
> + int i;
> + const uint32_t *wbuf = (const uint32_t *)buf;
> + int rxPushIndexStart = p->rxPushIndex;
> +
> + for (i = 0; i < ((size+3)/4); i++) {
> + p->rxBuffer[p->rxPushIndex] = cpu_to_be32(wbuf[i]);
> + p->rxPushIndex = (p->rxPushIndex + 1) % (FIFO_RAM_BYTES/4);
> + if (p->rxPushIndex == p->rxPopIndex) {
> + /* Packet didn't fit */
> + p->rxPushIndex = rxPushIndexStart;
> + return -1;
> + }
> + }
> +
> + if ((p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS == p->rxLengthPopIndex) {
> + /* Length didn't fit */
> + p->rxPushIndex = rxPushIndexStart;
> + return -1;
> + }
> +
> + p->rxLengthBuffer[p->rxLengthPushIndex] = size;
> + p->rxLengthPushIndex = (p->rxLengthPushIndex + 1) % LENGTH_FIFO_WORDS;
> +
> + p->fifoRegs[FIFO_INT_STATUS_ADDRESS] |= FIFO_INT_RC;
> + update_fifo_irq(p);
> +
> + return size;
> +}
> +
> +static void eth_cleanup(NetClientState *nc)
> +{
> + struct labx_ethernet *s = DO_UPCAST(NICState, nc, nc)->opaque;
> +
> + s->nic = NULL;
> +}
> +
> +static NetClientInfo net_labx_ethernet_info = {
> + .type = NET_CLIENT_OPTIONS_KIND_NIC,
> + .size = sizeof(NICState),
> + .can_receive = eth_can_rx,
> + .receive = eth_rx,
> + .cleanup = eth_cleanup,
> +};
> +
> +static int labx_ethernet_init(SysBusDevice *dev)
> +{
> + struct labx_ethernet *p = FROM_SYSBUS(typeof(*p), dev);
> +
> + /* Initialize defaults */
> + p->txBuffer = g_malloc0(FIFO_RAM_BYTES);
> + p->txLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
> + p->rxBuffer = g_malloc0(FIFO_RAM_BYTES);
> + p->rxLengthBuffer = g_malloc0(LENGTH_FIFO_WORDS*4);
> +
> + p->txPushIndex = 0;
> + p->txPopIndex = 0;
> + p->txLengthPushIndex = 0;
> + p->txLengthPopIndex = 0;
> + p->rxPushIndex = 0;
> + p->rxPopIndex = 0;
> + p->rxLengthPushIndex = 0;
> + p->rxLengthPopIndex = 0;
> +
> + /* Set up memory regions */
> + memory_region_init_io(&p->mmio_ethernet, ðernet_regs_ops, p,
> + "labx,ethernet-regs", 0x10 * 4);
> + memory_region_init_io(&p->mmio_mac, &mac_regs_ops, p,
> + "labx,ethernet-mac-regs", 0x10 * 4);
> + memory_region_init_io(&p->mmio_fifo, &fifo_regs_ops, p,
> + "labx,ethernet-fifo-regs", 0x10 * 4);
> +
> + sysbus_init_mmio(dev, &p->mmio_ethernet);
> + sysbus_init_mmio(dev, &p->mmio_mac);
> + sysbus_init_mmio(dev, &p->mmio_fifo);
> +
> + sysbus_mmio_map(dev, 0, p->baseAddress);
> + sysbus_mmio_map(dev, 1, p->baseAddress + (1 << (10+2)));
> + sysbus_mmio_map(dev, 2, p->baseAddress + (2 << (10+2)));
> +
> + /* Initialize the irqs */
> + sysbus_init_irq(dev, &p->hostIrq);
> + sysbus_init_irq(dev, &p->fifoIrq);
> + sysbus_init_irq(dev, &p->phyIrq);
> +
> + /* Set up the NIC */
> + qemu_macaddr_default_if_unset(&p->conf.macaddr);
> + p->nic = qemu_new_nic(&net_labx_ethernet_info, &p->conf,
> + object_get_typename(OBJECT(p)), dev->qdev.id, p);
> + qemu_format_nic_info_str(&p->nic->nc, p->conf.macaddr.a);
> + return 0;
> +}
> +
> +static Property labx_ethernet_properties[] = {
> + DEFINE_PROP_UINT32("baseAddress", struct labx_ethernet, baseAddress, 0),
> + DEFINE_NIC_PROPERTIES(struct labx_ethernet, conf),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void labx_ethernet_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = labx_ethernet_init;
> + dc->props = labx_ethernet_properties;
> +}
> +
> +static TypeInfo labx_ethernet_info = {
> + .name = "labx,ethernet",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct labx_ethernet),
> + .class_init = labx_ethernet_class_init,
> +};
> +
> +static void labx_ethernet_register(void)
> +{
> + type_register_static(&labx_ethernet_info);
> +}
> +
> +type_init(labx_ethernet_register)
> +
> diff --git a/hw/labx_ptp.c b/hw/labx_ptp.c
> new file mode 100644
> index 0000000..68d4b54
> --- /dev/null
> +++ b/hw/labx_ptp.c
> @@ -0,0 +1,291 @@
> +
> +/*
> + * QEMU model of the LabX PTP.
> + *
> + * Copyright (c) 2010 Lab X Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu.h"
> +
> +#define min_bits qemu_fls
> +#define RAM_INDEX(addr, size) (((addr)>>2)&((1<<min_bits((size)-1))-1))
> +
> +#define PTP_MAX_PACKETS 8
> +#define PTP_MAX_PACKET_BYTES 256
> +#define PTP_RAM_BYTES (PTP_MAX_PACKETS*PTP_MAX_PACKET_BYTES)
> +#define PTP_HOST_RAM_WORDS (PTP_RAM_BYTES/4)
> +
> +struct labx_ptp {
> + SysBusDevice busdev;
> +
> + MemoryRegion mmio_ptp;
> + MemoryRegion mmio_tx;
> + MemoryRegion mmio_rx;
> +
> + /* Device Configuration */
> + uint32_t baseAddress;
> +
> + /* Values set by drivers */
> +
> + /* Tx buffers */
> + uint32_t *txRam;
> +
> + /* Rx buffers */
> + uint32_t *rxRam;
> +};
> +
> +/*
> + * PTP registers
> + */
> +static uint64_t ptp_regs_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + /*struct labx_ptp *p = opaque; */
> +
> + uint32_t retval = 0;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x00: /* rx */
> + break;
> +
> + case 0x01: /* tx */
> + break;
> +
> + case 0x02: /* irq mask */
> + break;
> +
> + case 0x03: /* irq flags */
> + break;
> +
> + case 0x04: /* rtc increment */
> + break;
> +
> + case 0x05: /* seconds high */
> + break;
> +
> + case 0x06: /* seconds low */
> + break;
> +
> + case 0x07: /* nanoseconds */
> + break;
> +
> + case 0x08: /* timer */
> + break;
> +
> + case 0x09: /* local seconds high */
> + break;
> +
> + case 0x0A: /* local seconds low */
> + break;
> +
> + case 0x0B: /* local nanoseconds */
> + break;
> +
> + case 0x0F: /* revision */
> + retval = 0x00000111; /* Report 1 port, revision 1.1 */
> + break;
> +
> + default:
> + printf("labx-ptp: Read of unknown register %08X\n", addr);
> + break;
> + }
> +
> + return retval;
> +}
> +
> +static void ptp_regs_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + /*struct labx_ptp *p = opaque; */
> + uint32_t value = val64;
> +
> + switch ((addr>>2) & 0x0F) {
> + case 0x00: /* rx */
> + break;
> +
> + case 0x01: /* tx */
> + break;
> +
> + case 0x02: /* irq mask */
> + break;
> +
> + case 0x03: /* irq flags */
> + break;
> +
> + case 0x04: /* rtc increment */
> + break;
> +
> + case 0x05: /* seconds high */
> + break;
> +
> + case 0x06: /* seconds low */
> + break;
> +
> + case 0x07: /* nanoseconds */
> + break;
> +
> + case 0x08: /* timer */
> + break;
> +
> + case 0x09: /* local seconds high */
> + break;
> +
> + case 0x0A: /* local seconds low */
> + break;
> +
> + case 0x0B: /* local nanoseconds */
> + break;
> +
> + case 0x0F: /* revision */
> + break;
> +
> + default:
> + printf("labx-ptp: Write of unknown register %08X = %08X\n",
> + addr, value);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps ptp_regs_ops = {
> + .read = ptp_regs_read,
> + .write = ptp_regs_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Tx Ram
> + */
> +static uint64_t tx_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_ptp *p = opaque;
> +
> + return p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
> +}
> +
> +static void tx_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct labx_ptp *p = opaque;
> + uint32_t value = val64;
> +
> + p->txRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
> +}
> +
> +static const MemoryRegionOps tx_ram_ops = {
> + .read = tx_ram_read,
> + .write = tx_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +/*
> + * Rx Ram
> + */
> +static uint64_t rx_ram_read(void *opaque, target_phys_addr_t addr,
> + unsigned int size)
> +{
> + struct labx_ptp *p = opaque;
> +
> + return p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)];
> +}
> +
> +static void rx_ram_write(void *opaque, target_phys_addr_t addr,
> + uint64_t val64, unsigned int size)
> +{
> + struct labx_ptp *p = opaque;
> + uint32_t value = val64;
> +
> + p->rxRam[RAM_INDEX(addr, PTP_HOST_RAM_WORDS)] = value;
> +}
> +
> +static const MemoryRegionOps rx_ram_ops = {
> + .read = rx_ram_read,
> + .write = rx_ram_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4
> + }
> +};
> +
> +
> +static int labx_ptp_init(SysBusDevice *dev)
> +{
> + struct labx_ptp *p = FROM_SYSBUS(typeof(*p), dev);
> +
> + /* Initialize defaults */
> + p->txRam = g_malloc0(PTP_RAM_BYTES);
> + p->rxRam = g_malloc0(PTP_RAM_BYTES);
> +
> + /* Set up memory regions */
> + memory_region_init_io(&p->mmio_ptp, &ptp_regs_ops, p, "labx,ptp-regs",
> + 0x100 * 4);
> + memory_region_init_io(&p->mmio_tx, &tx_ram_ops, p, "labx,ptp-tx",
> + PTP_RAM_BYTES);
> + memory_region_init_io(&p->mmio_rx, &rx_ram_ops, p, "labx,ptp-rx",
> + PTP_RAM_BYTES);
> +
> + sysbus_init_mmio(dev, &p->mmio_ptp);
> + sysbus_init_mmio(dev, &p->mmio_tx);
> + sysbus_init_mmio(dev, &p->mmio_rx);
> +
> + sysbus_mmio_map(dev, 0, p->baseAddress);
> + sysbus_mmio_map(dev, 1, p->baseAddress + (1 << min_bits(PTP_RAM_BYTES-1)));
> + sysbus_mmio_map(dev, 2, p->baseAddress + (2 << min_bits(PTP_RAM_BYTES-1)));
> +
> + return 0;
> +}
> +
> +static Property labx_ptp_properties[] = {
> + DEFINE_PROP_UINT32("baseAddress", struct labx_ptp, baseAddress, 0),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void labx_ptp_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = labx_ptp_init;
> + dc->props = labx_ptp_properties;
> +}
> +
> +static TypeInfo labx_ptp_info = {
> + .name = "labx,ptp",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(struct labx_ptp),
> + .class_init = labx_ptp_class_init,
> +};
> +
> +static void labx_ptp_register(void)
> +{
> + type_register_static(&labx_ptp_info);
> +}
> +
> +type_init(labx_ptp_register)
> +
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (3 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 4/9] LabX: Support for some Lab X FPGA devices crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-12 0:12 ` Peter Crosthwaite
2012-09-10 0:20 ` [Qemu-devel] [PATCH 6/9] NiosII: Build system and documentation integration crwulff
` (4 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
device_tree.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
device_tree.h | 18 ++++++++++++
2 files changed, 106 insertions(+)
diff --git a/device_tree.c b/device_tree.c
index d7a9b6b..7fa7646 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -27,6 +27,14 @@
#include <libfdt.h>
+#define CHECK_HEADER(fdt) \
+ { \
+ int err = fdt_check_header(fdt); \
+ if (err != 0) { \
+ return err; \
+ } \
+ }
+
#define FDT_MAX_SIZE 0x10000
void *create_device_tree(int *sizep)
@@ -304,3 +312,83 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
g_free(dupname);
return retval;
}
+
+int qemu_devtree_node_offset(void *fdt, const char *node_path)
+{
+ return fdt_path_offset(fdt, node_path);
+}
+
+int qemu_devtree_subnode_offset_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+}
+
+int qemu_devtree_next_child_offset(void *fdt, int parentoffset, int childoffset)
+{
+ int level = 0;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ CHECK_HEADER(fdt);
+ tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
+ if (tag != FDT_BEGIN_NODE) {
+ return -FDT_ERR_BADOFFSET;
+ }
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ return -FDT_ERR_TRUNCATED;
+
+ case FDT_BEGIN_NODE:
+ level++;
+ if (level != 1) {
+ continue;
+ }
+ if (offset > childoffset) {
+ return offset;
+ }
+ break;
+
+ case FDT_END_NODE:
+ level--;
+ break;
+
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ default:
+ return -FDT_ERR_BADSTRUCTURE;
+ }
+ } while (level >= 0);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+const char *qemu_devtree_get_name(const void *fdt, int nodeoffset, int *lenp)
+{
+ return fdt_get_name(fdt, nodeoffset, lenp);
+}
+
+const void *qemu_devtree_getprop_offset(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+uint32_t qemu_devtree_int_array_index(const void *propval, unsigned int index)
+{
+ return be32_to_cpu(((uint32_t *)propval)[index]);
+}
+
+int qemu_devtree_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ return fdt_node_check_compatible(fdt, nodeoffset, compatible);
+}
+
diff --git a/device_tree.h b/device_tree.h
index f7a3e6c..e01b460 100644
--- a/device_tree.h
+++ b/device_tree.h
@@ -49,4 +49,22 @@ int qemu_devtree_add_subnode(void *fdt, const char *name);
sizeof(qdt_tmp)); \
} while (0)
+int qemu_devtree_node_offset(void *fdt, const char *node_path);
+
+int qemu_devtree_subnode_offset_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+
+int qemu_devtree_next_child_offset(void *fdt, int parentoffset,
+ int childoffset);
+
+const char *qemu_devtree_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+const void *qemu_devtree_getprop_offset(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+
+uint32_t qemu_devtree_int_array_index(const void *propval, unsigned int index);
+
+int qemu_devtree_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
#endif /* __DEVICE_TREE_H__ */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children crwulff
@ 2012-09-12 0:12 ` Peter Crosthwaite
0 siblings, 0 replies; 31+ messages in thread
From: Peter Crosthwaite @ 2012-09-12 0:12 UTC (permalink / raw)
To: crwulff; +Cc: peter.maydell, peter.crosthwaite, qemu-devel
On Sun, 2012-09-09 at 20:20 -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> device_tree.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> device_tree.h | 18 ++++++++++++
> 2 files changed, 106 insertions(+)
>
> diff --git a/device_tree.c b/device_tree.c
> index d7a9b6b..7fa7646 100644
> --- a/device_tree.c
> +++ b/device_tree.c
> @@ -27,6 +27,14 @@
>
> #include <libfdt.h>
>
> +#define CHECK_HEADER(fdt) \
> + { \
> + int err = fdt_check_header(fdt); \
> + if (err != 0) { \
> + return err; \
> + } \
> + }
> +
Im thinking the best way (and I have recently done some patches along
this line) is to use the qemu error reporting mechanism. Rather than
return 1, populate an Error ** with an error code. Not a blocker as this
is out of scope of this series, but would make this macro foo go away
with a nice inline function.
> #define FDT_MAX_SIZE 0x10000
>
> void *create_device_tree(int *sizep)
> @@ -304,3 +312,83 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
> g_free(dupname);
> return retval;
> }
> +
> +int qemu_devtree_node_offset(void *fdt, const char *node_path)
> +{
> + return fdt_path_offset(fdt, node_path);
> +}
device_tree so far has been able to insulate clients from dealing with
offsets. Good to keep it that way and use node_paths instead. Offsets
have the problem where they are globally invalidated as soon as the
device tree is manipulated.
I have an alternate API extension that does a lot of what you are trying
to do but nodepaths are used as handles to nodes rather than offsets.
Will send patch.
> +
> +int qemu_devtree_subnode_offset_namelen(void *fdt, int parentoffset,
> + const char *name, int namelen)
> +{
> + return fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
> +}
No need for no-operation wrappers, just call into lib-fdt directly. Most
device-tree api calls exit(1) on failure which is why we have these thin
wrappers.
> +
> +int qemu_devtree_next_child_offset(void *fdt, int parentoffset, int childoffset)
> +{
> + int level = 0;
> + uint32_t tag;
> + int offset, nextoffset;
> +
> + CHECK_HEADER(fdt);
> + tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
> + if (tag != FDT_BEGIN_NODE) {
> + return -FDT_ERR_BADOFFSET;
> + }
> +
> + do {
> + offset = nextoffset;
> + tag = fdt_next_tag(fdt, offset, &nextoffset);
> +
> + switch (tag) {
> + case FDT_END:
> + return -FDT_ERR_TRUNCATED;
> +
> + case FDT_BEGIN_NODE:
> + level++;
> + if (level != 1) {
> + continue;
> + }
> + if (offset > childoffset) {
> + return offset;
> + }
> + break;
> +
> + case FDT_END_NODE:
> + level--;
> + break;
> +
> + case FDT_PROP:
> + case FDT_NOP:
> + break;
> +
> + default:
> + return -FDT_ERR_BADSTRUCTURE;
> + }
> + } while (level >= 0);
> +
> + return -FDT_ERR_NOTFOUND;
> +}
> +
> +const char *qemu_devtree_get_name(const void *fdt, int nodeoffset, int *lenp)
> +{
> + return fdt_get_name(fdt, nodeoffset, lenp);
> +}
> +
> +const void *qemu_devtree_getprop_offset(const void *fdt, int nodeoffset,
> + const char *name, int *lenp)
> +{
> + return fdt_getprop(fdt, nodeoffset, name, lenp);
> +}
> +
> +uint32_t qemu_devtree_int_array_index(const void *propval, unsigned int index)
> +{
> + return be32_to_cpu(((uint32_t *)propval)[index]);
> +}
> +
> +int qemu_devtree_node_check_compatible(const void *fdt, int nodeoffset,
> + const char *compatible)
> +{
> + return fdt_node_check_compatible(fdt, nodeoffset, compatible);
> +}
> +
> diff --git a/device_tree.h b/device_tree.h
> index f7a3e6c..e01b460 100644
> --- a/device_tree.h
> +++ b/device_tree.h
> @@ -49,4 +49,22 @@ int qemu_devtree_add_subnode(void *fdt, const char *name);
> sizeof(qdt_tmp)); \
> } while (0)
>
> +int qemu_devtree_node_offset(void *fdt, const char *node_path);
> +
> +int qemu_devtree_subnode_offset_namelen(void *fdt, int parentoffset,
> + const char *name, int namelen);
> +
> +int qemu_devtree_next_child_offset(void *fdt, int parentoffset,
> + int childoffset);
> +
> +const char *qemu_devtree_get_name(const void *fdt, int nodeoffset, int *lenp);
> +
> +const void *qemu_devtree_getprop_offset(const void *fdt, int nodeoffset,
> + const char *name, int *lenp);
> +
> +uint32_t qemu_devtree_int_array_index(const void *propval, unsigned int index);
This is get_prop_cell but with an index. We debated this on the list
recently (cc PMM) that get_prop_cell should(nt) have an index field into
it that lets you get the non-zeroth property. I have a patch in my tree
that changes this that ill send you along with the rest.
> +
> +int qemu_devtree_node_check_compatible(const void *fdt, int nodeoffset,
> + const char *compatible);
> +
> #endif /* __DEVICE_TREE_H__ */
Regards,
Peter
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 6/9] NiosII: Build system and documentation integration.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (4 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 5/9] FDT: Add additional access methods for array types and walking children crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-10 0:20 ` [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file crwulff
` (3 subsequent siblings)
9 siblings, 0 replies; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
MAINTAINERS | 5 +++++
arch_init.c | 2 ++
arch_init.h | 1 +
configure | 11 +++++++++++
cpu-exec.c | 12 +++++++++++-
dis-asm.h | 3 +++
disas.c | 3 +++
elf.h | 2 ++
exec.c | 6 ++++--
gdbstub.c | 29 +++++++++++++++++++++++++++++
linux-user/elfload.c | 29 +++++++++++++++++++++++++++++
monitor.c | 5 +++--
qapi-schema.json | 2 +-
qemu-doc.texi | 3 +++
14 files changed, 107 insertions(+), 6 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 61f8b45..e20adc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -91,6 +91,11 @@ M: Aurelien Jarno <aurelien@aurel32.net>
S: Odd Fixes
F: target-mips/
+NiosII
+M: Chris Wulff <crwulff@gmail.com>
+S: Odd Fixes
+F: target-nios2/
+
PowerPC
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
diff --git a/arch_init.c b/arch_init.c
index 5a1173e..6e9d993 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -81,6 +81,8 @@ int graphic_depth = 15;
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
#elif defined(TARGET_MIPS)
#define QEMU_ARCH QEMU_ARCH_MIPS
+#elif defined(TARGET_NIOS2)
+#define QEMU_ARCH QEMU_ARCH_NIOS2
#elif defined(TARGET_OPENRISC)
#define QEMU_ARCH QEMU_ARCH_OPENRISC
#elif defined(TARGET_PPC)
diff --git a/arch_init.h b/arch_init.h
index d9c572a..b3d01c4 100644
--- a/arch_init.h
+++ b/arch_init.h
@@ -20,6 +20,7 @@ enum {
QEMU_ARCH_XTENSA = 4096,
QEMU_ARCH_OPENRISC = 8192,
QEMU_ARCH_UNICORE32 = 0x4000,
+ QEMU_ARCH_NIOS2 = 0x8000,
};
extern const uint32_t arch_type;
diff --git a/configure b/configure
index d97fd81..bfefc42 100755
--- a/configure
+++ b/configure
@@ -969,6 +969,7 @@ mips-softmmu \
mipsel-softmmu \
mips64-softmmu \
mips64el-softmmu \
+nios2-softmmu \
or32-softmmu \
ppc-softmmu \
ppcemb-softmmu \
@@ -997,6 +998,7 @@ microblaze-linux-user \
microblazeel-linux-user \
mips-linux-user \
mipsel-linux-user \
+nios2-linux-user \
or32-linux-user \
ppc-linux-user \
ppc64-linux-user \
@@ -3746,6 +3748,11 @@ case "$target_arch2" in
target_phys_bits=64
target_long_alignment=8
;;
+ nios2)
+ target_nptl="yes"
+ target_phys_bits=32
+ target_libs_softmmu="$fdt_libs"
+ ;;
or32)
TARGET_ARCH=openrisc
TARGET_BASE_ARCH=openrisc
@@ -4013,6 +4020,10 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
echo "CONFIG_MIPS_DIS=y" >> $config_target_mak
echo "CONFIG_MIPS_DIS=y" >> $libdis_config_mak
;;
+ nios2)
+ echo "CONFIG_NIOS2_DIS=y" >> $config_target_mak
+ echo "CONFIG_NIOS2_DIS=y" >> $libdis_config_mak
+ ;;
or32)
echo "CONFIG_OPENRISC_DIS=y" >> $config_target_mak
echo "CONFIG_OPENRISC_DIS=y" >> $libdis_config_mak
diff --git a/cpu-exec.c b/cpu-exec.c
index 134b3c4..2d9034b 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -222,6 +222,7 @@ int cpu_exec(CPUArchState *env)
#elif defined(TARGET_LM32)
#elif defined(TARGET_MICROBLAZE)
#elif defined(TARGET_MIPS)
+#elif defined(TARGET_NIOS2)
#elif defined(TARGET_OPENRISC)
#elif defined(TARGET_SH4)
#elif defined(TARGET_CRIS)
@@ -277,7 +278,8 @@ int cpu_exec(CPUArchState *env)
}
#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \
defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) || \
- defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || defined(TARGET_UNICORE32)
+ defined(TARGET_MICROBLAZE) || defined(TARGET_LM32) || \
+ defined(TARGET_UNICORE32) || defined(TARGET_NIOS2)
if (interrupt_request & CPU_INTERRUPT_HALT) {
env->interrupt_request &= ~CPU_INTERRUPT_HALT;
env->halted = 1;
@@ -531,6 +533,13 @@ int cpu_exec(CPUArchState *env)
do_interrupt(env);
next_tb = 0;
}
+#elif defined(TARGET_NIOS2)
+ if ((interrupt_request & CPU_INTERRUPT_HARD)
+ && (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
+ env->exception_index = EXCP_IRQ;
+ do_interrupt(env);
+ next_tb = 0;
+ }
#endif
/* Don't use the cached interrupt_request value,
do_interrupt may have updated the EXITTB flag. */
@@ -656,6 +665,7 @@ int cpu_exec(CPUArchState *env)
| env->cc_dest | (env->cc_x << 4);
#elif defined(TARGET_MICROBLAZE)
#elif defined(TARGET_MIPS)
+#elif defined(TARGET_NIOS2)
#elif defined(TARGET_OPENRISC)
#elif defined(TARGET_SH4)
#elif defined(TARGET_ALPHA)
diff --git a/dis-asm.h b/dis-asm.h
index 3944b3c..a7010bf 100644
--- a/dis-asm.h
+++ b/dis-asm.h
@@ -221,6 +221,8 @@ enum bfd_architecture
bfd_arch_ia64, /* HP/Intel ia64 */
#define bfd_mach_ia64_elf64 64
#define bfd_mach_ia64_elf32 32
+ bfd_arch_nios2,
+#define bfd_mach_nios2 1
bfd_arch_lm32, /* Lattice Mico32 */
#define bfd_mach_lm32 1
bfd_arch_last
@@ -407,6 +409,7 @@ int print_insn_crisv10 (bfd_vma, disassemble_info*);
int print_insn_microblaze (bfd_vma, disassemble_info*);
int print_insn_ia64 (bfd_vma, disassemble_info*);
int print_insn_lm32 (bfd_vma, disassemble_info*);
+int print_insn_nios2 (bfd_vma, disassemble_info*);
#if 0
/* Fetch the disassembler for a given BFD, if that support is available. */
diff --git a/disas.c b/disas.c
index 7b2acc9..310bac2 100644
--- a/disas.c
+++ b/disas.c
@@ -245,6 +245,9 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
#elif defined(TARGET_MICROBLAZE)
disasm_info.mach = bfd_arch_microblaze;
print_insn = print_insn_microblaze;
+#elif defined(TARGET_NIOS2)
+ disasm_info.mach = bfd_arch_nios2;
+ print_insn = print_insn_nios2;
#elif defined(TARGET_LM32)
disasm_info.mach = bfd_mach_lm32;
print_insn = print_insn_lm32;
diff --git a/elf.h b/elf.h
index a21ea53..e5e667b 100644
--- a/elf.h
+++ b/elf.h
@@ -124,6 +124,8 @@ typedef int64_t Elf64_Sxword;
*/
#define EM_S390_OLD 0xA390
+#define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */
+
#define EM_MICROBLAZE 189
#define EM_MICROBLAZE_OLD 0xBAAB
diff --git a/exec.c b/exec.c
index 5834766..383197a 100644
--- a/exec.c
+++ b/exec.c
@@ -2843,7 +2843,8 @@ static uint64_t unassigned_mem_read(void *opaque, target_phys_addr_t addr,
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
#endif
-#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || \
+ defined(TARGET_MICROBLAZE) || defined(TARGET_NIOS2)
cpu_unassigned_access(cpu_single_env, addr, 0, 0, 0, size);
#endif
return 0;
@@ -2855,7 +2856,8 @@ static void unassigned_mem_write(void *opaque, target_phys_addr_t addr,
#ifdef DEBUG_UNASSIGNED
printf("Unassigned mem write " TARGET_FMT_plx " = 0x%"PRIx64"\n", addr, val);
#endif
-#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || defined(TARGET_MICROBLAZE)
+#if defined(TARGET_ALPHA) || defined(TARGET_SPARC) || \
+ defined(TARGET_MICROBLAZE) || defined(TARGET_NIOS2)
cpu_unassigned_access(cpu_single_env, addr, 1, 0, 0, size);
#endif
}
diff --git a/gdbstub.c b/gdbstub.c
index 5d37dd9..c040676 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -1331,6 +1331,33 @@ static int cpu_gdb_write_register(CPUMBState *env, uint8_t *mem_buf, int n)
}
return 4;
}
+
+#elif defined(TARGET_NIOS2)
+
+static int cpu_gdb_read_register(CPUNios2State *env, uint8_t *mem_buf, int n)
+{
+ if (n > NUM_CORE_REGS) {
+ return 0;
+ }
+
+ GET_REG32(env->regs[n]);
+ return 0;
+}
+
+static int cpu_gdb_write_register(CPUNios2State *env, uint8_t *mem_buf, int n)
+{
+ uint32_t tmp;
+
+ if (n > NUM_CORE_REGS) {
+ return 0;
+ }
+
+ tmp = ldl_p(mem_buf);
+
+ env->regs[n] = tmp;
+ return 4;
+}
+
#elif defined (TARGET_CRIS)
#define NUM_CORE_REGS 49
@@ -1986,6 +2013,8 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc)
}
#elif defined (TARGET_MICROBLAZE)
s->c_cpu->sregs[SR_PC] = pc;
+#elif defined(TARGET_NIOS2)
+ s->c_cpu->regs[R_PC] = pc;
#elif defined(TARGET_OPENRISC)
s->c_cpu->pc = pc;
#elif defined (TARGET_CRIS)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 819fdd5..2750f58 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -804,6 +804,35 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env
#endif /* TARGET_MICROBLAZE */
+#ifdef TARGET_NIOS2
+
+#define ELF_START_MMAP 0x80000000
+
+#define elf_check_arch(x) ((x) == EM_ALTERA_NIOS2)
+
+#define ELF_CLASS ELFCLASS32
+#define ELF_ARCH EM_ALTERA_NIOS2
+
+static inline void init_thread(struct target_pt_regs *regs,
+ struct image_info *infop)
+{
+ /* TODO */
+}
+
+#define ELF_EXEC_PAGESIZE 4096
+
+#define USE_ELF_CORE_DUMP
+/* TODO - #define ELF_NREG 38 */
+typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG];
+
+/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */
+static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env)
+{
+ /* TODO */
+}
+
+#endif /* TARGET_NIOS2 */
+
#ifdef TARGET_OPENRISC
#define ELF_START_MMAP 0x08000000
diff --git a/monitor.c b/monitor.c
index b17b1bb..84dbe9b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -2051,7 +2051,8 @@ static void tlb_info(Monitor *mon)
#endif
-#if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA)
+#if defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_XTENSA) || \
+ defined(TARGET_NIOS2)
static void tlb_info(Monitor *mon)
{
CPUArchState *env1 = mon_get_cpu();
@@ -2759,7 +2760,7 @@ static mon_cmd_t info_cmds[] = {
.mhandler.info = hmp_info_pci,
},
#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \
- defined(TARGET_PPC) || defined(TARGET_XTENSA)
+ defined(TARGET_PPC) || defined(TARGET_XTENSA) || defined(TARGET_NIOS2)
{
.name = "tlb",
.args_type = "",
diff --git a/qapi-schema.json b/qapi-schema.json
index bd8ad74..0a10521 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2467,7 +2467,7 @@
##
{ 'enum': 'TargetType',
'data': [ 'alpha', 'arm', 'cris', 'i386', 'lm32', 'm68k', 'microblazeel',
- 'microblaze', 'mips64el', 'mips64', 'mipsel', 'mips', 'or32',
+ 'microblaze', 'mips64el', 'mips64', 'mipsel', 'mips', 'nios2', 'or32',
'ppc64', 'ppcemb', 'ppc', 's390x', 'sh4eb', 'sh4', 'sparc64',
'sparc', 'unicore32', 'x86_64', 'xtensaeb', 'xtensa' ] }
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 35cabbc..3515f15 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -2475,6 +2475,9 @@ The binary format is detected automatically.
@command{qemu-mips} TODO.
@command{qemu-mipsel} TODO.
+@cindex user mode (NiosII)
+@command{qemu-nios2} TODO.
+
@cindex user mode (PowerPC)
@command{qemu-ppc64abi32} TODO.
@command{qemu-ppc64} TODO.
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (5 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 6/9] NiosII: Build system and documentation integration crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-11 19:40 ` Blue Swirl
2012-09-10 0:20 ` [Qemu-devel] [PATCH 8/9] MicroBlaze: " crwulff
` (2 subsequent siblings)
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
default-configs/nios2-linux-user.mak | 1 +
default-configs/nios2-softmmu.mak | 6 +
hw/labx_nios2_devicetree.c | 665 ++++++++++++++++++++++++++++++++++
hw/nios2/Makefile.objs | 6 +
4 files changed, 678 insertions(+)
create mode 100644 default-configs/nios2-linux-user.mak
create mode 100644 default-configs/nios2-softmmu.mak
create mode 100644 hw/labx_nios2_devicetree.c
create mode 100644 hw/nios2/Makefile.objs
diff --git a/default-configs/nios2-linux-user.mak b/default-configs/nios2-linux-user.mak
new file mode 100644
index 0000000..5be3eb7
--- /dev/null
+++ b/default-configs/nios2-linux-user.mak
@@ -0,0 +1 @@
+# Default configuration for nios2-linux-user
diff --git a/default-configs/nios2-softmmu.mak b/default-configs/nios2-softmmu.mak
new file mode 100644
index 0000000..09f6046
--- /dev/null
+++ b/default-configs/nios2-softmmu.mak
@@ -0,0 +1,6 @@
+# Default configuration for nios2-softmmu
+
+CONFIG_PTIMER=y
+CONFIG_PFLASH_CFI01=y
+CONFIG_ALTERA=y
+CONFIG_LABX=y
diff --git a/hw/labx_nios2_devicetree.c b/hw/labx_nios2_devicetree.c
new file mode 100644
index 0000000..d91bd9b
--- /dev/null
+++ b/hw/labx_nios2_devicetree.c
@@ -0,0 +1,665 @@
+/*
+ * Flexible model of nios2 designs that use a device-tree to determine
+ * the hardware configuration.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "nios2.h"
+#include "altera.h"
+#include "labx_devices.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+#include "exec-memory.h"
+
+#define LMB_BRAM_SIZE (128 * 1024)
+
+static int sopc_device_probe(void *fdt, int node, int pass, uint32_t offset);
+
+static struct {
+ uint32_t bootstrap_pc;
+ uint32_t cmdline;
+ uint32_t initrd;
+ uint32_t fdt;
+} boot_info;
+
+/* Current ethernet device index for multiple network interfaces */
+static int eth_dev_index = -1;
+
+static void main_cpu_reset(void *opaque)
+{
+ Nios2CPU *cpu = opaque;
+ CPUNios2State *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+
+ env->regs[R_ARG0] = boot_info.cmdline;
+ env->regs[R_ARG1] = boot_info.initrd;
+ env->regs[R_ARG2] = boot_info.fdt;
+ env->regs[R_PC] = boot_info.bootstrap_pc;
+}
+
+#ifndef CONFIG_FDT
+#error "Device-tree support is required for this target to function"
+#endif
+
+static uint32_t fdt_get_int_from_array(void *fdt, int node,
+ const char *name, int index)
+{
+ int array_length;
+ const void *array = qemu_devtree_getprop_offset(fdt, node, name,
+ &array_length);
+ if (index >= array_length) {
+ printf("fdt_get_int_from_array: requesting %s from node %d, "
+ "index %d out of range (%d max)\n",
+ name, node, index, array_length);
+ return 0;
+ } else {
+ return qemu_devtree_int_array_index(array, index);
+ }
+}
+
+#define BINARY_DEVICE_TREE_FILE "labx-nios2.dtb"
+static void *get_device_tree(int *fdt_size)
+{
+ char *path;
+ void *fdt;
+
+ /* Try the local "local.dtb" override. */
+ fdt = load_device_tree("local.dtb", fdt_size);
+ if (!fdt) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, fdt_size);
+ g_free(path);
+ }
+ }
+
+ return fdt;
+}
+
+static int labx_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ int fdt_size;
+ void *fdt;
+ int r;
+
+ fdt = get_device_tree(&fdt_size);
+
+ if (!fdt) {
+ return 0;
+ }
+
+ if (kernel_cmdline && strlen(kernel_cmdline)) {
+ r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
+ if (r < 0) {
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ }
+ }
+ cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
+
+ return fdt_size;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return addr - 0xC0000000LL;
+}
+
+static ram_addr_t get_dram_base(void *fdt)
+{
+ int root = qemu_devtree_node_offset(fdt, "/");
+ int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
+ if (memory > 0) {
+ int reglen;
+ const void *reg = qemu_devtree_getprop_offset(fdt, memory, "reg",
+ ®len);
+
+ if (reglen >= 4) {
+ printf("DRAM base %08X, size %08X\n",
+ qemu_devtree_int_array_index(reg, 0),
+ qemu_devtree_int_array_index(reg, 1));
+ return qemu_devtree_int_array_index(reg, 0);
+ }
+ }
+
+ printf("DRAM base not found. Defaulting to 0x00000000\n");
+
+ return 0x00000000; /* Default to something reasonable */
+}
+
+typedef void (*device_init_func_t)(void *fdt, int node, uint32_t offset);
+
+typedef struct {
+ device_init_func_t probe;
+ int pass;
+ const char **compat;
+
+} devinfo_t;
+
+/*
+ * Interrupt controller device
+ */
+Nios2CPU *g_cpu;
+static qemu_irq irq[32] = {};
+static qemu_irq *cpu_irq;
+
+static void cpu_probe(void *fdt, int node, uint32_t offset)
+{
+ int i;
+ DeviceState *dev;
+#if 0 /* TODO: Finish off the vectored-interrupt-controller */
+ int reglen;
+ const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
+ uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0) + offset;
+ int nrIrqLen;
+ const void *nrIrq =
+ qemu_devtree_getprop_offset(fdt, node, "ALTR,num-intr-inputs",
+ &nrIrqLen);
+ uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
+
+ printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
+
+ cpu_irq = nios2_pic_init_cpu(env);
+ dev = altera_vic_create(irq_addr, cpu_irq[0], 2);
+#else
+ /* Internal interrupt controller (IIC) */
+ cpu_irq = nios2_pic_init_cpu(&g_cpu->env);
+ dev = altera_iic_create(cpu_irq[0], 2);
+#endif
+
+ /* TODO: use the entrypoint of the passed in elf file or
+ the device-tree one */
+#if 0
+ g_cpu->env.reset_addr =
+ fdt_get_int_from_array(fdt, node, "ALTR,reset-addr", 0);
+#else
+ g_cpu->env.reset_addr = 0xc0000000;
+#endif
+
+ g_cpu->env.exception_addr =
+ fdt_get_int_from_array(fdt, node, "ALTR,exception-addr", 0);
+ g_cpu->env.fast_tlb_miss_addr =
+ fdt_get_int_from_array(fdt, node, "ALTR,fast-tlb-miss-addr", 0);
+
+ /* reset again to use the new reset vector */
+ cpu_reset(CPU(g_cpu));
+
+ for (i = 0; i < 32; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+}
+
+devinfo_t cpu_device = {
+ .probe = &cpu_probe,
+ .pass = 0,
+ .compat = (const char * []) {
+ "ALTR,nios2-11.0",
+ "ALTR,nios2-11.1",
+ "ALTR,nios2-12.0",
+ NULL
+ }
+};
+
+/*
+ * Flash device
+ */
+static void flash_probe(void *fdt, int node, uint32_t offset)
+{
+ int reglen;
+ const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
+ uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0) + offset;
+ uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
+
+ DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(flash_addr, NULL,
+ qemu_devtree_get_name(fdt, node, NULL), flash_size,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ flash_size >> 16,
+ 1, 0x89, 0x18, 0x0000, 0x0, 0);
+ printf("-- loaded %d bytes to %08X\n",
+ load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
+ flash_addr, flash_size), flash_addr);
+}
+
+devinfo_t flash_device = {
+ .probe = &flash_probe,
+ .pass = 1,
+ .compat = (const char * []) { "cfi-flash", NULL }
+};
+
+/*
+ * LabX audio packetizer device
+ */
+static void labx_audio_packetizer_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
+ offset;
+ uint32_t packetizer_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t clock_domains =
+ fdt_get_int_from_array(fdt, node, "labx,num-clock-domains", 0);
+ uint32_t cache_words =
+ fdt_get_int_from_array(fdt, node, "labx,cache-data-words", 0);
+
+ labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
+ clock_domains, cache_words);
+}
+
+devinfo_t labx_audio_packetizer_device = {
+ .probe = &labx_audio_packetizer_probe,
+ .pass = 1,
+ .compat = (const char * []) { "labx,labx_audio_packetizer-1.0", NULL }
+};
+
+/*
+ * LabX audio depacketizer device
+ */
+static void labx_audio_depacketizer_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
+ offset;
+ uint32_t depacketizer_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t clock_domains =
+ fdt_get_int_from_array(fdt, node, "labx,num-clock-domains", 0);
+ uint32_t cache_words =
+ fdt_get_int_from_array(fdt, node, "labx,cache-data-words", 0);
+
+ int ifLen;
+ const void *ifType =
+ qemu_devtree_getprop_offset(fdt, node, "labx,interface-type", &ifLen);
+ int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
+
+ labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
+ clock_domains, cache_words, hasDMA);
+}
+
+devinfo_t labx_audio_depacketizer_device = {
+ .probe = &labx_audio_depacketizer_probe,
+ .pass = 1,
+ .compat = (const char * []) {
+ "labx,labx-audio-depacketizer-1.00.a",
+ "labx,labx-audio-depacketizer-1.01.a",
+ NULL
+ }
+};
+
+/*
+ * LabX dma device
+ */
+static void labx_dma_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
+
+ labx_dma_create(dma_addr, 1024);
+}
+
+devinfo_t labx_dma_device = {
+ .probe = &labx_dma_probe,
+ .pass = 1,
+ .compat = (const char * []) { "labx,labx_dma-1.0", NULL }
+};
+
+/*
+ * LabX ethernet device
+ */
+static void labx_ethernet_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
+ offset;
+ uint32_t host_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t fifo_irq = 0; /*
+ fdt_get_int_from_array(fdt, node, "interrupts", 2); */
+ uint32_t phy_irq = 0; /*
+ fdt_get_int_from_array(fdt, node, "interrupts", 4); */
+
+ labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
+ irq[host_irq], irq[fifo_irq], irq[phy_irq]);
+}
+
+devinfo_t labx_ethernet_device = {
+ .probe = &labx_ethernet_probe,
+ .pass = 1,
+ .compat = (const char * []) { "labx,labx_ethernet-1.0", NULL }
+};
+
+/*
+ * LabX ptp device
+ */
+static void labx_ptp_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
+
+ labx_ptp_create(ptp_addr);
+}
+
+devinfo_t labx_ptp_device = {
+ .probe = &labx_ptp_probe,
+ .pass = 1,
+ .compat = (const char * []) { "labx,labx_ptp-1.0", NULL }
+};
+
+/*
+ * Altera uart device
+ */
+static void altera_uart_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
+ uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+ printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
+
+ sysbus_create_simple("altera,uart", uart_addr, irq[uart_irq]);
+}
+
+devinfo_t altera_uart_device = {
+ .probe = &altera_uart_probe,
+ .pass = 1,
+ .compat = (const char * []) { "ALTR,uart-1.0", NULL }
+};
+
+/*
+ * Altera timer device
+ */
+static void altera_timer_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
+ uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t timer_freq =
+ fdt_get_int_from_array(fdt, node, "clock-frequency", 0);
+
+ printf(" TIMER BASE %08X IRQ %d FREQUENCY %d\n",
+ timer_addr, timer_irq, timer_freq);
+
+ altera_timer_create(timer_addr, irq[timer_irq], timer_freq);
+}
+
+devinfo_t altera_timer_device = {
+ .probe = &altera_timer_probe,
+ .pass = 1,
+ .compat = (const char * []) { "ALTR,timer-1.0", NULL }
+};
+
+/*
+ * Altera clock domain crossing bridge
+ */
+static void altera_bridge_probe(void *fdt, int node, uint32_t offset)
+{
+ uint32_t bridge_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
+
+ printf(" BRIDGE %08X\n", bridge_addr);
+
+ /* Do multiple passes through the devices. Some have dependencies
+ on others being first */
+ int pass = 0;
+ int again = 0;
+ do {
+ int child = node;
+ again = 0;
+ do {
+ child = qemu_devtree_next_child_offset(fdt, node, child);
+ if (child < 0) {
+ break;
+ }
+
+ again |= sopc_device_probe(fdt, child, pass, bridge_addr);
+
+ } while (1);
+
+ pass++;
+
+ } while (again);
+}
+
+devinfo_t altera_bridge_device = {
+ .probe = &altera_bridge_probe,
+ .pass = 1,
+ .compat = (const char * []) { "simple-bus", NULL }
+};
+
+/*
+ * Table of available devices
+ */
+devinfo_t *devices[] = {
+ &cpu_device,
+ &flash_device,
+ &labx_audio_packetizer_device,
+ &labx_audio_depacketizer_device,
+ &labx_dma_device,
+ &labx_ethernet_device,
+ &labx_ptp_device,
+ &altera_uart_device,
+ &altera_timer_device,
+ &altera_bridge_device,
+ NULL
+};
+
+static int sopc_device_probe(void *fdt, int node, int pass, uint32_t offset)
+{
+ devinfo_t **dev = &(devices[0]);
+
+ while (*dev) {
+ const char **compat = &((*dev)->compat[0]);
+ while (*compat) {
+ if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
+ if (pass == (*dev)->pass) {
+ printf("Adding a device for node %s\n",
+ qemu_devtree_get_name(fdt, node, NULL));
+
+ (*dev)->probe(fdt, node, offset);
+ return 0;
+ }
+
+ if (pass < (*dev)->pass) {
+ /* Probe again on the next pass */
+ return 1;
+ }
+ }
+
+ compat++;
+ }
+
+ dev++;
+ }
+
+ return 0;
+}
+
+static void cpus_probe(void *fdt)
+{
+ int root = qemu_devtree_node_offset(fdt, "/");
+ int cpus = qemu_devtree_subnode_offset_namelen(fdt, root, "cpus", 4);
+ if (cpus > 0) {
+ int child = cpus;
+ do {
+ child = qemu_devtree_next_child_offset(fdt, cpus, child);
+ if (child < 0) {
+ break;
+ }
+
+ sopc_device_probe(fdt, child, 0, 0xE0000000);
+ } while (1);
+ }
+}
+
+static void sopc_bus_probe(void *fdt)
+{
+ int root = qemu_devtree_node_offset(fdt, "/");
+ int sopc = qemu_devtree_subnode_offset_namelen(fdt, root, "sopc", 4);
+ if (sopc > 0) {
+ /* Do multiple passes through the devices. Some have dependencies
+ on others being first */
+ int pass = 0;
+ int again = 0;
+ do {
+ int child = sopc;
+ again = 0;
+ do {
+ child = qemu_devtree_next_child_offset(fdt, sopc, child);
+ if (child < 0) {
+ break;
+ }
+
+ again |= sopc_device_probe(fdt, child, pass, 0xE0000000);
+
+ } while (1);
+
+ pass++;
+
+ } while (again);
+ }
+}
+
+static void
+labx_nios2_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ MemoryRegion *address_space_mem = get_system_memory();
+
+ int kernel_size;
+ int fdt_size;
+ void *fdt = get_device_tree(&fdt_size);
+ target_phys_addr_t ddr_base = get_dram_base(fdt);
+ MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
+ MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
+ MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1);
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "nios2";
+ }
+ g_cpu = cpu_nios2_init(cpu_model);
+
+ qemu_register_reset(main_cpu_reset, g_cpu);
+
+ /* Attach emulated BRAM through the LMB. LMB size is not specified
+ in the device-tree but there must be one to hold the vector table. */
+ memory_region_init_ram(phys_lmb_bram, "nios2.lmb_bram", LMB_BRAM_SIZE);
+ vmstate_register_ram_global(phys_lmb_bram);
+ memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
+
+ memory_region_init_ram(phys_ram, "nios2.ram", ram_size);
+ vmstate_register_ram_global(phys_ram);
+ memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
+ memory_region_init_alias(phys_ram_alias, "nios2.ram.mirror",
+ phys_ram, 0, ram_size);
+ memory_region_add_subregion(address_space_mem, ddr_base + 0xc0000000,
+ phys_ram_alias);
+
+ /* Create cpus listed in the device-tree */
+ cpus_probe(fdt);
+
+ /* Create other devices listed in the device-tree */
+ sopc_bus_probe(fdt);
+
+ if (kernel_filename) {
+ uint64_t entry = 0, low = 0, high = 0;
+ uint32_t base32 = 0;
+
+ /* Boots a kernel elf binary. */
+ kernel_size = load_elf(kernel_filename, NULL, NULL,
+ &entry, &low, &high,
+ 0, ELF_MACHINE, 0);
+ base32 = entry;
+ if (base32 == 0xc0000000) {
+ kernel_size = load_elf(kernel_filename, translate_kernel_address,
+ NULL, &entry, NULL, NULL,
+ 0, ELF_MACHINE, 0);
+ }
+ /* Always boot into physical ram. */
+ boot_info.bootstrap_pc = ddr_base + 0xc0000000 + (entry & 0x07ffffff);
+
+ /* If it wasn't an ELF image, try an u-boot image. */
+ if (kernel_size < 0) {
+ target_phys_addr_t uentry, loadaddr;
+
+ kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+ boot_info.bootstrap_pc = uentry;
+ high = (loadaddr + kernel_size + 3) & ~3;
+ }
+
+ /* Not an ELF image nor an u-boot image, try a RAW image. */
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, ddr_base,
+ ram_size);
+ boot_info.bootstrap_pc = ddr_base;
+ high = (ddr_base + kernel_size + 3) & ~3;
+ }
+
+ if (initrd_filename) {
+ uint32_t initrd_base = 0x88c00000;
+ uint32_t initrd_size =
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size <= 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+
+ boot_info.initrd = initrd_base;
+ } else {
+ boot_info.initrd = 0x00000000;
+ }
+
+ boot_info.cmdline = high + 4096;
+ if (kernel_cmdline && strlen(kernel_cmdline)) {
+ pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+ }
+ /* Provide a device-tree. */
+ boot_info.fdt = boot_info.cmdline + 4096;
+ labx_load_device_tree(boot_info.fdt, ram_size,
+ 0, 0,
+ kernel_cmdline);
+ }
+}
+
+static QEMUMachine labx_nios2_machine = {
+ .name = "labx-nios2-devicetree",
+ .desc = "Nios II design based on the peripherals specified "
+ "in the device-tree.",
+ .init = labx_nios2_init,
+ .is_default = 1
+};
+
+static void labx_nios2_machine_init(void)
+{
+ qemu_register_machine(&labx_nios2_machine);
+}
+
+machine_init(labx_nios2_machine_init);
+
diff --git a/hw/nios2/Makefile.objs b/hw/nios2/Makefile.objs
new file mode 100644
index 0000000..33cdd9b
--- /dev/null
+++ b/hw/nios2/Makefile.objs
@@ -0,0 +1,6 @@
+obj-y = labx_nios2_devicetree.o
+
+obj-y += nios2_pic_cpu.o
+obj-$(CONFIG_FDT) += ../device_tree.o
+
+obj-y := $(addprefix ../,$(obj-y))
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file crwulff
@ 2012-09-11 19:40 ` Blue Swirl
0 siblings, 0 replies; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 19:40 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> default-configs/nios2-linux-user.mak | 1 +
> default-configs/nios2-softmmu.mak | 6 +
> hw/labx_nios2_devicetree.c | 665 ++++++++++++++++++++++++++++++++++
> hw/nios2/Makefile.objs | 6 +
> 4 files changed, 678 insertions(+)
> create mode 100644 default-configs/nios2-linux-user.mak
> create mode 100644 default-configs/nios2-softmmu.mak
> create mode 100644 hw/labx_nios2_devicetree.c
> create mode 100644 hw/nios2/Makefile.objs
>
> diff --git a/default-configs/nios2-linux-user.mak b/default-configs/nios2-linux-user.mak
> new file mode 100644
> index 0000000..5be3eb7
> --- /dev/null
> +++ b/default-configs/nios2-linux-user.mak
> @@ -0,0 +1 @@
> +# Default configuration for nios2-linux-user
> diff --git a/default-configs/nios2-softmmu.mak b/default-configs/nios2-softmmu.mak
> new file mode 100644
> index 0000000..09f6046
> --- /dev/null
> +++ b/default-configs/nios2-softmmu.mak
> @@ -0,0 +1,6 @@
> +# Default configuration for nios2-softmmu
> +
> +CONFIG_PTIMER=y
> +CONFIG_PFLASH_CFI01=y
> +CONFIG_ALTERA=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_nios2_devicetree.c b/hw/labx_nios2_devicetree.c
> new file mode 100644
> index 0000000..d91bd9b
> --- /dev/null
> +++ b/hw/labx_nios2_devicetree.c
> @@ -0,0 +1,665 @@
> +/*
> + * Flexible model of nios2 designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "nios2.h"
> +#include "altera.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
This interface may not be used by devices.
> +
> +#define LMB_BRAM_SIZE (128 * 1024)
> +
> +static int sopc_device_probe(void *fdt, int node, int pass, uint32_t offset);
> +
> +static struct {
> + uint32_t bootstrap_pc;
> + uint32_t cmdline;
> + uint32_t initrd;
> + uint32_t fdt;
> +} boot_info;
> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;
> +
> +static void main_cpu_reset(void *opaque)
> +{
> + Nios2CPU *cpu = opaque;
> + CPUNios2State *env = &cpu->env;
> +
> + cpu_reset(CPU(cpu));
> +
> + env->regs[R_ARG0] = boot_info.cmdline;
> + env->regs[R_ARG1] = boot_info.initrd;
> + env->regs[R_ARG2] = boot_info.fdt;
> + env->regs[R_PC] = boot_info.bootstrap_pc;
> +}
> +
> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
Probably the file should be built only if CONFIG_FDT is set, otherwise
this will break build.
> +#endif
> +
> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> + const char *name, int index)
> +{
> + int array_length;
> + const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> + &array_length);
> + if (index >= array_length) {
> + printf("fdt_get_int_from_array: requesting %s from node %d, "
> + "index %d out of range (%d max)\n",
> + name, node, index, array_length);
> + return 0;
> + } else {
> + return qemu_devtree_int_array_index(array, index);
> + }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-nios2.dtb"
This belongs to the beginning of the file.
> +static void *get_device_tree(int *fdt_size)
> +{
> + char *path;
> + void *fdt;
> +
> + /* Try the local "local.dtb" override. */
> + fdt = load_device_tree("local.dtb", fdt_size);
> + if (!fdt) {
> + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> + if (path) {
> + fdt = load_device_tree(path, fdt_size);
> + g_free(path);
> + }
> + }
> +
> + return fdt;
> +}
> +
> +static int labx_load_device_tree(target_phys_addr_t addr,
> + uint32_t ramsize,
> + target_phys_addr_t initrd_base,
> + target_phys_addr_t initrd_size,
> + const char *kernel_cmdline)
> +{
> + int fdt_size;
> + void *fdt;
> + int r;
> +
> + fdt = get_device_tree(&fdt_size);
> +
> + if (!fdt) {
> + return 0;
> + }
> +
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> + kernel_cmdline);
> + if (r < 0) {
> + fprintf(stderr, "couldn't set /chosen/bootargs\n");
> + }
> + }
> + cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> + return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> + return addr - 0xC0000000LL;
ULL
> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> + if (memory > 0) {
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, memory, "reg",
> + ®len);
> +
> + if (reglen >= 4) {
> + printf("DRAM base %08X, size %08X\n",
Leftover debugging
> + qemu_devtree_int_array_index(reg, 0),
> + qemu_devtree_int_array_index(reg, 1));
> + return qemu_devtree_int_array_index(reg, 0);
> + }
> + }
> +
> + printf("DRAM base not found. Defaulting to 0x00000000\n");
> +
> + return 0x00000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node, uint32_t offset);
> +
> +typedef struct {
> + device_init_func_t probe;
> + int pass;
> + const char **compat;
> +
> +} devinfo_t;
CamelCase
> +
> +/*
> + * Interrupt controller device
> + */
> +Nios2CPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;
No globals
> +
> +static void cpu_probe(void *fdt, int node, uint32_t offset)
> +{
> + int i;
> + DeviceState *dev;
> +#if 0 /* TODO: Finish off the vectored-interrupt-controller */
No dead code
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0) + offset;
> + int nrIrqLen;
> + const void *nrIrq =
> + qemu_devtree_getprop_offset(fdt, node, "ALTR,num-intr-inputs",
> + &nrIrqLen);
> + uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> + printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
> +
> + cpu_irq = nios2_pic_init_cpu(env);
> + dev = altera_vic_create(irq_addr, cpu_irq[0], 2);
> +#else
> + /* Internal interrupt controller (IIC) */
> + cpu_irq = nios2_pic_init_cpu(&g_cpu->env);
> + dev = altera_iic_create(cpu_irq[0], 2);
> +#endif
> +
> + /* TODO: use the entrypoint of the passed in elf file or
> + the device-tree one */
> +#if 0
> + g_cpu->env.reset_addr =
> + fdt_get_int_from_array(fdt, node, "ALTR,reset-addr", 0);
> +#else
> + g_cpu->env.reset_addr = 0xc0000000;
> +#endif
> +
> + g_cpu->env.exception_addr =
> + fdt_get_int_from_array(fdt, node, "ALTR,exception-addr", 0);
> + g_cpu->env.fast_tlb_miss_addr =
> + fdt_get_int_from_array(fdt, node, "ALTR,fast-tlb-miss-addr", 0);
> +
> + /* reset again to use the new reset vector */
> + cpu_reset(CPU(g_cpu));
> +
> + for (i = 0; i < 32; i++) {
> + irq[i] = qdev_get_gpio_in(dev, i);
> + }
> +}
> +
> +devinfo_t cpu_device = {
static const, CamelCase, no _t
> + .probe = &cpu_probe,
> + .pass = 0,
> + .compat = (const char * []) {
> + "ALTR,nios2-11.0",
> + "ALTR,nios2-11.1",
> + "ALTR,nios2-12.0",
> + NULL
> + }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node, uint32_t offset)
> +{
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0) + offset;
> + uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> + pflash_cfi01_register(flash_addr, NULL,
> + qemu_devtree_get_name(fdt, node, NULL), flash_size,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + flash_size >> 16,
> + 1, 0x89, 0x18, 0x0000, 0x0, 0);
> + printf("-- loaded %d bytes to %08X\n",
> + load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> + flash_addr, flash_size), flash_addr);
> +}
> +
> +devinfo_t flash_device = {
> + .probe = &flash_probe,
> + .pass = 1,
> + .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
> + offset;
> + uint32_t packetizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "labx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "labx,cache-data-words", 0);
> +
> + labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> + clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> + .probe = &labx_audio_packetizer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "labx,labx_audio_packetizer-1.0", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
> + offset;
> + uint32_t depacketizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "labx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "labx,cache-data-words", 0);
> +
> + int ifLen;
No CamelCase
> + const void *ifType =
> + qemu_devtree_getprop_offset(fdt, node, "labx,interface-type", &ifLen);
> + int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> + labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> + clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> + .probe = &labx_audio_depacketizer_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "labx,labx-audio-depacketizer-1.00.a",
> + "labx,labx-audio-depacketizer-1.01.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
> +
> + labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> + .probe = &labx_dma_probe,
> + .pass = 1,
> + .compat = (const char * []) { "labx,labx_dma-1.0", NULL }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0) +
> + offset;
> + uint32_t host_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t fifo_irq = 0; /*
> + fdt_get_int_from_array(fdt, node, "interrupts", 2); */
> + uint32_t phy_irq = 0; /*
> + fdt_get_int_from_array(fdt, node, "interrupts", 4); */
> +
> + labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> + irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> + .probe = &labx_ethernet_probe,
> + .pass = 1,
> + .compat = (const char * []) { "labx,labx_ethernet-1.0", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
> +
> + labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> + .probe = &labx_ptp_probe,
> + .pass = 1,
> + .compat = (const char * []) { "labx,labx_ptp-1.0", NULL }
> +};
> +
> +/*
> + * Altera uart device
> + */
> +static void altera_uart_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
> + uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> + sysbus_create_simple("altera,uart", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t altera_uart_device = {
> + .probe = &altera_uart_probe,
> + .pass = 1,
> + .compat = (const char * []) { "ALTR,uart-1.0", NULL }
> +};
> +
> +/*
> + * Altera timer device
> + */
> +static void altera_timer_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
> + uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t timer_freq =
> + fdt_get_int_from_array(fdt, node, "clock-frequency", 0);
> +
> + printf(" TIMER BASE %08X IRQ %d FREQUENCY %d\n",
> + timer_addr, timer_irq, timer_freq);
> +
> + altera_timer_create(timer_addr, irq[timer_irq], timer_freq);
> +}
> +
> +devinfo_t altera_timer_device = {
> + .probe = &altera_timer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "ALTR,timer-1.0", NULL }
> +};
> +
> +/*
> + * Altera clock domain crossing bridge
> + */
> +static void altera_bridge_probe(void *fdt, int node, uint32_t offset)
> +{
> + uint32_t bridge_addr = fdt_get_int_from_array(fdt, node, "reg", 0) + offset;
> +
> + printf(" BRIDGE %08X\n", bridge_addr);
> +
> + /* Do multiple passes through the devices. Some have dependencies
> + on others being first */
> + int pass = 0;
Variable declarations must be before code statements.
> + int again = 0;
bool
> + do {
> + int child = node;
> + again = 0;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, node, child);
> + if (child < 0) {
> + break;
> + }
> +
> + again |= sopc_device_probe(fdt, child, pass, bridge_addr);
> +
> + } while (1);
> +
> + pass++;
> +
> + } while (again);
> +}
> +
> +devinfo_t altera_bridge_device = {
> + .probe = &altera_bridge_probe,
> + .pass = 1,
> + .compat = (const char * []) { "simple-bus", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> + &cpu_device,
> + &flash_device,
> + &labx_audio_packetizer_device,
> + &labx_audio_depacketizer_device,
> + &labx_dma_device,
> + &labx_ethernet_device,
> + &labx_ptp_device,
> + &altera_uart_device,
> + &altera_timer_device,
> + &altera_bridge_device,
> + NULL
> +};
> +
> +static int sopc_device_probe(void *fdt, int node, int pass, uint32_t offset)
> +{
> + devinfo_t **dev = &(devices[0]);
> +
> + while (*dev) {
You could drop the NULL and use indexing up to ARRAY_SIZE instead.
> + const char **compat = &((*dev)->compat[0]);
> + while (*compat) {
> + if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
> + if (pass == (*dev)->pass) {
> + printf("Adding a device for node %s\n",
> + qemu_devtree_get_name(fdt, node, NULL));
> +
> + (*dev)->probe(fdt, node, offset);
> + return 0;
> + }
> +
> + if (pass < (*dev)->pass) {
> + /* Probe again on the next pass */
> + return 1;
> + }
> + }
> +
> + compat++;
> + }
> +
> + dev++;
> + }
> +
> + return 0;
> +}
> +
> +static void cpus_probe(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int cpus = qemu_devtree_subnode_offset_namelen(fdt, root, "cpus", 4);
> + if (cpus > 0) {
> + int child = cpus;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, cpus, child);
> + if (child < 0) {
> + break;
> + }
> +
> + sopc_device_probe(fdt, child, 0, 0xE0000000);
> + } while (1);
> + }
> +}
> +
> +static void sopc_bus_probe(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int sopc = qemu_devtree_subnode_offset_namelen(fdt, root, "sopc", 4);
> + if (sopc > 0) {
> + /* Do multiple passes through the devices. Some have dependencies
> + on others being first */
> + int pass = 0;
> + int again = 0;
bool
> + do {
> + int child = sopc;
> + again = 0;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, sopc, child);
> + if (child < 0) {
> + break;
> + }
> +
> + again |= sopc_device_probe(fdt, child, pass, 0xE0000000);
> +
> + } while (1);
> +
> + pass++;
> +
> + } while (again);
> + }
> +}
> +
> +static void
> +labx_nios2_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename,
> + const char *kernel_cmdline,
> + const char *initrd_filename,
> + const char *cpu_model)
> +{
> + MemoryRegion *address_space_mem = get_system_memory();
> +
> + int kernel_size;
> + int fdt_size;
> + void *fdt = get_device_tree(&fdt_size);
> + target_phys_addr_t ddr_base = get_dram_base(fdt);
> + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> + MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> + MemoryRegion *phys_ram_alias = g_new(MemoryRegion, 1);
> +
> + /* init CPUs */
> + if (cpu_model == NULL) {
> + cpu_model = "nios2";
> + }
> + g_cpu = cpu_nios2_init(cpu_model);
> +
> + qemu_register_reset(main_cpu_reset, g_cpu);
> +
> + /* Attach emulated BRAM through the LMB. LMB size is not specified
> + in the device-tree but there must be one to hold the vector table. */
> + memory_region_init_ram(phys_lmb_bram, "nios2.lmb_bram", LMB_BRAM_SIZE);
> + vmstate_register_ram_global(phys_lmb_bram);
> + memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
> +
> + memory_region_init_ram(phys_ram, "nios2.ram", ram_size);
> + vmstate_register_ram_global(phys_ram);
> + memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> + memory_region_init_alias(phys_ram_alias, "nios2.ram.mirror",
> + phys_ram, 0, ram_size);
> + memory_region_add_subregion(address_space_mem, ddr_base + 0xc0000000,
> + phys_ram_alias);
> +
> + /* Create cpus listed in the device-tree */
> + cpus_probe(fdt);
> +
> + /* Create other devices listed in the device-tree */
> + sopc_bus_probe(fdt);
> +
> + if (kernel_filename) {
> + uint64_t entry = 0, low = 0, high = 0;
> + uint32_t base32 = 0;
> +
> + /* Boots a kernel elf binary. */
> + kernel_size = load_elf(kernel_filename, NULL, NULL,
> + &entry, &low, &high,
> + 0, ELF_MACHINE, 0);
> + base32 = entry;
> + if (base32 == 0xc0000000) {
> + kernel_size = load_elf(kernel_filename, translate_kernel_address,
> + NULL, &entry, NULL, NULL,
> + 0, ELF_MACHINE, 0);
> + }
> + /* Always boot into physical ram. */
> + boot_info.bootstrap_pc = ddr_base + 0xc0000000 + (entry & 0x07ffffff);
> +
> + /* If it wasn't an ELF image, try an u-boot image. */
> + if (kernel_size < 0) {
> + target_phys_addr_t uentry, loadaddr;
> +
> + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
> + boot_info.bootstrap_pc = uentry;
> + high = (loadaddr + kernel_size + 3) & ~3;
> + }
> +
> + /* Not an ELF image nor an u-boot image, try a RAW image. */
> + if (kernel_size < 0) {
> + kernel_size = load_image_targphys(kernel_filename, ddr_base,
> + ram_size);
> + boot_info.bootstrap_pc = ddr_base;
> + high = (ddr_base + kernel_size + 3) & ~3;
> + }
> +
> + if (initrd_filename) {
> + uint32_t initrd_base = 0x88c00000;
> + uint32_t initrd_size =
> + load_image_targphys(initrd_filename, initrd_base,
> + ram_size - initrd_base);
> + if (initrd_size <= 0) {
initrd_size is unsigned, the test will only catch 0 case.
> + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> + initrd_filename);
> + exit(1);
> + }
> +
> + boot_info.initrd = initrd_base;
> + } else {
> + boot_info.initrd = 0x00000000;
> + }
> +
> + boot_info.cmdline = high + 4096;
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
> + }
> + /* Provide a device-tree. */
> + boot_info.fdt = boot_info.cmdline + 4096;
> + labx_load_device_tree(boot_info.fdt, ram_size,
> + 0, 0,
> + kernel_cmdline);
> + }
> +}
> +
> +static QEMUMachine labx_nios2_machine = {
> + .name = "labx-nios2-devicetree",
> + .desc = "Nios II design based on the peripherals specified "
> + "in the device-tree.",
> + .init = labx_nios2_init,
> + .is_default = 1
> +};
> +
> +static void labx_nios2_machine_init(void)
> +{
> + qemu_register_machine(&labx_nios2_machine);
> +}
> +
> +machine_init(labx_nios2_machine_init);
> +
zap newline
> diff --git a/hw/nios2/Makefile.objs b/hw/nios2/Makefile.objs
> new file mode 100644
> index 0000000..33cdd9b
> --- /dev/null
> +++ b/hw/nios2/Makefile.objs
> @@ -0,0 +1,6 @@
> +obj-y = labx_nios2_devicetree.o
> +
> +obj-y += nios2_pic_cpu.o
> +obj-$(CONFIG_FDT) += ../device_tree.o
> +
> +obj-y := $(addprefix ../,$(obj-y))
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (6 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 7/9] NiosII: Add a config that is dynamically set up by a device tree file crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-11 19:27 ` Blue Swirl
2012-09-11 23:59 ` Peter Crosthwaite
2012-09-10 0:20 ` [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled crwulff
2012-09-11 23:40 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support Peter Crosthwaite
9 siblings, 2 replies; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
default-configs/microblaze-softmmu.mak | 1 +
hw/labx_microblaze_devicetree.c | 597 ++++++++++++++++++++++++++++++++
hw/microblaze/Makefile.objs | 3 +-
3 files changed, 600 insertions(+), 1 deletion(-)
create mode 100644 hw/labx_microblaze_devicetree.c
diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
index 64c9485..7e4cab8 100644
--- a/default-configs/microblaze-softmmu.mak
+++ b/default-configs/microblaze-softmmu.mak
@@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
CONFIG_SERIAL=y
CONFIG_XILINX=y
CONFIG_XILINX_AXI=y
+CONFIG_LABX=y
diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
new file mode 100644
index 0000000..581ad14
--- /dev/null
+++ b/hw/labx_microblaze_devicetree.c
@@ -0,0 +1,597 @@
+/*
+ * Flexible model of microblaze designs that use a device-tree to determine
+ * the hardware configuration.
+ *
+ * Copyright (c) 2010 LabX Technologies, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "sysbus.h"
+#include "hw.h"
+#include "net.h"
+#include "flash.h"
+#include "sysemu.h"
+#include "devices.h"
+#include "boards.h"
+#include "device_tree.h"
+#include "xilinx.h"
+#include "labx_devices.h"
+#include "loader.h"
+#include "elf.h"
+#include "blockdev.h"
+#include "exec-memory.h"
+#include "microblaze_pic_cpu.h"
+
+#define LMB_BRAM_SIZE (128 * 1024)
+
+static struct
+{
+ uint32_t bootstrap_pc;
+ uint32_t cmdline;
+ uint32_t initrd;
+ uint32_t fdt;
+} boot_info;
+
+/* Current ethernet device index for multiple network interfaces */
+static int eth_dev_index = -1;
+
+static void main_cpu_reset(void *opaque)
+{
+ MicroBlazeCPU *cpu = opaque;
+ CPUMBState *env = &cpu->env;
+
+ cpu_reset(CPU(cpu));
+
+ env->regs[5] = boot_info.cmdline;
+ env->regs[6] = boot_info.initrd;
+ env->regs[7] = boot_info.fdt;
+ env->sregs[SR_PC] = boot_info.bootstrap_pc;
+}
+
+#ifndef CONFIG_FDT
+#error "Device-tree support is required for this target to function"
+#endif
+
+static uint32_t fdt_get_int_from_array(void *fdt, int node,
+ const char *name, int index)
+{
+ int array_length;
+ const void *array = qemu_devtree_getprop_offset(fdt, node, name,
+ &array_length);
+ if (index >= array_length) {
+ printf("fdt_get_int_from_array: requesting %s from node %d, "
+ "index %d out of range (%d max)\n",
+ name, node, index, array_length);
+ return 0;
+ } else {
+ return qemu_devtree_int_array_index(array, index);
+ }
+}
+
+#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
+static void *get_device_tree(int *fdt_size)
+{
+ char *path;
+ void *fdt;
+
+ /* Try the local "mb.dtb" override. */
+ fdt = load_device_tree("mb.dtb", fdt_size);
+ if (!fdt) {
+ path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
+ if (path) {
+ fdt = load_device_tree(path, fdt_size);
+ g_free(path);
+ }
+ }
+
+ return fdt;
+}
+
+static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
+ uint32_t ramsize,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *kernel_cmdline)
+{
+ int fdt_size;
+ void *fdt;
+ int r;
+
+ fdt = get_device_tree(&fdt_size);
+
+ if (!fdt) {
+ return 0;
+ }
+
+ if (kernel_cmdline && strlen(kernel_cmdline)) {
+ r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
+ kernel_cmdline);
+ if (r < 0) {
+ fprintf(stderr, "couldn't set /chosen/bootargs\n");
+ }
+ }
+ cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
+
+ return fdt_size;
+}
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+ return addr - 0x30000000LL;
+}
+
+static ram_addr_t get_dram_base(void *fdt)
+{
+ int root = qemu_devtree_node_offset(fdt, "/");
+ int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
+ if (memory > 0) {
+ int reglen;
+ const void *reg =
+ qemu_devtree_getprop_offset(fdt, memory, "reg", ®len);
+
+ if (reglen >= 4) {
+ printf("DRAM base %08X, size %08X\n",
+ qemu_devtree_int_array_index(reg, 0),
+ qemu_devtree_int_array_index(reg, 1));
+ return qemu_devtree_int_array_index(reg, 0);
+ }
+ }
+
+ printf("DRAM base not found. Defaulting to 0x88000000\n");
+
+ return 0x88000000; /* Default to something reasonable */
+}
+
+typedef void (*device_init_func_t)(void *fdt, int node);
+
+typedef struct {
+ device_init_func_t probe;
+ int pass;
+ const char **compat;
+
+} devinfo_t;
+
+/*
+ * Xilinx interrupt controller device
+ */
+MicroBlazeCPU *g_cpu;
+static qemu_irq irq[32] = {};
+static qemu_irq *cpu_irq;
+
+static void xilinx_interrupt_controller_probe(void *fdt, int node)
+{
+ int i;
+ DeviceState *dev;
+ int reglen;
+ const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
+ uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
+ int nrIrqLen;
+ const void *nrIrq =
+ qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
+ &nrIrqLen);
+ uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
+
+ printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
+
+ cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
+ dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
+ for (i = 0; i < nrIrqs; i++) {
+ irq[i] = qdev_get_gpio_in(dev, i);
+ }
+}
+
+devinfo_t xilinx_interrupt_controller_device = {
+ .probe = &xilinx_interrupt_controller_probe,
+ .pass = 0,
+ .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
+};
+
+/*
+ * Flash device
+ */
+static void flash_probe(void *fdt, int node)
+{
+ int reglen;
+ const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
+ uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
+ uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
+
+ DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
+ pflash_cfi01_register(flash_addr, NULL,
+ qemu_devtree_get_name(fdt, node, NULL), flash_size,
+ dinfo ? dinfo->bdrv : NULL, (64 * 1024),
+ flash_size >> 16,
+ 1, 0x89, 0x18, 0x0000, 0x0, 1);
+ printf("-- loaded %d bytes to %08X\n",
+ load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
+ flash_addr, flash_size),
+ flash_addr);
+}
+
+devinfo_t flash_device = {
+ .probe = &flash_probe,
+ .pass = 1,
+ .compat = (const char * []) { "cfi-flash", NULL }
+};
+
+/*
+ * Xilinx timer device
+ */
+static void xilinx_timer_probe(void *fdt, int node)
+{
+ uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+ printf(" TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
+
+ /* 2 timers at irq 2 @ 62 Mhz. */
+ xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
+}
+
+devinfo_t xilinx_timer_device = {
+ .probe = &xilinx_timer_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
+};
+
+/*
+ * Xilinx uartlite device
+ */
+static void xilinx_uartlite_probe(void *fdt, int node)
+{
+ uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+ printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
+
+ sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
+}
+
+devinfo_t xilinx_uartlite_device = {
+ .probe = &xilinx_uartlite_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
+};
+
+/*
+ * Xilinx ethernetlite device
+ */
+static void xilinx_ethlite_probe(void *fdt, int node)
+{
+ uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
+
+ xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
+ irq[eth_irq], 0, 0);
+}
+
+devinfo_t xilinx_ethlite_device = {
+ .probe = &xilinx_ethlite_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
+};
+
+/*
+ * LabX audio packetizer device
+ */
+static void labx_audio_packetizer_probe(void *fdt, int node)
+{
+ uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t packetizer_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t clock_domains =
+ fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
+ uint32_t cache_words =
+ fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
+
+ labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
+ clock_domains, cache_words);
+}
+
+devinfo_t labx_audio_packetizer_device = {
+ .probe = &labx_audio_packetizer_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
+};
+
+/*
+ * LabX audio depacketizer device
+ */
+static void labx_audio_depacketizer_probe(void *fdt, int node)
+{
+ uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t depacketizer_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t clock_domains =
+ fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
+ uint32_t cache_words =
+ fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
+
+ int ifLen;
+ const void *ifType =
+ qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
+ int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
+
+ labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
+ clock_domains, cache_words, hasDMA);
+}
+
+devinfo_t labx_audio_depacketizer_device = {
+ .probe = &labx_audio_depacketizer_probe,
+ .pass = 1,
+ .compat = (const char * []) {
+ "xlnx,labx-audio-depacketizer-1.00.a",
+ "xlnx,labx-audio-depacketizer-1.01.a",
+ NULL
+ }
+};
+
+/*
+ * LabX dma device
+ */
+static void labx_dma_probe(void *fdt, int node)
+{
+ uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+
+ labx_dma_create(dma_addr, 1024);
+}
+
+devinfo_t labx_dma_device = {
+ .probe = &labx_dma_probe,
+ .pass = 1,
+ .compat = (const char * []) {
+ "xlnx,labx-dma-1.00.a",
+ "xlnx,labx-dma-1.01.a",
+ "xlnx,labx-local-audio-1.00.a",
+ NULL
+ }
+};
+
+/*
+ * LabX ethernet device
+ */
+static void labx_ethernet_probe(void *fdt, int node)
+{
+ uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+ uint32_t host_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 0);
+ uint32_t fifo_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 2);
+ uint32_t phy_irq =
+ fdt_get_int_from_array(fdt, node, "interrupts", 4);
+
+ labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
+ irq[host_irq], irq[fifo_irq], irq[phy_irq]);
+}
+
+devinfo_t labx_ethernet_device = {
+ .probe = &labx_ethernet_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
+};
+
+/*
+ * LabX ptp device
+ */
+static void labx_ptp_probe(void *fdt, int node)
+{
+ uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
+
+ labx_ptp_create(ptp_addr);
+}
+
+devinfo_t labx_ptp_device = {
+ .probe = &labx_ptp_probe,
+ .pass = 1,
+ .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
+};
+
+/*
+ * Table of available devices
+ */
+devinfo_t *devices[] = {
+ &xilinx_interrupt_controller_device,
+ &flash_device,
+ &xilinx_timer_device,
+ &xilinx_uartlite_device,
+ &xilinx_ethlite_device,
+ &labx_audio_packetizer_device,
+ &labx_audio_depacketizer_device,
+ &labx_dma_device,
+ &labx_ethernet_device,
+ &labx_ptp_device,
+ NULL
+};
+
+static int plb_device_probe(void *fdt, int node, int pass)
+{
+ devinfo_t **dev = &(devices[0]);
+
+ while (*dev) {
+ const char **compat = &((*dev)->compat[0]);
+ while (*compat) {
+ if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
+ if (pass == (*dev)->pass) {
+ printf("Adding a device for node %s\n",
+ qemu_devtree_get_name(fdt, node, NULL));
+
+ (*dev)->probe(fdt, node);
+ return 0;
+ }
+
+ if (pass < (*dev)->pass) {
+ /* Probe again on the next pass */
+ return 1;
+ }
+ }
+
+ compat++;
+ }
+
+ dev++;
+ }
+
+ return 0;
+}
+
+static void plb_bus_probe(void *fdt)
+{
+ int root = qemu_devtree_node_offset(fdt, "/");
+ int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
+ if (plb > 0) {
+ /* Do multiple passes through the devices. Some have dependencies
+ on others being first */
+ int pass = 0;
+ int again = 0;
+ do {
+ int child = plb;
+ again = 0;
+ do {
+ child = qemu_devtree_next_child_offset(fdt, plb, child);
+ if (child < 0) {
+ break;
+ }
+
+ again |= plb_device_probe(fdt, child, pass);
+
+ } while (1);
+
+ pass++;
+
+ } while (again);
+ }
+}
+
+static void
+labx_microblaze_init(ram_addr_t ram_size,
+ const char *boot_device,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename,
+ const char *cpu_model)
+{
+ MemoryRegion *address_space_mem = get_system_memory();
+
+ int kernel_size;
+ int fdt_size;
+ void *fdt = get_device_tree(&fdt_size);
+ target_phys_addr_t ddr_base = get_dram_base(fdt);
+ MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
+ MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
+ CPUMBState *env;
+
+ /* init CPUs */
+ if (cpu_model == NULL) {
+ cpu_model = "microblaze";
+ }
+ g_cpu = cpu_mb_init(cpu_model);
+ env = &g_cpu->env;
+
+ env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
+ qemu_register_reset(main_cpu_reset, g_cpu);
+
+ /* Attach emulated BRAM through the LMB. LMB size is not specified in the
+ device-tree but there must be one to hold the vector table. */
+ memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
+ LMB_BRAM_SIZE);
+ vmstate_register_ram_global(phys_lmb_bram);
+ memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
+
+ memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
+ vmstate_register_ram_global(phys_ram);
+ memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
+
+
+ /* Create other devices listed in the device-tree */
+ plb_bus_probe(fdt);
+
+ if (kernel_filename) {
+ uint64_t entry, low, high;
+ uint32_t base32;
+
+ /* Boots a kernel elf binary. */
+ kernel_size = load_elf(kernel_filename, NULL, NULL,
+ &entry, &low, &high,
+ 1, ELF_MACHINE, 0);
+ base32 = entry;
+ if (base32 == 0xc0000000) {
+ kernel_size = load_elf(kernel_filename, translate_kernel_address,
+ NULL, &entry, NULL, NULL,
+ 1, ELF_MACHINE, 0);
+ }
+ /* Always boot into physical ram. */
+ boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
+
+ /* If it wasn't an ELF image, try an u-boot image. */
+ if (kernel_size < 0) {
+ target_phys_addr_t uentry, loadaddr;
+
+ kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
+ boot_info.bootstrap_pc = uentry;
+ high = (loadaddr + kernel_size + 3) & ~3;
+ }
+
+ /* Not an ELF image nor an u-boot image, try a RAW image. */
+ if (kernel_size < 0) {
+ kernel_size = load_image_targphys(kernel_filename, ddr_base,
+ ram_size);
+ boot_info.bootstrap_pc = ddr_base;
+ high = (ddr_base + kernel_size + 3) & ~3;
+ }
+
+ if (initrd_filename) {
+ uint32_t initrd_base = 0x88c00000;
+ uint32_t initrd_size =
+ load_image_targphys(initrd_filename, initrd_base,
+ ram_size - initrd_base);
+ if (initrd_size <= 0) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ initrd_filename);
+ exit(1);
+ }
+
+ boot_info.initrd = initrd_base;
+ } else {
+ boot_info.initrd = 0x00000000;
+ }
+
+ boot_info.cmdline = high + 4096;
+ if (kernel_cmdline && strlen(kernel_cmdline)) {
+ pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
+ }
+ /* Provide a device-tree. */
+ boot_info.fdt = boot_info.cmdline + 4096;
+ labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
+ 0, 0,
+ kernel_cmdline);
+ }
+}
+
+static QEMUMachine labx_microblaze_machine = {
+ .name = "labx-microblaze-devicetree",
+ .desc = "Microblaze design based on the peripherals specified "
+ "in the device-tree.",
+ .init = labx_microblaze_init,
+ .is_default = 1
+};
+
+static void labx_microblaze_machine_init(void)
+{
+ qemu_register_machine(&labx_microblaze_machine);
+}
+
+machine_init(labx_microblaze_machine_init);
+
diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
index 274d2c5..632cd60 100644
--- a/hw/microblaze/Makefile.objs
+++ b/hw/microblaze/Makefile.objs
@@ -1,4 +1,5 @@
-obj-y = petalogix_s3adsp1800_mmu.o
+obj-y = labx_microblaze_devicetree.o
+obj-y += petalogix_s3adsp1800_mmu.o
obj-y += petalogix_ml605_mmu.o
obj-y += microblaze_boot.o
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 8/9] MicroBlaze: " crwulff
@ 2012-09-11 19:27 ` Blue Swirl
2012-09-12 0:17 ` Peter Crosthwaite
2012-09-11 23:59 ` Peter Crosthwaite
1 sibling, 1 reply; 31+ messages in thread
From: Blue Swirl @ 2012-09-11 19:27 UTC (permalink / raw)
To: crwulff; +Cc: qemu-devel
On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> default-configs/microblaze-softmmu.mak | 1 +
> hw/labx_microblaze_devicetree.c | 597 ++++++++++++++++++++++++++++++++
> hw/microblaze/Makefile.objs | 3 +-
> 3 files changed, 600 insertions(+), 1 deletion(-)
> create mode 100644 hw/labx_microblaze_devicetree.c
>
> diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
> index 64c9485..7e4cab8 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
> CONFIG_SERIAL=y
> CONFIG_XILINX=y
> CONFIG_XILINX_AXI=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
> new file mode 100644
> index 0000000..581ad14
> --- /dev/null
> +++ b/hw/labx_microblaze_devicetree.c
> @@ -0,0 +1,597 @@
> +/*
> + * Flexible model of microblaze designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2010 LabX Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "xilinx.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
> +#include "microblaze_pic_cpu.h"
> +
> +#define LMB_BRAM_SIZE (128 * 1024)
> +
> +static struct
> +{
> + uint32_t bootstrap_pc;
> + uint32_t cmdline;
> + uint32_t initrd;
> + uint32_t fdt;
> +} boot_info;
Please read CODING_STYLE, struct names must use CamelCase.
> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;
This looks fishy.
> +
> +static void main_cpu_reset(void *opaque)
> +{
> + MicroBlazeCPU *cpu = opaque;
> + CPUMBState *env = &cpu->env;
> +
> + cpu_reset(CPU(cpu));
> +
> + env->regs[5] = boot_info.cmdline;
> + env->regs[6] = boot_info.initrd;
> + env->regs[7] = boot_info.fdt;
> + env->sregs[SR_PC] = boot_info.bootstrap_pc;
> +}
> +
> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
> +#endif
> +
> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> + const char *name, int index)
> +{
> + int array_length;
> + const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> + &array_length);
> + if (index >= array_length) {
> + printf("fdt_get_int_from_array: requesting %s from node %d, "
> + "index %d out of range (%d max)\n",
> + name, node, index, array_length);
> + return 0;
> + } else {
> + return qemu_devtree_int_array_index(array, index);
> + }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> +static void *get_device_tree(int *fdt_size)
> +{
> + char *path;
> + void *fdt;
> +
> + /* Try the local "mb.dtb" override. */
> + fdt = load_device_tree("mb.dtb", fdt_size);
> + if (!fdt) {
> + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> + if (path) {
> + fdt = load_device_tree(path, fdt_size);
> + g_free(path);
> + }
> + }
> +
> + return fdt;
> +}
> +
> +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> + uint32_t ramsize,
> + target_phys_addr_t initrd_base,
> + target_phys_addr_t initrd_size,
> + const char *kernel_cmdline)
> +{
> + int fdt_size;
> + void *fdt;
> + int r;
> +
> + fdt = get_device_tree(&fdt_size);
> +
> + if (!fdt) {
> + return 0;
> + }
> +
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> + kernel_cmdline);
> + if (r < 0) {
> + fprintf(stderr, "couldn't set /chosen/bootargs\n");
> + }
> + }
> + cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> + return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> + return addr - 0x30000000LL;
ULL
> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> + if (memory > 0) {
> + int reglen;
> + const void *reg =
> + qemu_devtree_getprop_offset(fdt, memory, "reg", ®len);
> +
> + if (reglen >= 4) {
> + printf("DRAM base %08X, size %08X\n",
> + qemu_devtree_int_array_index(reg, 0),
> + qemu_devtree_int_array_index(reg, 1));
> + return qemu_devtree_int_array_index(reg, 0);
> + }
> + }
> +
> + printf("DRAM base not found. Defaulting to 0x88000000\n");
> +
> + return 0x88000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node);
> +
> +typedef struct {
> + device_init_func_t probe;
> + int pass;
> + const char **compat;
> +
> +} devinfo_t;
Although a common error, _t suffix is reserved.
> +
> +/*
> + * Xilinx interrupt controller device
> + */
> +MicroBlazeCPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;
These should form the device state structure, passed around instead of
static state.
> +
> +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> +{
> + int i;
> + DeviceState *dev;
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> + int nrIrqLen;
CamelCase is not used for other identifiers.
> + const void *nrIrq =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> + &nrIrqLen);
> + uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> + printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
Leftover debugging?
> +
> + cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> + dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> + for (i = 0; i < nrIrqs; i++) {
> + irq[i] = qdev_get_gpio_in(dev, i);
> + }
> +}
> +
> +devinfo_t xilinx_interrupt_controller_device = {
static const?
> + .probe = &xilinx_interrupt_controller_probe,
> + .pass = 0,
> + .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node)
> +{
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> + uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> + pflash_cfi01_register(flash_addr, NULL,
> + qemu_devtree_get_name(fdt, node, NULL), flash_size,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + flash_size >> 16,
> + 1, 0x89, 0x18, 0x0000, 0x0, 1);
> + printf("-- loaded %d bytes to %08X\n",
Leftover debugging?
> + load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> + flash_addr, flash_size),
> + flash_addr);
> +}
> +
> +devinfo_t flash_device = {
static const?
> + .probe = &flash_probe,
> + .pass = 1,
> + .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * Xilinx timer device
> + */
> +static void xilinx_timer_probe(void *fdt, int node)
> +{
> + uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
Please fix all of these.
> +
> + /* 2 timers at irq 2 @ 62 Mhz. */
> + xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
> +}
> +
> +devinfo_t xilinx_timer_device = {
Likewise.
> + .probe = &xilinx_timer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx uartlite device
> + */
> +static void xilinx_uartlite_probe(void *fdt, int node)
> +{
> + uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> + sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t xilinx_uartlite_device = {
> + .probe = &xilinx_uartlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx ethernetlite device
> + */
> +static void xilinx_ethlite_probe(void *fdt, int node)
> +{
> + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> + irq[eth_irq], 0, 0);
The device should not access nd_table, this should be handled at board level.
> +}
> +
> +devinfo_t xilinx_ethlite_device = {
> + .probe = &xilinx_ethlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node)
> +{
> + uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t packetizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> + clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> + .probe = &labx_audio_packetizer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node)
> +{
> + uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t depacketizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + int ifLen;
> + const void *ifType =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
> + int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> + labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> + clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> + .probe = &labx_audio_depacketizer_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-audio-depacketizer-1.00.a",
> + "xlnx,labx-audio-depacketizer-1.01.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node)
> +{
> + uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> + .probe = &labx_dma_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-dma-1.00.a",
> + "xlnx,labx-dma-1.01.a",
> + "xlnx,labx-local-audio-1.00.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node)
> +{
> + uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t host_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t fifo_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 2);
> + uint32_t phy_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 4);
> +
> + labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> + irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> + .probe = &labx_ethernet_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node)
> +{
> + uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> + .probe = &labx_ptp_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> + &xilinx_interrupt_controller_device,
> + &flash_device,
> + &xilinx_timer_device,
> + &xilinx_uartlite_device,
> + &xilinx_ethlite_device,
> + &labx_audio_packetizer_device,
> + &labx_audio_depacketizer_device,
> + &labx_dma_device,
> + &labx_ethernet_device,
> + &labx_ptp_device,
> + NULL
> +};
> +
> +static int plb_device_probe(void *fdt, int node, int pass)
> +{
> + devinfo_t **dev = &(devices[0]);
Unnecessary parentheses.
> +
> + while (*dev) {
> + const char **compat = &((*dev)->compat[0]);
> + while (*compat) {
> + if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
Usually the constant is on right hand side.
> + if (pass == (*dev)->pass) {
> + printf("Adding a device for node %s\n",
> + qemu_devtree_get_name(fdt, node, NULL));
> +
> + (*dev)->probe(fdt, node);
> + return 0;
> + }
> +
> + if (pass < (*dev)->pass) {
> + /* Probe again on the next pass */
> + return 1;
> + }
> + }
> +
> + compat++;
> + }
> +
> + dev++;
> + }
> +
> + return 0;
> +}
> +
> +static void plb_bus_probe(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> + if (plb > 0) {
> + /* Do multiple passes through the devices. Some have dependencies
> + on others being first */
> + int pass = 0;
> + int again = 0;
> + do {
> + int child = plb;
> + again = 0;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, plb, child);
> + if (child < 0) {
> + break;
> + }
> +
> + again |= plb_device_probe(fdt, child, pass);
> +
> + } while (1);
> +
> + pass++;
> +
> + } while (again);
> + }
> +}
> +
> +static void
> +labx_microblaze_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename,
> + const char *kernel_cmdline,
> + const char *initrd_filename,
> + const char *cpu_model)
> +{
> + MemoryRegion *address_space_mem = get_system_memory();
> +
> + int kernel_size;
> + int fdt_size;
> + void *fdt = get_device_tree(&fdt_size);
> + target_phys_addr_t ddr_base = get_dram_base(fdt);
> + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> + MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> + CPUMBState *env;
> +
> + /* init CPUs */
> + if (cpu_model == NULL) {
> + cpu_model = "microblaze";
> + }
> + g_cpu = cpu_mb_init(cpu_model);
> + env = &g_cpu->env;
> +
> + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
> + qemu_register_reset(main_cpu_reset, g_cpu);
> +
> + /* Attach emulated BRAM through the LMB. LMB size is not specified in the
> + device-tree but there must be one to hold the vector table. */
> + memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> + LMB_BRAM_SIZE);
> + vmstate_register_ram_global(phys_lmb_bram);
> + memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
> +
> + memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> + vmstate_register_ram_global(phys_ram);
> + memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> +
> +
> + /* Create other devices listed in the device-tree */
> + plb_bus_probe(fdt);
> +
> + if (kernel_filename) {
> + uint64_t entry, low, high;
> + uint32_t base32;
> +
> + /* Boots a kernel elf binary. */
> + kernel_size = load_elf(kernel_filename, NULL, NULL,
> + &entry, &low, &high,
> + 1, ELF_MACHINE, 0);
> + base32 = entry;
> + if (base32 == 0xc0000000) {
> + kernel_size = load_elf(kernel_filename, translate_kernel_address,
> + NULL, &entry, NULL, NULL,
> + 1, ELF_MACHINE, 0);
> + }
> + /* Always boot into physical ram. */
> + boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> +
> + /* If it wasn't an ELF image, try an u-boot image. */
> + if (kernel_size < 0) {
> + target_phys_addr_t uentry, loadaddr;
> +
> + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
> + boot_info.bootstrap_pc = uentry;
> + high = (loadaddr + kernel_size + 3) & ~3;
The constants should use ULL so that they are not truncated to 32 bits.
> + }
> +
> + /* Not an ELF image nor an u-boot image, try a RAW image. */
> + if (kernel_size < 0) {
> + kernel_size = load_image_targphys(kernel_filename, ddr_base,
> + ram_size);
> + boot_info.bootstrap_pc = ddr_base;
> + high = (ddr_base + kernel_size + 3) & ~3;
Also here.
> + }
> +
> + if (initrd_filename) {
> + uint32_t initrd_base = 0x88c00000;
> + uint32_t initrd_size =
> + load_image_targphys(initrd_filename, initrd_base,
> + ram_size - initrd_base);
> + if (initrd_size <= 0) {
> + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> + initrd_filename);
> + exit(1);
> + }
> +
> + boot_info.initrd = initrd_base;
> + } else {
> + boot_info.initrd = 0x00000000;
> + }
> +
> + boot_info.cmdline = high + 4096;
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
> + }
> + /* Provide a device-tree. */
> + boot_info.fdt = boot_info.cmdline + 4096;
> + labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> + 0, 0,
> + kernel_cmdline);
> + }
> +}
> +
> +static QEMUMachine labx_microblaze_machine = {
> + .name = "labx-microblaze-devicetree",
> + .desc = "Microblaze design based on the peripherals specified "
> + "in the device-tree.",
> + .init = labx_microblaze_init,
> + .is_default = 1
> +};
> +
> +static void labx_microblaze_machine_init(void)
> +{
> + qemu_register_machine(&labx_microblaze_machine);
> +}
> +
> +machine_init(labx_microblaze_machine_init);
> +
Useless newline before EOF.
> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> index 274d2c5..632cd60 100644
> --- a/hw/microblaze/Makefile.objs
> +++ b/hw/microblaze/Makefile.objs
> @@ -1,4 +1,5 @@
> -obj-y = petalogix_s3adsp1800_mmu.o
> +obj-y = labx_microblaze_devicetree.o
> +obj-y += petalogix_s3adsp1800_mmu.o
> obj-y += petalogix_ml605_mmu.o
> obj-y += microblaze_boot.o
>
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-11 19:27 ` Blue Swirl
@ 2012-09-12 0:17 ` Peter Crosthwaite
2012-09-14 19:13 ` Blue Swirl
0 siblings, 1 reply; 31+ messages in thread
From: Peter Crosthwaite @ 2012-09-12 0:17 UTC (permalink / raw)
To: Blue Swirl; +Cc: crwulff, qemu-devel
On Tue, 2012-09-11 at 19:27 +0000, Blue Swirl wrote:
> On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > +/*
> > + * Xilinx ethernetlite device
> > + */
> > +static void xilinx_ethlite_probe(void *fdt, int node)
> > +{
> > + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > +
> > + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> > + irq[eth_irq], 0, 0);
>
> The device should not access nd_table, this should be handled at board level.
>
This is board level isn't it? This function calls
xilinx_ethlite_create() which in turn does the qdev_create so this is
pre device creation. Im open to suggestions on how to do this in a
non-global hacking way however as I have the same problem in my tree.
Regards,
Peter
> > +}
> > +
> > +devinfo_t xilinx_ethlite_device = {
> > + .probe = &xilinx_ethlite_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> > +};
> > +
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-12 0:17 ` Peter Crosthwaite
@ 2012-09-14 19:13 ` Blue Swirl
0 siblings, 0 replies; 31+ messages in thread
From: Blue Swirl @ 2012-09-14 19:13 UTC (permalink / raw)
To: Peter Crosthwaite; +Cc: crwulff, qemu-devel
On Wed, Sep 12, 2012 at 12:17 AM, Peter Crosthwaite
<peter.crosthwaite@petalogix.com> wrote:
> On Tue, 2012-09-11 at 19:27 +0000, Blue Swirl wrote:
>> On Mon, Sep 10, 2012 at 12:20 AM, <crwulff@gmail.com> wrote:
>> > From: Chris Wulff <crwulff@gmail.com>
>> >
>> > +/*
>> > + * Xilinx ethernetlite device
>> > + */
>> > +static void xilinx_ethlite_probe(void *fdt, int node)
>> > +{
>> > + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
>> > + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
>> > +
>> > + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
>> > + irq[eth_irq], 0, 0);
>>
>> The device should not access nd_table, this should be handled at board level.
>>
>
> This is board level isn't it? This function calls
> xilinx_ethlite_create() which in turn does the qdev_create so this is
> pre device creation. Im open to suggestions on how to do this in a
> non-global hacking way however as I have the same problem in my tree.
OK, the device code is mixed with the board code and that was
confusing me. It could be better to move the device part to a separate
file.
>
> Regards,
> Peter
>
>> > +}
>> > +
>> > +devinfo_t xilinx_ethlite_device = {
>> > + .probe = &xilinx_ethlite_probe,
>> > + .pass = 1,
>> > + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
>> > +};
>> > +
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 8/9] MicroBlaze: " crwulff
2012-09-11 19:27 ` Blue Swirl
@ 2012-09-11 23:59 ` Peter Crosthwaite
2012-09-14 4:01 ` Chris Wulff
1 sibling, 1 reply; 31+ messages in thread
From: Peter Crosthwaite @ 2012-09-11 23:59 UTC (permalink / raw)
To: crwulff; +Cc: peter.crosthwaite, qemu-devel
Hi Chris,
This needs to be reviewed along with the petalogix implementation, but a
few top level comments.
Regards,
Peter
On Sun, 2012-09-09 at 20:20 -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
> default-configs/microblaze-softmmu.mak | 1 +
> hw/labx_microblaze_devicetree.c | 597 ++++++++++++++++++++++++++++++++
> hw/microblaze/Makefile.objs | 3 +-
> 3 files changed, 600 insertions(+), 1 deletion(-)
> create mode 100644 hw/labx_microblaze_devicetree.c
>
> diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak
> index 64c9485..7e4cab8 100644
> --- a/default-configs/microblaze-softmmu.mak
> +++ b/default-configs/microblaze-softmmu.mak
> @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
> CONFIG_SERIAL=y
> CONFIG_XILINX=y
> CONFIG_XILINX_AXI=y
> +CONFIG_LABX=y
> diff --git a/hw/labx_microblaze_devicetree.c b/hw/labx_microblaze_devicetree.c
> new file mode 100644
> index 0000000..581ad14
> --- /dev/null
> +++ b/hw/labx_microblaze_devicetree.c
> @@ -0,0 +1,597 @@
> +/*
> + * Flexible model of microblaze designs that use a device-tree to determine
> + * the hardware configuration.
> + *
> + * Copyright (c) 2010 LabX Technologies, LLC
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, see
> + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> + */
> +
> +#include "sysbus.h"
> +#include "hw.h"
> +#include "net.h"
> +#include "flash.h"
> +#include "sysemu.h"
> +#include "devices.h"
> +#include "boards.h"
> +#include "device_tree.h"
> +#include "xilinx.h"
> +#include "labx_devices.h"
> +#include "loader.h"
> +#include "elf.h"
> +#include "blockdev.h"
> +#include "exec-memory.h"
> +#include "microblaze_pic_cpu.h"
> +
> +#define LMB_BRAM_SIZE (128 * 1024)
> +
> +static struct
> +{
> + uint32_t bootstrap_pc;
> + uint32_t cmdline;
> + uint32_t initrd;
> + uint32_t fdt;
> +} boot_info;
> +
> +/* Current ethernet device index for multiple network interfaces */
> +static int eth_dev_index = -1;
> +
> +static void main_cpu_reset(void *opaque)
> +{
> + MicroBlazeCPU *cpu = opaque;
> + CPUMBState *env = &cpu->env;
> +
> + cpu_reset(CPU(cpu));
> +
> + env->regs[5] = boot_info.cmdline;
> + env->regs[6] = boot_info.initrd;
> + env->regs[7] = boot_info.fdt;
> + env->sregs[SR_PC] = boot_info.bootstrap_pc;
> +}
> +
Please use microblaze bootloader (hw/microblaze_boot.c). We need to
consolidate bootloaders. If there is functionality missing from mb_boot,
please add it there.
> +#ifndef CONFIG_FDT
> +#error "Device-tree support is required for this target to function"
> +#endif
> +
This should be handled by the make infrastructure. This machine model
should be obj-(CONIFG_FDT)+= .. so this machine model just dissappears
when QEMU is build without FDT.
> +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> + const char *name, int index)
> +{
> + int array_length;
> + const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> + &array_length);
> + if (index >= array_length) {
> + printf("fdt_get_int_from_array: requesting %s from node %d, "
> + "index %d out of range (%d max)\n",
> + name, node, index, array_length);
> + return 0;
> + } else {
> + return qemu_devtree_int_array_index(array, index);
> + }
> +}
> +
> +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> +static void *get_device_tree(int *fdt_size)
> +{
> + char *path;
> + void *fdt;
> +
> + /* Try the local "mb.dtb" override. */
> + fdt = load_device_tree("mb.dtb", fdt_size);
> + if (!fdt) {
> + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
> + if (path) {
> + fdt = load_device_tree(path, fdt_size);
> + g_free(path);
> + }
> + }
> +
> + return fdt;
> +}
> +
> +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> + uint32_t ramsize,
> + target_phys_addr_t initrd_base,
> + target_phys_addr_t initrd_size,
> + const char *kernel_cmdline)
> +{
> + int fdt_size;
> + void *fdt;
> + int r;
> +
> + fdt = get_device_tree(&fdt_size);
> +
> + if (!fdt) {
> + return 0;
> + }
> +
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> + kernel_cmdline);
> + if (r < 0) {
> + fprintf(stderr, "couldn't set /chosen/bootargs\n");
> + }
> + }
> + cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> +
> + return fdt_size;
> +}
> +
> +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> +{
> + return addr - 0x30000000LL;
> +}
> +
> +static ram_addr_t get_dram_base(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int memory = qemu_devtree_subnode_offset_namelen(fdt, root, "memory", 6);
> + if (memory > 0) {
> + int reglen;
> + const void *reg =
> + qemu_devtree_getprop_offset(fdt, memory, "reg", ®len);
> +
> + if (reglen >= 4) {
> + printf("DRAM base %08X, size %08X\n",
> + qemu_devtree_int_array_index(reg, 0),
> + qemu_devtree_int_array_index(reg, 1));
> + return qemu_devtree_int_array_index(reg, 0);
> + }
> + }
> +
> + printf("DRAM base not found. Defaulting to 0x88000000\n");
> +
> + return 0x88000000; /* Default to something reasonable */
> +}
> +
> +typedef void (*device_init_func_t)(void *fdt, int node);
> +
> +typedef struct {
> + device_init_func_t probe;
> + int pass;
> + const char **compat;
> +
> +} devinfo_t;
> +
> +/*
> + * Xilinx interrupt controller device
> + */
> +MicroBlazeCPU *g_cpu;
> +static qemu_irq irq[32] = {};
> +static qemu_irq *cpu_irq;
> +
> +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> +{
> + int i;
> + DeviceState *dev;
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> + int nrIrqLen;
> + const void *nrIrq =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> + &nrIrqLen);
> + uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> +
> + printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
> +
> + cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> + dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> + for (i = 0; i < nrIrqs; i++) {
> + irq[i] = qdev_get_gpio_in(dev, i);
> + }
> +}
> +
> +devinfo_t xilinx_interrupt_controller_device = {
> + .probe = &xilinx_interrupt_controller_probe,
> + .pass = 0,
> + .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> +};
> +
> +/*
> + * Flash device
> + */
> +static void flash_probe(void *fdt, int node)
> +{
> + int reglen;
> + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg", ®len);
> + uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> + uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> +
> + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> + pflash_cfi01_register(flash_addr, NULL,
> + qemu_devtree_get_name(fdt, node, NULL), flash_size,
> + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> + flash_size >> 16,
> + 1, 0x89, 0x18, 0x0000, 0x0, 1);
> + printf("-- loaded %d bytes to %08X\n",
> + load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> + flash_addr, flash_size),
> + flash_addr);
> +}
> +
> +devinfo_t flash_device = {
> + .probe = &flash_probe,
> + .pass = 1,
> + .compat = (const char * []) { "cfi-flash", NULL }
> +};
> +
> +/*
> + * Xilinx timer device
> + */
> +static void xilinx_timer_probe(void *fdt, int node)
> +{
> + uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t timer_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
> +
> + /* 2 timers at irq 2 @ 62 Mhz. */
> + xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
No check is made for the one_timer_only property.
> +}
> +
> +devinfo_t xilinx_timer_device = {
> + .probe = &xilinx_timer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx uartlite device
> + */
> +static void xilinx_uartlite_probe(void *fdt, int node)
> +{
> + uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> +
> + sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> +}
> +
> +devinfo_t xilinx_uartlite_device = {
> + .probe = &xilinx_uartlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> +};
> +
> +/*
> + * Xilinx ethernetlite device
> + */
> +static void xilinx_ethlite_probe(void *fdt, int node)
> +{
> + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts", 0);
> +
> + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> + irq[eth_irq], 0, 0);
> +}
> +
> +devinfo_t xilinx_ethlite_device = {
> + .probe = &xilinx_ethlite_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> +};
> +
> +/*
> + * LabX audio packetizer device
> + */
> +static void labx_audio_packetizer_probe(void *fdt, int node)
> +{
> + uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t packetizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> + clock_domains, cache_words);
> +}
> +
> +devinfo_t labx_audio_packetizer_device = {
> + .probe = &labx_audio_packetizer_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX audio depacketizer device
> + */
> +static void labx_audio_depacketizer_probe(void *fdt, int node)
> +{
> + uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t depacketizer_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t clock_domains =
> + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> + uint32_t cache_words =
> + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> +
> + int ifLen;
> + const void *ifType =
> + qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type", &ifLen);
> + int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType, ifLen));
> +
> + labx_audio_depacketizer_create(depacketizer_addr, irq[depacketizer_irq],
> + clock_domains, cache_words, hasDMA);
> +}
> +
> +devinfo_t labx_audio_depacketizer_device = {
> + .probe = &labx_audio_depacketizer_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-audio-depacketizer-1.00.a",
> + "xlnx,labx-audio-depacketizer-1.01.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX dma device
> + */
> +static void labx_dma_probe(void *fdt, int node)
> +{
> + uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_dma_create(dma_addr, 1024);
> +}
> +
> +devinfo_t labx_dma_device = {
> + .probe = &labx_dma_probe,
> + .pass = 1,
> + .compat = (const char * []) {
> + "xlnx,labx-dma-1.00.a",
> + "xlnx,labx-dma-1.01.a",
> + "xlnx,labx-local-audio-1.00.a",
> + NULL
> + }
> +};
> +
> +/*
> + * LabX ethernet device
> + */
> +static void labx_ethernet_probe(void *fdt, int node)
> +{
> + uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> + uint32_t host_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> + uint32_t fifo_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 2);
> + uint32_t phy_irq =
> + fdt_get_int_from_array(fdt, node, "interrupts", 4);
> +
> + labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> + irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> +}
> +
> +devinfo_t labx_ethernet_device = {
> + .probe = &labx_ethernet_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> +};
> +
> +/*
> + * LabX ptp device
> + */
> +static void labx_ptp_probe(void *fdt, int node)
> +{
> + uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> +
> + labx_ptp_create(ptp_addr);
> +}
> +
> +devinfo_t labx_ptp_device = {
> + .probe = &labx_ptp_probe,
> + .pass = 1,
> + .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> +};
> +
> +/*
> + * Table of available devices
> + */
> +devinfo_t *devices[] = {
> + &xilinx_interrupt_controller_device,
> + &flash_device,
> + &xilinx_timer_device,
> + &xilinx_uartlite_device,
> + &xilinx_ethlite_device,
> + &labx_audio_packetizer_device,
> + &labx_audio_depacketizer_device,
> + &labx_dma_device,
> + &labx_ethernet_device,
> + &labx_ptp_device,
> + NULL
> +};
> +
> +static int plb_device_probe(void *fdt, int node, int pass)
QEMU abstacts away interconnect implementations. You should probably
also drive off the "simple-bus" compat rather than PLB for machine model
compatibility. s/plb/bus/g or something like it.
> +{
> + devinfo_t **dev = &(devices[0]);
> +
> + while (*dev) {
> + const char **compat = &((*dev)->compat[0]);
> + while (*compat) {
> + if (0 == qemu_devtree_node_check_compatible(fdt, node, *compat)) {
> + if (pass == (*dev)->pass) {
> + printf("Adding a device for node %s\n",
> + qemu_devtree_get_name(fdt, node, NULL));
> +
> + (*dev)->probe(fdt, node);
> + return 0;
> + }
> +
> + if (pass < (*dev)->pass) {
> + /* Probe again on the next pass */
> + return 1;
> + }
> + }
> +
> + compat++;
> + }
> +
> + dev++;
> + }
> +
> + return 0;
> +}
> +
> +static void plb_bus_probe(void *fdt)
> +{
> + int root = qemu_devtree_node_offset(fdt, "/");
> + int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> + if (plb > 0) {
> + /* Do multiple passes through the devices. Some have dependencies
> + on others being first */
> + int pass = 0;
> + int again = 0;
> + do {
> + int child = plb;
> + again = 0;
> + do {
> + child = qemu_devtree_next_child_offset(fdt, plb, child);
> + if (child < 0) {
> + break;
> + }
> +
> + again |= plb_device_probe(fdt, child, pass);
> +
> + } while (1);
> +
> + pass++;
> +
> + } while (again);
> + }
> +}
> +
> +static void
> +labx_microblaze_init(ram_addr_t ram_size,
> + const char *boot_device,
> + const char *kernel_filename,
> + const char *kernel_cmdline,
> + const char *initrd_filename,
> + const char *cpu_model)
> +{
> + MemoryRegion *address_space_mem = get_system_memory();
> +
> + int kernel_size;
> + int fdt_size;
> + void *fdt = get_device_tree(&fdt_size);
> + target_phys_addr_t ddr_base = get_dram_base(fdt);
> + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> + MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> + CPUMBState *env;
> +
> + /* init CPUs */
> + if (cpu_model == NULL) {
> + cpu_model = "microblaze";
> + }
> + g_cpu = cpu_mb_init(cpu_model);
> + env = &g_cpu->env;
> +
> + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
I have dtb driven solution for this.
> + qemu_register_reset(main_cpu_reset, g_cpu);
> +
> + /* Attach emulated BRAM through the LMB. LMB size is not specified in the
> + device-tree but there must be one to hold the vector table. */
> + memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> + LMB_BRAM_SIZE);
> + vmstate_register_ram_global(phys_lmb_bram);
> + memory_region_add_subregion(address_space_mem, 0x00000000, phys_lmb_bram);
> +
> + memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> + vmstate_register_ram_global(phys_ram);
> + memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> +
> +
> + /* Create other devices listed in the device-tree */
> + plb_bus_probe(fdt);
> +
> + if (kernel_filename) {
> + uint64_t entry, low, high;
> + uint32_t base32;
> +
> + /* Boots a kernel elf binary. */
> + kernel_size = load_elf(kernel_filename, NULL, NULL,
> + &entry, &low, &high,
> + 1, ELF_MACHINE, 0);
> + base32 = entry;
> + if (base32 == 0xc0000000) {
> + kernel_size = load_elf(kernel_filename, translate_kernel_address,
> + NULL, &entry, NULL, NULL,
> + 1, ELF_MACHINE, 0);
> + }
> + /* Always boot into physical ram. */
> + boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> +
> + /* If it wasn't an ELF image, try an u-boot image. */
> + if (kernel_size < 0) {
> + target_phys_addr_t uentry, loadaddr;
> +
> + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0);
> + boot_info.bootstrap_pc = uentry;
> + high = (loadaddr + kernel_size + 3) & ~3;
> + }
> +
> + /* Not an ELF image nor an u-boot image, try a RAW image. */
> + if (kernel_size < 0) {
> + kernel_size = load_image_targphys(kernel_filename, ddr_base,
> + ram_size);
> + boot_info.bootstrap_pc = ddr_base;
> + high = (ddr_base + kernel_size + 3) & ~3;
> + }
> +
> + if (initrd_filename) {
> + uint32_t initrd_base = 0x88c00000;
> + uint32_t initrd_size =
> + load_image_targphys(initrd_filename, initrd_base,
> + ram_size - initrd_base);
> + if (initrd_size <= 0) {
> + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
> + initrd_filename);
> + exit(1);
> + }
> +
> + boot_info.initrd = initrd_base;
> + } else {
> + boot_info.initrd = 0x00000000;
> + }
> +
> + boot_info.cmdline = high + 4096;
> + if (kernel_cmdline && strlen(kernel_cmdline)) {
> + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline);
> + }
> + /* Provide a device-tree. */
> + boot_info.fdt = boot_info.cmdline + 4096;
> + labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> + 0, 0,
> + kernel_cmdline);
> + }
> +}
> +
> +static QEMUMachine labx_microblaze_machine = {
Its a generic machine, so should it be badged as labx?
> + .name = "labx-microblaze-devicetree",
> + .desc = "Microblaze design based on the peripherals specified "
> + "in the device-tree.",
> + .init = labx_microblaze_init,
> + .is_default = 1
> +};
> +
> +static void labx_microblaze_machine_init(void)
> +{
> + qemu_register_machine(&labx_microblaze_machine);
> +}
> +
> +machine_init(labx_microblaze_machine_init);
> +
> diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> index 274d2c5..632cd60 100644
> --- a/hw/microblaze/Makefile.objs
> +++ b/hw/microblaze/Makefile.objs
> @@ -1,4 +1,5 @@
> -obj-y = petalogix_s3adsp1800_mmu.o
> +obj-y = labx_microblaze_devicetree.o
obj-y += labx_microblaze_devicetree.(CONFIG-FDT)
> +obj-y += petalogix_s3adsp1800_mmu.o
> obj-y += petalogix_ml605_mmu.o
> obj-y += microblaze_boot.o
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 8/9] MicroBlaze: Add a config that is dynamically set up by a device tree file.
2012-09-11 23:59 ` Peter Crosthwaite
@ 2012-09-14 4:01 ` Chris Wulff
0 siblings, 0 replies; 31+ messages in thread
From: Chris Wulff @ 2012-09-14 4:01 UTC (permalink / raw)
To: Peter Crosthwaite; +Cc: peter.crosthwaite, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 23883 bytes --]
I'm all for consolidating the different device-tree based "boards" as much
as possible. This, the petalogix variant, and the NiosII one that I created
as part of this patch set too, are all very similar. We just need some
generic way to register the set of devices that can be created and then the
walking of the device tree itself could probably be generic. I'd be happy
to work with you to move in that direction.
The only thing that I ran into that is a bit different is that the NiosII
declares the type of attached interrupt controller inside the CPU block and
I ended up enumerating that as if the cpu were itself a bus.
-- Chris Wulff
On Tue, Sep 11, 2012 at 7:59 PM, Peter Crosthwaite <
peter.crosthwaite@petalogix.com> wrote:
> Hi Chris,
>
> This needs to be reviewed along with the petalogix implementation, but a
> few top level comments.
>
> Regards,
> Peter
>
> On Sun, 2012-09-09 at 20:20 -0400, crwulff@gmail.com wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > Signed-off-by: Chris Wulff <crwulff@gmail.com>
> > ---
> > default-configs/microblaze-softmmu.mak | 1 +
> > hw/labx_microblaze_devicetree.c | 597
> ++++++++++++++++++++++++++++++++
> > hw/microblaze/Makefile.objs | 3 +-
> > 3 files changed, 600 insertions(+), 1 deletion(-)
> > create mode 100644 hw/labx_microblaze_devicetree.c
> >
> > diff --git a/default-configs/microblaze-softmmu.mak
> b/default-configs/microblaze-softmmu.mak
> > index 64c9485..7e4cab8 100644
> > --- a/default-configs/microblaze-softmmu.mak
> > +++ b/default-configs/microblaze-softmmu.mak
> > @@ -5,3 +5,4 @@ CONFIG_PFLASH_CFI01=y
> > CONFIG_SERIAL=y
> > CONFIG_XILINX=y
> > CONFIG_XILINX_AXI=y
> > +CONFIG_LABX=y
> > diff --git a/hw/labx_microblaze_devicetree.c
> b/hw/labx_microblaze_devicetree.c
> > new file mode 100644
> > index 0000000..581ad14
> > --- /dev/null
> > +++ b/hw/labx_microblaze_devicetree.c
> > @@ -0,0 +1,597 @@
> > +/*
> > + * Flexible model of microblaze designs that use a device-tree to
> determine
> > + * the hardware configuration.
> > + *
> > + * Copyright (c) 2010 LabX Technologies, LLC
> > + *
> > + * This library is free software; you can redistribute it and/or
> > + * modify it under the terms of the GNU Lesser General Public
> > + * License as published by the Free Software Foundation; either
> > + * version 2.1 of the License, or (at your option) any later version.
> > + *
> > + * This library is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> > + * Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public
> > + * License along with this library; if not, see
> > + * <http://www.gnu.org/licenses/lgpl-2.1.html>
> > + */
> > +
> > +#include "sysbus.h"
> > +#include "hw.h"
> > +#include "net.h"
> > +#include "flash.h"
> > +#include "sysemu.h"
> > +#include "devices.h"
> > +#include "boards.h"
> > +#include "device_tree.h"
> > +#include "xilinx.h"
> > +#include "labx_devices.h"
> > +#include "loader.h"
> > +#include "elf.h"
> > +#include "blockdev.h"
> > +#include "exec-memory.h"
> > +#include "microblaze_pic_cpu.h"
> > +
> > +#define LMB_BRAM_SIZE (128 * 1024)
> > +
> > +static struct
> > +{
> > + uint32_t bootstrap_pc;
> > + uint32_t cmdline;
> > + uint32_t initrd;
> > + uint32_t fdt;
> > +} boot_info;
> > +
> > +/* Current ethernet device index for multiple network interfaces */
> > +static int eth_dev_index = -1;
> > +
> > +static void main_cpu_reset(void *opaque)
> > +{
> > + MicroBlazeCPU *cpu = opaque;
> > + CPUMBState *env = &cpu->env;
> > +
> > + cpu_reset(CPU(cpu));
> > +
> > + env->regs[5] = boot_info.cmdline;
> > + env->regs[6] = boot_info.initrd;
> > + env->regs[7] = boot_info.fdt;
> > + env->sregs[SR_PC] = boot_info.bootstrap_pc;
> > +}
> > +
>
> Please use microblaze bootloader (hw/microblaze_boot.c). We need to
> consolidate bootloaders. If there is functionality missing from mb_boot,
> please add it there.
>
> > +#ifndef CONFIG_FDT
> > +#error "Device-tree support is required for this target to function"
> > +#endif
> > +
>
> This should be handled by the make infrastructure. This machine model
> should be obj-(CONIFG_FDT)+= .. so this machine model just dissappears
> when QEMU is build without FDT.
>
> > +static uint32_t fdt_get_int_from_array(void *fdt, int node,
> > + const char *name, int index)
> > +{
> > + int array_length;
> > + const void *array = qemu_devtree_getprop_offset(fdt, node, name,
> > + &array_length);
> > + if (index >= array_length) {
> > + printf("fdt_get_int_from_array: requesting %s from node %d, "
> > + "index %d out of range (%d max)\n",
> > + name, node, index, array_length);
> > + return 0;
> > + } else {
> > + return qemu_devtree_int_array_index(array, index);
> > + }
> > +}
> > +
> > +#define BINARY_DEVICE_TREE_FILE "labx-microblaze.dtb"
> > +static void *get_device_tree(int *fdt_size)
> > +{
> > + char *path;
> > + void *fdt;
> > +
> > + /* Try the local "mb.dtb" override. */
> > + fdt = load_device_tree("mb.dtb", fdt_size);
> > + if (!fdt) {
> > + path = qemu_find_file(QEMU_FILE_TYPE_BIOS,
> BINARY_DEVICE_TREE_FILE);
> > + if (path) {
> > + fdt = load_device_tree(path, fdt_size);
> > + g_free(path);
> > + }
> > + }
> > +
> > + return fdt;
> > +}
> > +
> > +static int labx_microblaze_load_device_tree(target_phys_addr_t addr,
> > + uint32_t ramsize,
> > + target_phys_addr_t initrd_base,
> > + target_phys_addr_t initrd_size,
> > + const char *kernel_cmdline)
> > +{
> > + int fdt_size;
> > + void *fdt;
> > + int r;
> > +
> > + fdt = get_device_tree(&fdt_size);
> > +
> > + if (!fdt) {
> > + return 0;
> > + }
> > +
> > + if (kernel_cmdline && strlen(kernel_cmdline)) {
> > + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
> > + kernel_cmdline);
> > + if (r < 0) {
> > + fprintf(stderr, "couldn't set /chosen/bootargs\n");
> > + }
> > + }
> > + cpu_physical_memory_write(addr, (void *)fdt, fdt_size);
> > +
> > + return fdt_size;
> > +}
> > +
> > +static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
> > +{
> > + return addr - 0x30000000LL;
> > +}
> > +
> > +static ram_addr_t get_dram_base(void *fdt)
> > +{
> > + int root = qemu_devtree_node_offset(fdt, "/");
> > + int memory = qemu_devtree_subnode_offset_namelen(fdt, root,
> "memory", 6);
> > + if (memory > 0) {
> > + int reglen;
> > + const void *reg =
> > + qemu_devtree_getprop_offset(fdt, memory, "reg", ®len);
> > +
> > + if (reglen >= 4) {
> > + printf("DRAM base %08X, size %08X\n",
> > + qemu_devtree_int_array_index(reg, 0),
> > + qemu_devtree_int_array_index(reg, 1));
> > + return qemu_devtree_int_array_index(reg, 0);
> > + }
> > + }
> > +
> > + printf("DRAM base not found. Defaulting to 0x88000000\n");
> > +
> > + return 0x88000000; /* Default to something reasonable */
> > +}
> > +
> > +typedef void (*device_init_func_t)(void *fdt, int node);
> > +
> > +typedef struct {
> > + device_init_func_t probe;
> > + int pass;
> > + const char **compat;
> > +
> > +} devinfo_t;
> > +
> > +/*
> > + * Xilinx interrupt controller device
> > + */
> > +MicroBlazeCPU *g_cpu;
> > +static qemu_irq irq[32] = {};
> > +static qemu_irq *cpu_irq;
> > +
> > +static void xilinx_interrupt_controller_probe(void *fdt, int node)
> > +{
> > + int i;
> > + DeviceState *dev;
> > + int reglen;
> > + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg",
> ®len);
> > + uint32_t irq_addr = qemu_devtree_int_array_index(reg, 0);
> > + int nrIrqLen;
> > + const void *nrIrq =
> > + qemu_devtree_getprop_offset(fdt, node, "xlnx,num-intr-inputs",
> > + &nrIrqLen);
> > + uint32_t nrIrqs = qemu_devtree_int_array_index(nrIrq, 0);
> > +
> > + printf(" IRQ BASE %08X NIRQS %d\n", irq_addr, nrIrqs);
> > +
> > + cpu_irq = microblaze_pic_init_cpu(&g_cpu->env);
> > + dev = xilinx_intc_create(irq_addr, cpu_irq[0], 2);
> > + for (i = 0; i < nrIrqs; i++) {
> > + irq[i] = qdev_get_gpio_in(dev, i);
> > + }
> > +}
> > +
> > +devinfo_t xilinx_interrupt_controller_device = {
> > + .probe = &xilinx_interrupt_controller_probe,
> > + .pass = 0,
> > + .compat = (const char * []) { "xlnx,xps-intc-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Flash device
> > + */
> > +static void flash_probe(void *fdt, int node)
> > +{
> > + int reglen;
> > + const void *reg = qemu_devtree_getprop_offset(fdt, node, "reg",
> ®len);
> > + uint32_t flash_addr = qemu_devtree_int_array_index(reg, 0);
> > + uint32_t flash_size = qemu_devtree_int_array_index(reg, 1);
> > +
> > + DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
> > + pflash_cfi01_register(flash_addr, NULL,
> > + qemu_devtree_get_name(fdt, node, NULL),
> flash_size,
> > + dinfo ? dinfo->bdrv : NULL, (64 * 1024),
> > + flash_size >> 16,
> > + 1, 0x89, 0x18, 0x0000, 0x0, 1);
> > + printf("-- loaded %d bytes to %08X\n",
> > + load_image_targphys(qemu_devtree_get_name(fdt, node, NULL),
> > + flash_addr, flash_size),
> > + flash_addr);
> > +}
> > +
> > +devinfo_t flash_device = {
> > + .probe = &flash_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "cfi-flash", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx timer device
> > + */
> > +static void xilinx_timer_probe(void *fdt, int node)
> > +{
> > + uint32_t timer_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > + uint32_t timer_irq = fdt_get_int_from_array(fdt, node,
> "interrupts", 0);
> > +
> > + printf(" TIMER BASE %08X IRQ %d\n", timer_addr, timer_irq);
> > +
> > + /* 2 timers at irq 2 @ 62 Mhz. */
> > + xilinx_timer_create(timer_addr, irq[timer_irq], 0, 62 * 1000000);
>
> No check is made for the one_timer_only property.
>
> > +}
> > +
> > +devinfo_t xilinx_timer_device = {
> > + .probe = &xilinx_timer_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,xps-timer-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx uartlite device
> > + */
> > +static void xilinx_uartlite_probe(void *fdt, int node)
> > +{
> > + uint32_t uart_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > + uint32_t uart_irq = fdt_get_int_from_array(fdt, node, "interrupts",
> 0);
> > +
> > + printf(" UART BASE %08X IRQ %d\n", uart_addr, uart_irq);
> > +
> > + sysbus_create_simple("xlnx.xps-uartlite", uart_addr, irq[uart_irq]);
> > +}
> > +
> > +devinfo_t xilinx_uartlite_device = {
> > + .probe = &xilinx_uartlite_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,xps-uartlite-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Xilinx ethernetlite device
> > + */
> > +static void xilinx_ethlite_probe(void *fdt, int node)
> > +{
> > + uint32_t eth_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > + uint32_t eth_irq = fdt_get_int_from_array(fdt, node, "interrupts",
> 0);
> > +
> > + xilinx_ethlite_create(&nd_table[++eth_dev_index], eth_addr,
> > + irq[eth_irq], 0, 0);
> > +}
> > +
> > +devinfo_t xilinx_ethlite_device = {
> > + .probe = &xilinx_ethlite_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,xps-ethernetlite-2.00.b", NULL }
> > +};
> > +
> > +/*
> > + * LabX audio packetizer device
> > + */
> > +static void labx_audio_packetizer_probe(void *fdt, int node)
> > +{
> > + uint32_t packetizer_addr = fdt_get_int_from_array(fdt, node, "reg",
> 0);
> > + uint32_t packetizer_irq =
> > + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > + uint32_t clock_domains =
> > + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> > + uint32_t cache_words =
> > + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> > +
> > + labx_audio_packetizer_create(packetizer_addr, irq[packetizer_irq],
> > + clock_domains, cache_words);
> > +}
> > +
> > +devinfo_t labx_audio_packetizer_device = {
> > + .probe = &labx_audio_packetizer_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,labx-audio-packetizer-1.00.a",
> NULL }
> > +};
> > +
> > +/*
> > + * LabX audio depacketizer device
> > + */
> > +static void labx_audio_depacketizer_probe(void *fdt, int node)
> > +{
> > + uint32_t depacketizer_addr = fdt_get_int_from_array(fdt, node,
> "reg", 0);
> > + uint32_t depacketizer_irq =
> > + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > + uint32_t clock_domains =
> > + fdt_get_int_from_array(fdt, node, "xlnx,num-clock-domains", 0);
> > + uint32_t cache_words =
> > + fdt_get_int_from_array(fdt, node, "xlnx,cache-data-words", 0);
> > +
> > + int ifLen;
> > + const void *ifType =
> > + qemu_devtree_getprop_offset(fdt, node, "xlnx,interface-type",
> &ifLen);
> > + int hasDMA = (0 != strncmp("CACHE_RAM", (const char *)ifType,
> ifLen));
> > +
> > + labx_audio_depacketizer_create(depacketizer_addr,
> irq[depacketizer_irq],
> > + clock_domains, cache_words, hasDMA);
> > +}
> > +
> > +devinfo_t labx_audio_depacketizer_device = {
> > + .probe = &labx_audio_depacketizer_probe,
> > + .pass = 1,
> > + .compat = (const char * []) {
> > + "xlnx,labx-audio-depacketizer-1.00.a",
> > + "xlnx,labx-audio-depacketizer-1.01.a",
> > + NULL
> > + }
> > +};
> > +
> > +/*
> > + * LabX dma device
> > + */
> > +static void labx_dma_probe(void *fdt, int node)
> > +{
> > + uint32_t dma_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +
> > + labx_dma_create(dma_addr, 1024);
> > +}
> > +
> > +devinfo_t labx_dma_device = {
> > + .probe = &labx_dma_probe,
> > + .pass = 1,
> > + .compat = (const char * []) {
> > + "xlnx,labx-dma-1.00.a",
> > + "xlnx,labx-dma-1.01.a",
> > + "xlnx,labx-local-audio-1.00.a",
> > + NULL
> > + }
> > +};
> > +
> > +/*
> > + * LabX ethernet device
> > + */
> > +static void labx_ethernet_probe(void *fdt, int node)
> > +{
> > + uint32_t ethernet_addr = fdt_get_int_from_array(fdt, node, "reg",
> 0);
> > + uint32_t host_irq =
> > + fdt_get_int_from_array(fdt, node, "interrupts", 0);
> > + uint32_t fifo_irq =
> > + fdt_get_int_from_array(fdt, node, "interrupts", 2);
> > + uint32_t phy_irq =
> > + fdt_get_int_from_array(fdt, node, "interrupts", 4);
> > +
> > + labx_ethernet_create(&nd_table[++eth_dev_index], ethernet_addr,
> > + irq[host_irq], irq[fifo_irq], irq[phy_irq]);
> > +}
> > +
> > +devinfo_t labx_ethernet_device = {
> > + .probe = &labx_ethernet_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,labx-ethernet-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * LabX ptp device
> > + */
> > +static void labx_ptp_probe(void *fdt, int node)
> > +{
> > + uint32_t ptp_addr = fdt_get_int_from_array(fdt, node, "reg", 0);
> > +
> > + labx_ptp_create(ptp_addr);
> > +}
> > +
> > +devinfo_t labx_ptp_device = {
> > + .probe = &labx_ptp_probe,
> > + .pass = 1,
> > + .compat = (const char * []) { "xlnx,labx-ptp-1.00.a", NULL }
> > +};
> > +
> > +/*
> > + * Table of available devices
> > + */
> > +devinfo_t *devices[] = {
> > + &xilinx_interrupt_controller_device,
> > + &flash_device,
> > + &xilinx_timer_device,
> > + &xilinx_uartlite_device,
> > + &xilinx_ethlite_device,
> > + &labx_audio_packetizer_device,
> > + &labx_audio_depacketizer_device,
> > + &labx_dma_device,
> > + &labx_ethernet_device,
> > + &labx_ptp_device,
> > + NULL
> > +};
> > +
> > +static int plb_device_probe(void *fdt, int node, int pass)
>
> QEMU abstacts away interconnect implementations. You should probably
> also drive off the "simple-bus" compat rather than PLB for machine model
> compatibility. s/plb/bus/g or something like it.
>
> > +{
> > + devinfo_t **dev = &(devices[0]);
> > +
> > + while (*dev) {
> > + const char **compat = &((*dev)->compat[0]);
> > + while (*compat) {
> > + if (0 == qemu_devtree_node_check_compatible(fdt, node,
> *compat)) {
> > + if (pass == (*dev)->pass) {
> > + printf("Adding a device for node %s\n",
> > + qemu_devtree_get_name(fdt, node, NULL));
> > +
> > + (*dev)->probe(fdt, node);
> > + return 0;
> > + }
> > +
> > + if (pass < (*dev)->pass) {
> > + /* Probe again on the next pass */
> > + return 1;
> > + }
> > + }
> > +
> > + compat++;
> > + }
> > +
> > + dev++;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static void plb_bus_probe(void *fdt)
> > +{
> > + int root = qemu_devtree_node_offset(fdt, "/");
> > + int plb = qemu_devtree_subnode_offset_namelen(fdt, root, "plb", 3);
> > + if (plb > 0) {
> > + /* Do multiple passes through the devices. Some have
> dependencies
> > + on others being first */
> > + int pass = 0;
> > + int again = 0;
> > + do {
> > + int child = plb;
> > + again = 0;
> > + do {
> > + child = qemu_devtree_next_child_offset(fdt, plb, child);
> > + if (child < 0) {
> > + break;
> > + }
> > +
> > + again |= plb_device_probe(fdt, child, pass);
> > +
> > + } while (1);
> > +
> > + pass++;
> > +
> > + } while (again);
> > + }
> > +}
> > +
> > +static void
> > +labx_microblaze_init(ram_addr_t ram_size,
> > + const char *boot_device,
> > + const char *kernel_filename,
> > + const char *kernel_cmdline,
> > + const char *initrd_filename,
> > + const char *cpu_model)
> > +{
> > + MemoryRegion *address_space_mem = get_system_memory();
> > +
> > + int kernel_size;
> > + int fdt_size;
> > + void *fdt = get_device_tree(&fdt_size);
> > + target_phys_addr_t ddr_base = get_dram_base(fdt);
> > + MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1);
> > + MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
> > + CPUMBState *env;
> > +
> > + /* init CPUs */
> > + if (cpu_model == NULL) {
> > + cpu_model = "microblaze";
> > + }
> > + g_cpu = cpu_mb_init(cpu_model);
> > + env = &g_cpu->env;
> > +
> > + env->pvr.regs[10] = 0x0c000000; /* spartan 3a dsp family. */
>
> I have dtb driven solution for this.
>
> > + qemu_register_reset(main_cpu_reset, g_cpu);
> > +
> > + /* Attach emulated BRAM through the LMB. LMB size is not specified
> in the
> > + device-tree but there must be one to hold the vector table. */
> > + memory_region_init_ram(phys_lmb_bram, "labx_microblaze.lmb_bram",
> > + LMB_BRAM_SIZE);
> > + vmstate_register_ram_global(phys_lmb_bram);
> > + memory_region_add_subregion(address_space_mem, 0x00000000,
> phys_lmb_bram);
> > +
> > + memory_region_init_ram(phys_ram, "labx_microblaze.ram", ram_size);
> > + vmstate_register_ram_global(phys_ram);
> > + memory_region_add_subregion(address_space_mem, ddr_base, phys_ram);
> > +
> > +
> > + /* Create other devices listed in the device-tree */
> > + plb_bus_probe(fdt);
> > +
> > + if (kernel_filename) {
> > + uint64_t entry, low, high;
> > + uint32_t base32;
> > +
> > + /* Boots a kernel elf binary. */
> > + kernel_size = load_elf(kernel_filename, NULL, NULL,
> > + &entry, &low, &high,
> > + 1, ELF_MACHINE, 0);
> > + base32 = entry;
> > + if (base32 == 0xc0000000) {
> > + kernel_size = load_elf(kernel_filename,
> translate_kernel_address,
> > + NULL, &entry, NULL, NULL,
> > + 1, ELF_MACHINE, 0);
> > + }
> > + /* Always boot into physical ram. */
> > + boot_info.bootstrap_pc = ddr_base + (entry & 0x07ffffff);
> > +
> > + /* If it wasn't an ELF image, try an u-boot image. */
> > + if (kernel_size < 0) {
> > + target_phys_addr_t uentry, loadaddr;
> > +
> > + kernel_size = load_uimage(kernel_filename, &uentry,
> &loadaddr, 0);
> > + boot_info.bootstrap_pc = uentry;
> > + high = (loadaddr + kernel_size + 3) & ~3;
> > + }
> > +
> > + /* Not an ELF image nor an u-boot image, try a RAW image. */
> > + if (kernel_size < 0) {
> > + kernel_size = load_image_targphys(kernel_filename, ddr_base,
> > + ram_size);
> > + boot_info.bootstrap_pc = ddr_base;
> > + high = (ddr_base + kernel_size + 3) & ~3;
> > + }
> > +
> > + if (initrd_filename) {
> > + uint32_t initrd_base = 0x88c00000;
> > + uint32_t initrd_size =
> > + load_image_targphys(initrd_filename, initrd_base,
> > + ram_size - initrd_base);
> > + if (initrd_size <= 0) {
> > + fprintf(stderr, "qemu: could not load initial ram disk
> '%s'\n",
> > + initrd_filename);
> > + exit(1);
> > + }
> > +
> > + boot_info.initrd = initrd_base;
> > + } else {
> > + boot_info.initrd = 0x00000000;
> > + }
> > +
> > + boot_info.cmdline = high + 4096;
> > + if (kernel_cmdline && strlen(kernel_cmdline)) {
> > + pstrcpy_targphys("cmdline", boot_info.cmdline, 256,
> kernel_cmdline);
> > + }
> > + /* Provide a device-tree. */
> > + boot_info.fdt = boot_info.cmdline + 4096;
> > + labx_microblaze_load_device_tree(boot_info.fdt, ram_size,
> > + 0, 0,
> > + kernel_cmdline);
> > + }
> > +}
> > +
> > +static QEMUMachine labx_microblaze_machine = {
>
> Its a generic machine, so should it be badged as labx?
>
> > + .name = "labx-microblaze-devicetree",
> > + .desc = "Microblaze design based on the peripherals specified "
> > + "in the device-tree.",
> > + .init = labx_microblaze_init,
> > + .is_default = 1
>
> > +};
> > +
> > +static void labx_microblaze_machine_init(void)
> > +{
> > + qemu_register_machine(&labx_microblaze_machine);
> > +}
> > +
> > +machine_init(labx_microblaze_machine_init);
> > +
> > diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs
> > index 274d2c5..632cd60 100644
> > --- a/hw/microblaze/Makefile.objs
> > +++ b/hw/microblaze/Makefile.objs
> > @@ -1,4 +1,5 @@
> > -obj-y = petalogix_s3adsp1800_mmu.o
> > +obj-y = labx_microblaze_devicetree.o
>
> obj-y += labx_microblaze_devicetree.(CONFIG-FDT)
>
> > +obj-y += petalogix_s3adsp1800_mmu.o
> > obj-y += petalogix_ml605_mmu.o
> > obj-y += microblaze_boot.o
> >
>
>
>
[-- Attachment #2: Type: text/html, Size: 31252 bytes --]
^ permalink raw reply [flat|nested] 31+ messages in thread
* [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled.
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (7 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 8/9] MicroBlaze: " crwulff
@ 2012-09-10 0:20 ` crwulff
2012-09-12 0:25 ` Peter Crosthwaite
2012-09-11 23:40 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support Peter Crosthwaite
9 siblings, 1 reply; 31+ messages in thread
From: crwulff @ 2012-09-10 0:20 UTC (permalink / raw)
To: qemu-devel; +Cc: Chris Wulff
From: Chris Wulff <crwulff@gmail.com>
Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
hw/xilinx_timer.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
index b562bd0..18f354b 100644
--- a/hw/xilinx_timer.c
+++ b/hw/xilinx_timer.c
@@ -24,6 +24,7 @@
#include "sysbus.h"
#include "ptimer.h"
+#include "qemu-log.h"
#define D(x)
@@ -189,7 +190,7 @@ static void timer_hit(void *opaque)
{
struct xlx_timer *xt = opaque;
struct timerblock *t = xt->parent;
- D(printf("%s %d\n", __func__, timer));
+ D(printf("%s %d\n", __func__, xt->nr));
xt->regs[R_TCSR] |= TCSR_TINT;
if (xt->regs[R_TCSR] & TCSR_ARHT)
--
1.7.9.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled.
2012-09-10 0:20 ` [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled crwulff
@ 2012-09-12 0:25 ` Peter Crosthwaite
0 siblings, 0 replies; 31+ messages in thread
From: Peter Crosthwaite @ 2012-09-12 0:25 UTC (permalink / raw)
To: crwulff; +Cc: peter.crosthwaite, qemu-devel
Queued to microblaze-devs.
Thanks,
Peter
On Mon, Sep 10, 2012 at 10:20 AM, <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>-by: Chris Wulff <crwulff@gmail.com>
Reviewed-by: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
> ---
> hw/xilinx_timer.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/hw/xilinx_timer.c b/hw/xilinx_timer.c
> index b562bd0..18f354b 100644
> --- a/hw/xilinx_timer.c
> +++ b/hw/xilinx_timer.c
> @@ -24,6 +24,7 @@
>
> #include "sysbus.h"
> #include "ptimer.h"
> +#include "qemu-log.h"
>
> #define D(x)
>
> @@ -189,7 +190,7 @@ static void timer_hit(void *opaque)
> {
> struct xlx_timer *xt = opaque;
> struct timerblock *t = xt->parent;
> - D(printf("%s %d\n", __func__, timer));
> + D(printf("%s %d\n", __func__, xt->nr));
> xt->regs[R_TCSR] |= TCSR_TINT;
>
> if (xt->regs[R_TCSR] & TCSR_ARHT)
> --
> 1.7.9.5
>
>
^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: [Qemu-devel] [PATCH 0/9] Altera NiosII support
2012-09-10 0:19 ` [Qemu-devel] [PATCH 0/9] Altera NiosII support crwulff
` (8 preceding siblings ...)
2012-09-10 0:20 ` [Qemu-devel] [PATCH 9/9] xilinx_timer: Fix a compile error if debug messages are enabled crwulff
@ 2012-09-11 23:40 ` Peter Crosthwaite
9 siblings, 0 replies; 31+ messages in thread
From: Peter Crosthwaite @ 2012-09-11 23:40 UTC (permalink / raw)
To: crwulff; +Cc: peter.crosthwaite, qemu-devel
Hi Chris,
On Sun, 2012-09-09 at 20:19 -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> This set of patches adds support for the Altera NiosII soft-core processor,
> along with the hardware drivers needed to boot linux. I have tested the soft-mmu
> target and can boot to a shell with uClinux and a linux 3.0 kernel.
>
> Also included is a MicroBlaze configuration that is dynamically created
> from a device tree file that matches the NiosII one. These assign QEMU device
> emulation to the address space where specified in the device tree file.
>
We are currently maintaining a system out of tree that does exactly
this. Ill send patches shortly. We need to compare and converge
implementations.
We sent this to the mailing list about a year ago:
http://lists.gnu.org/archive/html/qemu-devel/2011-08/msg02953.html
We currently in an effort to make this machine independent, rather than
having to put dtb glue code in machine models.
Regards,
Peter
> Chris Wulff (9):
> NiosII: Add support for the Altera NiosII soft-core CPU.
> NiosII: Disassembly of NiosII instructions ported from GDB.
> Altera: Add support for Altera devices required to boot linux on
> NiosII.
> LabX: Support for some Lab X FPGA devices.
> FDT: Add additional access methods for array types and walking
> children.
> NiosII: Build system and documentation integration.
> NiosII: Add a config that is dynamically set up by a device tree
> file.
> MicroBlaze: Add a config that is dynamically set up by a device tree
> file.
> xilinx_timer: Fix a compile error if debug messages are enabled.
>
> MAINTAINERS | 5 +
> Makefile.objs | 2 +
> arch_init.c | 2 +
> arch_init.h | 1 +
> configure | 11 +
> cpu-exec.c | 12 +-
> default-configs/microblaze-softmmu.mak | 1 +
> default-configs/nios2-linux-user.mak | 1 +
> default-configs/nios2-softmmu.mak | 6 +
> device_tree.c | 88 ++
> device_tree.h | 18 +
> dis-asm.h | 3 +
> disas.c | 3 +
> elf.h | 2 +
> exec.c | 6 +-
> gdbstub.c | 29 +
> hw/Makefile.objs | 12 +
> hw/altera.h | 34 +
> hw/altera_timer.c | 198 +++
> hw/altera_uart.c | 218 ++++
> hw/altera_vic.c | 195 +++
> hw/labx_audio_depacketizer.c | 409 ++++++
> hw/labx_audio_packetizer.c | 397 ++++++
> hw/labx_devices.h | 103 ++
> hw/labx_dma.c | 241 ++++
> hw/labx_ethernet.c | 615 +++++++++
> hw/labx_microblaze_devicetree.c | 597 +++++++++
> hw/labx_nios2_devicetree.c | 665 ++++++++++
> hw/labx_ptp.c | 291 +++++
> hw/microblaze/Makefile.objs | 3 +-
> hw/nios2.h | 46 +
> hw/nios2/Makefile.objs | 6 +
> hw/nios2_pic_cpu.c | 48 +
> hw/xilinx_timer.c | 3 +-
> linux-user/elfload.c | 29 +
> monitor.c | 5 +-
> nios2-dis.c | 417 +++++++
> nios2-isa.h | 2150 ++++++++++++++++++++++++++++++++
> nios2-opc.c | 540 ++++++++
> nios2.h | 366 ++++++
> qapi-schema.json | 2 +-
> qemu-doc.texi | 3 +
> target-nios2/Makefile.objs | 5 +
> target-nios2/altera_iic.c | 100 ++
> target-nios2/cpu-qom.h | 69 +
> target-nios2/cpu.c | 83 ++
> target-nios2/cpu.h | 259 ++++
> target-nios2/exec.h | 60 +
> target-nios2/helper.c | 291 +++++
> target-nios2/helper.h | 45 +
> target-nios2/instruction.c | 1463 ++++++++++++++++++++++
> target-nios2/instruction.h | 290 +++++
> target-nios2/machine.c | 33 +
> target-nios2/mmu.c | 273 ++++
> target-nios2/mmu.h | 49 +
> target-nios2/op_helper.c | 125 ++
> target-nios2/translate.c | 252 ++++
> 57 files changed, 11172 insertions(+), 8 deletions(-)
> create mode 100644 default-configs/nios2-linux-user.mak
> create mode 100644 default-configs/nios2-softmmu.mak
> create mode 100644 hw/altera.h
> create mode 100644 hw/altera_timer.c
> create mode 100644 hw/altera_uart.c
> create mode 100644 hw/altera_vic.c
> create mode 100644 hw/labx_audio_depacketizer.c
> create mode 100644 hw/labx_audio_packetizer.c
> create mode 100644 hw/labx_devices.h
> create mode 100644 hw/labx_dma.c
> create mode 100644 hw/labx_ethernet.c
> create mode 100644 hw/labx_microblaze_devicetree.c
> create mode 100644 hw/labx_nios2_devicetree.c
> create mode 100644 hw/labx_ptp.c
> create mode 100644 hw/nios2.h
> create mode 100644 hw/nios2/Makefile.objs
> create mode 100644 hw/nios2_pic_cpu.c
> create mode 100644 nios2-dis.c
> create mode 100644 nios2-isa.h
> create mode 100644 nios2-opc.c
> create mode 100644 nios2.h
> create mode 100644 target-nios2/Makefile.objs
> create mode 100644 target-nios2/altera_iic.c
> create mode 100644 target-nios2/cpu-qom.h
> create mode 100644 target-nios2/cpu.c
> create mode 100644 target-nios2/cpu.h
> create mode 100644 target-nios2/exec.h
> create mode 100644 target-nios2/helper.c
> create mode 100644 target-nios2/helper.h
> create mode 100644 target-nios2/instruction.c
> create mode 100644 target-nios2/instruction.h
> create mode 100644 target-nios2/machine.c
> create mode 100644 target-nios2/mmu.c
> create mode 100644 target-nios2/mmu.h
> create mode 100644 target-nios2/op_helper.c
> create mode 100644 target-nios2/translate.c
>
^ permalink raw reply [flat|nested] 31+ messages in thread