diff mbox series

[RFC,v3,04/34] Hexagon (target/hexagon) scalar core definition

Message ID 1597765847-16637-5-git-send-email-tsimpson@quicinc.com (mailing list archive)
State New, archived
Headers show
Series [RFC,v3,01/34] Hexagon Update MAINTAINERS file | expand

Commit Message

Taylor Simpson Aug. 18, 2020, 3:50 p.m. UTC
Add target state header, target definitions and initialization routines

Signed-off-by: Taylor Simpson <tsimpson@quicinc.com>
---
 target/hexagon/cpu-param.h |  26 ++++
 target/hexagon/cpu.h       | 164 +++++++++++++++++++++++
 target/hexagon/cpu_bits.h  |  34 +++++
 target/hexagon/internal.h  |  40 ++++++
 target/hexagon/cpu.c       | 316 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 580 insertions(+)
 create mode 100644 target/hexagon/cpu-param.h
 create mode 100644 target/hexagon/cpu.h
 create mode 100644 target/hexagon/cpu_bits.h
 create mode 100644 target/hexagon/internal.h
 create mode 100644 target/hexagon/cpu.c

Comments

Richard Henderson Aug. 26, 2020, 1:35 p.m. UTC | #1
On 8/18/20 8:50 AM, Taylor Simpson wrote:
> +#include <fenv.h>

This should not be in cpu.h.  What's up?


> +#define TARGET_PAGE_BITS 16     /* 64K pages */
> +#define TARGET_LONG_BITS 32

Belongs in cpu-param.h

> +#ifdef CONFIG_USER_ONLY
> +#define TOTAL_PER_THREAD_REGS 64
> +#else
...
> +    target_ulong gpr[TOTAL_PER_THREAD_REGS];

Do I not understand hexagon enough to know why the number of general registers
would vary with system mode?  Why is the define conditional on user-only?

> +/*
> + * Change HEX_DEBUG to 1 to turn on debugging output
> + */
> +#define HEX_DEBUG 0
> +#define HEX_DEBUG_LOG(...) \
> +    do { \
> +        if (HEX_DEBUG) { \
> +            rcu_read_lock(); \
> +            fprintf(stderr, __VA_ARGS__); \
> +            rcu_read_unlock(); \
> +        } \
> +    } while (0)
> +

No.  There are plenty of bad examples of this in qemu, let's not add another.

First, the lock doesn't do what you think.
Second, stderr is never right.
Third, just about any time you want this, there's a tracepoint that you could
add that would be better, correct, and toggleable from the command-line.

> +/*
> + * One of the main debugging techniques is to use "-d cpu" and compare against
> + * LLDB output when single stepping.  However, the target and qemu put the
> + * stacks at different locations.  This is used to compensate so the diff is
> + * cleaner.
> + */
> +static inline target_ulong hack_stack_ptrs(CPUHexagonState *env,
> +                                           target_ulong addr)
> +{
> +    static bool first = true;
> +    if (first) {
> +        first = false;
> +        env->stack_start = env->gpr[HEX_REG_SP];
> +        env->gpr[HEX_REG_USR] = 0x56000;
> +
> +#define ADJUST_STACK 0
> +#if ADJUST_STACK
> +        /*
> +         * Change the two numbers below to
> +         *     1    qemu stack location
> +         *     2    hardware stack location
> +         * Or set to zero for normal mode (no stack adjustment)
> +         */
> +        env->stack_adjust = 0xfffeeb80 - 0xbf89f980;
> +#else
> +        env->stack_adjust = 0;
> +#endif
> +    }
> +
> +    target_ulong stack_start = env->stack_start;
> +    target_ulong stack_size = 0x10000;
> +    target_ulong stack_adjust = env->stack_adjust;
> +
> +    if (stack_start + 0x1000 >= addr && addr >= (stack_start - stack_size)) {
> +        return addr - stack_adjust;
> +    }
> +    return addr;
> +}

I understand your desire for this sort of comparison.  What I don't understand
is the method.  Surely it would be preferable to actually change the stack
location in qemu, rather than constantly adjust for it.

Add a per-target hook to linux-user/hexagon/target_elf.h that controls the
allocation of the stack in elfload.c, setup_arg_pages().

