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 <chao.liu.zevorn@gmail.com>
---
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
© 2016 - 2026 Red Hat, Inc.