Add the remaining TCG-mode helpers (64-bit register access,
resume-and-run, is_rv64) and the second batch of TCG tests:

  tcg/single-step               – single-step via DCSR.step
  tcg/postexec-impebreak        – postexec with implicit ebreak
  tcg/postexec-exception        – abstract cmd exception path
  tcg/trigger-debug-mode        – mcontrol6 breakpoint fires
  tcg/load-trigger-debug-mode   – load watchpoint enters debug
  tcg/store-trigger-debug-mode  – store watchpoint enters debug
  tcg/itrigger-debug-mode       – instruction trigger enters debug

Signed-off-by: Chao Liu <[email protected]>
---
 tests/qtest/riscv-dm-test.c | 547 ++++++++++++++++++++++++++++++++++++
 1 file changed, 547 insertions(+)

diff --git a/tests/qtest/riscv-dm-test.c b/tests/qtest/riscv-dm-test.c
index 2e7c29ed7c..b2fb47e8f0 100644
--- a/tests/qtest/riscv-dm-test.c
+++ b/tests/qtest/riscv-dm-test.c
@@ -288,6 +288,15 @@ static bool tcg_resume_hart(QTestState *qts)
     return false;
 }
 
+static bool tcg_resume_hart_and_run(QTestState *qts)
+{
+    if (!tcg_resume_hart(qts)) {
+        return false;
+    }
+
+    g_usleep(TCG_BOOT_US);
+    return true;
+}
 
 /*
  * Read a register via Access Register abstract command.
@@ -346,6 +355,62 @@ static bool tcg_abstract_write_reg(QTestState *qts, 
uint32_t regno,
  * Write a 64-bit register via Access Register abstract command (aarsize=3).
  * DATA1 holds high 32 bits, DATA0 holds low 32 bits.
  */