> +static void hexagon_dump(CPUHexagonState *env, FILE *f)
> +{
> +    static target_ulong last_pc;
> +    int i;
> +
> +    /*
> +     * When comparing with LLDB, it doesn't step through single-cycle
> +     * hardware loops the same way.  So, we just skip them here
> +     */
> +    if (env->gpr[HEX_REG_PC] == last_pc) {
> +        return;
> +    }

Multi-threaded data race.  Might as well move last_pc to env->dump_last_pc or
something.

But I'd also suggest that all of this lldb compatibility stuff be optional,
switchable from the command-line with a cpu property.  Because there are going
to be real cases where *not* single-stepping will result in dumps from the same
PC, and you've just squashed all of those.

Call the property x-lldb-compat, or something, and default it to off.  You then
turn it on with "-cpu v67,x-lldb-compat=on".


r~
Taylor Simpson Aug. 26, 2020, 11:51 p.m. UTC | #2
Thanks for the feedback, Richard!!



> -----Original Message-----
> From: Richard Henderson <richard.henderson@linaro.org>
> Sent: Wednesday, August 26, 2020 7:36 AM
> To: Taylor Simpson <tsimpson@quicinc.com>; qemu-devel@nongnu.org
> Cc: philmd@redhat.com; laurent@vivier.eu; riku.voipio@iki.fi;
> aleksandar.m.mail@gmail.com; ale@rev.ng
> Subject: Re: [RFC PATCH v3 04/34] Hexagon (target/hexagon) scalar core
> definition
>
> On 8/18/20 8:50 AM, Taylor Simpson wrote:
> > +#include <fenv.h>
>
> This should not be in cpu.h.  What's up?

We're not using qemu softfloat (it's on the TODO list), so there's a fenv_t field in CPUHexagonState.

> > +#define TARGET_PAGE_BITS 16     /* 64K pages */
> > +#define TARGET_LONG_BITS 32
>
> Belongs in cpu-param.h

OK

> > +#ifdef CONFIG_USER_ONLY
> > +#define TOTAL_PER_THREAD_REGS 64
> > +#else
> ...
> > +    target_ulong gpr[TOTAL_PER_THREAD_REGS];
>
> Do I not understand hexagon enough to know why the number of general
> registers
> would vary with system mode?  Why is the define conditional on user-only?

Yes, there are some registers that are only available in system mode.  Since this series is only for linux-user mode, I'll remove the ifdef for now.

We're working on system mode.  When that series is ready, we can put the ifdef back in.  At that time, you'll also see the extra registers in hex_regs.h.

> No.  There are plenty of bad examples of this in qemu, let's not add another.
>
> First, the lock doesn't do what you think.
> Second, stderr is never right.
> Third, just about any time you want this, there's a tracepoint that you could
> add that would be better, correct, and toggleable from the command-line.

OK

> I understand your desire for this sort of comparison.  What I don't
> understand
> is the method.  Surely it would be preferable to actually change the stack
> location in qemu, rather than constantly adjust for it.
>
> Add a per-target hook to linux-user/hexagon/target_elf.h that controls the
> allocation of the stack in elfload.c, setup_arg_pages().

OK, will look into this.  Thanks for the advice, I wasn't aware this was possible.

