[PATCH v3 03/32] target/hexagon: Implement start/stop helpers, soft reset

Brian Cain posted 32 patches 3 weeks, 6 days ago
[PATCH v3 03/32] target/hexagon: Implement start/stop helpers, soft reset
Posted by Brian Cain 3 weeks, 6 days ago
From: Brian Cain <bcain@quicinc.com>

Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/cpu.h        |   7 +++
 target/hexagon/cpu_helper.h |   3 ++
 target/hexagon/cpu.c        |  26 +++++++++-
 target/hexagon/cpu_helper.c | 100 ++++++++++++++++++++++++++++++++++++
 target/hexagon/op_helper.c  |   4 +-
 5 files changed, 136 insertions(+), 4 deletions(-)

diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 0017be9dff7..1a3c9014455 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -207,6 +207,13 @@ G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env,
                                             uint32_t exception,
                                             uintptr_t pc);
 
+#ifndef CONFIG_USER_ONLY
+uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg);
+uint32_t hexagon_sreg_read(CPUHexagonState *env, uint32_t reg);
+void hexagon_gdb_sreg_write(CPUHexagonState *env, uint32_t reg, uint32_t val);
+void hexagon_cpu_soft_reset(CPUHexagonState *env);
+#endif
+
 typedef HexagonCPU ArchCPU;
 
 void hexagon_translate_init(void);
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h
index d15385daf83..72f83d62a54 100644
--- a/target/hexagon/cpu_helper.h
+++ b/target/hexagon/cpu_helper.h
@@ -17,5 +17,8 @@ void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t);
 void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old);
 int get_exe_mode(CPUHexagonState *env);
 void clear_wait_mode(CPUHexagonState *env);
+void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause);
+void hexagon_start_threads(CPUHexagonState *env, uint32_t mask);
+void hexagon_stop_thread(CPUHexagonState *env);
 
 #endif
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 938492897fd..20c4b82a970 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -33,6 +33,7 @@
 #ifndef CONFIG_USER_ONLY
 #include "sys_macros.h"
 #include "accel/tcg/cpu-ldst.h"
+#include "qemu/main-loop.h"
 #endif
 
 static void hexagon_v66_cpu_init(Object *obj) { }
@@ -316,6 +317,26 @@ static void hexagon_restore_state_to_opc(CPUState *cs,
 }
 
 
+#ifndef CONFIG_USER_ONLY
+void hexagon_cpu_soft_reset(CPUHexagonState *env)
+{
+    BQL_LOCK_GUARD();
+    env->t_sreg[HEX_SREG_SSR] = 0;
+    hexagon_ssr_set_cause(env, HEX_CAUSE_RESET);
+
+    HexagonCPU *cpu = env_archcpu(env);
+    if (cpu->globalregs) {
+        uint32_t evb =
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_EVB,
+                                   env->threadId);
+        env->gpr[HEX_REG_PC] = evb;
+    } else {
+        env->gpr[HEX_REG_PC] = cpu->boot_addr;
+    }
+}
+#endif
+
+
 static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
 {
     CPUState *cs = CPU(obj);
@@ -343,9 +364,10 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
     HexagonCPU *cpu = HEXAGON_CPU(cs);
     env->t_sreg[HEX_SREG_HTID] = cpu->htid;
     env->threadId = cpu->htid;
-    env->gpr[HEX_REG_PC] = cpu->boot_addr;
-#endif
+    hexagon_cpu_soft_reset(env);
     env->cause_code = HEX_EVENT_NONE;
+    env->gpr[HEX_REG_PC] = cpu->boot_addr;
+#endif
 }
 
 static void hexagon_cpu_disas_set_info(const CPUState *cs,
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
index 6fbf5fc8e2f..c251790e5a5 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -10,6 +10,7 @@
 #include "system/cpus.h"
 #include "hw/core/boards.h"
 #include "hw/hexagon/hexagon.h"
+#include "system/runstate.h"
 #include "exec/cpu-interrupt.h"
 #include "exec/target_page.h"
 #include "accel/tcg/cpu-ldst.h"
@@ -79,7 +80,106 @@ void clear_wait_mode(CPUHexagonState *env)
     }
 }
 
+void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause)
+{
+    g_assert(bql_locked());
+
+    const uint32_t old = env->t_sreg[HEX_SREG_SSR];
+    SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1);
+    SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause);
+    const uint32_t new = env->t_sreg[HEX_SREG_SSR];
+
+    hexagon_modify_ssr(env, new, old);
+}
+
+
 int get_exe_mode(CPUHexagonState *env)
 {
     g_assert_not_reached();
 }