+static bool tcg_abstract_write_reg64(QTestState *qts, uint32_t regno,
+                                     uint64_t val)
+{
+    dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK);
+    dm_write(qts, A_DATA0, (uint32_t)val);
+    dm_write(qts, A_DATA1, (uint32_t)(val >> 32));
+
+    uint32_t cmd = (0u << 24) |     /* cmdtype = Access Register */
+                   (1u << 17) |     /* transfer = 1 */
+                   (1u << 16) |     /* write = 1 */
+                   (3u << 20) |     /* aarsize = 64-bit */
+                   (regno & 0xFFFF);
+    dm_write(qts, A_COMMAND, cmd);
+
+    if (!tcg_wait_for_cmd_done(qts)) {
+        return false;
+    }
+
+    uint32_t acs = dm_read(qts, A_ABSTRACTCS);
+    return !(acs & ABSTRACTCS_CMDERR_MASK);
+}
+
+/*
+ * Read a 64-bit register via Access Register abstract command (aarsize=3).
+ * DATA0 holds low 32 bits, DATA1 holds high 32 bits.
+ */
+static bool tcg_abstract_read_reg64(QTestState *qts, uint32_t regno,
+                                    uint64_t *val)
+{
+    dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK);
+
+    uint32_t cmd = (0u << 24) |     /* cmdtype = Access Register */
+                   (1u << 17) |     /* transfer = 1 */
+                   (3u << 20) |     /* aarsize = 64-bit */
+                   (regno & 0xFFFF);
+    dm_write(qts, A_COMMAND, cmd);
+
+    if (!tcg_wait_for_cmd_done(qts)) {
+        return false;
+    }
+
+    uint32_t acs = dm_read(qts, A_ABSTRACTCS);
+    if (acs & ABSTRACTCS_CMDERR_MASK) {
+        return false;
+    }
+
+    uint32_t lo = dm_read(qts, A_DATA0);
+    uint32_t hi = dm_read(qts, A_DATA1);
+    *val = ((uint64_t)hi << 32) | lo;
+    return true;
+}
+
+static bool is_rv64(void)
+{
+    return strstr(qtest_get_arch(), "64") != NULL;
+}
 
 /*
  * Test: dmactive gate.
@@ -1275,6 +1340,475 @@ static void test_tcg_command_ignored_on_cmderr(void)
     qtest_quit(qts);
 }
 
+/*
+ * Test: Single-step via DM.
+ *
+ * Verifies the Sdext single-step mechanism end-to-end:
+ * 1. Halt the hart via HALTREQ
+ * 2. Read DPC (save initial PC)
+ * 3. Set DCSR.step = 1 via abstract command
+ * 4. Resume the hart → CPU executes one instruction → re-enters debug mode
+ * 5. Verify DCSR.cause = STEP (4)
+ * 6. Verify DPC advanced from the initial value
+ * 7. Step again to confirm repeatability
+ * 8. Clear DCSR.step, resume, verify hart stays running
+ */
+static void test_tcg_single_step(void)
+{
+    QTestState *qts = tcg_dm_init();
+
+    /* Step 1: Halt the hart */
+    g_assert_true(tcg_halt_hart(qts));
+    uint32_t status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ALLHALTED, ==, DMSTATUS_ALLHALTED);
+
+    /* Step 2: Read initial DPC */
+    uint32_t dpc0;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &dpc0));
+    g_test_message("initial DPC = 0x%08x", dpc0);
+
+    /* Step 3: Set DCSR.step = 1 */
+    uint32_t dcsr;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    g_test_message("DCSR before step = 0x%08x", dcsr);
+    dcsr |= DCSR_STEP;
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_DCSR, dcsr));
+
+    /* Verify step bit is set */
+    uint32_t dcsr_check;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr_check));
+    g_assert_cmpuint(dcsr_check & DCSR_STEP, ==, DCSR_STEP);
+
+    /* Step 4: Resume → hart executes one instruction → re-halts */
+    g_assert_true(tcg_resume_hart_and_run(qts));
+    g_assert_true(tcg_wait_for_halt(qts));
+
+    /* Step 5: Verify DCSR.cause = STEP (4) */
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    uint32_t cause = (dcsr & DCSR_CAUSE_MASK) >> DCSR_CAUSE_SHIFT;
+    g_test_message("DCSR after step = 0x%08x, cause = %u", dcsr, cause);
+    g_assert_cmpuint(cause, ==, DCSR_CAUSE_STEP);
+
+    /* Step 6: Verify DPC advanced */
+    uint32_t dpc1;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &dpc1));
+    g_test_message("DPC after step = 0x%08x", dpc1);
+    g_assert_cmpuint(dpc1, !=, dpc0);
+
+    /* Step 7: Step again to confirm repeatability */
+    uint32_t dpc_prev = dpc1;
+    g_assert_true(tcg_resume_hart_and_run(qts));
+    g_assert_true(tcg_wait_for_halt(qts));
+
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    cause = (dcsr & DCSR_CAUSE_MASK) >> DCSR_CAUSE_SHIFT;
+    g_assert_cmpuint(cause, ==, DCSR_CAUSE_STEP);
+
+    uint32_t dpc2;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &dpc2));
+    g_test_message("DPC after 2nd step = 0x%08x", dpc2);
+    g_assert_cmpuint(dpc2, !=, dpc_prev);
+
+    /* Step 8: Clear step bit, resume, verify hart stays running */
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    dcsr &= ~DCSR_STEP;
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_DCSR, dcsr));
+
+    g_assert_true(tcg_resume_hart(qts));
+
+    /* Give CPU time to run freely, then verify it's still running */
+    g_usleep(TCG_BOOT_US);
+    status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, ==, DMSTATUS_ANYRUNNING);
+    g_assert_cmpuint(status & DMSTATUS_ANYHALTED, ==, 0);
+
+    qtest_quit(qts);
+}
+
+/*
+ * Test: POSTEXEC completes via the implicit ebreak after Program Buffer.
+ */
+static void test_tcg_postexec_impebreak(void)
+{
+    QTestState *qts = tcg_dm_init();
+    uint32_t cmd, acs, status;
+
+    g_assert_true(tcg_halt_hart(qts));
+
+    for (int i = 0; i < 8; i++) {
+        dm_write(qts, A_PROGBUF0 + i * 4, 0x00000013);
+    }
+
+    dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK);
+
+    cmd = (0u << 24) | (1u << 18);
+    dm_write(qts, A_COMMAND, cmd);
+
+    g_assert_true(tcg_wait_for_cmd_done(qts));
+
+    acs = dm_read(qts, A_ABSTRACTCS);
+    g_assert_cmpuint(acs & ABSTRACTCS_BUSY, ==, 0);
+    g_assert_cmpuint(acs & ABSTRACTCS_CMDERR_MASK, ==, 0);
+
+    status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ALLHALTED, ==, DMSTATUS_ALLHALTED);
+
+    qtest_quit(qts);
+}
+
+/*
+ * Test: POSTEXEC illegal instruction in Program Buffer.
+ *
+ * The hart is in Debug Mode and executes Program Buffer from DM ROM.
+ * If the first progbuf instruction is illegal, CPU should vector to the
+ * DM ROM exception entry and DM should latch CMDERR=EXCEPTION.
+ */
+static void test_tcg_postexec_exception(void)
+{
+    QTestState *qts = tcg_dm_init();
+    uint32_t cmd, acs, cmderr, status;
+
+    g_assert_true(tcg_halt_hart(qts));
+
+    /* 0x00000000 is an illegal instruction encoding. */
+    dm_write(qts, A_PROGBUF0, 0x00000000);
+    for (int i = 1; i < 8; i++) {
+        dm_write(qts, A_PROGBUF0 + i * 4, 0x00000013);
+    }
+
+    dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK);
+
+    cmd = (0u << 24) | (1u << 18);
+    dm_write(qts, A_COMMAND, cmd);
+
+    g_assert_true(tcg_wait_for_cmd_done(qts));
+
+    acs = dm_read(qts, A_ABSTRACTCS);
+    g_assert_cmpuint(acs & ABSTRACTCS_BUSY, ==, 0);
+    cmderr = (acs & ABSTRACTCS_CMDERR_MASK) >> ABSTRACTCS_CMDERR_SHIFT;
+    g_assert_cmpuint(cmderr, ==, 3);
+
+    status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ALLHALTED, ==, DMSTATUS_ALLHALTED);
+
+    dm_write(qts, A_ABSTRACTCS, ABSTRACTCS_CMDERR_MASK);
+
+    qtest_quit(qts);
+}
+
+/*
+ * Test: Trigger with action=debug_mode halts the hart via DM.
+ *
+ * Configures an mcontrol (type 2) trigger on the current DPC address with
+ * action=1 (enter debug mode).  After resume the CPU hits the trigger and
+ * re-enters debug mode.  Verifies DCSR.cause = TRIGGER and DPC matches.
+ */
+static void test_tcg_trigger_debug_mode(void)
+{
+    QTestState *qts = tcg_dm_init();
+    bool rv64 = is_rv64();
+    const uint64_t code_addr = 0x80400000ULL;
+    const uint32_t nop_insn = 0x00000013;
+    const uint32_t loop_insn = 0x0000006f;
+
+    /* Halt the hart */
+    g_assert_true(tcg_halt_hart(qts));
+
+    /*
+     * Use a fixed code location so the test does not depend on where the CPU
+     * happened to be halted during boot.
+     */
+    qtest_writel(qts, code_addr, nop_insn);
+    qtest_writel(qts, code_addr + 4, loop_insn);
+
+    uint64_t dpc = code_addr;
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_DPC, dpc));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_DPC, (uint32_t)dpc));
+    }
+    g_test_message("DPC before trigger = 0x%lx", (unsigned long)dpc);
+
+    /* Select trigger 1 to verify non-zero trigger index handling */
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_TSELECT, 1));
+
+    /*
+     * Configure mcontrol type 2 trigger:
+     * - type=2, action=1 (DBG_ACTION_DBG_MODE)
+     * - match on EXEC in M/S/U modes
+     * - exact address match (match=0)
+     *
+     * On RV64 the type field is at bits[63:60] and tdata2 is 64-bit,
+     * so both require 64-bit abstract commands to avoid sign-extension.
+     */
+    uint64_t tdata1_lo = TDATA1_TYPE2_ACTION_DBG |
+                         TDATA1_TYPE2_M | TDATA1_TYPE2_S | TDATA1_TYPE2_U |
+                         TDATA1_TYPE2_EXEC;
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA2, dpc));
+        uint64_t tdata1_64 = (2ULL << 60) | tdata1_lo;
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA1, tdata1_64));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA2, 
(uint32_t)dpc));
+        uint32_t tdata1 = TDATA1_TYPE2_TYPE | (uint32_t)tdata1_lo;
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA1, tdata1));
+    }
+
+    /* Verify trigger configuration was accepted (low 32 bits) */
+    uint32_t td1_rb;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_TDATA1, &td1_rb));
+    g_test_message("tdata1 readback low = 0x%08x", td1_rb);
+    g_assert_cmpuint(td1_rb & TDATA1_TYPE2_EXEC, ==, TDATA1_TYPE2_EXEC);
+
+    /* Resume -> CPU tries to execute at DPC -> trigger fires -> debug mode */
+    g_assert_true(tcg_resume_hart_and_run(qts));
+    g_assert_true(tcg_wait_for_halt(qts));
+
+    /* Verify DCSR.cause = TRIGGER (2) */
+    uint32_t dcsr;
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    uint32_t cause = (dcsr & DCSR_CAUSE_MASK) >> DCSR_CAUSE_SHIFT;
+    g_test_message("DCSR after trigger = 0x%08x, cause = %u", dcsr, cause);
+    g_assert_cmpuint(cause, ==, DCSR_CAUSE_TRIGGER);
+
+    /* Verify DPC is the trigger address */
+    uint64_t dpc_after;
+    if (rv64) {
+        g_assert_true(tcg_abstract_read_reg64(qts, REGNO_DPC, &dpc_after));
+    } else {
+        uint32_t tmp;
+        g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &tmp));
+        dpc_after = tmp;
+    }
+    g_test_message("DPC after trigger = 0x%lx", (unsigned long)dpc_after);
+    g_assert_cmpuint(dpc_after, ==, dpc);
+
+    /* Disable trigger: write tdata1 with type=2 but no EXEC/LOAD/STORE bits */
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_TSELECT, 1));
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA1,
+                                                2ULL << 60));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA1,
+                                              TDATA1_TYPE2_TYPE));
+    }
+
+    /*
+     * Advance DPC past the trigger address so resume doesn't re-trigger.
+     * The loop instruction keeps the hart running at a stable address.
+     */
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_DPC, dpc + 4));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_DPC,
+                                              (uint32_t)(dpc + 4)));
+    }
+
+    /* Resume and verify hart stays running */
+    g_assert_true(tcg_resume_hart(qts));
+    g_usleep(TCG_BOOT_US);
+    uint32_t status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, ==, DMSTATUS_ANYRUNNING);
+    g_assert_cmpuint(status & DMSTATUS_ANYHALTED, ==, 0);
+
+    qtest_quit(qts);
+}
+
+
+static void test_tcg_watchpoint_trigger_debug_mode(bool store)
+{
+    QTestState *qts = tcg_dm_init();
+    bool rv64 = is_rv64();
+    const uint64_t code_addr = 0x80400000ULL;
+    const uint64_t data_addr = 0x80401000ULL;
+    const uint32_t load_insn = 0x0002a383;  /* lw x7, 0(x5) */
+    const uint32_t store_insn = 0x0062a023; /* sw x6, 0(x5) */
+    const uint32_t loop_insn = 0x0000006f;  /* j . */
+    const uint32_t data_init = 0x11223344;
+    const uint32_t store_val = 0x55667788;
+    const uint32_t trigger_idx = store ? 3 : 2;
+    uint32_t dcsr, cause, status;
+    uint64_t tdata1_lo = TDATA1_TYPE2_ACTION_DBG |
+                         TDATA1_TYPE2_M | TDATA1_TYPE2_S | TDATA1_TYPE2_U |
+                         (store ? TDATA1_TYPE2_STORE : TDATA1_TYPE2_LOAD);
+
+    g_assert_true(tcg_halt_hart(qts));
+
+    qtest_writel(qts, code_addr, store ? store_insn : load_insn);
+    qtest_writel(qts, code_addr + 4, loop_insn);
+    qtest_writel(qts, data_addr, data_init);
+
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_GPR(5), data_addr));
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_GPR(6), store_val));
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_GPR(7), 0));
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_DPC, code_addr));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_GPR(5),
+                                             (uint32_t)data_addr));
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_GPR(6), store_val));
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_GPR(7), 0));
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_DPC,
+                                             (uint32_t)code_addr));
+    }
+
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_TSELECT, trigger_idx));
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA2, data_addr));
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA1,
+                                               (2ULL << 60) | tdata1_lo));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA2,
+                                             (uint32_t)data_addr));
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA1,
+                                             TDATA1_TYPE2_TYPE |
+                                             (uint32_t)tdata1_lo));
+    }
+
+    g_assert_true(tcg_resume_hart_and_run(qts));
+    g_assert_true(tcg_wait_for_halt(qts));
+
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    cause = (dcsr & DCSR_CAUSE_MASK) >> DCSR_CAUSE_SHIFT;
+    g_assert_cmpuint(cause, ==, DCSR_CAUSE_TRIGGER);
+
+    if (rv64) {
+        uint64_t dpc;
+        g_assert_true(tcg_abstract_read_reg64(qts, REGNO_DPC, &dpc));
+        g_assert_cmpuint(dpc, ==, code_addr);
+    } else {
+        uint32_t dpc;
+        g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &dpc));
+        g_assert_cmpuint(dpc, ==, (uint32_t)code_addr);
+    }
+
+    if (store) {
+        g_assert_cmpuint(qtest_readl(qts, data_addr), ==, data_init);
+    }
+
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_TSELECT, trigger_idx));
+    if (rv64) {
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA1, 2ULL << 60));
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_DPC, code_addr));
+    } else {
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA1,
+                                             TDATA1_TYPE2_TYPE));
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_DPC,
+                                             (uint32_t)code_addr));
+    }
+
+    g_assert_true(tcg_resume_hart(qts));
+    g_usleep(TCG_BOOT_US);
+
+    if (store) {
+        g_assert_cmpuint(qtest_readl(qts, data_addr), ==, store_val);
+        status = dm_read(qts, A_DMSTATUS);
+        g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, ==, 
DMSTATUS_ANYRUNNING);
+    } else {
+        uint64_t load_val;
+
+        g_assert_true(tcg_halt_hart(qts));
+        if (rv64) {
+            g_assert_true(tcg_abstract_read_reg64(qts, REGNO_GPR(7),
+                                                  &load_val));
+        } else {
+            uint32_t tmp;
+
+            g_assert_true(tcg_abstract_read_reg(qts, REGNO_GPR(7), &tmp));
+            load_val = tmp;
+        }
+        g_assert_cmpuint(load_val, ==, data_init);
+        status = dm_read(qts, A_DMSTATUS);
+        g_assert_cmpuint(status & DMSTATUS_ALLHALTED, ==, DMSTATUS_ALLHALTED);
+    }
+
+    qtest_quit(qts);
+}
+
+static void test_tcg_load_trigger_debug_mode(void)
+{
+    test_tcg_watchpoint_trigger_debug_mode(false);
+}
+
+static void test_tcg_store_trigger_debug_mode(void)
+{
+    test_tcg_watchpoint_trigger_debug_mode(true);
+}
+
+/*
+ * Test: itrigger with action=debug_mode enters Debug Mode.
+ *
+ * Configures an itrigger with count=1 so the hart executes one instruction,
+ * re-enters debug mode with cause=TRIGGER, and clears the count.
+ */
+static void test_tcg_itrigger_debug_mode(void)
+{
+    QTestState *qts = tcg_dm_init();
+    bool rv64 = is_rv64();
+    uint64_t dpc;
+    uint64_t dpc_after;
+    uint64_t tdata1;
+    uint32_t dcsr;
+    uint32_t status;
+    uint32_t cause;
+
+    g_assert_true(tcg_halt_hart(qts));
+    g_assert_true(tcg_abstract_write_reg(qts, REGNO_TSELECT, 1));
+
+    if (rv64) {
+        g_assert_true(tcg_abstract_read_reg64(qts, REGNO_DPC, &dpc));
+        tdata1 = (3ULL << 60) |
+                 TDATA1_ITRIGGER_ACTION_DBG |
+                 TDATA1_ITRIGGER_U |
+                 TDATA1_ITRIGGER_S |
+                 TDATA1_ITRIGGER_M |
+                 TDATA1_ITRIGGER_COUNT(1);
+        g_assert_true(tcg_abstract_write_reg64(qts, REGNO_TDATA1, tdata1));
+    } else {
+        uint32_t tmp;
+
+        g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &tmp));
+        dpc = tmp;
+        tdata1 = TDATA1_ITRIGGER_TYPE |
+                 TDATA1_ITRIGGER_ACTION_DBG |
+                 TDATA1_ITRIGGER_U |
+                 TDATA1_ITRIGGER_S |
+                 TDATA1_ITRIGGER_M |
+                 TDATA1_ITRIGGER_COUNT(1);
+        g_assert_true(tcg_abstract_write_reg(qts, REGNO_TDATA1,
+                                             (uint32_t)tdata1));
+    }
+
+    g_assert_true(tcg_resume_hart(qts));
+    g_assert_true(tcg_wait_for_halt(qts));
+
+    g_assert_true(tcg_abstract_read_reg(qts, REGNO_DCSR, &dcsr));
+    cause = (dcsr & DCSR_CAUSE_MASK) >> DCSR_CAUSE_SHIFT;
+    g_assert_cmpuint(cause, ==, DCSR_CAUSE_TRIGGER);
+
+    if (rv64) {
+        g_assert_true(tcg_abstract_read_reg64(qts, REGNO_DPC, &dpc_after));
+        g_assert_true(tcg_abstract_read_reg64(qts, REGNO_TDATA1, &tdata1));
+    } else {
+        uint32_t tmp;
+
+        g_assert_true(tcg_abstract_read_reg(qts, REGNO_DPC, &tmp));
+        dpc_after = tmp;
+        g_assert_true(tcg_abstract_read_reg(qts, REGNO_TDATA1, &tmp));
+        tdata1 = tmp;
+    }
+
+    g_assert_cmpuint(dpc_after, !=, dpc);
+    g_assert_cmpuint(tdata1 & TDATA1_ITRIGGER_COUNT_MASK, ==, 0);
+
+    g_assert_true(tcg_resume_hart(qts));
+    g_usleep(TCG_BOOT_US);
+    status = dm_read(qts, A_DMSTATUS);
+    g_assert_cmpuint(status & DMSTATUS_ANYRUNNING, ==, DMSTATUS_ANYRUNNING);
+    g_assert_cmpuint(status & DMSTATUS_ANYHALTED, ==, 0);
+
+    qtest_quit(qts);
+}
 
 /*
  * Test: Halt and resume cycle via ROM simulation.
@@ -1362,6 +1896,19 @@ int main(int argc, char *argv[])
     qtest_add_func("/riscv-dm/rv32-gpr64-notsup", test_rv32_gpr64_notsup);
     qtest_add_func("/riscv-dm/tcg/command-ignored-on-cmderr",
                    test_tcg_command_ignored_on_cmderr);
+    qtest_add_func("/riscv-dm/tcg/single-step", test_tcg_single_step);
+    qtest_add_func("/riscv-dm/tcg/postexec-impebreak",
+                   test_tcg_postexec_impebreak);
+    qtest_add_func("/riscv-dm/tcg/postexec-exception",
+                   test_tcg_postexec_exception);
+    qtest_add_func("/riscv-dm/tcg/trigger-debug-mode",
+                   test_tcg_trigger_debug_mode);
+    qtest_add_func("/riscv-dm/tcg/load-trigger-debug-mode",
+                   test_tcg_load_trigger_debug_mode);
+    qtest_add_func("/riscv-dm/tcg/store-trigger-debug-mode",
+                   test_tcg_store_trigger_debug_mode);
+    qtest_add_func("/riscv-dm/tcg/itrigger-debug-mode",
+                   test_tcg_itrigger_debug_mode);
 
     return g_test_run();
 }
-- 
2.53.0


Reply via email to