diff options
-rw-r--r-- | src/target/riscv/debug_defines.h | 193 | ||||
-rw-r--r-- | src/target/riscv/opcodes.h | 3 | ||||
-rw-r--r-- | src/target/riscv/program.h | 4 | ||||
-rw-r--r-- | src/target/riscv/riscv-013.c | 479 | ||||
-rw-r--r-- | src/target/riscv/riscv.c | 26 | ||||
-rw-r--r-- | src/target/riscv/riscv.h | 3 |
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.*/ |