+
+static void set_enable_mask(CPUHexagonState *env)
+{
+    g_assert(bql_locked());
+
+    HexagonCPU *cpu = env_archcpu(env);
+    const uint32_t modectl = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                               env->threadId) : 0;
+    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
+    thread_enabled_mask |= 0x1 << env->threadId;
+    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
+}
+
+static uint32_t clear_enable_mask(CPUHexagonState *env)
+{
+    g_assert(bql_locked());
+
+    HexagonCPU *cpu = env_archcpu(env);
+    const uint32_t modectl = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                               env->threadId) : 0;
+    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
+    thread_enabled_mask &= ~(0x1 << env->threadId);
+    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
+    return thread_enabled_mask;
+}
+static void do_start_thread(CPUState *cs, run_on_cpu_data tbd)
+{
+    BQL_LOCK_GUARD();
+
+    CPUHexagonState *env = cpu_env(cs);
+
+    hexagon_cpu_soft_reset(env);
+
+    set_enable_mask(env);
+
+    cs->halted = 0;
+    cs->exception_index = HEX_EVENT_NONE;
+    cpu_resume(cs);
+}
+
+void hexagon_start_threads(CPUHexagonState *current_env, uint32_t mask)
+{
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        CPUHexagonState *env = cpu_env(cs);
+        if (!(mask & (0x1 << env->threadId))) {
+            continue;
+        }
+
+        if (current_env->threadId != env->threadId) {
+            async_safe_run_on_cpu(cs, do_start_thread, RUN_ON_CPU_NULL);
+        }
+    }
+}
+
+/*
+ * When we have all threads stopped, the return
+ * value to the shell is register 2 from thread 0.
+ */
+static uint32_t get_thread0_r2(void)
+{
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        CPUHexagonState *thread = cpu_env(cs);
+        if (thread->threadId == 0) {
+            return thread->gpr[2];
+        }
+    }
+    g_assert_not_reached();
+}
+
+void hexagon_stop_thread(CPUHexagonState *env)
+{
+    BQL_LOCK_GUARD();
+
+    uint32_t thread_enabled_mask = clear_enable_mask(env);
+    CPUState *cs = env_cpu(env);
+    cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+    if (!thread_enabled_mask) {
+        /* All threads are stopped, request shutdown */
+        qemu_system_shutdown_request_with_code(
+            SHUTDOWN_CAUSE_GUEST_SHUTDOWN, get_thread0_r2());
+    }
+}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 6ae07cc68e5..82605f1d4aa 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1476,12 +1476,12 @@ uint32_t HELPER(iassignr)(CPUHexagonState *env, uint32_t src)
 
 void HELPER(start)(CPUHexagonState *env, uint32_t imask)
 {
-    g_assert_not_reached();
+    hexagon_start_threads(env, imask);
 }
 
 void HELPER(stop)(CPUHexagonState *env)
 {
-    g_assert_not_reached();
+    hexagon_stop_thread(env);
 }
 
 void HELPER(wait)(CPUHexagonState *env, target_ulong PC)
-- 
2.34.1

Re: [PATCH v3 03/32] target/hexagon: Implement start/stop helpers, soft reset
Posted by Taylor Simpson 3 weeks, 5 days ago
On Tue, Mar 10, 2026 at 10:08 PM Brian Cain <brian.cain@oss.qualcomm.com>
wrote:

> From: Brian Cain <bcain@quicinc.com>
>
> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
> ---
>  target/hexagon/cpu.h        |   7 +++
>  target/hexagon/cpu_helper.h |   3 ++
>  target/hexagon/cpu.c        |  26 +++++++++-
>  target/hexagon/cpu_helper.c | 100 ++++++++++++++++++++++++++++++++++++
>  target/hexagon/op_helper.c  |   4 +-
>  5 files changed, 136 insertions(+), 4 deletions(-)
>
> diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
> index 0017be9dff7..1a3c9014455 100644
> --- a/target/hexagon/cpu.h
> +++ b/target/hexagon/cpu.h
> @@ -207,6 +207,13 @@ G_NORETURN void
> hexagon_raise_exception_err(CPUHexagonState *env,
>                                              uint32_t exception,
>                                              uintptr_t pc);
>
> +#ifndef CONFIG_USER_ONLY
> +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg);
> +uint32_t hexagon_sreg_read(CPUHexagonState *env, uint32_t reg);
> +void hexagon_gdb_sreg_write(CPUHexagonState *env, uint32_t reg, uint32_t
> val);
> +void hexagon_cpu_soft_reset(CPUHexagonState *env);
> +#endif
> +
>

The first three above belong in a different patch.



> diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
> index 6fbf5fc8e2f..c251790e5a5 100644
> +static void set_enable_mask(CPUHexagonState *env)
> +{
> +    g_assert(bql_locked());
> +
> +    HexagonCPU *cpu = env_archcpu(env);
> +    const uint32_t modectl = cpu->globalregs ?
> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
> +                               env->threadId) : 0;
> +    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
> +    thread_enabled_mask |= 0x1 << env->threadId;
> +    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E,
> thread_enabled_mask);
>

Do you want to set modectl when cpu->globalregs is false?


> +}
> +
> +static uint32_t clear_enable_mask(CPUHexagonState *env)
> +{
> +    g_assert(bql_locked());
> +
> +    HexagonCPU *cpu = env_archcpu(env);
> +    const uint32_t modectl = cpu->globalregs ?
> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
> +                               env->threadId) : 0;
> +    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
> +    thread_enabled_mask &= ~(0x1 << env->threadId);
> +    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E,
> thread_enabled_mask);
>

Ditto


> +    return thread_enabled_mask;
>

It's strange that set_enable_mask returns void and clear_enable_mask
returns the new mask.

Thanks,
Taylor
Re: [PATCH v3 03/32] target/hexagon: Implement start/stop helpers, soft reset
Posted by Brian Cain 3 weeks, 3 days ago
On Thu, Mar 12, 2026 at 4:37 PM Taylor Simpson <ltaylorsimpson@gmail.com>
wrote:

>
>
> On Tue, Mar 10, 2026 at 10:08 PM Brian Cain <brian.cain@oss.qualcomm.com>
> wrote:
>
>> From: Brian Cain <bcain@quicinc.com>
>>
>> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
>> ---
>>  target/hexagon/cpu.h        |   7 +++
>>  target/hexagon/cpu_helper.h |   3 ++
>>  target/hexagon/cpu.c        |  26 +++++++++-
>>  target/hexagon/cpu_helper.c | 100 ++++++++++++++++++++++++++++++++++++
>>  target/hexagon/op_helper.c  |   4 +-
>>  5 files changed, 136 insertions(+), 4 deletions(-)
>>
>> diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
>> index 0017be9dff7..1a3c9014455 100644
>> --- a/target/hexagon/cpu.h
>> +++ b/target/hexagon/cpu.h
>> @@ -207,6 +207,13 @@ G_NORETURN void
>> hexagon_raise_exception_err(CPUHexagonState *env,
>>                                              uint32_t exception,
>>                                              uintptr_t pc);
>>
>> +#ifndef CONFIG_USER_ONLY
>> +uint32_t hexagon_greg_read(CPUHexagonState *env, uint32_t reg);
>> +uint32_t hexagon_sreg_read(CPUHexagonState *env, uint32_t reg);
>> +void hexagon_gdb_sreg_write(CPUHexagonState *env, uint32_t reg, uint32_t
>> val);
>> +void hexagon_cpu_soft_reset(CPUHexagonState *env);
>> +#endif
>> +
>>
>
> The first three above belong in a different patch.
>

I'll fix this.



>
>
>
>> diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
>> index 6fbf5fc8e2f..c251790e5a5 100644
>> +static void set_enable_mask(CPUHexagonState *env)
>> +{
>> +    g_assert(bql_locked());
>> +
>> +    HexagonCPU *cpu = env_archcpu(env);
>> +    const uint32_t modectl = cpu->globalregs ?
>> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
>> +                               env->threadId) : 0;
>> +    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
>> +    thread_enabled_mask |= 0x1 << env->threadId;
>> +    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E,
>> thread_enabled_mask);
>>
>
> Do you want to set modectl when cpu->globalregs is false?
>

IMO it's near meaningless to consider what it means to be a Hexagon DSP
when globalregs is false.  But since there's tests that exercise the "null"
machine configuration without anything connected, it doesn't always make
sense to assert globalregs either.

So ... sure - I can skip modectl or just early return, because ... *shrug*
what does this thing do without that integral piece?  Who knows?


>
>
>> +}
>> +
>> +static uint32_t clear_enable_mask(CPUHexagonState *env)
>> +{
>> +    g_assert(bql_locked());
>> +
>> +    HexagonCPU *cpu = env_archcpu(env);
>> +    const uint32_t modectl = cpu->globalregs ?
>> +        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
>> +                               env->threadId) : 0;
>> +    uint32_t thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
>> +    thread_enabled_mask &= ~(0x1 << env->threadId);
>> +    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E,
>> thread_enabled_mask);
>>
>
> Ditto
>
>
>> +    return thread_enabled_mask;
>>
>
> It's strange that set_enable_mask returns void and clear_enable_mask
> returns the new mask.
>
>

I'll rethink the interface and review the callers.



> Thanks,
> Taylor
>
>