aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/target/riscv/debug_defines.h193
-rw-r--r--src/target/riscv/opcodes.h3
-rw-r--r--src/target/riscv/program.h4
-rw-r--r--src/target/riscv/riscv-013.c479
-rw-r--r--src/target/riscv/riscv.c26
-rw-r--r--src/target/riscv/riscv.h3
6 files changed, 585 insertions, 123 deletions
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
index 04500e5..17b1444 100644
--- a/src/target/riscv/debug_defines.h
+++ b/src/target/riscv/debug_defines.h
@@ -343,6 +343,16 @@
#define CSR_MCONTROL_MASKMAX_LENGTH 6
#define CSR_MCONTROL_MASKMAX (0x3fULL << CSR_MCONTROL_MASKMAX_OFFSET)
/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched. If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_MCONTROL_HIT_OFFSET 20
+#define CSR_MCONTROL_HIT_LENGTH 1
+#define CSR_MCONTROL_HIT (0x1ULL << CSR_MCONTROL_HIT_OFFSET)
+/*
* 0: Perform a match on the virtual address.
*
* 1: Perform a match on the data value loaded/stored, or the
@@ -368,7 +378,7 @@
* which case the debugger has a little more control.
*
* Data load triggers with \Ftiming of 0 will result in the same load
-* happening again when the debugger lets the core run. For data load
+* happening again when the debugger lets the hart run. For data load
* triggers, debuggers must first attempt to set the breakpoint with
* \Ftiming of 1.
*
@@ -479,6 +489,16 @@
#define CSR_ICOUNT_DMODE_LENGTH 1
#define CSR_ICOUNT_DMODE (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
/*
+* If this optional bit is implemented, the hardware sets it when this
+* trigger matches. The trigger's user can set or clear it at any
+* time. The trigger's user can use this bit to determine which
+* trigger(s) matched. If the bit is not implemented, it is always 0
+* and writing it has no effect.
+ */
+#define CSR_ICOUNT_HIT_OFFSET 24
+#define CSR_ICOUNT_HIT_LENGTH 1
+#define CSR_ICOUNT_HIT (0x1ULL << CSR_ICOUNT_HIT_OFFSET)
+/*
* When count is decremented to 0, the trigger fires. Instead of
* changing \Fcount from 1 to 0, it is also acceptable for hardware to
* clear \Fm, \Fs, and \Fu. This allows \Fcount to be hard-wired
@@ -674,7 +694,7 @@
*/
#define DMI_DMCONTROL_HALTREQ_OFFSET 31
#define DMI_DMCONTROL_HALTREQ_LENGTH 1
-#define DMI_DMCONTROL_HALTREQ (0x1ULL << DMI_DMCONTROL_HALTREQ_OFFSET)
+#define DMI_DMCONTROL_HALTREQ (0x1U << DMI_DMCONTROL_HALTREQ_OFFSET)
/*
* Writes the resume request bit for all currently selected harts.
* When set to 1, each selected hart will resume if it is currently
@@ -687,7 +707,7 @@
*/
#define DMI_DMCONTROL_RESUMEREQ_OFFSET 30
#define DMI_DMCONTROL_RESUMEREQ_LENGTH 1
-#define DMI_DMCONTROL_RESUMEREQ (0x1ULL << DMI_DMCONTROL_RESUMEREQ_OFFSET)
+#define DMI_DMCONTROL_RESUMEREQ (0x1U << DMI_DMCONTROL_RESUMEREQ_OFFSET)
/*
* This optional field writes the reset bit for all the currently
* selected harts. To perform a reset the debugger writes 1, and then
@@ -701,7 +721,7 @@
*/
#define DMI_DMCONTROL_HARTRESET_OFFSET 29
#define DMI_DMCONTROL_HARTRESET_LENGTH 1
-#define DMI_DMCONTROL_HARTRESET (0x1ULL << DMI_DMCONTROL_HARTRESET_OFFSET)
+#define DMI_DMCONTROL_HARTRESET (0x1U << DMI_DMCONTROL_HARTRESET_OFFSET)
/*
* Writing 1 to this bit clears the {\tt havereset} bits for
* any selected harts.
@@ -710,7 +730,7 @@
*/
#define DMI_DMCONTROL_ACKHAVERESET_OFFSET 28
#define DMI_DMCONTROL_ACKHAVERESET_LENGTH 1
-#define DMI_DMCONTROL_ACKHAVERESET (0x1ULL << DMI_DMCONTROL_ACKHAVERESET_OFFSET)
+#define DMI_DMCONTROL_ACKHAVERESET (0x1U << DMI_DMCONTROL_ACKHAVERESET_OFFSET)
/*
* Selects the definition of currently selected harts.
*
@@ -720,20 +740,27 @@
* plus those selected by the hart array mask register.
*
* An implementation which does not implement the hart array mask register
-* should tie this field to 0. A debugger which wishes to use the hart array
+* must tie this field to 0. A debugger which wishes to use the hart array
* mask register feature should set this bit and read back to see if the functionality
* is supported.
*/
#define DMI_DMCONTROL_HASEL_OFFSET 26
#define DMI_DMCONTROL_HASEL_LENGTH 1
-#define DMI_DMCONTROL_HASEL (0x1ULL << DMI_DMCONTROL_HASEL_OFFSET)
+#define DMI_DMCONTROL_HASEL (0x1U << DMI_DMCONTROL_HASEL_OFFSET)
/*
-* The DM-specific index of the hart to select. This hart is always part of the
-* currently selected harts.
+* The low 10 bits of \Fhartsel: the DM-specific index of the hart to
+* select. This hart is always part of the currently selected harts.
*/
-#define DMI_DMCONTROL_HARTSEL_OFFSET 16
-#define DMI_DMCONTROL_HARTSEL_LENGTH HARTSELLEN
-#define DMI_DMCONTROL_HARTSEL (((1L<<HARTSELLEN)-1) << DMI_DMCONTROL_HARTSEL_OFFSET)
+#define DMI_DMCONTROL_HARTSELLO_OFFSET 16
+#define DMI_DMCONTROL_HARTSELLO_LENGTH 10
+#define DMI_DMCONTROL_HARTSELLO (0x3ffU << DMI_DMCONTROL_HARTSELLO_OFFSET)
+/*
+* The high 10 bits of \Fhartsel: the DM-specific index of the hart to
+* select. This hart is always part of the currently selected harts.
+ */
+#define DMI_DMCONTROL_HARTSELHI_OFFSET 6
+#define DMI_DMCONTROL_HARTSELHI_LENGTH 10
+#define DMI_DMCONTROL_HARTSELHI (0x3ffU << DMI_DMCONTROL_HARTSELHI_OFFSET)
/*
* This bit controls the reset signal from the DM to the rest of the
* system. The signal should reset every part of the system, including
@@ -745,7 +772,7 @@
*/
#define DMI_DMCONTROL_NDMRESET_OFFSET 1
#define DMI_DMCONTROL_NDMRESET_LENGTH 1
-#define DMI_DMCONTROL_NDMRESET (0x1ULL << DMI_DMCONTROL_NDMRESET_OFFSET)
+#define DMI_DMCONTROL_NDMRESET (0x1U << DMI_DMCONTROL_NDMRESET_OFFSET)
/*
* This bit serves as a reset signal for the Debug Module itself.
*
@@ -768,7 +795,7 @@
*/
#define DMI_DMCONTROL_DMACTIVE_OFFSET 0
#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
-#define DMI_DMCONTROL_DMACTIVE (0x1ULL << DMI_DMCONTROL_DMACTIVE_OFFSET)
+#define DMI_DMCONTROL_DMACTIVE (0x1U << DMI_DMCONTROL_DMACTIVE_OFFSET)
#define DMI_HARTINFO 0x12
/*
* Number of {\tt dscratch} registers available for the debugger
@@ -813,107 +840,15 @@
#define DMI_HARTINFO_DATAADDR_OFFSET 0
#define DMI_HARTINFO_DATAADDR_LENGTH 12
#define DMI_HARTINFO_DATAADDR (0xfffU << DMI_HARTINFO_DATAADDR_OFFSET)
-#define DMI_HALTSUM 0x13
-#define DMI_HALTSUM_HALT1023_992_OFFSET 31
-#define DMI_HALTSUM_HALT1023_992_LENGTH 1
-#define DMI_HALTSUM_HALT1023_992 (0x1U << DMI_HALTSUM_HALT1023_992_OFFSET)
-#define DMI_HALTSUM_HALT991_960_OFFSET 30
-#define DMI_HALTSUM_HALT991_960_LENGTH 1
-#define DMI_HALTSUM_HALT991_960 (0x1U << DMI_HALTSUM_HALT991_960_OFFSET)
-#define DMI_HALTSUM_HALT959_928_OFFSET 29
-#define DMI_HALTSUM_HALT959_928_LENGTH 1
-#define DMI_HALTSUM_HALT959_928 (0x1U << DMI_HALTSUM_HALT959_928_OFFSET)
-#define DMI_HALTSUM_HALT927_896_OFFSET 28
-#define DMI_HALTSUM_HALT927_896_LENGTH 1
-#define DMI_HALTSUM_HALT927_896 (0x1U << DMI_HALTSUM_HALT927_896_OFFSET)
-#define DMI_HALTSUM_HALT895_864_OFFSET 27
-#define DMI_HALTSUM_HALT895_864_LENGTH 1
-#define DMI_HALTSUM_HALT895_864 (0x1U << DMI_HALTSUM_HALT895_864_OFFSET)
-#define DMI_HALTSUM_HALT863_832_OFFSET 26
-#define DMI_HALTSUM_HALT863_832_LENGTH 1
-#define DMI_HALTSUM_HALT863_832 (0x1U << DMI_HALTSUM_HALT863_832_OFFSET)
-#define DMI_HALTSUM_HALT831_800_OFFSET 25
-#define DMI_HALTSUM_HALT831_800_LENGTH 1
-#define DMI_HALTSUM_HALT831_800 (0x1U << DMI_HALTSUM_HALT831_800_OFFSET)
-#define DMI_HALTSUM_HALT799_768_OFFSET 24
-#define DMI_HALTSUM_HALT799_768_LENGTH 1
-#define DMI_HALTSUM_HALT799_768 (0x1U << DMI_HALTSUM_HALT799_768_OFFSET)
-#define DMI_HALTSUM_HALT767_736_OFFSET 23
-#define DMI_HALTSUM_HALT767_736_LENGTH 1
-#define DMI_HALTSUM_HALT767_736 (0x1U << DMI_HALTSUM_HALT767_736_OFFSET)
-#define DMI_HALTSUM_HALT735_704_OFFSET 22
-#define DMI_HALTSUM_HALT735_704_LENGTH 1
-#define DMI_HALTSUM_HALT735_704 (0x1U << DMI_HALTSUM_HALT735_704_OFFSET)
-#define DMI_HALTSUM_HALT703_672_OFFSET 21
-#define DMI_HALTSUM_HALT703_672_LENGTH 1
-#define DMI_HALTSUM_HALT703_672 (0x1U << DMI_HALTSUM_HALT703_672_OFFSET)
-#define DMI_HALTSUM_HALT671_640_OFFSET 20
-#define DMI_HALTSUM_HALT671_640_LENGTH 1
-#define DMI_HALTSUM_HALT671_640 (0x1U << DMI_HALTSUM_HALT671_640_OFFSET)
-#define DMI_HALTSUM_HALT639_608_OFFSET 19
-#define DMI_HALTSUM_HALT639_608_LENGTH 1
-#define DMI_HALTSUM_HALT639_608 (0x1U << DMI_HALTSUM_HALT639_608_OFFSET)
-#define DMI_HALTSUM_HALT607_576_OFFSET 18
-#define DMI_HALTSUM_HALT607_576_LENGTH 1
-#define DMI_HALTSUM_HALT607_576 (0x1U << DMI_HALTSUM_HALT607_576_OFFSET)
-#define DMI_HALTSUM_HALT575_544_OFFSET 17
-#define DMI_HALTSUM_HALT575_544_LENGTH 1
-#define DMI_HALTSUM_HALT575_544 (0x1U << DMI_HALTSUM_HALT575_544_OFFSET)
-#define DMI_HALTSUM_HALT543_512_OFFSET 16
-#define DMI_HALTSUM_HALT543_512_LENGTH 1
-#define DMI_HALTSUM_HALT543_512 (0x1U << DMI_HALTSUM_HALT543_512_OFFSET)
-#define DMI_HALTSUM_HALT511_480_OFFSET 15
-#define DMI_HALTSUM_HALT511_480_LENGTH 1
-#define DMI_HALTSUM_HALT511_480 (0x1U << DMI_HALTSUM_HALT511_480_OFFSET)
-#define DMI_HALTSUM_HALT479_448_OFFSET 14
-#define DMI_HALTSUM_HALT479_448_LENGTH 1
-#define DMI_HALTSUM_HALT479_448 (0x1U << DMI_HALTSUM_HALT479_448_OFFSET)
-#define DMI_HALTSUM_HALT447_416_OFFSET 13
-#define DMI_HALTSUM_HALT447_416_LENGTH 1
-#define DMI_HALTSUM_HALT447_416 (0x1U << DMI_HALTSUM_HALT447_416_OFFSET)
-#define DMI_HALTSUM_HALT415_384_OFFSET 12
-#define DMI_HALTSUM_HALT415_384_LENGTH 1
-#define DMI_HALTSUM_HALT415_384 (0x1U << DMI_HALTSUM_HALT415_384_OFFSET)
-#define DMI_HALTSUM_HALT383_352_OFFSET 11
-#define DMI_HALTSUM_HALT383_352_LENGTH 1
-#define DMI_HALTSUM_HALT383_352 (0x1U << DMI_HALTSUM_HALT383_352_OFFSET)
-#define DMI_HALTSUM_HALT351_320_OFFSET 10
-#define DMI_HALTSUM_HALT351_320_LENGTH 1
-#define DMI_HALTSUM_HALT351_320 (0x1U << DMI_HALTSUM_HALT351_320_OFFSET)
-#define DMI_HALTSUM_HALT319_288_OFFSET 9
-#define DMI_HALTSUM_HALT319_288_LENGTH 1
-#define DMI_HALTSUM_HALT319_288 (0x1U << DMI_HALTSUM_HALT319_288_OFFSET)
-#define DMI_HALTSUM_HALT287_256_OFFSET 8
-#define DMI_HALTSUM_HALT287_256_LENGTH 1
-#define DMI_HALTSUM_HALT287_256 (0x1U << DMI_HALTSUM_HALT287_256_OFFSET)
-#define DMI_HALTSUM_HALT255_224_OFFSET 7
-#define DMI_HALTSUM_HALT255_224_LENGTH 1
-#define DMI_HALTSUM_HALT255_224 (0x1U << DMI_HALTSUM_HALT255_224_OFFSET)
-#define DMI_HALTSUM_HALT223_192_OFFSET 6
-#define DMI_HALTSUM_HALT223_192_LENGTH 1
-#define DMI_HALTSUM_HALT223_192 (0x1U << DMI_HALTSUM_HALT223_192_OFFSET)
-#define DMI_HALTSUM_HALT191_160_OFFSET 5
-#define DMI_HALTSUM_HALT191_160_LENGTH 1
-#define DMI_HALTSUM_HALT191_160 (0x1U << DMI_HALTSUM_HALT191_160_OFFSET)
-#define DMI_HALTSUM_HALT159_128_OFFSET 4
-#define DMI_HALTSUM_HALT159_128_LENGTH 1
-#define DMI_HALTSUM_HALT159_128 (0x1U << DMI_HALTSUM_HALT159_128_OFFSET)
-#define DMI_HALTSUM_HALT127_96_OFFSET 3
-#define DMI_HALTSUM_HALT127_96_LENGTH 1
-#define DMI_HALTSUM_HALT127_96 (0x1U << DMI_HALTSUM_HALT127_96_OFFSET)
-#define DMI_HALTSUM_HALT95_64_OFFSET 2
-#define DMI_HALTSUM_HALT95_64_LENGTH 1
-#define DMI_HALTSUM_HALT95_64 (0x1U << DMI_HALTSUM_HALT95_64_OFFSET)
-#define DMI_HALTSUM_HALT63_32_OFFSET 1
-#define DMI_HALTSUM_HALT63_32_LENGTH 1
-#define DMI_HALTSUM_HALT63_32 (0x1U << DMI_HALTSUM_HALT63_32_OFFSET)
-#define DMI_HALTSUM_HALT31_0_OFFSET 0
-#define DMI_HALTSUM_HALT31_0_LENGTH 1
-#define DMI_HALTSUM_HALT31_0 (0x1U << DMI_HALTSUM_HALT31_0_OFFSET)
#define DMI_HAWINDOWSEL 0x14
+/*
+* The high bits of this field may be tied to 0, depending on how large
+* the array mask register is. Eg. on a system with 48 harts only bit 0
+* of this field may actually be writable.
+ */
#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0
-#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH 5
-#define DMI_HAWINDOWSEL_HAWINDOWSEL (0x1fU << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET)
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH 15
+#define DMI_HAWINDOWSEL_HAWINDOWSEL (0x7fffU << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET)
#define DMI_HAWINDOW 0x15
#define DMI_HAWINDOW_MASKDATA_OFFSET 0
#define DMI_HAWINDOW_MASKDATA_LENGTH 32
@@ -945,15 +880,14 @@
* \Rabstractcs, \Rabstractauto was written, or when one
* of the {\tt data} or {\tt progbuf} registers was read or written.
*
-* 2 (not supported): The requested command is not supported. A
-* command that is not supported while the hart is running may be
-* supported when it is halted.
+* 2 (not supported): The requested command is not supported,
+* regardless of whether the hart is running or not.
*
* 3 (exception): An exception occurred while executing the command
* (eg. while executing the Program Buffer).
*
-* 4 (halt/resume): An abstract command couldn't execute because the
-* hart wasn't in the expected state (running/halted).
+* 4 (halt/resume): The abstract command couldn't execute because the
+* hart wasn't in the required state (running/halted).
*
* 7 (other): The command failed for another reason.
*/
@@ -1004,6 +938,10 @@
#define DMI_DEVTREEADDR1 0x1a
#define DMI_DEVTREEADDR2 0x1b
#define DMI_DEVTREEADDR3 0x1c
+#define DMI_NEXTDM 0x1d
+#define DMI_NEXTDM_ADDR_OFFSET 0
+#define DMI_NEXTDM_ADDR_LENGTH 32
+#define DMI_NEXTDM_ADDR (0xffffffffU << DMI_NEXTDM_ADDR_OFFSET)
#define DMI_DATA0 0x04
#define DMI_DATA0_DATA_OFFSET 0
#define DMI_DATA0_DATA_LENGTH 32
@@ -1018,6 +956,22 @@
#define DMI_AUTHDATA_DATA_OFFSET 0
#define DMI_AUTHDATA_DATA_LENGTH 32
#define DMI_AUTHDATA_DATA (0xffffffffU << DMI_AUTHDATA_DATA_OFFSET)
+#define DMI_HALTSUM0 0x40
+#define DMI_HALTSUM0_HALTSUM0_OFFSET 0
+#define DMI_HALTSUM0_HALTSUM0_LENGTH 32
+#define DMI_HALTSUM0_HALTSUM0 (0xffffffffU << DMI_HALTSUM0_HALTSUM0_OFFSET)
+#define DMI_HALTSUM1 0x13
+#define DMI_HALTSUM1_HALTSUM1_OFFSET 0
+#define DMI_HALTSUM1_HALTSUM1_LENGTH 32
+#define DMI_HALTSUM1_HALTSUM1 (0xffffffffU << DMI_HALTSUM1_HALTSUM1_OFFSET)
+#define DMI_HALTSUM2 0x34
+#define DMI_HALTSUM2_HALTSUM2_OFFSET 0
+#define DMI_HALTSUM2_HALTSUM2_LENGTH 32
+#define DMI_HALTSUM2_HALTSUM2 (0xffffffffU << DMI_HALTSUM2_HALTSUM2_OFFSET)
+#define DMI_HALTSUM3 0x35
+#define DMI_HALTSUM3_HALTSUM3_OFFSET 0
+#define DMI_HALTSUM3_HALTSUM3_LENGTH 32
+#define DMI_HALTSUM3_HALTSUM3 (0xffffffffU << DMI_HALTSUM3_HALTSUM3_OFFSET)
#define DMI_SBADDRESS3 0x37
/*
* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
@@ -1235,6 +1189,9 @@
* If \Fsize specifies a size larger than the register's actual size,
* then the access must fail. If a register is accessible, then reads of \Fsize
* less than or equal to the register's actual size must be supported.
+*
+* This field controls the Argument Width as referenced in
+* Table~\ref{tab:datareg}.
*/
#define AC_ACCESS_REGISTER_SIZE_OFFSET 20
#define AC_ACCESS_REGISTER_SIZE_LENGTH 3
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
index dd51c80..de85aad 100644
--- a/src/target/riscv/opcodes.h
+++ b/src/target/riscv/opcodes.h
@@ -224,6 +224,9 @@ static uint32_t ebreak_c(void)
return MATCH_C_EBREAK;
}
+static uint32_t wfi(void) __attribute__ ((unused));
+static uint32_t wfi(void) { return MATCH_WFI; }
+
static uint32_t fence_i(void) __attribute__ ((unused));
static uint32_t fence_i(void)
{
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
index d641be1..310460c 100644
--- a/src/target/riscv/program.h
+++ b/src/target/riscv/program.h
@@ -52,8 +52,8 @@ int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
* memory. */
int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
-/* Helpers to assembly various instructions. Return 0 on success. These might
- * assembly into a multi-instruction sequence that overwrites some other
+/* Helpers to assemble various instructions. Return 0 on success. These might
+ * assemble into a multi-instruction sequence that overwrites some other
* register, but those will be properly saved and restored. */
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index f3fe76b..29a39f0 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -65,6 +65,7 @@ static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer);
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer);
+static int riscv013_test_compliance(struct target *target);
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -266,7 +267,8 @@ static dm013_info_t *get_dm(struct target *target)
static uint32_t hartsel_mask(const struct target *target)
{
RISCV013_INFO(info);
- return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSEL_OFFSET;
+ /* TODO: Properly handle hartselhi as well*/
+ return ((1L<<info->hartsellen)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
}
static void decode_dmi(char *text, unsigned address, unsigned data)
@@ -280,7 +282,8 @@ static void decode_dmi(char *text, unsigned address, unsigned data)
{ DMI_DMCONTROL, DMI_DMCONTROL_RESUMEREQ, "resumereq" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HARTRESET, "hartreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_HASEL, "hasel" },
- { DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET, "hartsel" },
+ { DMI_DMCONTROL, ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET, "hartsello" },
+ /* TODO: hartsellhi */
{ DMI_DMCONTROL, DMI_DMCONTROL_NDMRESET, "ndmreset" },
{ DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE, "dmactive" },
{ DMI_DMCONTROL, DMI_DMCONTROL_ACKHAVERESET, "ackhavereset" },
@@ -1329,7 +1332,7 @@ static int examine(struct target *target)
dm->was_reset = true;
}
- uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSEL_OFFSET;
+ uint32_t max_hartsel_mask = ((1L<<10)-1) << DMI_DMCONTROL_HARTSELLO_OFFSET;
dmi_write(target, DMI_DMCONTROL, max_hartsel_mask | DMI_DMCONTROL_DMACTIVE);
uint32_t dmcontrol;
if (dmi_read(target, &dmcontrol, DMI_DMCONTROL) != ERROR_OK)
@@ -1545,6 +1548,7 @@ static int init_target(struct command_context *cmd_ctx,
generic_info->authdata_write = &riscv013_authdata_write;
generic_info->dmi_read = &dmi_read;
generic_info->dmi_write = &dmi_write;
+ generic_info->test_compliance = &riscv013_test_compliance;
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
return ERROR_FAIL;
@@ -3004,3 +3008,472 @@ void riscv013_clear_abstract_error(struct target *target)
/* Clear the error status. */
dmi_write(target, DMI_ABSTRACTCS, abstractcs & DMI_ABSTRACTCS_CMDERR);
}
+
+#define COMPLIANCE_TEST(b, message) \
+{ \
+ int pass = 0; \
+ if (b) { \
+ pass = 1; \
+ passed_tests++; \
+ } \
+ LOG_INFO("%s test %d (%s)\n", (pass) ? "PASSED" : "FAILED", total_tests, message); \
+ assert(pass); \
+ total_tests++; \
+}
+
+int riscv013_test_compliance(struct target *target)
+{
+ LOG_INFO("Testing Compliance against RISC-V Debug Spec v0.13");
+
+ if (!riscv_rtos_enabled(target)) {
+ LOG_ERROR("Please run with -rtos riscv to run compliance test.");
+ return ERROR_FAIL;
+ }
+
+ int total_tests = 0;
+ int passed_tests = 0;
+
+ uint32_t dmcontrol_orig = DMI_DMCONTROL_DMACTIVE;
+ uint32_t dmcontrol;
+ uint32_t testvar;
+ uint32_t testvar_read;
+ riscv_reg_t value;
+ RISCV013_INFO(info);
+
+ /* TODO: Support HARTSELLHI as well */
+ dmcontrol = dmcontrol_orig | DMI_DMCONTROL_HARTSELLO;
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST(get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO) == (uint32_t) ((1 << info->hartsellen) - 1),
+ "DMCONTROL.hartsello should hold all the harts allowed by its hartsellen.");
+
+ dmcontrol = set_field(dmcontrol_orig, DMI_DMCONTROL_HARTSELLO, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST(get_field(dmcontrol, DMI_DMCONTROL_HARTSELLO) == 0,
+ "DMCONTROL.hartsello should hold Hart ID 0");
+
+ /* hartreset */
+ /* This field is optional. Either we can read and write it to 1/0,
+ or it is tied to 0. */
+ dmcontrol = set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ testvar = get_field(dmcontrol, DMI_DMCONTROL_HARTRESET);
+ dmcontrol = set_field(dmcontrol_orig, DMI_DMCONTROL_HARTRESET, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST(((testvar == 0) || (get_field(dmcontrol, DMI_DMCONTROL_HARTRESET)) == 0),
+ "DMCONTROL.hartreset can be 0 or RW.");
+
+ /* hasel */
+ dmcontrol = set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 1);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ testvar = get_field(dmcontrol, DMI_DMCONTROL_HASEL);
+ dmcontrol = set_field(dmcontrol_orig, DMI_DMCONTROL_HASEL, 0);
+ dmi_write(target, DMI_DMCONTROL, dmcontrol);
+ dmi_read(target, &dmcontrol, DMI_DMCONTROL);
+ COMPLIANCE_TEST(((testvar == 0) || (get_field(dmcontrol, DMI_DMCONTROL_HASEL)) == 0),
+ "DMCONTROL.hasel can be 0 or RW.");
+ /* TODO: test that hamask registers exist if hasel does. */
+
+ /* haltreq */
+ riscv_halt_all_harts(target);
+ /* This bit is not actually readable according to the spec, so nothing to check.*/
+
+ /* DMSTATUS */
+ uint32_t dmstatus, dmstatus_read;
+ dmi_read(target, &dmstatus, DMI_DMSTATUS);
+ dmi_write(target, DMI_DMSTATUS, ~dmstatus);
+ dmi_read(target, &dmstatus_read, DMI_DMSTATUS);
+ COMPLIANCE_TEST(dmstatus_read == dmstatus, "DMSTATUS is R/O");
+
+ /* resumereq */
+ /* This bit is not actually readable according to the spec, so nothing to check.*/
+ riscv_resume_all_harts(target);
+
+ /* Halt all harts again so the test can continue.*/
+ riscv_halt_all_harts(target);
+
+ /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ riscv_set_current_hartid(target, hartsel);
+
+ uint32_t hartinfo, hartinfo_read;
+ dmi_read(target, &hartinfo, DMI_HARTINFO);
+ dmi_write(target, DMI_HARTINFO, ~hartinfo);
+ dmi_read(target, &hartinfo_read, DMI_HARTINFO);
+ COMPLIANCE_TEST((hartinfo_read == hartinfo), "DMHARTINFO should be Read-Only.");
+
+ /* $dscratch CSRs */
+ uint32_t nscratch = get_field(hartinfo, DMI_HARTINFO_NSCRATCH);
+ for (unsigned int d = 0; d < nscratch; d++) {
+ riscv_reg_t testval, testval_read;
+ /* Because DSCRATCH is not guaranteed to last across PB executions, need to put
+ this all into one PB execution. Which may not be possible on all implementations.*/
+ if (info->progbufsize >= 5) {
+ for (testval = 0x0011223300112233;
+ testval != 0xDEAD;
+ testval = testval == 0x0011223300112233 ? ~testval : 0xDEAD) {
+ COMPLIANCE_TEST(register_write_direct(target, GDB_REGNO_S0, testval) == ERROR_OK,
+ "Need to be able to write S0 in order to test DSCRATCH.");
+ struct riscv_program program32;
+ riscv_program_init(&program32, target);
+ riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH + d);
+ riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH + d);
+ riscv_program_fence(&program32);
+ riscv_program_ebreak(&program32);
+ COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK,
+ "Accessing DSCRATCH with program buffer should succeed.");
+ COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_S1) == ERROR_OK,
+ "Need to be able to read S1 in order to test DSCRATCH.");
+ if (riscv_xlen(target) > 32) {
+ COMPLIANCE_TEST(testval == testval_read,
+ "All DSCRATCH registers in HARTINFO must be R/W.");
+ } else {
+ COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF),
+ "All DSCRATCH registers in HARTINFO must be R/W.");
+ }
+ }
+ }
+ }
+ /* TODO: dataaccess */
+ if (get_field(hartinfo, DMI_HARTINFO_DATAACCESS)) {
+ /* TODO: Shadowed in memory map. */
+ /* TODO: datasize */
+ /* TODO: dataaddr */
+ } else {
+ /* TODO: Shadowed in CSRs. */
+ /* TODO: datasize */
+ /* TODO: dataaddr */
+ }
+
+ }
+
+ /* HALTSUM -- TODO: More than 32 harts. Would need to loop over this to set hartsel */
+ /* TODO: HALTSUM2, HALTSUM3 */
+ /* HALTSUM0 */
+ uint32_t expected_haltsum0 = 0;
+ for (int i = 0; i < MIN(riscv_count_harts(target), 32); i++)
+ expected_haltsum0 |= (1 << i);
+
+ dmi_read(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0,
+ "HALTSUM0 should report summary of up to 32 halted harts");
+
+ dmi_write(target, DMI_HALTSUM0, 0xffffffff);
+ dmi_read(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
+
+ dmi_write(target, DMI_HALTSUM0, 0x0);
+ dmi_read(target, &testvar_read, DMI_HALTSUM0);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
+
+ /* HALTSUM1 */
+ uint32_t expected_haltsum1 = 0;
+ for (int i = 0; i < MIN(riscv_count_harts(target), 1024); i += 32)
+ expected_haltsum1 |= (1 << (i/32));
+
+ dmi_read(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1,
+ "HALTSUM1 should report summary of up to 1024 halted harts");
+
+ dmi_write(target, DMI_HALTSUM1, 0xffffffff);
+ dmi_read(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
+
+ dmi_write(target, DMI_HALTSUM1, 0x0);
+ dmi_read(target, &testvar_read, DMI_HALTSUM1);
+ COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
+
+ /* TODO: HAWINDOWSEL */
+
+ /* TODO: HAWINDOW */
+
+ /* ABSTRACTCS */
+
+ uint32_t abstractcs;
+ dmi_read(target, &abstractcs, DMI_ABSTRACTCS);
+
+ /* Check that all reported Data Words are really R/W */
+ for (int invert = 0; invert < 2; invert++) {
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ dmi_write(target, DMI_DATA0 + i, testvar);
+ }
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ dmi_read(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "All reported DATA words must be R/W");
+ }
+ }
+
+ /* Check that all reported ProgBuf words are really R/W */
+ for (int invert = 0; invert < 2; invert++) {
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ dmi_write(target, DMI_PROGBUF0 + i, testvar);
+ }
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ if (invert)
+ testvar = ~testvar;
+ dmi_read(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "All reported PROGBUF words must be R/W");
+ }
+ }
+
+ /* TODO: Cause and clear all error types */
+
+ /* COMMAND
+ According to the spec, this register is only W, so can't really check the read result.
+ But at any rate, this is not legal and should cause an error. */
+ dmi_write(target, DMI_COMMAND, 0xAAAAAAAA);
+ dmi_read(target, &testvar_read, DMI_COMMAND);
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
+ "Illegal COMMAND should result in UNSUPPORTED");
+ dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+ dmi_write(target, DMI_COMMAND, 0x55555555);
+ dmi_read(target, &testvar_read, DMI_COMMAND);
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED, \
+ "Illegal COMMAND should result in UNSUPPORTED");
+ dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+
+ /* Basic Abstract Commands */
+ for (unsigned int i = 1; i < 32; i = i << 1) {
+ riscv_reg_t testval = i | ((i + 1ULL) << 32);
+ riscv_reg_t testval_read;
+ COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_ZERO + i, testval),
+ "GPR Writes should be supported.");
+ write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64);
+ COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i),
+ "GPR Reads should be supported.");
+ if (riscv_xlen(target) > 32) {
+ /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
+ COMPLIANCE_TEST(testval == testval_read, "GPR Reads and writes should be supported.");
+ } else {
+ /* Dummy comment to satisfy linter, since removing the brances here doesn't actually compile. */
+ COMPLIANCE_TEST((testval & 0xFFFFFFFF) == testval_read, "GPR Reads and writes should be supported.");
+ }
+ }
+
+ /* ABSTRACTAUTO
+ See which bits are actually writable */
+ dmi_write(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ uint32_t abstractauto;
+ uint32_t busy;
+ dmi_read(target, &abstractauto, DMI_ABSTRACTAUTO);
+ dmi_write(target, DMI_ABSTRACTAUTO, 0x0);
+ if (abstractauto > 0) {
+ /* This mechanism only works when you have a reasonable sized progbuf, which is not
+ a true compliance requirement. */
+ if (info->progbufsize >= 3) {
+
+ testvar = 0;
+ COMPLIANCE_TEST(ERROR_OK == register_write_direct(target, GDB_REGNO_S0, 0),
+ "Need to be able to write S0 to test ABSTRACTAUTO");
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ /* This is also testing that WFI() is a NOP during debug mode. */
+ riscv_program_insert(&program, wfi());
+ riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1);
+ riscv_program_ebreak(&program);
+ dmi_write(target, DMI_ABSTRACTAUTO, 0x0);
+ riscv_program_exec(&program, target);
+ testvar++;
+ dmi_write(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ dmi_read(target, &abstractauto, DMI_ABSTRACTAUTO);
+ uint32_t autoexec_data = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECDATA);
+ uint32_t autoexec_progbuf = get_field(abstractauto, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
+ for (unsigned int i = 0; i < 12; i++) {
+ dmi_read(target, &testvar_read, DMI_DATA0 + i);
+ do {
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
+ } while (busy);
+ if (autoexec_data & (1 << i)) {
+ COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT),
+ "AUTOEXEC may be writable up to DATACOUNT bits.");
+ testvar++;
+ }
+ }
+ for (unsigned int i = 0; i < 16; i++) {
+ dmi_read(target, &testvar_read, DMI_PROGBUF0 + i);
+ do {
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ busy = get_field(testvar_read, DMI_ABSTRACTCS_BUSY);
+ } while (busy);
+ if (autoexec_progbuf & (1 << i)) {
+ COMPLIANCE_TEST(i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE),
+ "AUTOEXEC may be writable up to PROGBUFSIZE bits.");
+ testvar++;
+ }
+ }
+
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+ COMPLIANCE_TEST(ERROR_OK == register_read_direct(target, &value, GDB_REGNO_S0),
+ "Need to be able to read S0 to test ABSTRACTAUTO");
+
+ COMPLIANCE_TEST(testvar == value,
+ "ABSTRACTAUTO should cause COMMAND to run the expected number of times.");
+ }
+ }
+
+ /* Single-Step each hart. */
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ riscv_set_current_hartid(target, hartsel);
+ riscv013_on_step(target);
+ riscv013_step_current_hart(target);
+ COMPLIANCE_TEST(riscv_halt_reason(target, hartsel) == RISCV_HALT_SINGLESTEP,
+ "Single Step should result in SINGLESTEP");
+ }
+
+ /* Core Register Tests */
+ uint64_t bogus_dpc = 0xdeadbeef;
+ for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
+ riscv_set_current_hartid(target, hartsel);
+
+ /* DCSR Tests */
+ register_write_direct(target, GDB_REGNO_DCSR, 0x0);
+ register_read_direct(target, &value, GDB_REGNO_DCSR);
+ COMPLIANCE_TEST(value != 0, "Not all bits in DCSR are writable by Debugger");
+ register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF);
+ register_read_direct(target, &value, GDB_REGNO_DCSR);
+ COMPLIANCE_TEST(value != 0, "At least some bits in DCSR must be 1");
+
+ /* DPC. Note that DPC is sign-extended. */
+ riscv_reg_t dpcmask = 0xFFFFFFFCUL;
+ riscv_reg_t dpc;
+
+ if (riscv_xlen(target) > 32)
+ dpcmask |= (0xFFFFFFFFULL << 32);
+
+ if (riscv_supports_extension(target, riscv_current_hartid(target), 'C'))
+ dpcmask |= 0x2;
+
+ register_write_direct(target, GDB_REGNO_DPC, dpcmask);
+ register_read_direct(target, &dpc, GDB_REGNO_DPC);
+ COMPLIANCE_TEST(dpcmask == dpc,
+ "DPC must be sign-extended to XLEN and writable to all-1s (except the least significant bits)");
+ register_write_direct(target, GDB_REGNO_DPC, 0);
+ register_read_direct(target, &dpc, GDB_REGNO_DPC);
+ COMPLIANCE_TEST(dpc == 0, "DPC must be writable to 0.");
+ if (hartsel == 0)
+ bogus_dpc = dpc; /* For a later test step */
+ }
+
+ /* NDMRESET
+ Asserting non-debug module reset should not reset Debug Module state.
+ But it should reset Hart State, e.g. DPC should get a different value.
+ Also make sure that DCSR reports cause of 'HALT' even though previously we single-stepped.
+ */
+
+ /* Write some registers. They should not be impacted by ndmreset. */
+ dmi_write(target, DMI_COMMAND, 0xFFFFFFFF);
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_write(target, DMI_PROGBUF0 + i, testvar);
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_write(target, DMI_DATA0 + i, testvar);
+ }
+
+ dmi_write(target, DMI_ABSTRACTAUTO, 0xFFFFFFFF);
+ dmi_read(target, &abstractauto, DMI_ABSTRACTAUTO);
+
+ /* Pulse reset. */
+ target->reset_halt = true;
+ riscv_set_current_hartid(target, 0);
+ COMPLIANCE_TEST(ERROR_OK == assert_reset(target), "Must be able to assert NDMRESET");
+ COMPLIANCE_TEST(ERROR_OK == deassert_reset(target), "Must be able to deassert NDMRESET");
+
+ /* Verify that most stuff is not affected by ndmreset. */
+ dmi_read(target, &testvar_read, DMI_COMMAND);
+ COMPLIANCE_TEST(testvar_read == 0xFFFFFFFF, "NDMRESET should not affect DMI_COMMAND");
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
+ "NDMRESET should not affect DMI_ABSTRACTCS");
+ dmi_read(target, &testvar_read, DMI_ABSTRACTAUTO);
+ COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DMI_ABSTRACTAUTO");
+
+ /* Clean up to avoid future test failures */
+ dmi_write(target, DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
+ dmi_write(target, DMI_ABSTRACTAUTO, 0);
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_read(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET");
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_read(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == testvar, "DATA words must not be affected by NDMRESET");
+ }
+
+ /* Verify that DPC *is* affected by ndmreset. Since we don't know what it *should* be,
+ just verify that at least it's not the bogus value anymore. */
+
+ COMPLIANCE_TEST(bogus_dpc != 0xdeadbeef, "BOGUS DPC should have been set somehow (bug in compliance test)");
+ register_read_direct(target, &value, GDB_REGNO_DPC);
+ COMPLIANCE_TEST(bogus_dpc != value, "NDMRESET should move DPC to reset value.");
+
+ COMPLIANCE_TEST(riscv_halt_reason(target, 0) == RISCV_HALT_INTERRUPT,
+ "After NDMRESET halt, DCSR should report cause of halt");
+
+ /* DMACTIVE -- deasserting DMACTIVE should reset all the above values. */
+
+ /* Toggle dmactive */
+ dmi_write(target, DMI_DMCONTROL, 0);
+ dmi_write(target, DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
+ dmi_read(target, &testvar_read, DMI_COMMAND);
+ COMPLIANCE_TEST(testvar_read == 0, "DMI_COMMAND should reset to 0");
+ dmi_read(target, &testvar_read, DMI_ABSTRACTCS);
+ COMPLIANCE_TEST(get_field(testvar_read, DMI_ABSTRACTCS_CMDERR) == 0, "ABSTRACTCS.cmderr should reset to 0");
+ dmi_read(target, &testvar_read, DMI_ABSTRACTAUTO);
+ COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0");
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_PROGBUFSIZE); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_read(target, &testvar_read, DMI_PROGBUF0 + i);
+ COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0");
+ }
+
+ for (unsigned int i = 0; i < get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT); i++) {
+ testvar = (i + 1) * 0x11111111;
+ dmi_read(target, &testvar_read, DMI_DATA0 + i);
+ COMPLIANCE_TEST(testvar_read == 0, "DATA words should reset to 0");
+ }
+
+ /*
+ * TODO:
+ * DCSR.cause priorities
+ * DCSR.stoptime/stopcycle
+ * DCSR.stepie
+ * DCSR.ebreak
+ * DCSR.prv
+ */
+
+ /* Halt every hart for any follow-up tests*/
+ riscv_halt_all_harts(target);
+
+ LOG_INFO("PASSED %d of %d TESTS\n", passed_tests, total_tests);
+
+ if (total_tests == passed_tests)
+ return ERROR_OK;
+ else
+ return ERROR_FAIL;
+}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 6281fad..23e2b14 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -1224,6 +1224,25 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
return ERROR_OK;
}
+COMMAND_HANDLER(riscv_test_compliance) {
+
+ struct target *target = get_current_target(CMD_CTX);
+
+ RISCV_INFO(r);
+
+ if (CMD_ARGC > 0) {
+ LOG_ERROR("Command does not take any parameters.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ if (r->test_compliance) {
+ return r->test_compliance(target);
+ } else {
+ LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
+ return ERROR_FAIL;
+ }
+}
+
COMMAND_HANDLER(riscv_set_scratch_ram)
{
if (CMD_ARGC != 1) {
@@ -1450,6 +1469,13 @@ COMMAND_HANDLER(riscv_dmi_write)
static const struct command_registration riscv_exec_command_handlers[] = {
{
+ .name = "test_compliance",
+ .handler = riscv_test_compliance,
+ .mode = COMMAND_EXEC,
+ .usage = "riscv test_compliance",
+ .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
+ },
+ {
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,
.mode = COMMAND_ANY,
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index 63a3b79..48039d0 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -116,6 +116,9 @@ typedef struct {
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
+
+ int (*test_compliance)(struct target *target);
+
} riscv_info_t;
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/