>
> > +static void hexagon_dump(CPUHexagonState *env, FILE *f)
> > +{
> > +    static target_ulong last_pc;
> > +    int i;
> > +
> > +    /*
> > +     * When comparing with LLDB, it doesn't step through single-cycle
> > +     * hardware loops the same way.  So, we just skip them here
> > +     */
> > +    if (env->gpr[HEX_REG_PC] == last_pc) {
> > +        return;
> > +    }
>
> Multi-threaded data race.  Might as well move last_pc to env->dump_last_pc
> or
> something.
>
> But I'd also suggest that all of this lldb compatibility stuff be optional,
> switchable from the command-line with a cpu property.  Because there are
> going
> to be real cases where *not* single-stepping will result in dumps from the
> same
> PC, and you've just squashed all of those.
>
> Call the property x-lldb-compat, or something, and default it to off.  You then
> turn it on with "-cpu v67,x-lldb-compat=on".

OK
diff mbox series

Patch

diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h
new file mode 100644
index 0000000..3a6b727
--- /dev/null
+++ b/target/hexagon/cpu-param.h
@@ -0,0 +1,26 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEXAGON_CPU_PARAM_H
+#define HEXAGON_CPU_PARAM_H
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define NB_MMU_MODES 1
+
+#endif
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
new file mode 100644
index 0000000..af3d644
--- /dev/null
+++ b/target/hexagon/cpu.h
@@ -0,0 +1,164 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEXAGON_CPU_H
+#define HEXAGON_CPU_H
+
+/* Forward declaration needed by some of the header files */
+typedef struct CPUHexagonState CPUHexagonState;
+
+#include <fenv.h>
+
+#define TARGET_PAGE_BITS 16     /* 64K pages */
+#define TARGET_LONG_BITS 32
+
+#include "qemu/compiler.h"
+#include "qemu-common.h"
+#include "exec/cpu-defs.h"
+#include "hex_regs.h"
+
+#define NUM_PREGS 4
+#ifdef CONFIG_USER_ONLY
+#define TOTAL_PER_THREAD_REGS 64
+#else
+#error System mode not implemented
+#endif
+
+#define SLOTS_MAX 4
+#define STORES_MAX 2
+#define REG_WRITES_MAX 32
+#define PRED_WRITES_MAX 5                   /* 4 insns + endloop */
+
+#define TYPE_HEXAGON_CPU "hexagon-cpu"
+
+#define HEXAGON_CPU_TYPE_SUFFIX "-" TYPE_HEXAGON_CPU
+#define HEXAGON_CPU_TYPE_NAME(name) (name HEXAGON_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU
+
+#define TYPE_HEXAGON_CPU_V67 HEXAGON_CPU_TYPE_NAME("v67")
+
+#define MMU_USER_IDX 0
+
+struct MemLog {
+    target_ulong va;
+    uint8_t width;
+    uint32_t data32;
+    uint64_t data64;
+};
+
+#define EXEC_STATUS_OK          0x0000
+#define EXEC_STATUS_STOP        0x0002
+#define EXEC_STATUS_REPLAY      0x0010
+#define EXEC_STATUS_LOCKED      0x0020
+#define EXEC_STATUS_EXCEPTION   0x0100
+
+
+#define EXCEPTION_DETECTED      (env->status & EXEC_STATUS_EXCEPTION)
+#define REPLAY_DETECTED         (env->status & EXEC_STATUS_REPLAY)
+#define CLEAR_EXCEPTION         (env->status &= (~EXEC_STATUS_EXCEPTION))
+#define SET_EXCEPTION           (env->status |= EXEC_STATUS_EXCEPTION)
+
+struct CPUHexagonState {
+    target_ulong gpr[TOTAL_PER_THREAD_REGS];
+    target_ulong pred[NUM_PREGS];
+    target_ulong branch_taken;
+    target_ulong next_PC;
+
+    /* For comparing with LLDB on target - see hack_stack_ptrs function */
+    target_ulong stack_start;
+    target_ulong stack_adjust;
+
+    uint8_t slot_cancelled;
+    target_ulong new_value[TOTAL_PER_THREAD_REGS];
+
+    /*
+     * Only used when HEX_DEBUG is on, but unconditionally included
+     * to reduce recompile time when turning HEX_DEBUG on/off.
+     */
+    target_ulong this_PC;
+    target_ulong reg_written[TOTAL_PER_THREAD_REGS];
+
+    target_ulong new_pred_value[NUM_PREGS];
+    target_ulong pred_written;
+
+    struct MemLog mem_log_stores[STORES_MAX];
+    target_ulong pkt_has_store_s1;
+    target_ulong dczero_addr;
+
+    fenv_t fenv;
+
+    target_ulong llsc_addr;
+    target_ulong llsc_val;
+    uint64_t     llsc_val_i64;
+
+    target_ulong is_gather_store_insn;
+    target_ulong gather_issued;
+};
+
+#define HEXAGON_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(HexagonCPUClass, (klass), TYPE_HEXAGON_CPU)
+#define HEXAGON_CPU(obj) \
+    OBJECT_CHECK(HexagonCPU, (obj), TYPE_HEXAGON_CPU)
+#define HEXAGON_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(HexagonCPUClass, (obj), TYPE_HEXAGON_CPU)
+
+typedef struct HexagonCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+    DeviceRealize parent_realize;
+    DeviceReset parent_reset;
+} HexagonCPUClass;
+
+typedef struct HexagonCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+    CPUNegativeOffsetState neg;
+    CPUHexagonState env;
+} HexagonCPU;
+
+static inline HexagonCPU *hexagon_env_get_cpu(CPUHexagonState *env)
+{
+    return container_of(env, HexagonCPU, env);
+}
+
+#include "cpu_bits.h"
+
+#define cpu_signal_handler cpu_hexagon_signal_handler
+extern int cpu_hexagon_signal_handler(int host_signum, void *pinfo, void *puc);
+
+static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->gpr[HEX_REG_PC];
+    *cs_base = 0;
+#ifdef CONFIG_USER_ONLY
+    *flags = 0;
+#else
+#error System mode not supported on Hexagon yet
+#endif
+}
+
+typedef struct CPUHexagonState CPUArchState;
+typedef HexagonCPU ArchCPU;
+
+void hexagon_translate_init(void);
+
+#include "exec/cpu-all.h"
+
+#endif /* HEXAGON_CPU_H */
diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h
new file mode 100644
index 0000000..586c717
--- /dev/null
+++ b/target/hexagon/cpu_bits.h
@@ -0,0 +1,34 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEXAGON_CPU_BITS_H
+#define HEXAGON_CPU_BITS_H
+
+#define HEX_EXCP_FETCH_NO_UPAGE  0x012
+#define HEX_EXCP_INVALID_PACKET  0x015
+#define HEX_EXCP_INVALID_OPCODE  0x015
+#define HEX_EXCP_PRIV_NO_UREAD   0x024
+#define HEX_EXCP_PRIV_NO_UWRITE  0x025
+
+#define HEX_EXCP_TRAP0           0x172
+
+#define PACKET_WORDS_MAX         4
+
+extern int disassemble_hexagon(uint32_t *words, int nwords,
+                               char *buf, int bufsize);
+
+#endif
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h
new file mode 100644
index 0000000..d3e4412
--- /dev/null
+++ b/target/hexagon/internal.h
@@ -0,0 +1,40 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HEXAGON_INTERNAL_H
+#define HEXAGON_INTERNAL_H
+
+/*
+ * Change HEX_DEBUG to 1 to turn on debugging output
+ */
+#define HEX_DEBUG 0
+#define HEX_DEBUG_LOG(...) \
+    do { \
+        if (HEX_DEBUG) { \
+            rcu_read_lock(); \
+            fprintf(stderr, __VA_ARGS__); \
+            rcu_read_unlock(); \
+        } \
+    } while (0)
+
+extern void hexagon_debug(CPUHexagonState *env);
+
+extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS];
+
+extern void init_genptr(void);
+
+#endif
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
new file mode 100644
index 0000000..d812913
--- /dev/null
+++ b/target/hexagon/cpu.c
@@ -0,0 +1,316 @@ 
+/*
+ *  Copyright(c) 2019-2020 Qualcomm Innovation Center, Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/qemu-print.h"
+#include "cpu.h"
+#include "internal.h"
+#include "exec/exec-all.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+
+static void hexagon_v67_cpu_init(Object *obj)
+{
+}
+
+static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    char *typename;
+    char **cpuname;
+
+    cpuname = g_strsplit(cpu_model, ",", 1);
+    typename = g_strdup_printf(HEXAGON_CPU_TYPE_NAME("%s"), cpuname[0]);
+    oc = object_class_by_name(typename);
+    g_strfreev(cpuname);
+    g_free(typename);
+    if (!oc || !object_class_dynamic_cast(oc, TYPE_HEXAGON_CPU) ||
+        object_class_is_abstract(oc)) {
+        return NULL;
+    }
+    return oc;
+}
+
+const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = {
+   "r0", "r1",  "r2",  "r3",  "r4",   "r5",  "r6",  "r7",
+   "r8", "r9",  "r10", "r11", "r12",  "r13", "r14", "r15",
+  "r16", "r17", "r18", "r19", "r20",  "r21", "r22", "r23",
+  "r24", "r25", "r26", "r27", "r28",  "r29", "r30", "r31",
+  "sa0", "lc0", "sa1", "lc1", "p3_0", "c5",  "m0",  "m1",
+  "usr", "pc",  "ugp", "gp",  "cs0",  "cs1", "c14", "c15",
+  "c16", "c17", "c18", "c19", "pkt_cnt",  "insn_cnt", "c22", "c23",
+  "c24", "c25", "c26", "c27", "c28",  "c29", "c30", "c31",
+};
+
+/*
+ * One of the main debugging techniques is to use "-d cpu" and compare against
+ * LLDB output when single stepping.  However, the target and qemu put the
+ * stacks at different locations.  This is used to compensate so the diff is
+ * cleaner.
+ */
+static inline target_ulong hack_stack_ptrs(CPUHexagonState *env,
+                                           target_ulong addr)
+{
+    static bool first = true;
+    if (first) {
+        first = false;
+        env->stack_start = env->gpr[HEX_REG_SP];
+        env->gpr[HEX_REG_USR] = 0x56000;
+
+#define ADJUST_STACK 0
+#if ADJUST_STACK
+        /*
+         * Change the two numbers below to
+         *     1    qemu stack location
+         *     2    hardware stack location
+         * Or set to zero for normal mode (no stack adjustment)
+         */
+        env->stack_adjust = 0xfffeeb80 - 0xbf89f980;
+#else
+        env->stack_adjust = 0;
+#endif
+    }
+
+    target_ulong stack_start = env->stack_start;
+    target_ulong stack_size = 0x10000;
+    target_ulong stack_adjust = env->stack_adjust;
+
+    if (stack_start + 0x1000 >= addr && addr >= (stack_start - stack_size)) {
+        return addr - stack_adjust;
+    }
+    return addr;
+}
+
+/* HEX_REG_P3_0 (aka C4) is an alias for the predicate registers */
+static inline target_ulong read_p3_0(CPUHexagonState *env)
+{
+    int32_t control_reg = 0;
+    int i;
+    for (i = NUM_PREGS - 1; i >= 0; i--) {
+        control_reg <<= 8;
+        control_reg |= env->pred[i] & 0xff;
+    }
+    return control_reg;
+}
+
+static void print_reg(FILE *f, CPUHexagonState *env, int regnum)
+{
+    target_ulong value;
+
+    if (regnum == HEX_REG_P3_0) {
+        value = read_p3_0(env);
+    } else {
+        value = regnum < 32 ? hack_stack_ptrs(env, env->gpr[regnum])
+                            : env->gpr[regnum];
+    }
+
+    qemu_fprintf(f, "  %s = 0x" TARGET_FMT_lx "\n",
+                 hexagon_regnames[regnum], value);
+}
+
+static void hexagon_dump(CPUHexagonState *env, FILE *f)
+{
+    static target_ulong last_pc;
+    int i;
+
+    /*
+     * When comparing with LLDB, it doesn't step through single-cycle
+     * hardware loops the same way.  So, we just skip them here
+     */
+    if (env->gpr[HEX_REG_PC] == last_pc) {
+        return;
+    }
+    last_pc = env->gpr[HEX_REG_PC];
+    qemu_fprintf(f, "General Purpose Registers = {\n");
+    for (i = 0; i < 32; i++) {
+        print_reg(f, env, i);
+    }
+    print_reg(f, env, HEX_REG_SA0);
+    print_reg(f, env, HEX_REG_LC0);
+    print_reg(f, env, HEX_REG_SA1);
+    print_reg(f, env, HEX_REG_LC1);
+    print_reg(f, env, HEX_REG_M0);
+    print_reg(f, env, HEX_REG_M1);
+    print_reg(f, env, HEX_REG_USR);
+    print_reg(f, env, HEX_REG_P3_0);
+    print_reg(f, env, HEX_REG_GP);
+    print_reg(f, env, HEX_REG_UGP);
+    print_reg(f, env, HEX_REG_PC);
+#ifdef CONFIG_USER_ONLY
+    /*
+     * Not modelled in user mode, print junk to minimize the diff's
+     * with LLDB output
+     */
+    qemu_fprintf(f, "  cause = 0x000000db\n");
+    qemu_fprintf(f, "  badva = 0x00000000\n");
+    qemu_fprintf(f, "  cs0 = 0x00000000\n");
+    qemu_fprintf(f, "  cs1 = 0x00000000\n");
+#else
+    print_reg(f, env, HEX_REG_CAUSE);
+    print_reg(f, env, HEX_REG_BADVA);
+    print_reg(f, env, HEX_REG_CS0);
+    print_reg(f, env, HEX_REG_CS1);
+#endif
+    qemu_fprintf(f, "}\n");
+}
+
+static void hexagon_dump_state(CPUState *cs, FILE *f, int flags)
+{
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
+    CPUHexagonState *env = &cpu->env;
+
+    hexagon_dump(env, f);
+}
+
+void hexagon_debug(CPUHexagonState *env)
+{
+    hexagon_dump(env, stdout);
+}
+
+static void hexagon_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
+    CPUHexagonState *env = &cpu->env;
+    env->gpr[HEX_REG_PC] = value;
+}
+
+static void hexagon_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
+    CPUHexagonState *env = &cpu->env;
+    env->gpr[HEX_REG_PC] = tb->pc;
+}
+
+static bool hexagon_cpu_has_work(CPUState *cs)
+{
+    return true;
+}
+
+void restore_state_to_opc(CPUHexagonState *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->gpr[HEX_REG_PC] = data[0];
+}
+
+static void hexagon_cpu_reset(DeviceState *dev)
+{
+    CPUState *cs = CPU(dev);
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
+    HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(cpu);
+
+    mcc->parent_reset(dev);
+}
+
+static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info)
+{
+    info->print_insn = print_insn_hexagon;
+}
+
+static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    mcc->parent_realize(dev, errp);
+}
+
+static void hexagon_cpu_init(Object *obj)
+{
+    HexagonCPU *cpu = HEXAGON_CPU(obj);
+
+    cpu_set_cpustate_pointers(cpu);
+}
+
+static bool hexagon_tlb_fill(CPUState *cs, vaddr address, int size,
+                             MMUAccessType access_type, int mmu_idx,
+                             bool probe, uintptr_t retaddr)
+{
+#ifdef CONFIG_USER_ONLY
+    switch (access_type) {
+    case MMU_INST_FETCH:
+        cs->exception_index = HEX_EXCP_FETCH_NO_UPAGE;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = HEX_EXCP_PRIV_NO_UREAD;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = HEX_EXCP_PRIV_NO_UWRITE;
+        break;
+    }
+    cpu_loop_exit_restore(cs, retaddr);
+#else
+#error System mode not implemented for Hexagon
+#endif
+}
+
+static void hexagon_cpu_class_init(ObjectClass *c, void *data)
+{
+    HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c);
+    CPUClass *cc = CPU_CLASS(c);
+    DeviceClass *dc = DEVICE_CLASS(c);
+
+    device_class_set_parent_realize(dc, hexagon_cpu_realize,
+                                    &mcc->parent_realize);
+
+    device_class_set_parent_reset(dc, hexagon_cpu_reset, &mcc->parent_reset);
+
+    cc->class_by_name = hexagon_cpu_class_by_name;
+    cc->has_work = hexagon_cpu_has_work;
+    cc->dump_state = hexagon_dump_state;
+    cc->set_pc = hexagon_cpu_set_pc;
+    cc->synchronize_from_tb = hexagon_cpu_synchronize_from_tb;
+    cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS;
+    cc->gdb_stop_before_watchpoint = true;
+    cc->disas_set_info = hexagon_cpu_disas_set_info;
+#ifdef CONFIG_TCG
+    cc->tcg_initialize = hexagon_translate_init;
+    cc->tlb_fill = hexagon_tlb_fill;
+#endif
+}
+
+#define DEFINE_CPU(type_name, initfn)      \
+    {                                      \
+        .name = type_name,                 \
+        .parent = TYPE_HEXAGON_CPU,        \
+        .instance_init = initfn            \
+    }
+
+static const TypeInfo hexagon_cpu_type_infos[] = {
+    {
+        .name = TYPE_HEXAGON_CPU,
+        .parent = TYPE_CPU,
+        .instance_size = sizeof(HexagonCPU),
+        .instance_init = hexagon_cpu_init,
+        .abstract = true,
+        .class_size = sizeof(HexagonCPUClass),
+        .class_init = hexagon_cpu_class_init,
+    },
+    DEFINE_CPU(TYPE_HEXAGON_CPU_V67,              hexagon_v67_cpu_init),
+};
+
+DEFINE_TYPES(hexagon_cpu_type_infos)