On 1/14/2026 1:46 AM, Nicholas Piggin wrote:
When a trigger type is "disabled", tdata2 and tdata3 must accept values
that are valid for some supported trigger type. Additionally, writing 0
to tdata1 must result in the type becoming "disabled". This is important
for the prescribed sequences for updating triggers.

Implement write tdata=0 -> disabled behaviour and permissive accepting
of tdata2/3 values in disabled state. This implementation could be
improved by checking tdata2/3 values against supported trigger types,
but it is good enough to be usable by software.

 From the RISC-V Debug Specification for tdata1:

   Writing 0 to this register must result in a trigger that is disabled.
   If this trigger supports multiple types, then the hardware should
   disable it by changing type to 15.

and, when type=15:

   This trigger is disabled. In this state, tdata2 and tdata3 can be
   written with any value that is supported for any of the types this
   trigger implements.

Signed-off-by: Nicholas Piggin <[email protected]>
---

Reviewed-by: Daniel Henrique Barboza <[email protected]>

  target/riscv/debug.c | 30 +++++++++++++++++++++++++++++-
  1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/target/riscv/debug.c b/target/riscv/debug.c
index 2190c25f23..c92bd9860e 100644
--- a/target/riscv/debug.c
+++ b/target/riscv/debug.c
@@ -827,6 +827,28 @@ static void itrigger_reg_write(CPURISCVState *env, 
target_ulong index,
      }
  }
+static void anytype_reg_write(CPURISCVState *env, target_ulong index,
+                              int tdata_index, target_ulong val)
+{
+    /*
+     * This should check the value is valid for at least one of the supported
+     * trigger types.
+     */
+    switch (tdata_index) {
+    case TDATA1:
+        env->tdata1[env->trigger_cur] = val;
+        break;
+    case TDATA2:
+        env->tdata2[env->trigger_cur] = val;
+        break;
+    case TDATA3:
+        env->tdata3[env->trigger_cur] = val;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
  static int itrigger_get_adjust_count(CPURISCVState *env)
  {
      int count = itrigger_get_count(env, env->trigger_cur), executed;
@@ -883,6 +905,10 @@ void tdata_csr_write(CPURISCVState *env, int tdata_index, 
target_ulong val)
      }
if (tdata_index == TDATA1) {
+        if (val == 0) {
+            /* special case, writing 0 results in disabled trigger */
+            val = build_tdata1(env, TRIGGER_TYPE_UNAVAIL, 0, 0);
+        }
          trigger_type = extract_trigger_type(env, val);
      }
@@ -897,6 +923,9 @@ void tdata_csr_write(CPURISCVState *env, int tdata_index, target_ulong val)
          itrigger_reg_write(env, env->trigger_cur, tdata_index, val);
          check_itrigger = true;
          break;
+    case TRIGGER_TYPE_UNAVAIL:
+        anytype_reg_write(env, env->trigger_cur, tdata_index, val);
+        break;
      case TRIGGER_TYPE_INT:
      case TRIGGER_TYPE_EXCP:
      case TRIGGER_TYPE_EXT_SRC:
@@ -904,7 +933,6 @@ void tdata_csr_write(CPURISCVState *env, int tdata_index, 
target_ulong val)
                        trigger_type);
          break;
      case TRIGGER_TYPE_NO_EXIST:
-    case TRIGGER_TYPE_UNAVAIL:
          qemu_log_mask(LOG_GUEST_ERROR, "trigger type: %d does not exit\n",
                        trigger_type);
          break;


Reply via email to