Upstream a whole host of RISC-V changes.
authorTim Newsome <tim@sifive.com>
Wed, 1 Sep 2021 22:00:46 +0000 (15:00 -0700)
committerAntonio Borneo <borneo.antonio@gmail.com>
Mon, 25 Oct 2021 16:12:05 +0000 (16:12 +0000)
Made no attempt to separate this out into reviewable chunks, since this
is all RISC-V-specific code developed at
https://github.com/riscv/riscv-openocd

Memory sample and repeat read functionality was left out of this change
since it requires some target-independent changes that I'll upstream
some other time.

Change-Id: I92917c86d549c232cbf36ffbfefc93331c05accd
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/6529
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
src/target/riscv/batch.c
src/target/riscv/debug_defines.h
src/target/riscv/opcodes.h
src/target/riscv/program.h
src/target/riscv/riscv-011.c
src/target/riscv/riscv-013.c
src/target/riscv/riscv.c
src/target/riscv/riscv.h
src/target/riscv/riscv_semihosting.c

index 3c062e15011aefb03af5fecd56dc4314c55af155..a3d68a91f336c435c5e30e1f4d412874aec74261 100644 (file)
@@ -27,23 +27,33 @@ struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_
        out->allocated_scans = scans;
        out->idle_count = idle;
        out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE);
-       if (!out->data_out)
+       if (!out->data_out) {
+               LOG_ERROR("Failed to allocate data_out in RISC-V batch.");
                goto error1;
+       };
        out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE);
-       if (!out->data_in)
+       if (!out->data_in) {
+               LOG_ERROR("Failed to allocate data_in in RISC-V batch.");
                goto error2;
+       }
        out->fields = malloc(sizeof(*out->fields) * (scans));
-       if (!out->fields)
+       if (!out->fields) {
+               LOG_ERROR("Failed to allocate fields in RISC-V batch.");
                goto error3;
+       }
        if (bscan_tunnel_ir_width != 0) {
                out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans));
-               if (!out->bscan_ctxt)
+               if (!out->bscan_ctxt) {
+                       LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch.");
                        goto error4;
+               }
        }
        out->last_scan = RISCV_SCAN_TYPE_INVALID;
        out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
-       if (!out->read_keys)
+       if (!out->read_keys) {
+               LOG_ERROR("Failed to allocate read_keys in RISC-V batch.");
                goto error5;
+       }
        return out;
 
 error5:
@@ -82,8 +92,6 @@ int riscv_batch_run(struct riscv_batch *batch)
                return ERROR_OK;
        }
 
-       keep_alive();
-
        riscv_batch_add_nop(batch);
 
        for (size_t i = 0; i < batch->used_scans; ++i) {
@@ -96,11 +104,15 @@ int riscv_batch_run(struct riscv_batch *batch)
                        jtag_add_runtest(batch->idle_count, TAP_IDLE);
        }
 
+       keep_alive();
+
        if (jtag_execute_queue() != ERROR_OK) {
                LOG_ERROR("Unable to execute JTAG queue");
                return ERROR_FAIL;
        }
 
+       keep_alive();
+
        if (bscan_tunnel_ir_width != 0) {
                /* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
                for (size_t i = 0; i < batch->used_scans; ++i)
index cb518a8911fbfb575942f176a4162e18110664a3..b5104d53060f58186053c2430c0ee9c643e29724 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * This file is auto-generated by running 'make debug_defines.h' in
- * https://github.com/riscv/riscv-debug-spec/ (30b1a97)
+ * https://github.com/riscv/riscv-debug-spec/ (63c985f)
+ * License: Creative Commons Attribution 4.0 International Public License (CC BY 4.0)
  */
 
 #define DTM_IDCODE                          0x01
@@ -89,7 +90,7 @@
 /*
  * 0: Version described in spec version 0.11.
  *
- * 1: Version described in spec version 0.13.
+ * 1: Version described in spec versions 0.13 and 1.0.
  *
  * 15: Version not described in any available version of this spec.
  */
 #define DTM_DMI_OP                          (0x3ULL << DTM_DMI_OP_OFFSET)
 #define CSR_DCSR                            0x7b0
 /*
- * 0: There is no external debug support.
+ * 0: There is no debug support.
  *
- * 4: External debug support exists as it is described in this document.
+ * 4: Debug support exists as it is described in this document.
  *
- * 15: There is external debug support, but it does not conform to any
+ * 15: There is debug support, but it does not conform to any
  * available version of this spec.
  */
-#define CSR_DCSR_XDEBUGVER_OFFSET           28
-#define CSR_DCSR_XDEBUGVER_LENGTH           4
-#define CSR_DCSR_XDEBUGVER                  (0xfU << CSR_DCSR_XDEBUGVER_OFFSET)
+#define CSR_DCSR_DEBUGVER_OFFSET            28
+#define CSR_DCSR_DEBUGVER_LENGTH            4
+#define CSR_DCSR_DEBUGVER                   (0xfU << CSR_DCSR_DEBUGVER_OFFSET)
+/*
+ * 0: {\tt ebreak} instructions in VS-mode behave as described in the
+ * Privileged Spec.
+ *
+ * 1: {\tt ebreak} instructions in VS-mode enter Debug Mode.
+ *
+ * This bit is hardwired to 0 if the hart does not support virtualization mode.
+ */
+#define CSR_DCSR_EBREAKVS_OFFSET            17
+#define CSR_DCSR_EBREAKVS_LENGTH            1
+#define CSR_DCSR_EBREAKVS                   (0x1U << CSR_DCSR_EBREAKVS_OFFSET)
+/*
+ * 0: {\tt ebreak} instructions in VU-mode behave as described in the
+ * Privileged Spec.
+ *
+ * 1: {\tt ebreak} instructions in VU-mode enter Debug Mode.
+ *
+ * This bit is hardwired to 0 if the hart does not support virtualization mode.
+ */
+#define CSR_DCSR_EBREAKVU_OFFSET            16
+#define CSR_DCSR_EBREAKVU_LENGTH            1
+#define CSR_DCSR_EBREAKVU                   (0x1U << CSR_DCSR_EBREAKVU_OFFSET)
 /*
  * 0: {\tt ebreak} instructions in M-mode behave as described in the
  * Privileged Spec.
  *
  * 1: {\tt ebreak} instructions in S-mode enter Debug Mode.
  *
- * This bit is hardwired to 0 if the hart does not support S mode.
+ * This bit is hardwired to 0 if the hart does not support S-mode.
  */
 #define CSR_DCSR_EBREAKS_OFFSET             13
 #define CSR_DCSR_EBREAKS_LENGTH             1
  *
  * 1: {\tt ebreak} instructions in U-mode enter Debug Mode.
  *
- * This bit is hardwired to 0 if the hart does not support U mode.
+ * This bit is hardwired to 0 if the hart does not support U-mode.
  */
 #define CSR_DCSR_EBREAKU_OFFSET             12
 #define CSR_DCSR_EBREAKU_LENGTH             1
 #define CSR_DCSR_CAUSE_LENGTH               3
 #define CSR_DCSR_CAUSE                      (0x7U << CSR_DCSR_CAUSE_OFFSET)
 /*
- * 0: \FcsrMcontrolMprv in \Rmstatus is ignored in Debug Mode.
+ * Extends the prv field with the virtualization mode the hart was operating
+ * in when Debug Mode was entered. The encoding is described in Table
+ * \ref{tab:privlevel}.
+ * A debugger can change this value to change the hart's virtualization mode
+ * when exiting Debug Mode.
+ * This bit is hardwired to 0 on harts that do not support virtualization mode.
+ */
+#define CSR_DCSR_V_OFFSET                   5
+#define CSR_DCSR_V_LENGTH                   1
+#define CSR_DCSR_V                          (0x1U << CSR_DCSR_V_OFFSET)
+/*
+ * 0: \FcsrMstatusMprv in \Rmstatus is ignored in Debug Mode.
  *
- * 1: \FcsrMcontrolMprv in \Rmstatus takes effect in Debug Mode.
+ * 1: \FcsrMstatusMprv in \Rmstatus takes effect in Debug Mode.
  *
  * Implementing this bit is optional. It may be tied to either 0 or 1.
  */
  * 5: The trigger is an exception trigger. The remaining bits
  * in this register act as described in \RcsrEtrigger.
  *
+ * 6: The trigger is an address/data match trigger. The remaining bits
+ * in this register act as described in \RcsrMcontrolSix. This is similar
+ * to a type 2 trigger, but provides additional functionality and
+ * should be used instead of type 2 in newer implementations.
+ *
+ * 7: The trigger is a trigger source external to the TM.  The
+ * remaining bits in this register act as described in \RcsrTmexttrigger.
+ *
  * 12--14: These trigger types are available for non-standard use.
  *
  * 15: This trigger exists (so enumeration shouldn't terminate), but
  * selected \RcsrTselect.  Writes from other modes are ignored.
  *
  * This bit is only writable from Debug Mode.
- * When clearing this bit, the debugger should also clear the action field
+ * In ordinary use, external debuggers will always set this bit when
+ * configuring a trigger.
+ * When clearing this bit, debuggers should also clear the action field
  * (whose location depends on \FcsrTdataOneType).
  */
 #define CSR_TDATA1_DMODE_OFFSET             (XLEN-5)
 #define CSR_TINFO_INFO_LENGTH               16
 #define CSR_TINFO_INFO                      (0xffffULL << CSR_TINFO_INFO_OFFSET)
 #define CSR_TCONTROL                        0x7a5
+/*
+ * \RcsrHcontext enable.
+ *
+ * 0: \RcsrHcontext is set to 0 and writes are ignored.
+ *
+ * 1: \RcsrHcontext may be written and read.
+ */
+#define CSR_TCONTROL_HCXE_OFFSET            9
+#define CSR_TCONTROL_HCXE_LENGTH            1
+#define CSR_TCONTROL_HCXE                   (0x1ULL << CSR_TCONTROL_HCXE_OFFSET)
+/*
+ * \RcsrScontext enable.
+ *
+ * 0: \RcsrScontext is set to 0 and writes are ignored.
+ *
+ * 1: \RcsrScontext may be written and read.
+ *
+ * Enabling \RcsrScontext can be a security risk in a
+ * virtualized system with a hypervisor that does not swap \RcsrScontext.
+ */
+#define CSR_TCONTROL_SCXE_OFFSET            8
+#define CSR_TCONTROL_SCXE_LENGTH            1
+#define CSR_TCONTROL_SCXE                   (0x1ULL << CSR_TCONTROL_SCXE_OFFSET)
 /*
  * M-mode previous trigger enable field.
  *
+ * \FcsrTcontrolMpte and \FcsrTcontrolMte provide one solution to a problem
+ * regarding triggers with action=0 firing in M-mode trap handlers. See
+ * Section~\ref{sec:mmtrigger} for more details.
+ *
  * When a trap into M-mode is taken, \FcsrTcontrolMpte is set to the value of
  * \FcsrTcontrolMte.
  */
 #define CSR_TCONTROL_MTE_OFFSET             3
 #define CSR_TCONTROL_MTE_LENGTH             1
 #define CSR_TCONTROL_MTE                    (0x1ULL << CSR_TCONTROL_MTE_OFFSET)
-#define CSR_MCONTEXT                        0x7a8
+#define CSR_HCONTEXT                        0x6a8
 /*
- * Machine mode software can write a context number to this register,
+ * Hypervisor mode software can write a context number to this register,
  * which can be used to set triggers that only fire in that specific
  * context.
  *
  * An implementation may tie any number of upper bits in this field to
- * 0. It's recommended to implement no more than 6 bits on RV32, and
- * 13 on RV64.
+ * 0. If the H extension is not implemented, it's recommended to implement
+ * no more than 6 bits on RV32 and 13 on RV64 (as visible through the
+ * \RcsrMcontext register).  If the H extension is implemented,
+ * it's recommended to implement no more than 7 bits on RV32
+ * and 14 on RV64.
  */
-#define CSR_MCONTEXT_MCONTEXT_OFFSET        0
-#define CSR_MCONTEXT_MCONTEXT_LENGTH        XLEN
-#define CSR_MCONTEXT_MCONTEXT               (((1L << XLEN) - 1) << CSR_MCONTEXT_MCONTEXT_OFFSET)
-#define CSR_SCONTEXT                        0x7aa
+#define CSR_HCONTEXT_HCONTEXT_OFFSET        0
+#define CSR_HCONTEXT_HCONTEXT_LENGTH        XLEN
+#define CSR_HCONTEXT_HCONTEXT               (((1L << XLEN) - 1) << CSR_HCONTEXT_HCONTEXT_OFFSET)
+#define CSR_SCONTEXT                        0x5a8
 /*
  * Supervisor mode software can write a context number to this
  * register, which can be used to set triggers that only fire in that
 #define CSR_SCONTEXT_DATA_OFFSET            0
 #define CSR_SCONTEXT_DATA_LENGTH            XLEN
 #define CSR_SCONTEXT_DATA                   (((1L << XLEN) - 1) << CSR_SCONTEXT_DATA_OFFSET)
+#define CSR_MCONTEXT                        0x7a8
+#define CSR_MSCONTEXT                       0x7aa
 #define CSR_MCONTROL                        0x7a1
 #define CSR_MCONTROL_TYPE_OFFSET            (XLEN-4)
 #define CSR_MCONTROL_TYPE_LENGTH            4
 /*
  * Specifies the largest naturally aligned powers-of-two (NAPOT) range
  * supported by the hardware when \FcsrMcontrolMatch is 1. The value is the
- * logarithm base 2 of the
- * number of bytes in that range.  A value of 0 indicates that only
- * exact value matches are supported (one byte range). A value of 63
- * corresponds to the maximum NAPOT range, which is $2^{63}$ bytes in
- * size.
+ * logarithm base 2 of the number of bytes in that range.
+ * A value of 0 indicates \FcsrMcontrolMatch 1 is not supported.
+ * A value of 63 corresponds to the maximum NAPOT range, which is
+ * $2^{63}$ bytes in size.
  */
 #define CSR_MCONTROL_MASKMAX_OFFSET         (XLEN-11)
 #define CSR_MCONTROL_MASKMAX_LENGTH         6
 #define CSR_MCONTROL_SIZEHI_LENGTH          2
 #define CSR_MCONTROL_SIZEHI                 (0x3ULL << CSR_MCONTROL_SIZEHI_OFFSET)
 /*
- * If this bit is implemented, the hardware sets it when this
- * trigger matches. The trigger's user can set or clear it at any
+ * If this bit is implemented then it must become set when this
+ * trigger fires and may become set when this trigger matches.
+ * The trigger's user can set or clear it at any
  * time. It is used 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_LENGTH             1
 #define CSR_MCONTROL_HIT                    (0x1ULL << CSR_MCONTROL_HIT_OFFSET)
 /*
- * 0: Perform a match on the lowest virtual address of the access.  In
- * addition, it is recommended that the trigger also fires if any of
- * the other accessed virtual addresses match.
+ * This bit determines the contents of the XLEN-bit compare values.
+ *
+ * 0: There is at least one compare value and it contains the lowest
+ * virtual address of the access.
+ * It is recommended that there are additional compare values for
+ * the other accessed virtual addresses.
  * (E.g. on a 32-bit read from 0x4000, the lowest address is 0x4000
  * and the other addresses are 0x4001, 0x4002, and 0x4003.)
  *
- * 1: Perform a match on the data value loaded or stored, or the
- * instruction executed.
+ * 1: There is exactly one compare value and it contains the data
+ * value loaded or stored, or the instruction executed.
+ * Any bits beyond the size of the data access will contain 0.
  */
 #define CSR_MCONTROL_SELECT_OFFSET          19
 #define CSR_MCONTROL_SELECT_LENGTH          1
 #define CSR_MCONTROL_SELECT                 (0x1ULL << CSR_MCONTROL_SELECT_OFFSET)
 /*
  * 0: The action for this trigger will be taken just before the
- * instruction that triggered it is executed, but after all preceding
- * instructions are committed. \Rmepc or \RcsrDpc (depending on
- * \FcsrMcontrolAction) must be set to the virtual address of the
+ * instruction that triggered it is committed, but after all preceding
+ * instructions are committed. \Rxepc or \RcsrDpc (depending
+ * on \FcsrMcontrolAction) must be set to the virtual address of the
  * instruction that matched.
  *
- * If this is combined with \FcsrMcontrolLoad then a memory access will be
+ * If this is combined with \FcsrMcontrolLoad and
+ * \FcsrMcontrolSelect=1 then a memory access will be
  * performed (including any side effects of performing such an access) even
  * though the load will not update its destination register. Debuggers
  * should consider this when setting such breakpoints on, for example,
  * memory-mapped I/O addresses.
  *
  * 1: The action for this trigger will be taken after the instruction
- * that triggered it is executed. It should be taken before the next
- * instruction is executed, but it is better to implement triggers imprecisely
- * than to not implement them at all.
- * \Rmepc or \RcsrDpc (depending on \FcsrMcontrolAction) must be set to
+ * that triggered it is committed. It should be taken before the next
+ * instruction is committed, but it is better to implement triggers imprecisely
+ * than to not implement them at all.  \Rxepc or
+ * \RcsrDpc (depending on \FcsrMcontrolAction) must be set to
  * the virtual address of the next instruction that must be executed to
  * preserve the program flow.
  *
  * execution of 128-bit instructions.
  *
  * An implementation must support the value of 0, but all other values
- * are optional. It is recommended to support triggers for every
- * access size the hart supports, as well as for every instruction
- * size the hart supports.
+ * are optional. When an implementation supports address triggers
+ * (\FcsrMcontrolSelect=0), it is recommended that those triggers
+ * support every access size that the hart supports, as well as for
+ * every instruction size that the hart supports.
+ *
+ * Implementations such as RV32D or RV64V are able to perform loads
+ * and stores that are wider than XLEN. Custom extensions may also
+ * support instructions that are wider than XLEN. Because
+ * \RcsrTdataTwo is of size XLEN, there is a known limitation that
+ * data value triggers (\FcsrMcontrolSelect=1) can only be supported
+ * for access sizes up to XLEN bits.  When an implementation supports
+ * data value triggers (\FcsrMcontrolSelect=1), it is recommended
+ * that those triggers support every access size up to XLEN that the
+ * hart supports, as well as for every instruction length up to XLEN
+ * that the hart supports.
  */
 #define CSR_MCONTROL_SIZELO_OFFSET          16
 #define CSR_MCONTROL_SIZELO_LENGTH          2
  * trigger will be taken if and only if all the triggers in the chain
  * match at the same time.
  *
+ * Debuggers should not terminate a chain with a trigger with a
+ * different type. It is undefined when exactly such a chain fires.
+ *
  * Because \FcsrMcontrolChain affects the next trigger, hardware must zero it in
  * writes to \RcsrMcontrol that set \FcsrTdataOneDmode to 0 if the next trigger has
  * \FcsrTdataOneDmode of 1.
 #define CSR_MCONTROL_CHAIN_LENGTH           1
 #define CSR_MCONTROL_CHAIN                  (0x1ULL << CSR_MCONTROL_CHAIN_OFFSET)
 /*
- * 0: Matches when the value equals \RcsrTdataTwo.
+ * 0: Matches when any compare value equals \RcsrTdataTwo.
  *
- * 1: Matches when the top M bits of the value match the top M bits of
- * \RcsrTdataTwo. M is XLEN-1 minus the index of the least-significant
+ * 1: Matches when the top $M$ bits of any compare value match the top
+ * $M$ bits of \RcsrTdataTwo.
+ * $M$ is $|XLEN|-1$ minus the index of the least-significant
  * bit containing 0 in \RcsrTdataTwo. Debuggers should only write values
- * to \RcsrTdataTwo such that M + \FcsrMcontrolMaskmax $\geq$ XLEN, otherwise it's
- * undefined on what conditions the trigger will fire.
+ * to \RcsrTdataTwo such that $M + $\FcsrMcontrolMaskmax$ \geq |XLEN|$
+ * and $M\gt0$ , otherwise it's undefined on what conditions the
+ * trigger will match.
  *
- * 2: Matches when the value is greater than (unsigned) or equal to
- * \RcsrTdataTwo.
+ * 2: Matches when any compare value is greater than (unsigned) or
+ * equal to \RcsrTdataTwo.
  *
- * 3: Matches when the value is less than (unsigned) \RcsrTdataTwo.
+ * 3: Matches when any compare value is less than (unsigned)
+ * \RcsrTdataTwo.
  *
- * 4: Matches when the lower half of the value equals the lower half
- * of \RcsrTdataTwo after the lower half of the value is ANDed with the
- * upper half of \RcsrTdataTwo.
+ * 4: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value
+ * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after
+ * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo.
  *
- * 5: Matches when the upper half of the value equals the lower half
- * of \RcsrTdataTwo after the upper half of the value is ANDed with the
- * upper half of \RcsrTdataTwo.
+ * 5: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare
+ * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo.
  *
  * 8: Matches when \FcsrMcontrolMatch$=0$ would not match.
  *
  * 13: Matches when \FcsrMcontrolMatch$=5$ would not match.
  *
  * Other values are reserved for future use.
+ *
+ * All comparisons only look at the lower XLEN (in the current mode)
+ * bits of the compare values and of \RcsrTdataTwo.
+ * When \FcsrMcontrolSelect=1 and access size is N, this is further
+ * reduced, and comparisons only look at the lower N bits of the
+ * compare values and of \RcsrTdataTwo.
  */
 #define CSR_MCONTROL_MATCH_OFFSET           7
 #define CSR_MCONTROL_MATCH_LENGTH           4
 #define CSR_MCONTROL_M_LENGTH               1
 #define CSR_MCONTROL_M                      (0x1ULL << CSR_MCONTROL_M_OFFSET)
 /*
- * When set, enable this trigger in S-mode.
+ * When set, enable this trigger in S/HS-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * S-mode.
  */
 #define CSR_MCONTROL_S_OFFSET               4
 #define CSR_MCONTROL_S_LENGTH               1
 #define CSR_MCONTROL_S                      (0x1ULL << CSR_MCONTROL_S_OFFSET)
 /*
  * When set, enable this trigger in U-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * U-mode.
  */
 #define CSR_MCONTROL_U_OFFSET               3
 #define CSR_MCONTROL_U_LENGTH               1
 #define CSR_MCONTROL_LOAD_OFFSET            0
 #define CSR_MCONTROL_LOAD_LENGTH            1
 #define CSR_MCONTROL_LOAD                   (0x1ULL << CSR_MCONTROL_LOAD_OFFSET)
+#define CSR_MCONTROL6                       0x7a1
+#define CSR_MCONTROL6_TYPE_OFFSET           (XLEN-4)
+#define CSR_MCONTROL6_TYPE_LENGTH           4
+#define CSR_MCONTROL6_TYPE                  (0xfULL << CSR_MCONTROL6_TYPE_OFFSET)
+#define CSR_MCONTROL6_DMODE_OFFSET          (XLEN-5)
+#define CSR_MCONTROL6_DMODE_LENGTH          1
+#define CSR_MCONTROL6_DMODE                 (0x1ULL << CSR_MCONTROL6_DMODE_OFFSET)
+/*
+ * When set, enable this trigger in VS-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_MCONTROL6_VS_OFFSET             24
+#define CSR_MCONTROL6_VS_LENGTH             1
+#define CSR_MCONTROL6_VS                    (0x1ULL << CSR_MCONTROL6_VS_OFFSET)
+/*
+ * When set, enable this trigger in VU-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_MCONTROL6_VU_OFFSET             23
+#define CSR_MCONTROL6_VU_LENGTH             1
+#define CSR_MCONTROL6_VU                    (0x1ULL << CSR_MCONTROL6_VU_OFFSET)
+/*
+ * If this bit is implemented, the hardware sets it when this
+ * trigger matches. The trigger's user can set or clear it at any
+ * time. It is used to determine which
+ * trigger(s) matched.  If the bit is not implemented, it is always 0
+ * and writing it has no effect.
+ */
+#define CSR_MCONTROL6_HIT_OFFSET            22
+#define CSR_MCONTROL6_HIT_LENGTH            1
+#define CSR_MCONTROL6_HIT                   (0x1ULL << CSR_MCONTROL6_HIT_OFFSET)
+/*
+ * This bit determines the contents of the XLEN-bit compare values.
+ *
+ * 0: There is at least one compare value and it contains the lowest
+ * virtual address of the access.
+ * In addition, it is recommended that there are additional compare
+ * values for the other accessed virtual addresses match.
+ * (E.g. on a 32-bit read from 0x4000, the lowest address is 0x4000
+ * and the other addresses are 0x4001, 0x4002, and 0x4003.)
+ *
+ * 1: There is exactly one compare value and it contains the data
+ * value loaded or stored, or the instruction executed.
+ * Any bits beyond the size of the data access will contain 0.
+ */
+#define CSR_MCONTROL6_SELECT_OFFSET         21
+#define CSR_MCONTROL6_SELECT_LENGTH         1
+#define CSR_MCONTROL6_SELECT                (0x1ULL << CSR_MCONTROL6_SELECT_OFFSET)
+/*
+ * 0: The action for this trigger will be taken just before the
+ * instruction that triggered it is committed, but after all preceding
+ * instructions are committed. \Rxepc or \RcsrDpc (depending
+ * on \FcsrMcontrolSixAction) must be set to the virtual address of the
+ * instruction that matched.
+ *
+ * If this is combined with \FcsrMcontrolSixLoad and
+ * \FcsrMcontrolSixSelect=1 then a memory access will be
+ * performed (including any side effects of performing such an access) even
+ * though the load will not update its destination register. Debuggers
+ * should consider this when setting such breakpoints on, for example,
+ * memory-mapped I/O addresses.
+ *
+ * 1: The action for this trigger will be taken after the instruction
+ * that triggered it is committed. It should be taken before the next
+ * instruction is committed, but it is better to implement triggers imprecisely
+ * than to not implement them at all.  \Rxepc or
+ * \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set to
+ * the virtual address of the next instruction that must be executed to
+ * preserve the program flow.
+ *
+ * Most hardware will only implement one timing or the other, possibly
+ * dependent on \FcsrMcontrolSixSelect, \FcsrMcontrolSixExecute,
+ * \FcsrMcontrolSixLoad, and \FcsrMcontrolSixStore. This bit
+ * primarily exists for the hardware to communicate to the debugger
+ * what will happen. Hardware may implement the bit fully writable, in
+ * which case the debugger has a little more control.
+ *
+ * Data load triggers with \FcsrMcontrolSixTiming of 0 will result in the same load
+ * happening again when the debugger lets the hart run. For data load
+ * triggers, debuggers must first attempt to set the breakpoint with
+ * \FcsrMcontrolSixTiming of 1.
+ *
+ * If a trigger with \FcsrMcontrolSixTiming of 0 matches, it is
+ * implementation-dependent whether that prevents a trigger with
+ * \FcsrMcontrolSixTiming of 1 matching as well.
+ */
+#define CSR_MCONTROL6_TIMING_OFFSET         20
+#define CSR_MCONTROL6_TIMING_LENGTH         1
+#define CSR_MCONTROL6_TIMING                (0x1ULL << CSR_MCONTROL6_TIMING_OFFSET)
+/*
+ * 0: The trigger will attempt to match against an access of any size.
+ * The behavior is only well-defined if $|select|=0$, or if the access
+ * size is XLEN.
+ *
+ * 1: The trigger will only match against 8-bit memory accesses.
+ *
+ * 2: The trigger will only match against 16-bit memory accesses or
+ * execution of 16-bit instructions.
+ *
+ * 3: The trigger will only match against 32-bit memory accesses or
+ * execution of 32-bit instructions.
+ *
+ * 4: The trigger will only match against execution of 48-bit instructions.
+ *
+ * 5: The trigger will only match against 64-bit memory accesses or
+ * execution of 64-bit instructions.
+ *
+ * 6: The trigger will only match against execution of 80-bit instructions.
+ *
+ * 7: The trigger will only match against execution of 96-bit instructions.
+ *
+ * 8: The trigger will only match against execution of 112-bit instructions.
+ *
+ * 9: The trigger will only match against 128-bit memory accesses or
+ * execution of 128-bit instructions.
+ *
+ * An implementation must support the value of 0, but all other values
+ * are optional. When an implementation supports address triggers
+ * (\FcsrMcontrolSixSelect=0), it is recommended that those triggers
+ * support every access size that the hart supports, as well as for
+ * every instruction size that the hart supports.
+ *
+ * Implementations such as RV32D or RV64V are able to perform loads
+ * and stores that are wider than XLEN. Custom extensions may also
+ * support instructions that are wider than XLEN. Because
+ * \RcsrTdataTwo is of size XLEN, there is a known limitation that
+ * data value triggers (\FcsrMcontrolSixSelect=1) can only be supported
+ * for access sizes up to XLEN bits.  When an implementation supports
+ * data value triggers (\FcsrMcontrolSixSelect=1), it is recommended
+ * that those triggers support every access size up to XLEN that the
+ * hart supports, as well as for every instruction length up to XLEN
+ * that the hart supports.
+ */
+#define CSR_MCONTROL6_SIZE_OFFSET           16
+#define CSR_MCONTROL6_SIZE_LENGTH           4
+#define CSR_MCONTROL6_SIZE                  (0xfULL << CSR_MCONTROL6_SIZE_OFFSET)
+/*
+ * The action to take when the trigger fires. The values are explained
+ * in Table~\ref{tab:action}.
+ */
+#define CSR_MCONTROL6_ACTION_OFFSET         12
+#define CSR_MCONTROL6_ACTION_LENGTH         4
+#define CSR_MCONTROL6_ACTION                (0xfULL << CSR_MCONTROL6_ACTION_OFFSET)
+/*
+ * 0: When this trigger matches, the configured action is taken.
+ *
+ * 1: While this trigger does not match, it prevents the trigger with
+ * the next index from matching.
+ *
+ * A trigger chain starts on the first trigger with $|chain|=1$ after
+ * a trigger with $|chain|=0$, or simply on the first trigger if that
+ * has $|chain|=1$. It ends on the first trigger after that which has
+ * $|chain|=0$. This final trigger is part of the chain. The action
+ * on all but the final trigger is ignored.  The action on that final
+ * trigger will be taken if and only if all the triggers in the chain
+ * match at the same time.
+ *
+ * Debuggers should not terminate a chain with a trigger with a
+ * different type. It is undefined when exactly such a chain fires.
+ *
+ * Because \FcsrMcontrolSixChain affects the next trigger, hardware must zero it in
+ * writes to \RcsrMcontrolSix that set \FcsrTdataOneDmode to 0 if the next trigger has
+ * \FcsrTdataOneDmode of 1.
+ * In addition hardware should ignore writes to \RcsrMcontrolSix that set
+ * \FcsrTdataOneDmode to 1 if the previous trigger has both \FcsrTdataOneDmode of 0 and
+ * \FcsrMcontrolSixChain of 1. Debuggers must avoid the latter case by checking
+ * \FcsrMcontrolSixChain on the previous trigger if they're writing \RcsrMcontrolSix.
+ *
+ * Implementations that wish to limit the maximum length of a trigger
+ * chain (eg. to meet timing requirements) may do so by zeroing
+ * \FcsrMcontrolSixChain in writes to \RcsrMcontrolSix that would make the chain too long.
+ */
+#define CSR_MCONTROL6_CHAIN_OFFSET          11
+#define CSR_MCONTROL6_CHAIN_LENGTH          1
+#define CSR_MCONTROL6_CHAIN                 (0x1ULL << CSR_MCONTROL6_CHAIN_OFFSET)
+/*
+ * 0: Matches when any compare value equals \RcsrTdataTwo.
+ *
+ * 1: Matches when the top $M$ bits of any compare value match the top
+ * $M$ bits of \RcsrTdataTwo.
+ * $M$ is $|XLEN|-1$ minus the index of the least-significant bit
+ * containing 0 in \RcsrTdataTwo.
+ * \RcsrTdataTwo is WARL and bit $|maskmax6|-1$ will be set to 0 if no
+ * less significant bits are written with 0.
+ * Legal values for \RcsrTdataTwo require $M + |maskmax6| \geq |XLEN|$ and $M\gt0$.
+ * See above for how to determine maskmax6.
+ *
+ * 2: Matches when any compare value is greater than (unsigned) or
+ * equal to \RcsrTdataTwo.
+ *
+ * 3: Matches when any compare value is less than (unsigned)
+ * \RcsrTdataTwo.
+ *
+ * 4: Matches when $\frac{|XLEN|}{2}-1$:$0$ of any compare value
+ * equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after
+ * $\frac{|XLEN|}{2}-1$:$0$ of the compare value is ANDed with
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo.
+ *
+ * 5: Matches when $|XLEN|-1$:$\frac{|XLEN|}{2}$ of any compare
+ * value equals $\frac{|XLEN|}{2}-1$:$0$ of \RcsrTdataTwo after
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of the compare value is ANDed with
+ * $|XLEN|-1$:$\frac{|XLEN|}{2}$ of \RcsrTdataTwo.
+ *
+ * 8: Matches when \FcsrMcontrolSixMatch$=0$ would not match.
+ *
+ * 9: Matches when \FcsrMcontrolSixMatch$=1$ would not match.
+ *
+ * 12: Matches when \FcsrMcontrolSixMatch$=4$ would not match.
+ *
+ * 13: Matches when \FcsrMcontrolSixMatch$=5$ would not match.
+ *
+ * Other values are reserved for future use.
+ *
+ * All comparisons only look at the lower XLEN (in the current mode)
+ * bits of the compare values and of \RcsrTdataTwo.
+ * When \FcsrMcontrolSelect=1 and access size is N, this is further
+ * reduced, and comparisons only look at the lower N bits of the
+ * compare values and of \RcsrTdataTwo.
+ */
+#define CSR_MCONTROL6_MATCH_OFFSET          7
+#define CSR_MCONTROL6_MATCH_LENGTH          4
+#define CSR_MCONTROL6_MATCH                 (0xfULL << CSR_MCONTROL6_MATCH_OFFSET)
+/*
+ * When set, enable this trigger in M-mode.
+ */
+#define CSR_MCONTROL6_M_OFFSET              6
+#define CSR_MCONTROL6_M_LENGTH              1
+#define CSR_MCONTROL6_M                     (0x1ULL << CSR_MCONTROL6_M_OFFSET)
+/*
+ * When set, enable this trigger in S/HS-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * S-mode.
+ */
+#define CSR_MCONTROL6_S_OFFSET              4
+#define CSR_MCONTROL6_S_LENGTH              1
+#define CSR_MCONTROL6_S                     (0x1ULL << CSR_MCONTROL6_S_OFFSET)
+/*
+ * When set, enable this trigger in U-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * U-mode.
+ */
+#define CSR_MCONTROL6_U_OFFSET              3
+#define CSR_MCONTROL6_U_LENGTH              1
+#define CSR_MCONTROL6_U                     (0x1ULL << CSR_MCONTROL6_U_OFFSET)
+/*
+ * When set, the trigger fires on the virtual address or opcode of an
+ * instruction that is executed.
+ */
+#define CSR_MCONTROL6_EXECUTE_OFFSET        2
+#define CSR_MCONTROL6_EXECUTE_LENGTH        1
+#define CSR_MCONTROL6_EXECUTE               (0x1ULL << CSR_MCONTROL6_EXECUTE_OFFSET)
+/*
+ * When set, the trigger fires on the virtual address or data of any
+ * store.
+ */
+#define CSR_MCONTROL6_STORE_OFFSET          1
+#define CSR_MCONTROL6_STORE_LENGTH          1
+#define CSR_MCONTROL6_STORE                 (0x1ULL << CSR_MCONTROL6_STORE_OFFSET)
+/*
+ * When set, the trigger fires on the virtual address or data of any
+ * load.
+ */
+#define CSR_MCONTROL6_LOAD_OFFSET           0
+#define CSR_MCONTROL6_LOAD_LENGTH           1
+#define CSR_MCONTROL6_LOAD                  (0x1ULL << CSR_MCONTROL6_LOAD_OFFSET)
 #define CSR_ICOUNT                          0x7a1
 #define CSR_ICOUNT_TYPE_OFFSET              (XLEN-4)
 #define CSR_ICOUNT_TYPE_LENGTH              4
 #define CSR_ICOUNT_DMODE_OFFSET             (XLEN-5)
 #define CSR_ICOUNT_DMODE_LENGTH             1
 #define CSR_ICOUNT_DMODE                    (0x1ULL << CSR_ICOUNT_DMODE_OFFSET)
+/*
+ * When set, enable this trigger in VS-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ICOUNT_VS_OFFSET                26
+#define CSR_ICOUNT_VS_LENGTH                1
+#define CSR_ICOUNT_VS                       (0x1ULL << CSR_ICOUNT_VS_OFFSET)
+/*
+ * When set, enable this trigger in VU-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ICOUNT_VU_OFFSET                25
+#define CSR_ICOUNT_VU_LENGTH                1
+#define CSR_ICOUNT_VU                       (0x1ULL << CSR_ICOUNT_VU_OFFSET)
 /*
  * If this bit is implemented, the hardware sets it when this
  * trigger matches. The trigger's user can set or clear it at any
 /*
  * When count is decremented to 0, the trigger fires. Instead of
  * changing \FcsrIcountCount from 1 to 0, it is also acceptable for hardware to
- * clear \FcsrMcontrolM, \FcsrMcontrolS, and \FcsrMcontrolU. This allows \FcsrIcountCount to be hard-wired
+ * clear \FcsrIcountM, \FcsrIcountS, \FcsrIcountU, \FcsrIcountVs, and
+ * \FcsrIcountVu. This allows \FcsrIcountCount to be hard-wired
  * to 1 if this register just exists for single step.
  */
 #define CSR_ICOUNT_COUNT_OFFSET             10
 #define CSR_ICOUNT_COUNT_LENGTH             14
 #define CSR_ICOUNT_COUNT                    (0x3fffULL << CSR_ICOUNT_COUNT_OFFSET)
 /*
- * When set, every instruction completed in or trap taken from
- * M-mode decrements \FcsrIcountCount by 1.
+ * When set, enable this trigger in M-mode.
  */
 #define CSR_ICOUNT_M_OFFSET                 9
 #define CSR_ICOUNT_M_LENGTH                 1
 #define CSR_ICOUNT_M                        (0x1ULL << CSR_ICOUNT_M_OFFSET)
 /*
- * When set, every instruction completed in or trap taken from
- * S-mode decrements \FcsrIcountCount by 1.
+ * This bit becomes set when \FcsrIcountCount is decremented from 1
+ * to 0. It is cleared when the trigger fires.
+ */
+#define CSR_ICOUNT_PENDING_OFFSET           8
+#define CSR_ICOUNT_PENDING_LENGTH           1
+#define CSR_ICOUNT_PENDING                  (0x1ULL << CSR_ICOUNT_PENDING_OFFSET)
+/*
+ * When set, enable this trigger in S/HS-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * S-mode.
  */
 #define CSR_ICOUNT_S_OFFSET                 7
 #define CSR_ICOUNT_S_LENGTH                 1
 #define CSR_ICOUNT_S                        (0x1ULL << CSR_ICOUNT_S_OFFSET)
 /*
- * When set, every instruction completed in or trap taken from
- * U-mode decrements \FcsrIcountCount by 1.
+ * When set, enable this trigger in U-mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * U-mode.
  */
 #define CSR_ICOUNT_U_OFFSET                 6
 #define CSR_ICOUNT_U_LENGTH                 1
 #define CSR_ITRIGGER_HIT_OFFSET             (XLEN-6)
 #define CSR_ITRIGGER_HIT_LENGTH             1
 #define CSR_ITRIGGER_HIT                    (0x1ULL << CSR_ITRIGGER_HIT_OFFSET)
+/*
+ * When set, enable this trigger for interrupts that are taken from VS
+ * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ITRIGGER_VS_OFFSET              12
+#define CSR_ITRIGGER_VS_LENGTH              1
+#define CSR_ITRIGGER_VS                     (0x1ULL << CSR_ITRIGGER_VS_OFFSET)
+/*
+ * When set, enable this trigger for interrupts that are taken from VU
+ * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ITRIGGER_VU_OFFSET              11
+#define CSR_ITRIGGER_VU_LENGTH              1
+#define CSR_ITRIGGER_VU                     (0x1ULL << CSR_ITRIGGER_VU_OFFSET)
 /*
  * When set, enable this trigger for interrupts that are taken from M
  * mode.
 #define CSR_ITRIGGER_M_LENGTH               1
 #define CSR_ITRIGGER_M                      (0x1ULL << CSR_ITRIGGER_M_OFFSET)
 /*
- * When set, enable this trigger for interrupts that are taken from S
+ * When set, enable this trigger for interrupts that are taken from S/HS
  * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * S-mode.
  */
 #define CSR_ITRIGGER_S_OFFSET               7
 #define CSR_ITRIGGER_S_LENGTH               1
 /*
  * When set, enable this trigger for interrupts that are taken from U
  * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * U-mode.
  */
 #define CSR_ITRIGGER_U_OFFSET               6
 #define CSR_ITRIGGER_U_LENGTH               1
 #define CSR_ETRIGGER_HIT_OFFSET             (XLEN-6)
 #define CSR_ETRIGGER_HIT_LENGTH             1
 #define CSR_ETRIGGER_HIT                    (0x1ULL << CSR_ETRIGGER_HIT_OFFSET)
+/*
+ * When set, enable this trigger for exceptions that are taken from VS
+ * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ETRIGGER_VS_OFFSET              12
+#define CSR_ETRIGGER_VS_LENGTH              1
+#define CSR_ETRIGGER_VS                     (0x1ULL << CSR_ETRIGGER_VS_OFFSET)
+/*
+ * When set, enable this trigger for exceptions that are taken from VU
+ * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * virtualization mode.
+ */
+#define CSR_ETRIGGER_VU_OFFSET              11
+#define CSR_ETRIGGER_VU_LENGTH              1
+#define CSR_ETRIGGER_VU                     (0x1ULL << CSR_ETRIGGER_VU_OFFSET)
 /*
  * When set, non-maskable interrupts cause this
- * trigger to fire, regardless of the values of \FcsrMcontrolM, \FcsrMcontrolS, and \FcsrMcontrolU.
+ * trigger to fire, regardless of the values of \FcsrEtriggerM, \FcsrEtriggerS, and \FcsrEtriggerU.
  */
 #define CSR_ETRIGGER_NMI_OFFSET             10
 #define CSR_ETRIGGER_NMI_LENGTH             1
 #define CSR_ETRIGGER_M_LENGTH               1
 #define CSR_ETRIGGER_M                      (0x1ULL << CSR_ETRIGGER_M_OFFSET)
 /*
- * When set, enable this trigger for exceptions that are taken from S
+ * When set, enable this trigger for exceptions that are taken from S/HS
  * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * S-mode.
  */
 #define CSR_ETRIGGER_S_OFFSET               7
 #define CSR_ETRIGGER_S_LENGTH               1
 /*
  * When set, enable this trigger for exceptions that are taken from U
  * mode.
+ * This bit is hard-wired to 0 if the hart does not support
+ * U-mode.
  */
 #define CSR_ETRIGGER_U_OFFSET               6
 #define CSR_ETRIGGER_U_LENGTH               1
 #define CSR_ETRIGGER_ACTION_OFFSET          0
 #define CSR_ETRIGGER_ACTION_LENGTH          6
 #define CSR_ETRIGGER_ACTION                 (0x3fULL << CSR_ETRIGGER_ACTION_OFFSET)
+#define CSR_TMEXTTRIGGER                    0x7a1
+#define CSR_TMEXTTRIGGER_TYPE_OFFSET        (XLEN-4)
+#define CSR_TMEXTTRIGGER_TYPE_LENGTH        4
+#define CSR_TMEXTTRIGGER_TYPE               (0xfULL << CSR_TMEXTTRIGGER_TYPE_OFFSET)
+#define CSR_TMEXTTRIGGER_DMODE_OFFSET       (XLEN-5)
+#define CSR_TMEXTTRIGGER_DMODE_LENGTH       1
+#define CSR_TMEXTTRIGGER_DMODE              (0x1ULL << CSR_TMEXTTRIGGER_DMODE_OFFSET)
+/*
+ * If this bit is implemented, the hardware sets it when this
+ * trigger matches. The trigger's user can set or clear it at any
+ * time. It is used to determine which
+ * trigger(s) matched.  If the bit is not implemented, it is always 0
+ * and writing it has no effect.
+ */
+#define CSR_TMEXTTRIGGER_HIT_OFFSET         (XLEN-6)
+#define CSR_TMEXTTRIGGER_HIT_LENGTH         1
+#define CSR_TMEXTTRIGGER_HIT                (0x1ULL << CSR_TMEXTTRIGGER_HIT_OFFSET)
+/*
+ * This optional bit, when set, causes this trigger to fire whenever an attached
+ * interrupt controller signals a trigger.
+ */
+#define CSR_TMEXTTRIGGER_INTCTL_OFFSET      22
+#define CSR_TMEXTTRIGGER_INTCTL_LENGTH      1
+#define CSR_TMEXTTRIGGER_INTCTL             (0x1ULL << CSR_TMEXTTRIGGER_INTCTL_OFFSET)
+/*
+ * Selects any combination of up to 16 external debug trigger inputs
+ * that cause this trigger to fire.
+ */
+#define CSR_TMEXTTRIGGER_SELECT_OFFSET      6
+#define CSR_TMEXTTRIGGER_SELECT_LENGTH      16
+#define CSR_TMEXTTRIGGER_SELECT             (0xffffULL << CSR_TMEXTTRIGGER_SELECT_OFFSET)
+/*
+ * The action to take when the trigger fires. The values are explained
+ * in Table~\ref{tab:action}.
+ */
+#define CSR_TMEXTTRIGGER_ACTION_OFFSET      0
+#define CSR_TMEXTTRIGGER_ACTION_LENGTH      6
+#define CSR_TMEXTTRIGGER_ACTION             (0x3fULL << CSR_TMEXTTRIGGER_ACTION_OFFSET)
 #define CSR_TEXTRA32                        0x7a3
 /*
- * Data used together with \FcsrTextraThirtytwoMselect.
+ * Data used together with \FcsrTextraThirtytwoMhselect.
  */
-#define CSR_TEXTRA32_MVALUE_OFFSET          26
-#define CSR_TEXTRA32_MVALUE_LENGTH          6
-#define CSR_TEXTRA32_MVALUE                 (0x3fU << CSR_TEXTRA32_MVALUE_OFFSET)
+#define CSR_TEXTRA32_MHVALUE_OFFSET         26
+#define CSR_TEXTRA32_MHVALUE_LENGTH         6
+#define CSR_TEXTRA32_MHVALUE                (0x3fU << CSR_TEXTRA32_MHVALUE_OFFSET)
 /*
- * 0: Ignore \FcsrTextraThirtytwoMvalue.
+ * 0: Ignore \FcsrTextraThirtytwoMhvalue.
  *
- * 1: This trigger will only match if the low bits of
- * \RcsrMcontext equal \FcsrTextraThirtytwoMvalue.
+ * 4: This trigger will only match if the low bits of
+ * \RcsrMcontext/\RcsrHcontext equal \FcsrTextraThirtytwoMhvalue.
+ *
+ * 1, 5: This trigger will only match if the low bits of
+ * \RcsrMcontext/\RcsrHcontext equal \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}.
+ *
+ * 2, 6: This trigger will only match if VMID in hgatp equals the lower VMIDMAX
+ * (defined in the Privileged Spec) bits of \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}.
+ *
+ * 3, 7: Reserved.
+ *
+ * If the H extension is not supported, the only legal values are 0 and 4.
+ */
+#define CSR_TEXTRA32_MHSELECT_OFFSET        23
+#define CSR_TEXTRA32_MHSELECT_LENGTH        3
+#define CSR_TEXTRA32_MHSELECT               (0x7U << CSR_TEXTRA32_MHSELECT_OFFSET)
+/*
+ * When the least significant bit of this field is 1, it causes bits 7:0
+ * in the comparison to be ignored, when \FcsrTextraThirtytwoSselect=1.
+ * When the next most significant bit of this field is 1, it causes bits 15:8
+ * to be ignored in the comparison, when \FcsrTextraThirtytwoSselect=1.
  */
-#define CSR_TEXTRA32_MSELECT_OFFSET         25
-#define CSR_TEXTRA32_MSELECT_LENGTH         1
-#define CSR_TEXTRA32_MSELECT                (0x1U << CSR_TEXTRA32_MSELECT_OFFSET)
+#define CSR_TEXTRA32_SBYTEMASK_OFFSET       18
+#define CSR_TEXTRA32_SBYTEMASK_LENGTH       2
+#define CSR_TEXTRA32_SBYTEMASK              (0x3U << CSR_TEXTRA32_SBYTEMASK_OFFSET)
 /*
  * Data used together with \FcsrTextraThirtytwoSselect.
  *
  * 1: This trigger will only match if the low bits of
  * \RcsrScontext equal \FcsrTextraThirtytwoSvalue.
  *
- * 2: This trigger will only match if \Fasid in \Rsatp
+ * 2: This trigger will only match if the currently active ASID
+ * value, from either \Rsatp or \Rvsatp,
  * equals the lower ASIDMAX (defined in the Privileged Spec) bits of
  * \FcsrTextraThirtytwoSvalue.
  *
 #define CSR_TEXTRA32_SSELECT_LENGTH         2
 #define CSR_TEXTRA32_SSELECT                (0x3U << CSR_TEXTRA32_SSELECT_OFFSET)
 #define CSR_TEXTRA64                        0x7a3
-#define CSR_TEXTRA64_MVALUE_OFFSET          51
-#define CSR_TEXTRA64_MVALUE_LENGTH          13
-#define CSR_TEXTRA64_MVALUE                 (0x1fffULL << CSR_TEXTRA64_MVALUE_OFFSET)
-#define CSR_TEXTRA64_MSELECT_OFFSET         50
-#define CSR_TEXTRA64_MSELECT_LENGTH         1
-#define CSR_TEXTRA64_MSELECT                (0x1ULL << CSR_TEXTRA64_MSELECT_OFFSET)
+#define CSR_TEXTRA64_MHVALUE_OFFSET         51
+#define CSR_TEXTRA64_MHVALUE_LENGTH         13
+#define CSR_TEXTRA64_MHVALUE                (0x1fffULL << CSR_TEXTRA64_MHVALUE_OFFSET)
+#define CSR_TEXTRA64_MHSELECT_OFFSET        48
+#define CSR_TEXTRA64_MHSELECT_LENGTH        3
+#define CSR_TEXTRA64_MHSELECT               (0x7ULL << CSR_TEXTRA64_MHSELECT_OFFSET)
+/*
+ * When the least significant bit of this field is 1, it causes bits 7:0
+ * in the comparison to be ignored, when \FcsrTextraSixtyfourSselect=1.
+ * Likewise, the second bit controls the comparison of bits 15:8,
+ * third bit controls the comparison of bits 23:16,
+ * fourth bit controls the comparison of bits 31:24, and
+ * fifth bit controls the comparison of bits 33:32.
+ */
+#define CSR_TEXTRA64_SBYTEMASK_OFFSET       36
+#define CSR_TEXTRA64_SBYTEMASK_LENGTH       5
+#define CSR_TEXTRA64_SBYTEMASK              (0x1fULL << CSR_TEXTRA64_SBYTEMASK_OFFSET)
 #define CSR_TEXTRA64_SVALUE_OFFSET          2
 #define CSR_TEXTRA64_SVALUE_LENGTH          34
 #define CSR_TEXTRA64_SVALUE                 (0x3ffffffffULL << CSR_TEXTRA64_SVALUE_OFFSET)
 #define CSR_TEXTRA64_SSELECT_LENGTH         2
 #define CSR_TEXTRA64_SSELECT                (0x3ULL << CSR_TEXTRA64_SSELECT_OFFSET)
 #define DM_DMSTATUS                         0x11
+/*
+ * 0: Unimplemented, or \FdmDmcontrolNdmreset is zero and no ndmreset is currently
+ * in progress.
+ *
+ * 1: \FdmDmcontrolNdmreset is currently nonzero, or there is an ndmreset in progress.
+ */
+#define DM_DMSTATUS_NDMRESETPENDING_OFFSET  24
+#define DM_DMSTATUS_NDMRESETPENDING_LENGTH  1
+#define DM_DMSTATUS_NDMRESETPENDING         (0x1U << DM_DMSTATUS_NDMRESETPENDING_OFFSET)
+/*
+ * 0: The per-hart {\tt unavail} bits reflect the current state of the hart.
+ *
+ * 1: The per-hart {\tt unavail} bits are sticky. Once they are set, they will
+ * not clear until the debugger acknowledges them using \FdmDmcontrolAckunavail.
+ */
+#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET    23
+#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH    1
+#define DM_DMSTATUS_STICKYUNAVAIL           (0x1U << DM_DMSTATUS_STICKYUNAVAIL_OFFSET)
 /*
  * If 1, then there is an implicit {\tt ebreak} instruction at the
  * non-existent word immediately after the Program Buffer. This saves
 #define DM_DMSTATUS_ANYRESUMEACK            (0x1U << DM_DMSTATUS_ANYRESUMEACK_OFFSET)
 /*
  * This field is 1 when all currently selected harts do not exist in
- * this platform.
+ * this hardware platform.
  */
 #define DM_DMSTATUS_ALLNONEXISTENT_OFFSET   15
 #define DM_DMSTATUS_ALLNONEXISTENT_LENGTH   1
 #define DM_DMSTATUS_ALLNONEXISTENT          (0x1U << DM_DMSTATUS_ALLNONEXISTENT_OFFSET)
 /*
  * This field is 1 when any currently selected hart does not exist in
- * this platform.
+ * this hardware platform.
  */
 #define DM_DMSTATUS_ANYNONEXISTENT_OFFSET   14
 #define DM_DMSTATUS_ANYNONEXISTENT_LENGTH   1
 #define DM_DMSTATUS_ANYNONEXISTENT          (0x1U << DM_DMSTATUS_ANYNONEXISTENT_OFFSET)
 /*
- * This field is 1 when all currently selected harts are unavailable.
+ * This field is 1 when all currently selected harts are
+ * unavailable, or (if \FdmDmstatusStickyunavail is 1) were
+ * unavailable without that being acknowledged.
  */
 #define DM_DMSTATUS_ALLUNAVAIL_OFFSET       13
 #define DM_DMSTATUS_ALLUNAVAIL_LENGTH       1
 #define DM_DMSTATUS_ALLUNAVAIL              (0x1U << DM_DMSTATUS_ALLUNAVAIL_OFFSET)
 /*
- * This field is 1 when any currently selected hart is unavailable.
+ * This field is 1 when any currently selected hart is unavailable,
+ * or (if \FdmDmstatusStickyunavail is 1) was unavailable without
+ * that being acknowledged.
  */
 #define DM_DMSTATUS_ANYUNAVAIL_OFFSET       12
 #define DM_DMSTATUS_ANYUNAVAIL_LENGTH       1
  * 2: There is a Debug Module and it conforms to version 0.13 of this
  * specification.
  *
- * 3: There is a Debug Module and it conforms to version 0.14 of this
+ * 3: There is a Debug Module and it conforms to version 1.0 of this
  * specification.
  *
  * 15: There is a Debug Module but it does not conform to any
 #define DM_DMCONTROL_ACKHAVERESET_OFFSET    28
 #define DM_DMCONTROL_ACKHAVERESET_LENGTH    1
 #define DM_DMCONTROL_ACKHAVERESET           (0x1U << DM_DMCONTROL_ACKHAVERESET_OFFSET)
+/*
+ * 0: No effect.
+ *
+ * 1: Clears {\tt unavail} for any selected harts.
+ *
+ * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
+ */
+#define DM_DMCONTROL_ACKUNAVAIL_OFFSET      27
+#define DM_DMCONTROL_ACKUNAVAIL_LENGTH      1
+#define DM_DMCONTROL_ACKUNAVAIL             (0x1U << DM_DMCONTROL_ACKUNAVAIL_OFFSET)
 /*
  * Selects the definition of currently selected harts.
  *
 #define DM_DMCONTROL_HARTSELHI_OFFSET       6
 #define DM_DMCONTROL_HARTSELHI_LENGTH       10
 #define DM_DMCONTROL_HARTSELHI              (0x3ffU << DM_DMCONTROL_HARTSELHI_OFFSET)
+/*
+ * This optional field sets \Fkeepalive for all currently selected
+ * harts, unless \FdmDmcontrolClrkeepalive is simultaneously set to
+ * 1.
+ *
+ * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
+ */
+#define DM_DMCONTROL_SETKEEPALIVE_OFFSET    5
+#define DM_DMCONTROL_SETKEEPALIVE_LENGTH    1
+#define DM_DMCONTROL_SETKEEPALIVE           (0x1U << DM_DMCONTROL_SETKEEPALIVE_OFFSET)
+/*
+ * This optional field clears \Fkeepalive for all currently selected
+ * harts.
+ *
+ * Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
+ */
+#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET    4
+#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH    1
+#define DM_DMCONTROL_CLRKEEPALIVE           (0x1U << DM_DMCONTROL_CLRKEEPALIVE_OFFSET)
 /*
  * This optional field writes the halt-on-reset request bit for all
  * currently selected harts, unless \FdmDmcontrolClrresethaltreq is
 #define DM_DMCONTROL_CLRRESETHALTREQ        (0x1U << DM_DMCONTROL_CLRRESETHALTREQ_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
+ * hardware platform. The signal should reset every part of the hardware platform, including
  * every hart, except for the DM and any logic required to access the
  * DM.
- * To perform a system reset the debugger writes 1,
+ * To perform a hardware platform reset the debugger writes 1,
  * and then writes 0
  * to deassert the reset.
  */
 #define DM_DMCONTROL_NDMRESET               (0x1U << DM_DMCONTROL_NDMRESET_OFFSET)
 /*
  * This bit serves as a reset signal for the Debug Module itself.
+ * After changing the value of this bit, the debugger must poll
+ * \RdmDmcontrol until \FdmDmcontrolDmactive has taken the requested value
+ * before performing any action that assumes the requested \FdmDmcontrolDmactive
+ * state change has completed.  Hardware may
+ * take an arbitrarily long time to complete activation or deactivation and will
+ * indicate completion by setting \FdmDmcontrolDmactive to the requested value.
  *
  * 0: The module's state, including authentication mechanism,
  * takes its reset values (the \FdmDmcontrolDmactive bit is the only bit which can
  * be written to something other than its reset value). Any accesses
- * to the module may fail. Specifically, \FdmDmstatusVersion may not return
+ * to the module may fail. Specifically, \FdmDmstatusVersion might not return
  * correct data.
  *
- * 1: The module functions normally. After writing 1, the debugger should
- * poll \RdmDmcontrol until \FdmDmcontrolDmactive is high. Hardware may
- * take an arbitrarily long time to initialize and will indicate completion
- * by setting dmactive to 1.
+ * 1: The module functions normally.
  *
  * No other mechanism should exist that may result in resetting the
  * Debug Module after power up.
  *
- * A debugger may pulse this bit low to get the Debug Module into a
- * known state.
+ * To place the Debug Module into a known state, a debugger may write 0 to \FdmDmcontrolDmactive,
+ * poll until \FdmDmcontrolDmactive is observed 0, write 1 to \FdmDmcontrolDmactive, and
+ * poll until \FdmDmcontrolDmactive is observed 1.
  *
  * Implementations may pay attention to this bit to further aid
  * debugging, for example by preventing the Debug Module from being
 #define DM_HAWINDOWSEL                      0x14
 /*
  * The high bits of this field may be tied to 0, depending on how large
- * the array mask register is.  E.g.\ on a system with 48 harts only bit 0
+ * the array mask register is.  E.g.\ on a hardware platform with 48 harts only bit 0
  * of this field may actually be writable.
  */
 #define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET   0
 #define DM_DATA0_DATA_OFFSET                0
 #define DM_DATA0_DATA_LENGTH                32
 #define DM_DATA0_DATA                       (0xffffffffU << DM_DATA0_DATA_OFFSET)
+#define DM_DATA1                            0x05
+#define DM_DATA2                            0x06
+#define DM_DATA3                            0x07
+#define DM_DATA4                            0x08
+#define DM_DATA5                            0x09
+#define DM_DATA6                            0x0a
+#define DM_DATA7                            0x0b
+#define DM_DATA8                            0x0c
+#define DM_DATA9                            0x0d
+#define DM_DATA10                           0x0e
 #define DM_DATA11                           0x0f
 #define DM_PROGBUF0                         0x20
 #define DM_PROGBUF0_DATA_OFFSET             0
 #define DM_PROGBUF0_DATA_LENGTH             32
 #define DM_PROGBUF0_DATA                    (0xffffffffU << DM_PROGBUF0_DATA_OFFSET)
+#define DM_PROGBUF1                         0x21
+#define DM_PROGBUF2                         0x22
+#define DM_PROGBUF3                         0x23
+#define DM_PROGBUF4                         0x24
+#define DM_PROGBUF5                         0x25
+#define DM_PROGBUF6                         0x26
+#define DM_PROGBUF7                         0x27
+#define DM_PROGBUF8                         0x28
+#define DM_PROGBUF9                         0x29
+#define DM_PROGBUF10                        0x2a
+#define DM_PROGBUF11                        0x2b
+#define DM_PROGBUF12                        0x2c
+#define DM_PROGBUF13                        0x2d
+#define DM_PROGBUF14                        0x2e
 #define DM_PROGBUF15                        0x2f
 #define DM_AUTHDATA                         0x30
 #define DM_AUTHDATA_DATA_OFFSET             0
 #define DM_DMCS2_GROUPTYPE_LENGTH           1
 #define DM_DMCS2_GROUPTYPE                  (0x1U << DM_DMCS2_GROUPTYPE_OFFSET)
 /*
- * This field contains the currently selected external trigger.
+ * This field contains the currently selected DM external trigger.
  *
  * If a non-existent trigger value is written here, the hardware will
- * change it to a valid one or 0 if no external triggers exist.
+ * change it to a valid one or 0 if no DM external triggers exist.
  */
-#define DM_DMCS2_EXTTRIGGER_OFFSET          7
-#define DM_DMCS2_EXTTRIGGER_LENGTH          4
-#define DM_DMCS2_EXTTRIGGER                 (0xfU << DM_DMCS2_EXTTRIGGER_OFFSET)
+#define DM_DMCS2_DMEXTTRIGGER_OFFSET        7
+#define DM_DMCS2_DMEXTTRIGGER_LENGTH        4
+#define DM_DMCS2_DMEXTTRIGGER               (0xfU << DM_DMCS2_DMEXTTRIGGER_OFFSET)
 /*
  * When \FdmDmcsTwoHgselect is 0, contains the group of the hart
  * specified by \Fhartsel.
  *
- * When \FdmDmcsTwoHgselect is 1, contains the group of the external
- * trigger selected by \FdmDmcsTwoExttrigger.
+ * When \FdmDmcsTwoHgselect is 1, contains the group of the DM external
+ * trigger selected by \FdmDmcsTwoDmexttrigger.
  *
  * Writes only have an effect if \FdmDmcsTwoHgwrite is also written 1.
  *
 #define DM_DMCS2_GROUP_LENGTH               5
 #define DM_DMCS2_GROUP                      (0x1fU << DM_DMCS2_GROUP_OFFSET)
 /*
- * When \FdmDmcsTwoHgselect is 0, writing 1 changes the group of all
- * selected harts to the value written to \FdmDmcsTwoGroup.
- *
  * When 1 is written and \FdmDmcsTwoHgselect is 0, for every selected
  * hart the DM will change its group to the value written to \FdmDmcsTwoGroup,
  * if the hardware supports that group for that hart.
+ * Implementations may also change the group of a minimal set of
+ * unselected harts in the same way, if that is necessary due to
+ * a hardware limitation.
  *
  * When 1 is written and \FdmDmcsTwoHgselect is 1, the DM will change
- * the group of the external trigger selected by \FdmDmcsTwoExttrigger
+ * the group of the DM external trigger selected by \FdmDmcsTwoDmexttrigger
  * to the value written to \FdmDmcsTwoGroup, if the hardware supports
  * that group for that trigger.
  *
 /*
  * 0: Operate on harts.
  *
- * 1: Operate on external triggers.
+ * 1: Operate on DM external triggers.
  *
- * If there are no external triggers, this field must be tied to 0.
+ * If there are no DM external triggers, this field must be tied to 0.
  */
 #define DM_DMCS2_HGSELECT_OFFSET            0
 #define DM_DMCS2_HGSELECT_LENGTH            1
 #define DM_SBDATA3_DATA                     (0xffffffffU << DM_SBDATA3_DATA_OFFSET)
 #define DM_CUSTOM                           0x1f
 #define DM_CUSTOM0                          0x70
+#define DM_CUSTOM1                          0x71
+#define DM_CUSTOM2                          0x72
+#define DM_CUSTOM3                          0x73
+#define DM_CUSTOM4                          0x74
+#define DM_CUSTOM5                          0x75
+#define DM_CUSTOM6                          0x76
+#define DM_CUSTOM7                          0x77
+#define DM_CUSTOM8                          0x78
+#define DM_CUSTOM9                          0x79
+#define DM_CUSTOM10                         0x7a
+#define DM_CUSTOM11                         0x7b
+#define DM_CUSTOM12                         0x7c
+#define DM_CUSTOM13                         0x7d
+#define DM_CUSTOM14                         0x7e
 #define DM_CUSTOM15                         0x7f
 #define SHORTNAME                           0x123
 /*
  * 0: No effect. This variant must be supported.
  *
  * 1: After a successful register access, \FacAccessregisterRegno is
- * incremented (wrapping around to 0). Supporting this variant is
- * optional. It is undefined whether the increment happens when
- * \FacAccessregisterTransfer is 0.
+ * incremented. Incrementing past the highest supported value
+ * causes \FacAccessregisterRegno to become \unspecified.  Supporting
+ * this variant is optional. It is undefined whether the increment
+ * happens when \FacAccessregisterTransfer is 0.
  */
 #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 19
 #define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1
  * 0: Addresses are physical (to the hart they are performed on).
  *
  * 1: Addresses are virtual, and translated the way they would be from
- * M-mode, with \FcsrMcontrolMprv set.
+ * M-mode, with \FcsrMstatusMprv set.
+ *
+ * Debug Modules on systems without address translation (i.e. virtual addresses equal physical)
+ * may optionally allow \FacAccessmemoryAamvirtual set to 1, which would produce the same result as
+ * that same abstract command with \FacAccessmemoryAamvirtual cleared.
  */
 #define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET  23
 #define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH  1
 #define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2
 #define AC_ACCESS_MEMORY_TARGET_SPECIFIC    (0x3U << AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET)
 #define VIRT_PRIV                           virtual
+/*
+ * Contains the virtualization mode the hart was operating in when Debug
+ * Mode was entered. The encoding is described in Table \ref{tab:privlevel},
+ * and matches the virtualization mode encoding from the Privileged Spec.
+ * A user can write this value to change the hart's virtualization mode
+ * when exiting Debug Mode.
+ */
+#define VIRT_PRIV_V_OFFSET                  2
+#define VIRT_PRIV_V_LENGTH                  1
+#define VIRT_PRIV_V                         (0x1U << VIRT_PRIV_V_OFFSET)
 /*
  * Contains the privilege level the hart was operating in when Debug
  * Mode was entered. The encoding is described in Table
index 998290cb96ec0afb119ca949d5c741c3c7a64bf3..8faa154bae9632a43a42e7b002c9d0f9f539a935 100644 (file)
@@ -17,214 +17,202 @@ static uint32_t bit(uint32_t value, unsigned int b)
        return (value >> b) & 1;
 }
 
+static uint32_t inst_rd(uint32_t r) __attribute__ ((unused));
+static uint32_t inst_rd(uint32_t r)
+{
+       return bits(r, 4, 0) << 7;
+}
+
+static uint32_t inst_rs1(uint32_t r) __attribute__ ((unused));
+static uint32_t inst_rs1(uint32_t r)
+{
+       return bits(r, 4, 0) << 15;
+}
+
+static uint32_t inst_rs2(uint32_t r) __attribute__ ((unused));
+static uint32_t inst_rs2(uint32_t r)
+{
+       return bits(r, 4, 0) << 20;
+}
+
+static uint32_t imm_i(uint32_t imm) __attribute__ ((unused));
+static uint32_t imm_i(uint32_t imm)
+{
+       return bits(imm, 11, 0) << 20;
+}
+
+static uint32_t imm_s(uint32_t imm) __attribute__ ((unused));
+static uint32_t imm_s(uint32_t imm)
+{
+       return (bits(imm, 4, 0) << 7) | (bits(imm, 11, 5) << 25);
+}
+
+static uint32_t imm_b(uint32_t imm) __attribute__ ((unused));
+static uint32_t imm_b(uint32_t imm)
+{
+       return (bit(imm, 11) << 7) | (bits(imm, 4, 1) << 8) | (bits(imm, 10, 5) << 25) | (bit(imm, 12) << 31);
+}
+
+static uint32_t imm_u(uint32_t imm) __attribute__ ((unused));
+static uint32_t imm_u(uint32_t imm)
+{
+       return bits(imm, 31, 12) << 12;
+}
+
+static uint32_t imm_j(uint32_t imm) __attribute__ ((unused));
+static uint32_t imm_j(uint32_t imm)
+{
+       return (bits(imm, 19, 12) << 12) | (bit(imm, 11) << 20) | (bits(imm, 10, 1) << 21) | (bit(imm, 20) << 31);
+}
+
 static uint32_t jal(unsigned int rd, uint32_t imm) __attribute__ ((unused));
 static uint32_t jal(unsigned int rd, uint32_t imm)
 {
-       return (bit(imm, 20) << 31) |
-               (bits(imm, 10, 1) << 21) |
-               (bit(imm, 11) << 20) |
-               (bits(imm, 19, 12) << 12) |
-               (rd << 7) |
-               MATCH_JAL;
+       return imm_j(imm) | inst_rd(rd) | MATCH_JAL;
 }
 
 static uint32_t csrsi(unsigned int csr, uint16_t imm) __attribute__ ((unused));
 static uint32_t csrsi(unsigned int csr, uint16_t imm)
 {
-       return (csr << 20) |
-               (bits(imm, 4, 0) << 15) |
-               MATCH_CSRRSI;
+       return imm_i(csr) | inst_rs1(imm) | MATCH_CSRRSI;
 }
 
 static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t sw(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (src << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_SW;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SW;
 }
 
 static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t sd(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (src << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_SD;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SD;
 }
 
 static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t sh(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (src << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_SH;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SH;
 }
 
 static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t sb(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (src << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_SB;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_SB;
 }
 
 static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t ld(unsigned int rd, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(rd, 4, 0) << 7) |
-               MATCH_LD;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LD;
 }
 
 static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t lw(unsigned int rd, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(rd, 4, 0) << 7) |
-               MATCH_LW;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LW;
 }
 
 static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t lh(unsigned int rd, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(rd, 4, 0) << 7) |
-               MATCH_LH;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LH;
 }
 
 static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t lb(unsigned int rd, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(rd, 4, 0) << 7) |
-               MATCH_LB;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(rd) | MATCH_LB;
 }
 
 static uint32_t csrw(unsigned int source, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrw(unsigned int source, unsigned int csr)
 {
-       return (csr << 20) | (source << 15) | MATCH_CSRRW;
+       return imm_i(csr) | inst_rs1(source) | MATCH_CSRRW;
 }
 
 static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
 static uint32_t addi(unsigned int dest, unsigned int src, uint16_t imm)
 {
-       return (bits(imm, 11, 0) << 20) |
-               (src << 15) |
-               (dest << 7) |
-               MATCH_ADDI;
+       return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_ADDI;
 }
 
 static uint32_t csrr(unsigned int rd, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrr(unsigned int rd, unsigned int csr)
 {
-       return (csr << 20) | (rd << 7) | MATCH_CSRRS;
+       return imm_i(csr) | inst_rd(rd) | MATCH_CSRRS;
 }
 
 static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrrs(unsigned int rd, unsigned int rs, unsigned int csr)
 {
-       return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRS;
+       return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRS;
 }
 
 static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrrw(unsigned int rd, unsigned int rs, unsigned int csr)
 {
-       return (csr << 20) | (rs << 15) | (rd << 7) | MATCH_CSRRW;
+       return imm_i(csr) | inst_rs1(rs) | inst_rd(rd) | MATCH_CSRRW;
 }
 
 static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrrci(unsigned int rd, unsigned int zimm, unsigned int csr)
 {
-       return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRCI;
+       return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRCI;
 }
 
 static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr) __attribute__ ((unused));
 static uint32_t csrrsi(unsigned int rd, unsigned int zimm, unsigned int csr)
 {
-       return (csr << 20) | (zimm << 15) | (rd << 7) | MATCH_CSRRSI;
+       return imm_i(csr) | inst_rs1(zimm) | inst_rd(rd) | MATCH_CSRRSI;
 }
 
 static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t fsw(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (bits(src, 4, 0) << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_FSW;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSW;
 }
 
 static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t fsd(unsigned int src, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 5) << 25) |
-               (bits(src, 4, 0) << 20) |
-               (base << 15) |
-               (bits(offset, 4, 0) << 7) |
-               MATCH_FSD;
+       return imm_s(offset) | inst_rs2(src) | inst_rs1(base) | MATCH_FSD;
 }
 
 static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t flw(unsigned int dest, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(dest, 4, 0) << 7) |
-               MATCH_FLW;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLW;
 }
 
 static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset) __attribute__ ((unused));
 static uint32_t fld(unsigned int dest, unsigned int base, uint16_t offset)
 {
-       return (bits(offset, 11, 0) << 20) |
-               (base << 15) |
-               (bits(dest, 4, 0) << 7) |
-               MATCH_FLD;
+       return imm_i(offset) | inst_rs1(base) | inst_rd(dest) | MATCH_FLD;
 }
 
 static uint32_t fmv_x_w(unsigned dest, unsigned src) __attribute__ ((unused));
 static uint32_t fmv_x_w(unsigned dest, unsigned src)
 {
-       return src << 15 |
-               dest << 7 |
-               MATCH_FMV_X_W;
+       return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_W;
 }
 
 static uint32_t fmv_x_d(unsigned dest, unsigned src) __attribute__ ((unused));
 static uint32_t fmv_x_d(unsigned dest, unsigned src)
 {
-       return src << 15 |
-               dest << 7 |
-               MATCH_FMV_X_D;
+       return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_X_D;
 }
 
 static uint32_t fmv_w_x(unsigned dest, unsigned src) __attribute__ ((unused));
 static uint32_t fmv_w_x(unsigned dest, unsigned src)
 {
-       return src << 15 |
-               dest << 7 |
-               MATCH_FMV_W_X;
+       return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_W_X;
 }
 
 static uint32_t fmv_d_x(unsigned dest, unsigned src) __attribute__ ((unused));
 static uint32_t fmv_d_x(unsigned dest, unsigned src)
 {
-       return src << 15 |
-               dest << 7 |
-               MATCH_FMV_D_X;
+       return inst_rs1(src) | inst_rd(dest) | MATCH_FMV_D_X;
 }
 
 static uint32_t ebreak(void) __attribute__ ((unused));
@@ -250,9 +238,7 @@ static uint32_t fence_i(void)
 static uint32_t lui(unsigned int dest, uint32_t imm) __attribute__ ((unused));
 static uint32_t lui(unsigned int dest, uint32_t imm)
 {
-       return (bits(imm, 19, 0) << 12) |
-               (dest << 7) |
-               MATCH_LUI;
+       return imm_u(imm) | inst_rd(dest) | MATCH_LUI;
 }
 
 /*
@@ -299,19 +285,13 @@ static uint32_t nop(void)
 static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
 static uint32_t xori(unsigned int dest, unsigned int src, uint16_t imm)
 {
-       return (bits(imm, 11, 0) << 20) |
-               (src << 15) |
-               (dest << 7) |
-               MATCH_XORI;
+       return imm_i(imm) | inst_rs1(src) | inst_rd(dest) | MATCH_XORI;
 }
 
 static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt) __attribute__ ((unused));
 static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
 {
-       return (bits(shamt, 4, 0) << 20) |
-               (src << 15) |
-               (dest << 7) |
-               MATCH_SRLI;
+       return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI;
 }
 
 static uint32_t fence(void) __attribute__((unused));
@@ -323,28 +303,25 @@ static uint32_t fence(void)
 static uint32_t auipc(unsigned int dest) __attribute__((unused));
 static uint32_t auipc(unsigned int dest)
 {
-       return MATCH_AUIPC | (dest << 7);
+       return MATCH_AUIPC | inst_rd(dest);
 }
 
 static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm) __attribute__((unused));
 static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
 {
-       return (bits(imm, 10, 0) << 20) |
-               (src << 15) |
-               (dest << 7) |
-               MATCH_VSETVLI;
+       return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI;
 }
 
 static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
 static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
 {
-       return (vs2 << 20) | (rd << 7) | MATCH_VMV_X_S;
+       return inst_rs2(vs2) | inst_rd(rd) | MATCH_VMV_X_S;
 }
 
 static uint32_t vmv_s_x(unsigned int vd, unsigned int vs2) __attribute__((unused));
 static uint32_t vmv_s_x(unsigned int vd, unsigned int rs1)
 {
-       return (rs1 << 15) | (vd << 7) | MATCH_VMV_S_X;
+       return inst_rs1(rs1) | inst_rd(vd) | MATCH_VMV_S_X;
 }
 
 static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
@@ -352,6 +329,6 @@ static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
 static uint32_t vslide1down_vx(unsigned int vd, unsigned int vs2,
                unsigned int rs1, unsigned int vm)
 {
-       return (vm << 25) | (vs2 << 20) | (rs1 << 15) | (vd << 7) |
-               MATCH_VSLIDE1DOWN_VX;
+       return ((vm & 1) << 25) | inst_rs2(vs2) | inst_rs1(rs1) | inst_rd(vd) | MATCH_VSLIDE1DOWN_VX;
 }
+
index 2fa925aff776d58514308b9b9a0a48a19620e8fe..62a04f0933163e93b8c3a0f2715f1773448e15db 100644 (file)
@@ -40,20 +40,10 @@ int riscv_program_write(struct riscv_program *program);
  * program to execute.  That's OK, just make sure this eventually terminates.
  * */
 int riscv_program_exec(struct riscv_program *p, struct target *t);
-int riscv_program_load(struct riscv_program *p, struct target *t);
-
-/* Clears a program, removing all the state associated with it. */
-int riscv_program_clear(struct riscv_program *p, struct target *t);
 
 /* A lower level interface, you shouldn't use this unless you have a reason. */
 int riscv_program_insert(struct riscv_program *p, riscv_insn_t i);
 
-/* There is hardware support for saving at least one register.  This register
- * doesn't need to be saved/restored the usual way, which is useful during
- * early initialization when we can't save/restore arbitrary registerrs to host
- * memory. */
-int riscv_program_save_to_dscratch(struct riscv_program *p, enum gdb_regno to_save);
-
 /* 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. */
index 86a95f63507caf40a01d682f784a51f4c86e72f2..ae0b6e481b157cceb70fd2fca900a9be11c3d1d9 100644 (file)
@@ -152,6 +152,9 @@ typedef enum slot {
 #define DMINFO_AUTHTYPE                        (3<<2)
 #define DMINFO_VERSION                 3
 
+#define DMAUTHDATA0                            0x12
+#define DMAUTHDATA1                            0x13
+
 /*** Info about the core being debugged. ***/
 
 #define DBUS_ADDRESS_UNKNOWN   0xffff
@@ -216,8 +219,7 @@ typedef struct {
 
 static int poll_target(struct target *target, bool announce);
 static int riscv011_poll(struct target *target);
-static int get_register(struct target *target, riscv_reg_t *value, int hartid,
-               int regid);
+static int get_register(struct target *target, riscv_reg_t *value, int regid);
 
 /*** Utility functions. ***/
 
@@ -226,6 +228,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int hartid,
 static riscv011_info_t *get_info(const struct target *target)
 {
        riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       assert(info);
+       assert(info->version_specific);
        return (riscv011_info_t *) info->version_specific;
 }
 
@@ -1230,7 +1234,7 @@ static int update_mstatus_actual(struct target *target)
        /* Force reading the register. In that process mstatus_actual will be
         * updated. */
        riscv_reg_t mstatus;
-       return get_register(target, &mstatus, 0, GDB_REGNO_MSTATUS);
+       return get_register(target, &mstatus, GDB_REGNO_MSTATUS);
 }
 
 /*** OpenOCD target functions. ***/
@@ -1334,10 +1338,8 @@ static int register_write(struct target *target, unsigned int number,
        return ERROR_OK;
 }
 
-static int get_register(struct target *target, riscv_reg_t *value, int hartid,
-               int regid)
+static int get_register(struct target *target, riscv_reg_t *value, int regid)
 {
-       assert(hartid == 0);
        riscv011_info_t *info = get_info(target);
 
        maybe_write_tselect(target);
@@ -1380,10 +1382,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int hartid,
        return ERROR_OK;
 }
 
-static int set_register(struct target *target, int hartid, int regid,
-               uint64_t value)
+static int set_register(struct target *target, int regid, uint64_t value)
 {
-       assert(hartid == 0);
        return register_write(target, regid, value);
 }
 
@@ -1523,7 +1523,7 @@ static int examine(struct target *target)
        }
 
        /* Pretend this is a 32-bit system until we have found out the true value. */
-       r->xlen[0] = 32;
+       r->xlen = 32;
 
        /* Figure out XLEN, and test writing all of Debug RAM while we're at it. */
        cache_set32(target, 0, xori(S1, ZERO, -1));
@@ -1551,11 +1551,11 @@ static int examine(struct target *target)
        uint32_t word1 = cache_get32(target, 1);
        riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
        if (word0 == 1 && word1 == 0) {
-               generic_info->xlen[0] = 32;
+               generic_info->xlen = 32;
        } else if (word0 == 0xffffffff && word1 == 3) {
-               generic_info->xlen[0] = 64;
+               generic_info->xlen = 64;
        } else if (word0 == 0xffffffff && word1 == 0xffffffff) {
-               generic_info->xlen[0] = 128;
+               generic_info->xlen = 128;
        } else {
                uint32_t exception = cache_get32(target, info->dramsize-1);
                LOG_ERROR("Failed to discover xlen; word0=0x%x, word1=0x%x, exception=0x%x",
@@ -1565,11 +1565,11 @@ static int examine(struct target *target)
        }
        LOG_DEBUG("Discovered XLEN is %d", riscv_xlen(target));
 
-       if (read_remote_csr(target, &r->misa[0], CSR_MISA) != ERROR_OK) {
+       if (read_remote_csr(target, &r->misa, CSR_MISA) != ERROR_OK) {
                const unsigned old_csr_misa = 0xf10;
                LOG_WARNING("Failed to read misa at 0x%x; trying 0x%x.", CSR_MISA,
                                old_csr_misa);
-               if (read_remote_csr(target, &r->misa[0], old_csr_misa) != ERROR_OK) {
+               if (read_remote_csr(target, &r->misa, old_csr_misa) != ERROR_OK) {
                        /* Maybe this is an old core that still has $misa at the old
                         * address. */
                        LOG_ERROR("Failed to read misa at 0x%x.", old_csr_misa);
@@ -1591,7 +1591,7 @@ static int examine(struct target *target)
        for (size_t i = 0; i < 32; ++i)
                reg_cache_set(target, i, -1);
        LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
-                       riscv_xlen(target), r->misa[0]);
+                       riscv_xlen(target), r->misa);
 
        return ERROR_OK;
 }
@@ -2294,21 +2294,95 @@ static int arch_state(struct target *target)
        return ERROR_OK;
 }
 
+COMMAND_HELPER(riscv011_print_info, struct target *target)
+{
+       /* Abstract description. */
+       riscv_print_info_line(CMD, "target", "memory.read_while_running8", 0);
+       riscv_print_info_line(CMD, "target", "memory.write_while_running8", 0);
+       riscv_print_info_line(CMD, "target", "memory.read_while_running16", 0);
+       riscv_print_info_line(CMD, "target", "memory.write_while_running16", 0);
+       riscv_print_info_line(CMD, "target", "memory.read_while_running32", 0);
+       riscv_print_info_line(CMD, "target", "memory.write_while_running32", 0);
+       riscv_print_info_line(CMD, "target", "memory.read_while_running64", 0);
+       riscv_print_info_line(CMD, "target", "memory.write_while_running64", 0);
+       riscv_print_info_line(CMD, "target", "memory.read_while_running128", 0);
+       riscv_print_info_line(CMD, "target", "memory.write_while_running128", 0);
+
+       uint32_t dminfo = dbus_read(target, DMINFO);
+       riscv_print_info_line(CMD, "dm", "authenticated", get_field(dminfo, DMINFO_AUTHENTICATED));
+
+       return 0;
+}
+
+static int wait_for_authbusy(struct target *target)
+{
+       time_t start = time(NULL);
+       while (1) {
+               uint32_t dminfo = dbus_read(target, DMINFO);
+               if (!get_field(dminfo, DMINFO_AUTHBUSY))
+                       break;
+               if (time(NULL) - start > riscv_command_timeout_sec) {
+                       LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dminfo=0x%x). "
+                                       "Increase the timeout with riscv set_command_timeout_sec.",
+                                       riscv_command_timeout_sec,
+                                       dminfo);
+                       return ERROR_FAIL;
+               }
+       }
+
+       return ERROR_OK;
+}
+
+static int riscv011_authdata_read(struct target *target, uint32_t *value, unsigned int index)
+{
+       if (index > 1) {
+               LOG_ERROR("Spec 0.11 only has a two authdata registers.");
+               return ERROR_FAIL;
+       }
+
+       if (wait_for_authbusy(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       uint16_t authdata_address = index ? DMAUTHDATA1 : DMAUTHDATA0;
+       *value = dbus_read(target, authdata_address);
+
+       return ERROR_OK;
+}
+
+static int riscv011_authdata_write(struct target *target, uint32_t value, unsigned int index)
+{
+       if (index > 1) {
+               LOG_ERROR("Spec 0.11 only has a two authdata registers.");
+               return ERROR_FAIL;
+       }
+
+       if (wait_for_authbusy(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       uint16_t authdata_address = index ? DMAUTHDATA1 : DMAUTHDATA0;
+       dbus_write(target, authdata_address, value);
+
+       return ERROR_OK;
+}
+
 static int init_target(struct command_context *cmd_ctx,
                struct target *target)
 {
        LOG_DEBUG("init");
-       riscv_info_t *generic_info = (riscv_info_t *)target->arch_info;
+       RISCV_INFO(generic_info);
        generic_info->get_register = get_register;
        generic_info->set_register = set_register;
        generic_info->read_memory = read_memory;
+       generic_info->authdata_read = &riscv011_authdata_read;
+       generic_info->authdata_write = &riscv011_authdata_write;
+       generic_info->print_info = &riscv011_print_info;
 
        generic_info->version_specific = calloc(1, sizeof(riscv011_info_t));
        if (!generic_info->version_specific)
                return ERROR_FAIL;
 
        /* Assume 32-bit until we discover the real value in examine(). */
-       generic_info->xlen[0] = 32;
+       generic_info->xlen = 32;
        riscv_init_registers(target);
 
        return ERROR_OK;
index fdebdd413d9ff0b00960c9a8af210042476e69b2..006266a90c5e93190d2859013f9ed11c621e4074 100644 (file)
@@ -29,9 +29,6 @@
 #include "asm.h"
 #include "batch.h"
 
-#define DM_DATA1 (DM_DATA0 + 1)
-#define DM_PROGBUF1 (DM_PROGBUF0 + 1)
-
 static int riscv013_on_step_or_resume(struct target *target, bool step);
 static int riscv013_step_or_resume_current_hart(struct target *target,
                bool step, bool use_hasel);
@@ -39,8 +36,8 @@ static void riscv013_clear_abstract_error(struct target *target);
 
 /* Implementations of the functions in riscv_info_t. */
 static int riscv013_get_register(struct target *target,
-               riscv_reg_t *value, int hid, int rid);
-static int riscv013_set_register(struct target *target, int hartid, int regid, uint64_t value);
+               riscv_reg_t *value, int rid);
+static int riscv013_set_register(struct target *target, int regid, uint64_t value);
 static int riscv013_select_current_hart(struct target *target);
 static int riscv013_halt_prep(struct target *target);
 static int riscv013_halt_go(struct target *target);
@@ -74,7 +71,6 @@ void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t
                uint32_t write_size, uint32_t sbcs);
 void read_memory_sba_simple(struct target *target, target_addr_t addr,
                uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs);
-static int     riscv013_test_compliance(struct target *target);
 
 /**
  * Since almost everything can be accomplish by scanning the dbus register, all
@@ -206,6 +202,8 @@ typedef struct {
        bool abstract_read_fpr_supported;
        bool abstract_write_fpr_supported;
 
+       yes_no_maybe_t has_aampostincrement;
+
        /* When a function returns some error due to a failure indicated by the
         * target in cmderr, the caller can look here to see what that error was.
         * (Compare with errno.) */
@@ -228,6 +226,8 @@ LIST_HEAD(dm_list);
 static riscv013_info_t *get_info(const struct target *target)
 {
        riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       assert(info);
+       assert(info->version_specific);
        return (riscv013_info_t *) info->version_specific;
 }
 
@@ -588,6 +588,8 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
                        return ERROR_FAIL;
        }
 
+       keep_alive();
+
        time_t start = time(NULL);
        /* This first loop performs the request.  Note that if for some reason this
         * stays busy, it is actually due to the previous access. */
@@ -690,7 +692,7 @@ int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
                return result;
        int dmstatus_version = get_field(*dmstatus, DM_DMSTATUS_VERSION);
        if (dmstatus_version != 2 && dmstatus_version != 3) {
-               LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (0.14), not "
+               LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (1.0), not "
                                "%d (dmstatus=0x%x). This error might be caused by a JTAG "
                                "signal issue. Try reducing the JTAG clock speed.",
                                get_field(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus);
@@ -1324,7 +1326,7 @@ static int register_write_direct(struct target *target, unsigned number,
        scratch_mem_t scratch;
        bool use_scratch = false;
        if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
-                       riscv_supports_extension(target, riscv_current_hartid(target), 'D') &&
+                       riscv_supports_extension(target, 'D') &&
                        riscv_xlen(target) < 64) {
                /* There are no instructions to move all the bits from a register, so
                 * we need to use some scratch RAM. */
@@ -1354,7 +1356,7 @@ static int register_write_direct(struct target *target, unsigned number,
                        return ERROR_FAIL;
 
                if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
-                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D'))
+                       if (riscv_supports_extension(target, 'D'))
                                riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
                        else
                                riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
@@ -1395,7 +1397,7 @@ static int register_write_direct(struct target *target, unsigned number,
        return exec_out;
 }
 
-/** Return the cached value, or read from the target if necessary. */
+/** Read register value from the target. Also update the cached value. */
 static int register_read(struct target *target, uint64_t *value, uint32_t number)
 {
        if (number == GDB_REGNO_ZERO) {
@@ -1438,7 +1440,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
                        return ERROR_FAIL;
 
                if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
-                       if (riscv_supports_extension(target, riscv_current_hartid(target), 'D')
+                       if (riscv_supports_extension(target, 'D')
                                        && riscv_xlen(target) < 64) {
                                /* There are no instructions to move all the bits from a
                                 * register, so we need to use some scratch RAM. */
@@ -1454,8 +1456,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
                                        scratch_release(target, &scratch);
                                        return ERROR_FAIL;
                                }
-                       } else if (riscv_supports_extension(target,
-                                               riscv_current_hartid(target), 'D')) {
+                       } else if (riscv_supports_extension(target, 'D')) {
                                riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
                        } else {
                                riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
@@ -1498,7 +1499,7 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t
        return result;
 }
 
-int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
+static int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
 {
        time_t start = time(NULL);
        while (1) {
@@ -1544,7 +1545,7 @@ static int set_haltgroup(struct target *target, bool *supported)
        return ERROR_OK;
 }
 
-static int discover_vlenb(struct target *target, int hartid)
+static int discover_vlenb(struct target *target)
 {
        RISCV_INFO(r);
        riscv_reg_t vlenb;
@@ -1552,12 +1553,12 @@ static int discover_vlenb(struct target *target, int hartid)
        if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK) {
                LOG_WARNING("Couldn't read vlenb for %s; vector register access won't work.",
                                target_name(target));
-               r->vlenb[hartid] = 0;
+               r->vlenb = 0;
                return ERROR_OK;
        }
-       r->vlenb[hartid] = vlenb;
+       r->vlenb = vlenb;
 
-       LOG_INFO("hart %d: Vector support with vlenb=%d", hartid, r->vlenb[hartid]);
+       LOG_INFO("Vector support with vlenb=%d", r->vlenb);
 
        return ERROR_OK;
 }
@@ -1705,6 +1706,8 @@ static int examine(struct target *target)
                LOG_DEBUG("Detected %d harts.", dm->hart_count);
        }
 
+       r->current_hartid = target->coreid;
+
        if (dm->hart_count == 0) {
                LOG_ERROR("No harts found!");
                return ERROR_FAIL;
@@ -1712,54 +1715,49 @@ static int examine(struct target *target)
 
        /* Don't call any riscv_* functions until after we've counted the number of
         * cores and initialized registers. */
-       for (int i = 0; i < dm->hart_count; ++i) {
-               if (!riscv_rtos_enabled(target) && i != target->coreid)
-                       continue;
 
-               r->current_hartid = i;
-               if (riscv013_select_current_hart(target) != ERROR_OK)
-                       return ERROR_FAIL;
+       if (riscv013_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
 
-               bool halted = riscv_is_halted(target);
-               if (!halted) {
-                       if (riscv013_halt_go(target) != ERROR_OK) {
-                               LOG_ERROR("Fatal: Hart %d failed to halt during examine()", i);
-                               return ERROR_FAIL;
-                       }
+       bool halted = riscv_is_halted(target);
+       if (!halted) {
+               if (riscv013_halt_go(target) != ERROR_OK) {
+                       LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid);
+                       return ERROR_FAIL;
                }
+       }
 
-               /* Without knowing anything else we can at least mess with the
-                * program buffer. */
-               r->debug_buffer_size[i] = info->progbufsize;
+       /* Without knowing anything else we can at least mess with the
+               * program buffer. */
+       r->debug_buffer_size = info->progbufsize;
 
-               int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
-               if (result == ERROR_OK)
-                       r->xlen[i] = 64;
-               else
-                       r->xlen[i] = 32;
-
-               if (register_read(target, &r->misa[i], GDB_REGNO_MISA)) {
-                       LOG_ERROR("Fatal: Failed to read MISA from hart %d.", i);
-                       return ERROR_FAIL;
-               }
+       int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+       if (result == ERROR_OK)
+               r->xlen = 64;
+       else
+               r->xlen = 32;
 
-               if (riscv_supports_extension(target, i, 'V')) {
-                       if (discover_vlenb(target, i) != ERROR_OK)
-                               return ERROR_FAIL;
-               }
+       if (register_read(target, &r->misa, GDB_REGNO_MISA)) {
+               LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid);
+               return ERROR_FAIL;
+       }
 
-               /* Now init registers based on what we discovered. */
-               if (riscv_init_registers(target) != ERROR_OK)
+       if (riscv_supports_extension(target, 'V')) {
+               if (discover_vlenb(target) != ERROR_OK)
                        return ERROR_FAIL;
+       }
+
+       /* Now init registers based on what we discovered. */
+       if (riscv_init_registers(target) != ERROR_OK)
+               return ERROR_FAIL;
 
-               /* Display this as early as possible to help people who are using
-                * really slow simulators. */
-               LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
-                               r->misa[i]);
+       /* Display this as early as possible to help people who are using
+               * really slow simulators. */
+       LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
+                       r->misa);
 
-               if (!halted)
-                       riscv013_step_or_resume_current_hart(target, false, false);
-       }
+       if (!halted)
+               riscv013_step_or_resume_current_hart(target, false, false);
 
        target_set_examined(target);
 
@@ -1780,27 +1778,31 @@ static int examine(struct target *target)
         * We will need to update those suites if we want to change that text. */
        LOG_INFO("Examined RISC-V core; found %d harts",
                        riscv_count_harts(target));
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (riscv_hart_enabled(target, i)) {
-                       LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, i, r->xlen[i],
-                                       r->misa[i]);
-               } else {
-                       LOG_INFO(" hart %d: currently disabled", i);
-               }
-       }
+       LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
+                       r->misa);
        return ERROR_OK;
 }
 
-int riscv013_authdata_read(struct target *target, uint32_t *value)
+static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index)
 {
+       if (index > 0) {
+               LOG_ERROR("Spec 0.13 only has a single authdata register.");
+               return ERROR_FAIL;
+       }
+
        if (wait_for_authbusy(target, NULL) != ERROR_OK)
                return ERROR_FAIL;
 
        return dmi_read(target, value, DM_AUTHDATA);
 }
 
-int riscv013_authdata_write(struct target *target, uint32_t value)
+static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index)
 {
+       if (index > 0) {
+               LOG_ERROR("Spec 0.13 only has a single authdata register.");
+               return ERROR_FAIL;
+       }
+
        uint32_t before, after;
        if (wait_for_authbusy(target, &before) != ERROR_OK)
                return ERROR_FAIL;
@@ -1835,27 +1837,74 @@ static int riscv013_hart_count(struct target *target)
        return dm->hart_count;
 }
 
+/* Try to find out the widest memory access size depending on the selected memory access methods. */
 static unsigned riscv013_data_bits(struct target *target)
 {
        RISCV013_INFO(info);
-       /* TODO: Once there is a spec for discovering abstract commands, we can
-        * take those into account as well.  For now we assume abstract commands
-        * support XLEN-wide accesses. */
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return riscv_xlen(target);
+       RISCV_INFO(r);
 
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS128))
-               return 128;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS64))
-               return 64;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS32))
-               return 32;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS16))
-               return 16;
-       if (get_field(info->sbcs, DM_SBCS_SBACCESS8))
-               return 8;
-
-       return riscv_xlen(target);
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
+
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (has_sufficient_progbuf(target, 3))
+                               return riscv_xlen(target);
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS128))
+                               return 128;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS64))
+                               return 64;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS32))
+                               return 32;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS16))
+                               return 16;
+                       if (get_field(info->sbcs, DM_SBCS_SBACCESS8))
+                               return 8;
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       /* TODO: Once there is a spec for discovering abstract commands, we can
+                        * take those into account as well.  For now we assume abstract commands
+                        * support XLEN-wide accesses. */
+                       return riscv_xlen(target);
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+       }
+       LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits.");
+       return 32;
+}
+
+COMMAND_HELPER(riscv013_print_info, struct target *target)
+{
+       RISCV013_INFO(info);
+
+       /* Abstract description. */
+       riscv_print_info_line(CMD, "target", "memory.read_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "target", "memory.read_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+       riscv_print_info_line(CMD, "target", "memory.write_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+
+       /* Lower level description. */
+       riscv_print_info_line(CMD, "dm", "abits", info->abits);
+       riscv_print_info_line(CMD, "dm", "progbufsize", info->progbufsize);
+       riscv_print_info_line(CMD, "dm", "sbversion", get_field(info->sbcs, DM_SBCS_SBVERSION));
+       riscv_print_info_line(CMD, "dm", "sbasize", get_field(info->sbcs, DM_SBCS_SBASIZE));
+       riscv_print_info_line(CMD, "dm", "sbaccess128", get_field(info->sbcs, DM_SBCS_SBACCESS128));
+       riscv_print_info_line(CMD, "dm", "sbaccess64", get_field(info->sbcs, DM_SBCS_SBACCESS64));
+       riscv_print_info_line(CMD, "dm", "sbaccess32", get_field(info->sbcs, DM_SBCS_SBACCESS32));
+       riscv_print_info_line(CMD, "dm", "sbaccess16", get_field(info->sbcs, DM_SBCS_SBACCESS16));
+       riscv_print_info_line(CMD, "dm", "sbaccess8", get_field(info->sbcs, DM_SBCS_SBACCESS8));
+
+       uint32_t dmstatus;
+       if (dmstatus_read(target, &dmstatus, false) == ERROR_OK)
+               riscv_print_info_line(CMD, "dm", "authenticated", get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED));
+
+       return 0;
 }
 
 static int prep_for_vector_access(struct target *target, uint64_t *vtype,
@@ -1885,8 +1934,7 @@ static int prep_for_vector_access(struct target *target, uint64_t *vtype,
 
        if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
                return ERROR_FAIL;
-       *debug_vl = DIV_ROUND_UP(r->vlenb[r->current_hartid] * 8,
-                       riscv_xlen(target));
+       *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target));
        if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
                return ERROR_FAIL;
 
@@ -1909,6 +1957,9 @@ static int riscv013_get_register_buf(struct target *target,
 {
        assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
 
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
        riscv_reg_t s0;
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
                return ERROR_FAIL;
@@ -1965,6 +2016,9 @@ static int riscv013_set_register_buf(struct target *target,
 {
        assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
 
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+
        riscv_reg_t s0;
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
                return ERROR_FAIL;
@@ -2005,11 +2059,227 @@ static int riscv013_set_register_buf(struct target *target,
        return result;
 }
 
+static uint32_t sb_sbaccess(unsigned int size_bytes)
+{
+       switch (size_bytes) {
+               case 1:
+                       return set_field(0, DM_SBCS_SBACCESS, 0);
+               case 2:
+                       return set_field(0, DM_SBCS_SBACCESS, 1);
+               case 4:
+                       return set_field(0, DM_SBCS_SBACCESS, 2);
+               case 8:
+                       return set_field(0, DM_SBCS_SBACCESS, 3);
+               case 16:
+                       return set_field(0, DM_SBCS_SBACCESS, 4);
+       }
+       assert(0);
+       return 0;
+}
+
+static int sb_write_address(struct target *target, target_addr_t address,
+                                                       bool ensure_success)
+{
+       RISCV013_INFO(info);
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       /* There currently is no support for >64-bit addresses in OpenOCD. */
+       if (sbasize > 96)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false);
+       if (sbasize > 64)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false);
+       if (sbasize > 32)
+               dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false);
+       return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address,
+                                 false, ensure_success);
+}
+
+static int batch_run(const struct target *target, struct riscv_batch *batch)
+{
+       RISCV013_INFO(info);
+       RISCV_INFO(r);
+       if (r->reset_delays_wait >= 0) {
+               r->reset_delays_wait -= batch->used_scans;
+               if (r->reset_delays_wait <= 0) {
+                       batch->idle_count = 0;
+                       info->dmi_busy_delay = 0;
+                       info->ac_busy_delay = 0;
+               }
+       }
+       return riscv_batch_run(batch);
+}
+
+static int sba_supports_access(struct target *target, unsigned int size_bytes)
+{
+       RISCV013_INFO(info);
+       switch (size_bytes) {
+               case 1:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS8);
+               case 2:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS16);
+               case 4:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS32);
+               case 8:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS64);
+               case 16:
+                       return get_field(info->sbcs, DM_SBCS_SBACCESS128);
+               default:
+                       return 0;
+       }
+}
+
+static int sample_memory_bus_v1(struct target *target,
+                                                               struct riscv_sample_buf *buf,
+                                                               const riscv_sample_config_t *config,
+                                                               int64_t until_ms)
+{
+       RISCV013_INFO(info);
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       if (sbasize > 64) {
+               LOG_ERROR("Memory sampling is only implemented for sbasize <= 64.");
+               return ERROR_NOT_IMPLEMENTED;
+       }
+
+       if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) {
+               LOG_ERROR("Memory sampling is only implemented for SBA version 1.");
+               return ERROR_NOT_IMPLEMENTED;
+       }
+
+       uint32_t sbcs = 0;
+       uint32_t sbcs_valid = false;
+
+       uint32_t sbaddress0 = 0;
+       bool sbaddress0_valid = false;
+       uint32_t sbaddress1 = 0;
+       bool sbaddress1_valid = false;
+
+       /* How often to read each value in a batch. */
+       const unsigned int repeat = 5;
+
+       unsigned int enabled_count = 0;
+       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+               if (config->bucket[i].enabled)
+                       enabled_count++;
+       }
+
+       while (timeval_ms() < until_ms) {
+               /*
+                * batch_run() adds to the batch, so we can't simply reuse the same
+                * batch over and over. So we create a new one every time through the
+                * loop.
+                */
+               struct riscv_batch *batch = riscv_batch_alloc(
+                       target, 1 + enabled_count * 5 * repeat,
+                       info->dmi_busy_delay + info->bus_master_read_delay);
+               if (!batch)
+                       return ERROR_FAIL;
+
+               unsigned int result_bytes = 0;
+               for (unsigned int n = 0; n < repeat; n++) {
+                       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+                               if (config->bucket[i].enabled) {
+                                       if (!sba_supports_access(target, config->bucket[i].size_bytes)) {
+                                               LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.",
+                                                               config->bucket[i].size_bytes);
+                                               return ERROR_NOT_IMPLEMENTED;
+                                       }
+
+                                       uint32_t sbcs_write = DM_SBCS_SBREADONADDR;
+                                       if (enabled_count == 1)
+                                               sbcs_write |= DM_SBCS_SBREADONDATA;
+                                       sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes);
+                                       if (!sbcs_valid || sbcs_write != sbcs) {
+                                               riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write);
+                                               sbcs = sbcs_write;
+                                               sbcs_valid = true;
+                                       }
+
+                                       if (sbasize > 32 &&
+                                                       (!sbaddress1_valid ||
+                                                       sbaddress1 != config->bucket[i].address >> 32)) {
+                                               sbaddress1 = config->bucket[i].address >> 32;
+                                               riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1);
+                                               sbaddress1_valid = true;
+                                       }
+                                       if (!sbaddress0_valid ||
+                                                       sbaddress0 != (config->bucket[i].address & 0xffffffff)) {
+                                               sbaddress0 = config->bucket[i].address;
+                                               riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0);
+                                               sbaddress0_valid = true;
+                                       }
+                                       if (config->bucket[i].size_bytes > 4)
+                                               riscv_batch_add_dmi_read(batch, DM_SBDATA1);
+                                       riscv_batch_add_dmi_read(batch, DM_SBDATA0);
+                                       result_bytes += 1 + config->bucket[i].size_bytes;
+                               }
+                       }
+               }
+
+               if (buf->used + result_bytes >= buf->size) {
+                       riscv_batch_free(batch);
+                       break;
+               }
+
+               size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS);
+
+               int result = batch_run(target, batch);
+               if (result != ERROR_OK)
+                       return result;
+
+               uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key);
+               if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
+                       /* Discard this batch (too much hassle to try to recover partial
+                        * data) and try again with a larger delay. */
+                       info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+                       dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+                       riscv_batch_free(batch);
+                       continue;
+               }
+               if (get_field(sbcs_read, DM_SBCS_SBERROR)) {
+                       /* The memory we're sampling was unreadable, somehow. Give up. */
+                       dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+                       riscv_batch_free(batch);
+                       return ERROR_FAIL;
+               }
+
+               unsigned int read = 0;
+               for (unsigned int n = 0; n < repeat; n++) {
+                       for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
+                               if (config->bucket[i].enabled) {
+                                       assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
+                                       uint64_t value = 0;
+                                       if (config->bucket[i].size_bytes > 4)
+                                               value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32;
+                                       value |= riscv_batch_get_dmi_read_data(batch, read++);
+
+                                       buf->buf[buf->used] = i;
+                                       buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value);
+                                       buf->used += 1 + config->bucket[i].size_bytes;
+                               }
+                       }
+               }
+
+               riscv_batch_free(batch);
+       }
+
+       return ERROR_OK;
+}
+
+static int sample_memory(struct target *target,
+                                                struct riscv_sample_buf *buf,
+                                                riscv_sample_config_t *config,
+                                                int64_t until_ms)
+{
+       if (!config->enabled)
+               return ERROR_OK;
+
+       return sample_memory_bus_v1(target, buf, config, until_ms);
+}
+
 static int init_target(struct command_context *cmd_ctx,
                struct target *target)
 {
        LOG_DEBUG("init");
-       riscv_info_t *generic_info = (riscv_info_t *) target->arch_info;
+       RISCV_INFO(generic_info);
 
        generic_info->get_register = &riscv013_get_register;
        generic_info->set_register = &riscv013_set_register;
@@ -2038,12 +2308,13 @@ static int init_target(struct command_context *cmd_ctx,
        generic_info->dmi_write = &dmi_write;
        generic_info->read_memory = read_memory;
        generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg;
-       generic_info->test_compliance = &riscv013_test_compliance;
        generic_info->hart_count = &riscv013_hart_count;
        generic_info->data_bits = &riscv013_data_bits;
+       generic_info->print_info = &riscv013_print_info;
        generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
        if (!generic_info->version_specific)
                return ERROR_FAIL;
+       generic_info->sample_memory = sample_memory;
        riscv013_info_t *info = get_info(target);
 
        info->progbufsize = -1;
@@ -2063,6 +2334,8 @@ static int init_target(struct command_context *cmd_ctx,
        info->abstract_read_fpr_supported = true;
        info->abstract_write_fpr_supported = true;
 
+       info->has_aampostincrement = YNM_MAYBE;
+
        return ERROR_OK;
 }
 
@@ -2074,7 +2347,10 @@ static int assert_reset(struct target *target)
 
        uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
 
-       if (target->rtos) {
+       if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
+               /* Run the user-supplied script if there is one. */
+               target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
+       } else if (target->rtos) {
                /* There's only one target, and OpenOCD thinks each hart is a thread.
                 * We must reset them all. */
 
@@ -2082,15 +2358,12 @@ static int assert_reset(struct target *target)
 
                /* Set haltreq for each hart. */
                uint32_t control = control_base;
-               for (int i = 0; i < riscv_count_harts(target); ++i) {
-                       if (!riscv_hart_enabled(target, i))
-                               continue;
 
-                       control = set_hartsel(control_base, i);
-                       control = set_field(control, DM_DMCONTROL_HALTREQ,
-                                       target->reset_halt ? 1 : 0);
-                       dmi_write(target, DM_DMCONTROL, control);
-               }
+               control = set_hartsel(control_base, target->coreid);
+               control = set_field(control, DM_DMCONTROL_HALTREQ,
+                               target->reset_halt ? 1 : 0);
+               dmi_write(target, DM_DMCONTROL, control);
+
                /* Assert ndmreset */
                control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
                dmi_write(target, DM_DMCONTROL, control);
@@ -2138,7 +2411,7 @@ static int deassert_reset(struct target *target)
        for (int i = 0; i < riscv_count_harts(target); ++i) {
                int index = i;
                if (target->rtos) {
-                       if (!riscv_hart_enabled(target, index))
+                       if (index != target->coreid)
                                continue;
                        dmi_write(target, DM_DMCONTROL,
                                        set_hartsel(control, index));
@@ -2146,16 +2419,7 @@ static int deassert_reset(struct target *target)
                        index = r->current_hartid;
                }
 
-               char *operation;
-               uint32_t expected_field;
-               if (target->reset_halt) {
-                       operation = "halt";
-                       expected_field = DM_DMSTATUS_ALLHALTED;
-               } else {
-                       operation = "run";
-                       expected_field = DM_DMSTATUS_ALLRUNNING;
-               }
-               LOG_DEBUG("Waiting for hart %d to %s out of reset.", index, operation);
+               LOG_DEBUG("Waiting for hart %d to come out of reset.", index);
                while (1) {
                        int result = dmstatus_read_timeout(target, &dmstatus, true,
                                        riscv_reset_timeout_sec);
@@ -2166,13 +2430,20 @@ static int deassert_reset(struct target *target)
                                                index, riscv_reset_timeout_sec);
                        if (result != ERROR_OK)
                                return result;
-                       if (get_field(dmstatus, expected_field))
+                       /* Certain debug modules, like the one in GD32VF103
+                        * MCUs, violate the specification's requirement that
+                        * each hart is in "exactly one of four states" and,
+                        * during reset, report harts as both unavailable and
+                        * halted/running. To work around this, we check for
+                        * the absence of the unavailable state rather than
+                        * the presence of any other state. */
+                       if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
                                break;
                        if (time(NULL) - start > riscv_reset_timeout_sec) {
-                               LOG_ERROR("Hart %d didn't %s coming out of reset in %ds; "
+                               LOG_ERROR("Hart %d didn't leave reset in %ds; "
                                                "dmstatus=0x%x; "
                                                "Increase the timeout with riscv set_reset_timeout_sec.",
-                                               index, operation, riscv_reset_timeout_sec, dmstatus);
+                                               index, riscv_reset_timeout_sec, dmstatus);
                                return ERROR_FAIL;
                        }
                }
@@ -2194,8 +2465,6 @@ static int deassert_reset(struct target *target)
 
 static int execute_fence(struct target *target)
 {
-       int old_hartid = riscv_current_hartid(target);
-
        /* FIXME: For non-coherent systems we need to flush the caches right
         * here, but there's no ISA-defined way of doing that. */
        {
@@ -2208,27 +2477,6 @@ static int execute_fence(struct target *target)
                        LOG_DEBUG("Unable to execute pre-fence");
        }
 
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
-
-               if (i == old_hartid)
-                       /* Fence already executed for this hart */
-                       continue;
-
-               riscv_set_current_hartid(target, i);
-
-               struct riscv_program program;
-               riscv_program_init(&program, target);
-               riscv_program_fence_i(&program);
-               riscv_program_fence(&program);
-               int result = riscv_program_exec(&program, target);
-               if (result != ERROR_OK)
-                       LOG_DEBUG("Unable to execute fence on hart %d", i);
-       }
-
-       riscv_set_current_hartid(target, old_hartid);
-
        return ERROR_OK;
 }
 
@@ -2278,24 +2526,6 @@ static int read_memory_bus_word(struct target *target, target_addr_t address,
        return ERROR_OK;
 }
 
-static uint32_t sb_sbaccess(unsigned size_bytes)
-{
-       switch (size_bytes) {
-               case 1:
-                       return set_field(0, DM_SBCS_SBACCESS, 0);
-               case 2:
-                       return set_field(0, DM_SBCS_SBACCESS, 1);
-               case 4:
-                       return set_field(0, DM_SBCS_SBACCESS, 2);
-               case 8:
-                       return set_field(0, DM_SBCS_SBACCESS, 3);
-               case 16:
-                       return set_field(0, DM_SBCS_SBACCESS, 4);
-       }
-       assert(0);
-       return 0;       /* Make mingw happy. */
-}
-
 static target_addr_t sb_read_address(struct target *target)
 {
        RISCV013_INFO(info);
@@ -2312,20 +2542,6 @@ static target_addr_t sb_read_address(struct target *target)
        return address;
 }
 
-static int sb_write_address(struct target *target, target_addr_t address)
-{
-       RISCV013_INFO(info);
-       unsigned sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
-       /* There currently is no support for >64-bit addresses in OpenOCD. */
-       if (sbasize > 96)
-               dmi_write(target, DM_SBADDRESS3, 0);
-       if (sbasize > 64)
-               dmi_write(target, DM_SBADDRESS2, 0);
-       if (sbasize > 32)
-               dmi_write(target, DM_SBADDRESS1, address >> 32);
-       return dmi_write(target, DM_SBADDRESS0, address);
-}
-
 static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
 {
        time_t start = time(NULL);
@@ -2453,6 +2669,10 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
                }
        }
 
+       uint32_t sbcs;
+       if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
+               return ERROR_FAIL;
+
        return ERROR_OK;
 }
 
@@ -2482,7 +2702,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
                        return ERROR_FAIL;
 
                /* This address write will trigger the first read. */
-               if (sb_write_address(target, next_address) != ERROR_OK)
+               if (sb_write_address(target, next_address, true) != ERROR_OK)
                        return ERROR_FAIL;
 
                if (info->bus_master_read_delay) {
@@ -2509,6 +2729,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
                                                                  next_read);
                                                return ERROR_FAIL;
                                        }
+                                       keep_alive();
                                        dmi_status_t status = dmi_scan(target, NULL, &value,
                                                                                                   DMI_OP_READ, sbdata[j], 0, false);
                                        if (status == DMI_STATUS_BUSY)
@@ -2570,7 +2791,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
 
                if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
                        /* We read while the target was busy. Slow down and try again. */
-                       if (dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR) != ERROR_OK)
+                       if (dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK)
                                return ERROR_FAIL;
                        next_address = sb_read_address(target);
                        info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
@@ -2592,19 +2813,136 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
        return ERROR_OK;
 }
 
-static int batch_run(const struct target *target, struct riscv_batch *batch)
+static void log_mem_access_result(struct target *target, bool success, int method, bool read)
 {
-       RISCV013_INFO(info);
        RISCV_INFO(r);
-       if (r->reset_delays_wait >= 0) {
-               r->reset_delays_wait -= batch->used_scans;
-               if (r->reset_delays_wait <= 0) {
-                       batch->idle_count = 0;
-                       info->dmi_busy_delay = 0;
-                       info->ac_busy_delay = 0;
+       bool warn = false;
+       char msg[60];
+
+       /* Compose the message */
+       snprintf(msg, 60, "%s to %s memory via %s.",
+                       success ? "Succeeded" : "Failed",
+                       read ? "read" : "write",
+                       (method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" :
+                       (method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access");
+
+       /* Determine the log message severity. Show warnings only once. */
+       if (!success) {
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       warn = r->mem_access_progbuf_warn;
+                       r->mem_access_progbuf_warn = false;
+               }
+               if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       warn = r->mem_access_sysbus_warn;
+                       r->mem_access_sysbus_warn = false;
+               }
+               if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       warn = r->mem_access_abstract_warn;
+                       r->mem_access_abstract_warn = false;
                }
        }
-       return riscv_batch_run(batch);
+
+       if (warn)
+               LOG_WARNING("%s", msg);
+       else
+               LOG_DEBUG("%s", msg);
+}
+
+static bool mem_should_skip_progbuf(struct target *target, target_addr_t address,
+               uint32_t size, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       if (!has_sufficient_progbuf(target, 3)) {
+               LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (insufficient progbuf)";
+               return true;
+       }
+       if (target->state != TARGET_HALTED) {
+               LOG_DEBUG("Skipping mem %s via progbuf - target not halted.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (target not halted)";
+               return true;
+       }
+       if (riscv_xlen(target) < size * 8) {
+               LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.",
+                               read ? "read" : "write", riscv_xlen(target), size * 8);
+               *skip_reason = "skipped (XLEN too short)";
+               return true;
+       }
+       if (size > 8) {
+               LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+               LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
+                               read ? "read" : "write", riscv_xlen(target));
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+
+       return false;
+}
+
+static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t increment, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       RISCV013_INFO(info);
+       if (!sba_supports_access(target, size)) {
+               LOG_DEBUG("Skipping mem %s via system bus - unsupported size.",
+                               read ? "read" : "write");
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
+       if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) {
+               LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.",
+                               read ? "read" : "write", sbasize);
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+       if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
+               LOG_DEBUG("Skipping mem read via system bus - "
+                               "sba reads only support size==increment or also size==0 for sba v1.");
+               *skip_reason = "skipped (unsupported increment)";
+               return true;
+       }
+
+       return false;
+}
+
+static bool mem_should_skip_abstract(struct target *target, target_addr_t address,
+               uint32_t size, uint32_t increment, bool read, char **skip_reason)
+{
+       assert(skip_reason);
+
+       if (size > 8) {
+               /* TODO: Add 128b support if it's ever used. Involves modifying
+                                read/write_abstract_arg() to work on two 64b values. */
+               LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits",
+                               read ? "read" : "write", size * 8);
+               *skip_reason = "skipped (unsupported size)";
+               return true;
+       }
+       if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
+               LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
+                               read ? "read" : "write", riscv_xlen(target));
+               *skip_reason = "skipped (too large address)";
+               return true;
+       }
+       if (read && size != increment) {
+               LOG_ERROR("Skipping mem read via abstract access - "
+                               "abstract command reads only support size==increment.");
+               *skip_reason = "skipped (unsupported increment)";
+               return true;
+       }
+
+       return false;
 }
 
 /*
@@ -2615,12 +2953,10 @@ static int batch_run(const struct target *target, struct riscv_batch *batch)
 static int read_memory_abstract(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
 {
-       if (size != increment) {
-               LOG_ERROR("abstract command reads only support size==increment");
-               return ERROR_NOT_IMPLEMENTED;
-       }
+       RISCV013_INFO(info);
 
        int result = ERROR_OK;
+       bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
        LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
                          size, address);
@@ -2629,25 +2965,19 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 
        /* Convert the size (bytes) to width (bits) */
        unsigned width = size << 3;
-       if (width > 64) {
-               /* TODO: Add 128b support if it's ever used. Involves modifying
-                                read/write_abstract_arg() to work on two 64b values. */
-               LOG_ERROR("Unsupported size: %d bits", size);
-               return ERROR_FAIL;
-       }
 
        /* Create the command (physical address, postincrement, read) */
-       uint32_t command = access_memory_command(target, false, width, true, false);
+       uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false);
 
        /* Execute the reads */
        uint8_t *p = buffer;
        bool updateaddr = true;
-       unsigned width32 = (width + 31) / 32 * 32;
+       unsigned int width32 = (width < 32) ? 32 : width;
        for (uint32_t c = 0; c < count; c++) {
-               /* Only update the address initially and let postincrement update it */
+               /* Update the address if it is the first time or aampostincrement is not supported by the target. */
                if (updateaddr) {
                        /* Set arg1 to the address: address + c * size */
-                       result = write_abstract_arg(target, 1, address, riscv_xlen(target));
+                       result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
                        if (result != ERROR_OK) {
                                LOG_ERROR("Failed to write arg1 during read_memory_abstract().");
                                return result;
@@ -2656,16 +2986,38 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 
                /* Execute the command */
                result = execute_abstract_command(target, command);
-               if (result != ERROR_OK) {
-                       LOG_ERROR("Failed to execute command read_memory_abstract().");
-                       return result;
+
+               if (info->has_aampostincrement == YNM_MAYBE) {
+                       if (result == ERROR_OK) {
+                               /* Safety: double-check that the address was really auto-incremented */
+                               riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+                               if (new_address == address + size) {
+                                       LOG_DEBUG("aampostincrement is supported on this target.");
+                                       info->has_aampostincrement = YNM_YES;
+                               } else {
+                                       LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       } else {
+                               /* Try the same access but with postincrement disabled. */
+                               command = access_memory_command(target, false, width, false, false);
+                               result = execute_abstract_command(target, command);
+                               if (result == ERROR_OK) {
+                                       LOG_DEBUG("aampostincrement is not supported on this target.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       }
                }
 
+               if (result != ERROR_OK)
+                       return result;
+
                /* Copy arg0 to buffer (rounded width up to nearest 32) */
                riscv_reg_t value = read_abstract_arg(target, 0, width32);
                buf_set_u64(p, 0, 8 * size, value);
 
-               updateaddr = false;
+               if (info->has_aampostincrement == YNM_YES)
+                       updateaddr = false;
                p += size;
        }
 
@@ -2680,22 +3032,18 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
 static int write_memory_abstract(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, const uint8_t *buffer)
 {
+       RISCV013_INFO(info);
        int result = ERROR_OK;
+       bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
 
        LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
                          size, address);
 
        /* Convert the size (bytes) to width (bits) */
        unsigned width = size << 3;
-       if (width > 64) {
-               /* TODO: Add 128b support if it's ever used. Involves modifying
-                                read/write_abstract_arg() to work on two 64b values. */
-               LOG_ERROR("Unsupported size: %d bits", width);
-               return ERROR_FAIL;
-       }
 
        /* Create the command (physical address, postincrement, write) */
-       uint32_t command = access_memory_command(target, false, width, true, true);
+       uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true);
 
        /* Execute the writes */
        const uint8_t *p = buffer;
@@ -2709,10 +3057,10 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
                        return result;
                }
 
-               /* Only update the address initially and let postincrement update it */
+               /* Update the address if it is the first time or aampostincrement is not supported by the target. */
                if (updateaddr) {
                        /* Set arg1 to the address: address + c * size */
-                       result = write_abstract_arg(target, 1, address, riscv_xlen(target));
+                       result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
                        if (result != ERROR_OK) {
                                LOG_ERROR("Failed to write arg1 during write_memory_abstract().");
                                return result;
@@ -2721,12 +3069,34 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
 
                /* Execute the command */
                result = execute_abstract_command(target, command);
-               if (result != ERROR_OK) {
-                       LOG_ERROR("Failed to execute command write_memory_abstract().");
-                       return result;
+
+               if (info->has_aampostincrement == YNM_MAYBE) {
+                       if (result == ERROR_OK) {
+                               /* Safety: double-check that the address was really auto-incremented */
+                               riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+                               if (new_address == address + size) {
+                                       LOG_DEBUG("aampostincrement is supported on this target.");
+                                       info->has_aampostincrement = YNM_YES;
+                               } else {
+                                       LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       } else {
+                               /* Try the same access but with postincrement disabled. */
+                               command = access_memory_command(target, false, width, false, true);
+                               result = execute_abstract_command(target, command);
+                               if (result == ERROR_OK) {
+                                       LOG_DEBUG("aampostincrement is not supported on this target.");
+                                       info->has_aampostincrement = YNM_NO;
+                               }
+                       }
                }
 
-               updateaddr = false;
+               if (result != ERROR_OK)
+                       return result;
+
+               if (info->has_aampostincrement == YNM_YES)
+                       updateaddr = false;
                p += size;
        }
 
@@ -2982,9 +3352,10 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
                return ERROR_FAIL;
 
        uint64_t s0;
+       int result = ERROR_FAIL;
 
        if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
 
        /* Write the program (load, increment) */
        struct riscv_program program;
@@ -3006,40 +3377,42 @@ static int read_memory_progbuf_one(struct target *target, target_addr_t address,
                        break;
                default:
                        LOG_ERROR("Unsupported size: %d", size);
-                       return ERROR_FAIL;
+                       goto restore_mstatus;
        }
        if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
                riscv_program_csrrci(&program, GDB_REGNO_ZERO,  CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
 
        if (riscv_program_ebreak(&program) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
        if (riscv_program_write(&program) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
 
        /* Write address to S0, and execute buffer. */
        if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_mstatus;
        uint32_t command = access_register_command(target, GDB_REGNO_S0,
                        riscv_xlen(target), AC_ACCESS_REGISTER_WRITE |
                        AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
        if (execute_abstract_command(target, command) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_s0;
 
        uint64_t value;
        if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK)
-               return ERROR_FAIL;
+               goto restore_s0;
        buf_set_u64(buffer, 0, 8 * size, value);
        log_memory_access(address, value, size, true);
+       result = ERROR_OK;
 
+restore_s0:
        if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK)
-               return ERROR_FAIL;
+               result = ERROR_FAIL;
 
-       /* Restore MSTATUS */
+restore_mstatus:
        if (mstatus != mstatus_old)
                if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
-                       return ERROR_FAIL;
+                       result = ERROR_FAIL;
 
-       return ERROR_OK;
+       return result;
 }
 
 /**
@@ -3074,8 +3447,8 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
        if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
                return ERROR_FAIL;
 
-       /* s0 holds the next address to write to
-        * s1 holds the next data value to write
+       /* s0 holds the next address to read from
+        * s1 holds the next data value read
         * s2 is a counter in case increment is 0
         */
        uint64_t s0, s1, s2;
@@ -3083,7 +3456,7 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
                return ERROR_FAIL;
        if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
                return ERROR_FAIL;
-       if (increment == 0 && register_read(target, &s2, GDB_REGNO_S1) != ERROR_OK)
+       if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK)
                return ERROR_FAIL;
 
        /* Write the program (load, increment) */
@@ -3133,7 +3506,6 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
                uint8_t *buffer_i = buffer;
 
                for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) {
-                       keep_alive();
                        /* TODO: This is much slower than it needs to be because we end up
                         * writing the address to read for every word we read. */
                        result = read_memory_progbuf_inner(target, address_i, size, count_i, buffer_i, increment);
@@ -3168,30 +3540,62 @@ static int read_memory(struct target *target, target_addr_t address,
        if (count == 0)
                return ERROR_OK;
 
-       RISCV013_INFO(info);
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return read_memory_progbuf(target, address, size, count, buffer,
-                       increment);
-
-       if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) {
-               if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
-                       return read_memory_bus_v0(target, address, size, count, buffer,
-                               increment);
-               else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
-                       return read_memory_bus_v1(target, address, size, count, buffer,
-                               increment);
+       if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
+               LOG_ERROR("BUG: Unsupported size for memory read: %d", size);
+               return ERROR_FAIL;
        }
 
-       if (has_sufficient_progbuf(target, 3))
-               return read_memory_progbuf(target, address, size, count, buffer,
-                       increment);
+       int ret = ERROR_FAIL;
+       RISCV_INFO(r);
+       RISCV013_INFO(info);
+
+       char *progbuf_result = "disabled";
+       char *sysbus_result = "disabled";
+       char *abstract_result = "disabled";
+
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
+
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (mem_should_skip_progbuf(target, address, size, true, &progbuf_result))
+                               continue;
+
+                       ret = read_memory_progbuf(target, address, size, count, buffer, increment);
+
+                       if (ret != ERROR_OK)
+                               progbuf_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (mem_should_skip_sysbus(target, address, size, increment, true, &sysbus_result))
+                               continue;
+
+                       if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
+                               ret = read_memory_bus_v0(target, address, size, count, buffer, increment);
+                       else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
+                               ret = read_memory_bus_v1(target, address, size, count, buffer, increment);
 
-       return read_memory_abstract(target, address, size, count, buffer,
-               increment);
+                       if (ret != ERROR_OK)
+                               sysbus_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       if (mem_should_skip_abstract(target, address, size, increment, true, &abstract_result))
+                               continue;
+
+                       ret = read_memory_abstract(target, address, size, count, buffer, increment);
+
+                       if (ret != ERROR_OK)
+                               abstract_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+
+               log_mem_access_result(target, ret == ERROR_OK, method, true);
+
+               if (ret == ERROR_OK)
+                       return ret;
+       }
+
+       LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address);
+       LOG_ERROR("  progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+       return ret;
 }
 
 static int write_memory_bus_v0(struct target *target, target_addr_t address,
@@ -3260,7 +3664,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
 
        int result;
 
-       sb_write_address(target, next_address);
+       sb_write_address(target, next_address, true);
        while (next_address < end_address) {
                LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR,
                                next_address);
@@ -3310,54 +3714,76 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
                        next_address += size;
                }
 
+               /* Execute the batch of writes */
                result = batch_run(target, batch);
                riscv_batch_free(batch);
                if (result != ERROR_OK)
                        return result;
 
+               /* Read sbcs value.
+                * At the same time, detect if DMI busy has occurred during the batch write. */
                bool dmi_busy_encountered;
                if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ,
-                               DM_SBCS, 0, false, false) != ERROR_OK)
+                               DM_SBCS, 0, false, true) != ERROR_OK)
                        return ERROR_FAIL;
+               if (dmi_busy_encountered)
+                       LOG_DEBUG("DMI busy encountered during system bus write.");
 
+               /* Wait until sbbusy goes low */
                time_t start = time(NULL);
-               bool dmi_busy = dmi_busy_encountered;
-               while (get_field(sbcs, DM_SBCS_SBBUSY) || dmi_busy) {
+               while (get_field(sbcs, DM_SBCS_SBBUSY)) {
                        if (time(NULL) - start > riscv_command_timeout_sec) {
                                LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
-                                         "Increase the timeout with riscv set_command_timeout_sec.",
-                                         riscv_command_timeout_sec, sbcs);
+                                                 "Increase the timeout with riscv set_command_timeout_sec.",
+                                                 riscv_command_timeout_sec, sbcs);
                                return ERROR_FAIL;
                        }
-
-                       if (dmi_op(target, &sbcs, &dmi_busy, DMI_OP_READ,
-                                               DM_SBCS, 0, false, true) != ERROR_OK)
+                       if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
                                return ERROR_FAIL;
                }
 
                if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) {
-                       /* We wrote while the target was busy. Slow down and try again. */
-                       dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR);
+                       /* We wrote while the target was busy. */
+                       LOG_DEBUG("Sbbusyerror encountered during system bus write.");
+                       /* Clear the sticky error flag. */
+                       dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR);
+                       /* Slow down before trying again. */
                        info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
                }
 
                if (get_field(sbcs, DM_SBCS_SBBUSYERROR) || dmi_busy_encountered) {
+                       /* Recover from the case when the write commands were issued too fast.
+                        * Determine the address from which to resume writing. */
                        next_address = sb_read_address(target);
                        if (next_address < address) {
                                /* This should never happen, probably buggy hardware. */
-                               LOG_DEBUG("unexpected system bus address 0x%" TARGET_PRIxADDR,
-                                         next_address);
+                               LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+                                                 " - buggy sbautoincrement in hw?", next_address);
+                               /* Fail the whole operation. */
                                return ERROR_FAIL;
                        }
-
+                       /* Try again - resume writing. */
                        continue;
                }
 
-               unsigned error = get_field(sbcs, DM_SBCS_SBERROR);
-               if (error != 0) {
-                       /* Some error indicating the bus access failed, but not because of
-                        * something we did wrong. */
+               unsigned int sberror = get_field(sbcs, DM_SBCS_SBERROR);
+               if (sberror != 0) {
+                       /* Sberror indicates the bus access failed, but not because we issued the writes
+                        * too fast. Cannot recover. Sbaddress holds the address where the error occurred
+                        * (unless sbautoincrement in the HW is buggy).
+                        */
+                       target_addr_t sbaddress = sb_read_address(target);
+                       LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")",
+                                         sberror, sbaddress);
+                       if (sbaddress < address) {
+                               /* This should never happen, probably buggy hardware.
+                                * Make a note to the user not to trust the sbaddress value. */
+                               LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+                                                 " - buggy sbautoincrement in hw?", next_address);
+                       }
+                       /* Clear the sticky error flag */
                        dmi_write(target, DM_SBCS, DM_SBCS_SBERROR);
+                       /* Fail the whole operation */
                        return ERROR_FAIL;
                }
        }
@@ -3560,26 +3986,62 @@ error:
 static int write_memory(struct target *target, target_addr_t address,
                uint32_t size, uint32_t count, const uint8_t *buffer)
 {
+       if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
+               LOG_ERROR("BUG: Unsupported size for memory write: %d", size);
+               return ERROR_FAIL;
+       }
+
+       int ret = ERROR_FAIL;
+       RISCV_INFO(r);
        RISCV013_INFO(info);
 
-       if (has_sufficient_progbuf(target, 3) && !riscv_prefer_sba)
-               return write_memory_progbuf(target, address, size, count, buffer);
+       char *progbuf_result = "disabled";
+       char *sysbus_result = "disabled";
+       char *abstract_result = "disabled";
 
-       if ((get_field(info->sbcs, DM_SBCS_SBACCESS8) && size == 1) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS16) && size == 2) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS32) && size == 4) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS64) && size == 8) ||
-                       (get_field(info->sbcs, DM_SBCS_SBACCESS128) && size == 16)) {
-               if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
-                       return write_memory_bus_v0(target, address, size, count, buffer);
-               else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
-                       return write_memory_bus_v1(target, address, size, count, buffer);
-       }
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) {
+               int method = r->mem_access_methods[i];
 
-       if (has_sufficient_progbuf(target, 3))
-               return write_memory_progbuf(target, address, size, count, buffer);
+               if (method == RISCV_MEM_ACCESS_PROGBUF) {
+                       if (mem_should_skip_progbuf(target, address, size, false, &progbuf_result))
+                               continue;
+
+                       ret = write_memory_progbuf(target, address, size, count, buffer);
 
-       return write_memory_abstract(target, address, size, count, buffer);
+                       if (ret != ERROR_OK)
+                               progbuf_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_SYSBUS) {
+                       if (mem_should_skip_sysbus(target, address, size, 0, false, &sysbus_result))
+                               continue;
+
+                       if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0)
+                               ret = write_memory_bus_v0(target, address, size, count, buffer);
+                       else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1)
+                               ret = write_memory_bus_v1(target, address, size, count, buffer);
+
+                       if (ret != ERROR_OK)
+                               sysbus_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_ABSTRACT) {
+                       if (mem_should_skip_abstract(target, address, size, 0, false, &abstract_result))
+                               continue;
+
+                       ret = write_memory_abstract(target, address, size, count, buffer);
+
+                       if (ret != ERROR_OK)
+                               abstract_result = "failed";
+               } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED)
+                       /* No further mem access method to try. */
+                       break;
+
+               log_mem_access_result(target, ret == ERROR_OK, method, false);
+
+               if (ret == ERROR_OK)
+                       return ret;
+       }
+
+       LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address);
+       LOG_ERROR("  progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+       return ret;
 }
 
 static int arch_state(struct target *target)
@@ -3603,17 +4065,18 @@ struct target_type riscv013_target = {
 
        .write_memory = write_memory,
 
-       .arch_state = arch_state,
+       .arch_state = arch_state
 };
 
 /*** 0.13-specific implementations of various RISC-V helper functions. ***/
 static int riscv013_get_register(struct target *target,
-               riscv_reg_t *value, int hid, int rid)
+               riscv_reg_t *value, int rid)
 {
-       LOG_DEBUG("[%d] reading register %s on hart %d", target->coreid,
-                       gdb_regno_name(rid), hid);
+       LOG_DEBUG("[%s] reading register %s", target_name(target),
+                       gdb_regno_name(rid));
 
-       riscv_set_current_hartid(target, hid);
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
 
        int result = ERROR_OK;
        if (rid == GDB_REGNO_PC) {
@@ -3624,7 +4087,8 @@ static int riscv013_get_register(struct target *target,
                uint64_t dcsr;
                /* TODO: move this into riscv.c. */
                result = register_read(target, &dcsr, GDB_REGNO_DCSR);
-               *value = get_field(dcsr, CSR_DCSR_PRV);
+               *value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
+               *value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
        } else {
                result = register_read(target, value, rid);
                if (result != ERROR_OK)
@@ -3634,12 +4098,11 @@ static int riscv013_get_register(struct target *target,
        return result;
 }
 
-static int riscv013_set_register(struct target *target, int hid, int rid, uint64_t value)
+static int riscv013_set_register(struct target *target, int rid, uint64_t value)
 {
-       LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s on hart %d",
-                       target->coreid, value, gdb_regno_name(rid), hid);
-
-       riscv_set_current_hartid(target, hid);
+       riscv013_select_current_hart(target);
+       LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s",
+                       target->coreid, value, gdb_regno_name(rid));
 
        if (rid <= GDB_REGNO_XPR31) {
                return register_write_direct(target, rid, value);
@@ -3657,7 +4120,8 @@ static int riscv013_set_register(struct target *target, int hid, int rid, uint64
        } else if (rid == GDB_REGNO_PRIV) {
                uint64_t dcsr;
                register_read(target, &dcsr, GDB_REGNO_DCSR);
-               dcsr = set_field(dcsr, CSR_DCSR_PRV, value);
+               dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
+               dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
                return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
        } else {
                return register_write_direct(target, rid, value);
@@ -3748,10 +4212,8 @@ static int riscv013_halt_prep(struct target *target)
 static int riscv013_halt_go(struct target *target)
 {
        bool use_hasel = false;
-       if (!riscv_rtos_enabled(target)) {
-               if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
-                       return ERROR_FAIL;
-       }
+       if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+               return ERROR_FAIL;
 
        RISCV_INFO(r);
        LOG_DEBUG("halting hart %d", r->current_hartid);
@@ -3802,10 +4264,8 @@ static int riscv013_halt_go(struct target *target)
 static int riscv013_resume_go(struct target *target)
 {
        bool use_hasel = false;
-       if (!riscv_rtos_enabled(target)) {
-               if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
-                       return ERROR_FAIL;
-       }
+       if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+               return ERROR_FAIL;
 
        return riscv013_step_or_resume_current_hart(target, false, use_hasel);
 }
@@ -3865,6 +4325,8 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
        if (result != ERROR_OK)
                return RISCV_HALT_UNKNOWN;
 
+       LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
+
        switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
        case CSR_DCSR_CAUSE_SWBP:
                return RISCV_HALT_BREAKPOINT;
@@ -3884,7 +4346,7 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
                return RISCV_HALT_GROUP;
        }
 
-       LOG_ERROR("Unknown DCSR cause field: %x", (int)get_field(dcsr, CSR_DCSR_CAUSE));
+       LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
        LOG_ERROR("  dcsr=0x%016lx", (long)dcsr);
        return RISCV_HALT_UNKNOWN;
 }
@@ -4400,477 +4862,3 @@ void riscv013_clear_abstract_error(struct target *target)
        /* Clear the error status. */
        dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
 }
-
-#ifdef _WIN32
-#define FILE_SEP '\\'
-#else
-#define FILE_SEP '/'
-#endif
-#define COMPLIANCE_TEST(b, message) \
-{ \
-       const char *last_sep = strrchr(__FILE__, FILE_SEP); \
-       const char *fname = (!last_sep ? __FILE__ : last_sep + 1); \
-       LOG_INFO("Executing test %d (%s:%d): %s", total_tests, fname, __LINE__, message); \
-       int pass = 0;               \
-       if (b) {                    \
-               pass = 1;           \
-               passed_tests++;     \
-       }                           \
-       LOG_INFO("  %s", (pass) ? "PASSED" : "FAILED"); \
-       assert(pass);               \
-       total_tests++;              \
-}
-
-#define COMPLIANCE_MUST_PASS(b) COMPLIANCE_TEST(ERROR_OK == (b), "Regular calls must return ERROR_OK")
-
-#define COMPLIANCE_READ(target, addr, value) COMPLIANCE_MUST_PASS(dmi_read(target, addr, value))
-#define COMPLIANCE_WRITE(target, addr, value) COMPLIANCE_MUST_PASS(dmi_write(target, addr, value))
-
-#define COMPLIANCE_CHECK_RO(target, addr)                               \
-{                                                                       \
-       uint32_t orig;                                                      \
-       uint32_t inverse;                                                   \
-       COMPLIANCE_READ(target, &orig, addr);                               \
-       COMPLIANCE_WRITE(target, addr, ~orig);                              \
-       COMPLIANCE_READ(target, &inverse, addr);                            \
-       COMPLIANCE_TEST(orig == inverse, "Register must be read-only");     \
-}
-
-int riscv013_test_compliance(struct target *target)
-{
-       LOG_INFO("Basic compliance test against RISC-V Debug Spec v0.13");
-       LOG_INFO("This test is not complete, and not well supported.");
-       LOG_INFO("Your core might pass this test without being compliant.");
-       LOG_INFO("Your core might fail this test while being compliant.");
-       LOG_INFO("Use your judgment, and please contribute improvements.");
-
-       if (!riscv_rtos_enabled(target)) {
-               LOG_ERROR("Please run with -rtos riscv to run compliance test.");
-               return ERROR_FAIL;
-       }
-
-       if (!target_was_examined(target)) {
-               LOG_ERROR("Cannot run compliance test, because target has not yet "
-                       "been examined, or the examination failed.\n");
-               return ERROR_FAIL;
-       }
-
-       int total_tests = 0;
-       int passed_tests = 0;
-
-       uint32_t dmcontrol_orig = DM_DMCONTROL_DMACTIVE;
-       uint32_t dmcontrol;
-       uint32_t testvar;
-       uint32_t testvar_read;
-       riscv_reg_t value;
-       RISCV013_INFO(info);
-
-       /* All the bits of HARTSEL are covered by the examine sequence. */
-
-       /* hartreset */
-       /* This field is optional. Either we can read and write it to 1/0,
-       or it is tied to 0. This check doesn't really do anything, but
-       it does attempt to set the bit to 1 and then back to 0, which needs to
-       work if its implemented. */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 1));
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HARTRESET, 0));
-       COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL);
-       COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HARTRESET) == 0),
-                       "DMCONTROL.hartreset can be 0 or RW.");
-
-       /* hasel */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 1));
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, set_field(dmcontrol_orig, DM_DMCONTROL_HASEL, 0));
-       COMPLIANCE_READ(target, &dmcontrol, DM_DMCONTROL);
-       COMPLIANCE_TEST((get_field(dmcontrol, DM_DMCONTROL_HASEL) == 0),
-                       "DMCONTROL.hasel can be 0 or RW.");
-       /* TODO: test that hamask registers exist if hasel does. */
-
-       /* haltreq */
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-       /* This bit is not actually readable according to the spec, so nothing to check.*/
-
-       /* DMSTATUS */
-       COMPLIANCE_CHECK_RO(target, DM_DMSTATUS);
-
-       /* resumereq */
-       /* This bit is not actually readable according to the spec, so nothing to check.*/
-       COMPLIANCE_MUST_PASS(riscv_resume(target, true, 0, false, false, false));
-
-       /* Halt all harts again so the test can continue.*/
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-
-       /* HARTINFO: Read-Only. This is per-hart, so need to adjust hartsel. */
-       uint32_t hartinfo;
-       COMPLIANCE_READ(target, &hartinfo, DM_HARTINFO);
-       for (int hartsel = 0; hartsel < riscv_count_harts(target); hartsel++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-
-               COMPLIANCE_CHECK_RO(target, DM_HARTINFO);
-
-               /* $dscratch CSRs */
-               uint32_t nscratch = get_field(hartinfo, DM_HARTINFO_NSCRATCH);
-               for (unsigned int d = 0; d < nscratch; d++) {
-                       riscv_reg_t testval, testval_read;
-                       /* Because DSCRATCH0 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 DSCRATCH0.");
-                                       struct riscv_program program32;
-                                       riscv_program_init(&program32, target);
-                                       riscv_program_csrw(&program32, GDB_REGNO_S0, GDB_REGNO_DSCRATCH0 + d);
-                                       riscv_program_csrr(&program32, GDB_REGNO_S1, GDB_REGNO_DSCRATCH0 + d);
-                                       riscv_program_fence(&program32);
-                                       riscv_program_ebreak(&program32);
-                                       COMPLIANCE_TEST(riscv_program_exec(&program32, target) == ERROR_OK,
-                                                       "Accessing DSCRATCH0 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 DSCRATCH0.");
-                                       if (riscv_xlen(target) > 32) {
-                                               COMPLIANCE_TEST(testval == testval_read,
-                                                               "All DSCRATCH0 registers in HARTINFO must be R/W.");
-                                       } else {
-                                               COMPLIANCE_TEST(testval_read == (testval & 0xFFFFFFFF),
-                                                               "All DSCRATCH0 registers in HARTINFO must be R/W.");
-                                       }
-                               }
-                       }
-               }
-               /* TODO: dataaccess */
-               if (get_field(hartinfo, DM_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);
-
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum0,
-                       "HALTSUM0 should report summary of up to 32 halted harts");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM0, 0xffffffff);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM0);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum0, "HALTSUM0 should be R/O");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM0, 0x0);
-       COMPLIANCE_READ(target, &testvar_read, DM_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));
-
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1,
-                       "HALTSUM1 should report summary of up to 1024 halted harts");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM1, 0xffffffff);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
-
-       COMPLIANCE_WRITE(target, DM_HALTSUM1, 0x0);
-       COMPLIANCE_READ(target, &testvar_read, DM_HALTSUM1);
-       COMPLIANCE_TEST(testvar_read == expected_haltsum1, "HALTSUM1 should be R/O");
-
-       /* TODO: HAWINDOWSEL */
-
-       /* TODO: HAWINDOW */
-
-       /* ABSTRACTCS */
-
-       uint32_t abstractcs;
-       COMPLIANCE_READ(target, &abstractcs, DM_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, DM_ABSTRACTCS_DATACOUNT); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar);
-               }
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_READ(target, &testvar_read, DM_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, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar);
-               }
-               for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-                       testvar = (i + 1) * 0x11111111;
-                       if (invert)
-                               testvar = ~testvar;
-                       COMPLIANCE_READ(target, &testvar_read, DM_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. */
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0xAAAAAAAA);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
-                       "Illegal COMMAND should result in UNSUPPORTED");
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
-
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0x55555555);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR) == CMDERR_NOT_SUPPORTED,
-                       "Illegal COMMAND should result in UNSUPPORTED");
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_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(register_write_direct(target, GDB_REGNO_ZERO + i, testval) == ERROR_OK,
-                               "GPR Writes should be supported.");
-               COMPLIANCE_MUST_PASS(write_abstract_arg(target, 0, 0xDEADBEEFDEADBEEF, 64));
-               COMPLIANCE_TEST(register_read_direct(target, &testval_read, GDB_REGNO_ZERO + i) == ERROR_OK,
-                               "GPR Reads should be supported.");
-               if (riscv_xlen(target) > 32) {
-                       /* Dummy comment to satisfy linter, since removing the branches 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 branches 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 */
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-       uint32_t abstractauto;
-       uint32_t busy;
-       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-       COMPLIANCE_WRITE(target, DM_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(register_write_direct(target, GDB_REGNO_S0, 0) == ERROR_OK,
-                                       "Need to be able to write S0 to test ABSTRACTAUTO");
-                       struct riscv_program program;
-                       COMPLIANCE_MUST_PASS(riscv_program_init(&program, target));
-                       /* This is also testing that WFI() is a NOP during debug mode. */
-                       COMPLIANCE_MUST_PASS(riscv_program_insert(&program, wfi()));
-                       COMPLIANCE_MUST_PASS(riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, 1));
-                       COMPLIANCE_MUST_PASS(riscv_program_ebreak(&program));
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0x0);
-                       COMPLIANCE_MUST_PASS(riscv_program_exec(&program, target));
-                       testvar++;
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-                       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-                       uint32_t autoexec_data = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECDATA);
-                       uint32_t autoexec_progbuf = get_field(abstractauto, DM_ABSTRACTAUTO_AUTOEXECPROGBUF);
-                       for (unsigned int i = 0; i < 12; i++) {
-                               COMPLIANCE_READ(target, &testvar_read, DM_DATA0 + i);
-                               do {
-                                       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-                                       busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY);
-                               } while (busy);
-                               if (autoexec_data & (1 << i)) {
-                                       COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT),
-                                                       "AUTOEXEC may be writable up to DATACOUNT bits.");
-                                       testvar++;
-                               }
-                       }
-                       for (unsigned int i = 0; i < 16; i++) {
-                               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-                               do {
-                                       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-                                       busy = get_field(testvar_read, DM_ABSTRACTCS_BUSY);
-                               } while (busy);
-                               if (autoexec_progbuf & (1 << i)) {
-                                       COMPLIANCE_TEST(i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE),
-                                                       "AUTOEXEC may be writable up to PROGBUFSIZE bits.");
-                                       testvar++;
-                               }
-                       }
-
-                       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0);
-                       COMPLIANCE_TEST(register_read_direct(target, &value, GDB_REGNO_S0) == ERROR_OK,
-                                       "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++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-               COMPLIANCE_MUST_PASS(riscv013_on_step(target));
-               COMPLIANCE_MUST_PASS(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++) {
-               COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, hartsel));
-
-               /* DCSR Tests */
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0x0));
-               COMPLIANCE_MUST_PASS(register_read_direct(target, &value, GDB_REGNO_DCSR));
-               COMPLIANCE_TEST(value != 0,     "Not all bits in DCSR are writable by Debugger");
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DCSR, 0xFFFFFFFF));
-               COMPLIANCE_MUST_PASS(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;
-
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, dpcmask));
-               COMPLIANCE_MUST_PASS(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)");
-               COMPLIANCE_MUST_PASS(register_write_direct(target, GDB_REGNO_DPC, 0));
-               COMPLIANCE_MUST_PASS(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. */
-       COMPLIANCE_WRITE(target, DM_COMMAND, 0xFFFFFFFF);
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_WRITE(target, DM_PROGBUF0 + i, testvar);
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_WRITE(target, DM_DATA0 + i, testvar);
-       }
-
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0xFFFFFFFF);
-       COMPLIANCE_READ(target, &abstractauto, DM_ABSTRACTAUTO);
-
-       /* Pulse reset. */
-       target->reset_halt = true;
-       COMPLIANCE_MUST_PASS(riscv_set_current_hartid(target, 0));
-       COMPLIANCE_TEST(assert_reset(target) == ERROR_OK, "Must be able to assert NDMRESET");
-       COMPLIANCE_TEST(deassert_reset(target) == ERROR_OK, "Must be able to deassert NDMRESET");
-
-       /* Verify that most stuff is not affected by ndmreset. */
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR)   == CMDERR_NOT_SUPPORTED,
-                       "NDMRESET should not affect DM_ABSTRACTCS");
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO);
-       COMPLIANCE_TEST(testvar_read == abstractauto, "NDMRESET should not affect DM_ABSTRACTAUTO");
-
-       /* Clean up to avoid future test failures */
-       COMPLIANCE_WRITE(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
-       COMPLIANCE_WRITE(target, DM_ABSTRACTAUTO, 0);
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-               COMPLIANCE_TEST(testvar_read == testvar, "PROGBUF words must not be affected by NDMRESET");
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               testvar = (i + 1) * 0x11111111;
-               COMPLIANCE_READ(target, &testvar_read, DM_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)");
-       COMPLIANCE_MUST_PASS(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 */
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, 0);
-       COMPLIANCE_WRITE(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTCS);
-       COMPLIANCE_TEST(get_field(testvar_read, DM_ABSTRACTCS_CMDERR)   == 0, "ABSTRACTCS.cmderr should reset to 0");
-       COMPLIANCE_READ(target, &testvar_read, DM_ABSTRACTAUTO);
-       COMPLIANCE_TEST(testvar_read == 0, "ABSTRACTAUTO should reset to 0");
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); i++) {
-               COMPLIANCE_READ(target, &testvar_read, DM_PROGBUF0 + i);
-               COMPLIANCE_TEST(testvar_read == 0, "PROGBUF words should reset to 0");
-       }
-
-       for (unsigned int i = 0; i < get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); i++) {
-               COMPLIANCE_READ(target, &testvar_read, DM_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*/
-       COMPLIANCE_MUST_PASS(riscv_halt(target));
-
-       uint32_t failed_tests = total_tests - passed_tests;
-       if (total_tests == passed_tests) {
-               LOG_INFO("ALL TESTS PASSED\n");
-               return ERROR_OK;
-       } else {
-               LOG_INFO("%d TESTS FAILED\n", failed_tests);
-               return ERROR_FAIL;
-       }
-}
index 07fb95550c99cf379753e4b3c37be67f34755425..7579e0f0c3b2c1115434c60f8fc9373c9655c95c 100644 (file)
@@ -19,6 +19,8 @@
 #include "riscv.h"
 #include "gdb_regs.h"
 #include "rtos/rtos.h"
+#include "debug_defines.h"
+#include <helper/bits.h>
 
 #define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
 #define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
@@ -125,10 +127,10 @@ struct scan_field select_idcode = {
 bscan_tunnel_type_t bscan_tunnel_type;
 int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
 
-static uint8_t bscan_zero[4] = {0};
-static uint8_t bscan_one[4] = {1};
+static const uint8_t bscan_zero[4] = {0};
+static const uint8_t bscan_one[4] = {1};
 
-uint8_t ir_user4[4] = {0x23};
+uint8_t ir_user4[4];
 struct scan_field select_user4 = {
        .in_value = NULL,
        .out_value = ir_user4
@@ -202,7 +204,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
 /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
 int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
 
-bool riscv_prefer_sba;
 bool riscv_enable_virt2phys = true;
 bool riscv_ebreakm = true;
 bool riscv_ebreaks = true;
@@ -210,24 +211,12 @@ bool riscv_ebreaku = true;
 
 bool riscv_enable_virtual;
 
-typedef struct {
-       uint16_t low, high;
-} range_t;
-
-/* In addition to the ones in the standard spec, we'll also expose additional
- * CSRs in this list.
- * The list is either NULL, or a series of ranges (inclusive), terminated with
- * 1,0. */
-range_t *expose_csr;
-/* Same, but for custom registers. */
-range_t *expose_custom;
-
 static enum {
        RO_NORMAL,
        RO_REVERSED
 } resume_order;
 
-virt2phys_info_t sv32 = {
+const virt2phys_info_t sv32 = {
        .name = "Sv32",
        .va_bits = 32,
        .level = 2,
@@ -240,7 +229,7 @@ virt2phys_info_t sv32 = {
        .pa_ppn_mask = {0x3ff, 0xfff},
 };
 
-virt2phys_info_t sv39 = {
+const virt2phys_info_t sv39 = {
        .name = "Sv39",
        .va_bits = 39,
        .level = 3,
@@ -253,7 +242,7 @@ virt2phys_info_t sv39 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
 };
 
-virt2phys_info_t sv48 = {
+const virt2phys_info_t sv48 = {
        .name = "Sv48",
        .va_bits = 48,
        .level = 4,
@@ -266,6 +255,22 @@ virt2phys_info_t sv48 = {
        .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
 };
 
+void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before)
+{
+       RISCV_INFO(r);
+       uint32_t now = timeval_ms() & 0xffffffff;
+       if (r->sample_buf.used + 5 < r->sample_buf.size) {
+               if (before)
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE;
+               else
+                       r->sample_buf.buf[r->sample_buf.used++] = RISCV_SAMPLE_BUF_TIMESTAMP_AFTER;
+               r->sample_buf.buf[r->sample_buf.used++] = now & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 8) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 16) & 0xff;
+               r->sample_buf.buf[r->sample_buf.used++] = (now >> 24) & 0xff;
+       }
+}
+
 static int riscv_resume_go_all_harts(struct target *target);
 
 void select_dmi_via_bscan(struct target *target)
@@ -363,8 +368,6 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
        return in;
 }
 
-
-
 static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
 {
        struct scan_field field;
@@ -419,15 +422,23 @@ static struct target_type *get_target_type(struct target *target)
        }
 }
 
+static int riscv_create_target(struct target *target, Jim_Interp *interp)
+{
+       LOG_DEBUG("riscv_create_target()");
+       target->arch_info = calloc(1, sizeof(riscv_info_t));
+       if (!target->arch_info) {
+               LOG_ERROR("Failed to allocate RISC-V target structure.");
+               return ERROR_FAIL;
+       }
+       riscv_info_init(target, target->arch_info);
+       return ERROR_OK;
+}
+
 static int riscv_init_target(struct command_context *cmd_ctx,
                struct target *target)
 {
        LOG_DEBUG("riscv_init_target()");
-       target->arch_info = calloc(1, sizeof(riscv_info_t));
-       if (!target->arch_info)
-               return ERROR_FAIL;
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-       riscv_info_init(target, info);
+       RISCV_INFO(info);
        info->cmd_ctx = cmd_ctx;
 
        select_dtmcontrol.num_bits = target->tap->ir_length;
@@ -435,6 +446,12 @@ static int riscv_init_target(struct command_context *cmd_ctx,
        select_idcode.num_bits = target->tap->ir_length;
 
        if (bscan_tunnel_ir_width != 0) {
+               assert(target->tap->ir_length >= 6);
+               uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+               ir_user4[0] = (uint8_t)ir_user4_raw;
+               ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8);
+               ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8);
+               ir_user4[3] = (uint8_t)(ir_user4_raw >>= 8);
                select_user4.num_bits = target->tap->ir_length;
                bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
                if (bscan_tunnel_type == BSCAN_TUNNEL_DATA_REGISTER)
@@ -468,16 +485,29 @@ static void riscv_free_registers(struct target *target)
 static void riscv_deinit_target(struct target *target)
 {
        LOG_DEBUG("riscv_deinit_target()");
+
+       riscv_info_t *info = target->arch_info;
        struct target_type *tt = get_target_type(target);
-       if (tt) {
+
+       if (tt && info->version_specific)
                tt->deinit_target(target);
-               riscv_info_t *info = (riscv_info_t *) target->arch_info;
-               free(info->reg_names);
-               free(info);
-       }
 
        riscv_free_registers(target);
 
+       range_list_t *entry, *tmp;
+       list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
+               free(entry->name);
+               free(entry);
+       }
+
+       list_for_each_entry_safe(entry, tmp, &info->expose_custom, list) {
+               free(entry->name);
+               free(entry);
+       }
+
+       free(info->reg_names);
+       free(target->arch_info);
+
        target->arch_info = NULL;
 }
 
@@ -494,7 +524,7 @@ static void trigger_from_breakpoint(struct trigger *trigger,
        trigger->unique_id = breakpoint->unique_id;
 }
 
-static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t1(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -518,20 +548,19 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
        tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
        tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
        tdata1 = set_field(tdata1, bpcontrol_u,
-                       !!(r->misa[hartid] & (1 << ('U' - 'A'))));
+                       !!(r->misa & BIT('U' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_s,
-                       !!(r->misa[hartid] & (1 << ('S' - 'A'))));
+                       !!(r->misa & BIT('S' - 'A')));
        tdata1 = set_field(tdata1, bpcontrol_h,
-                       !!(r->misa[hartid] & (1 << ('H' - 'A'))));
+                       !!(r->misa & BIT('H' - 'A')));
        tdata1 |= bpcontrol_m;
        tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
        tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        riscv_reg_t tdata1_rb;
-       if (riscv_get_register_on_hart(target, &tdata1_rb, hartid,
-                               GDB_REGNO_TDATA1) != ERROR_OK)
+       if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
                return ERROR_FAIL;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
 
@@ -539,16 +568,16 @@ static int maybe_add_trigger_t1(struct target *target, unsigned hartid,
                LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
                                PRIx64 " to tdata1 it contains 0x%" PRIx64,
                                tdata1, tdata1_rb);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
 
        return ERROR_OK;
 }
 
-static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
+static int maybe_add_trigger_t2(struct target *target,
                struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
@@ -565,11 +594,9 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
                        MCONTROL_ACTION_DEBUG_MODE);
        tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
        tdata1 |= MCONTROL_M;
-       if (r->misa[hartid] & (1 << ('H' - 'A')))
-               tdata1 |= MCONTROL_H;
-       if (r->misa[hartid] & (1 << ('S' - 'A')))
+       if (r->misa & (1 << ('S' - 'A')))
                tdata1 |= MCONTROL_S;
-       if (r->misa[hartid] & (1 << ('U' - 'A')))
+       if (r->misa & (1 << ('U' - 'A')))
                tdata1 |= MCONTROL_U;
 
        if (trigger->execute)
@@ -579,10 +606,10 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
        if (trigger->write)
                tdata1 |= MCONTROL_STORE;
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, tdata1);
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
        uint64_t tdata1_rb;
-       int result = riscv_get_register_on_hart(target, &tdata1_rb, hartid, GDB_REGNO_TDATA1);
+       int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
        if (result != ERROR_OK)
                return result;
        LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
@@ -591,77 +618,104 @@ static int maybe_add_trigger_t2(struct target *target, unsigned hartid,
                LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
                                PRIx64 " to tdata1 it contains 0x%" PRIx64,
                                tdata1, tdata1_rb);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
 
-       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA2, trigger->address);
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
 
        return ERROR_OK;
 }
 
-static int add_trigger(struct target *target, struct trigger *trigger)
+static int maybe_add_trigger_t6(struct target *target,
+               struct trigger *trigger, uint64_t tdata1)
 {
        RISCV_INFO(r);
 
-       if (riscv_enumerate_triggers(target) != ERROR_OK)
-               return ERROR_FAIL;
+       /* tselect is already set */
+       if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) {
+               /* Trigger is already in use, presumably by user code. */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       }
+
+       /* address/data match trigger */
+       tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
+       tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION,
+                       MCONTROL_ACTION_DEBUG_MODE);
+       tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL);
+       tdata1 |= CSR_MCONTROL6_M;
+       if (r->misa & (1 << ('H' - 'A')))
+               tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU;
+       if (r->misa & (1 << ('S' - 'A')))
+               tdata1 |= CSR_MCONTROL6_S;
+       if (r->misa & (1 << ('U' - 'A')))
+               tdata1 |= CSR_MCONTROL6_U;
 
-       /* In RTOS mode, we need to set the same trigger in the same slot on every
-        * hart, to keep up the illusion that each hart is a thread running on the
-        * same core. */
+       if (trigger->execute)
+               tdata1 |= CSR_MCONTROL6_EXECUTE;
+       if (trigger->read)
+               tdata1 |= CSR_MCONTROL6_LOAD;
+       if (trigger->write)
+               tdata1 |= CSR_MCONTROL6_STORE;
 
-       /* Otherwise, we just set the trigger on the one hart this target deals
-        * with. */
+       riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
 
-       riscv_reg_t tselect[RISCV_MAX_HARTS];
+       uint64_t tdata1_rb;
+       int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
+       if (result != ERROR_OK)
+               return result;
+       LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
 
-       int first_hart = -1;
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               if (first_hart < 0)
-                       first_hart = hartid;
-               int result = riscv_get_register_on_hart(target, &tselect[hartid],
-                               hartid, GDB_REGNO_TSELECT);
-               if (result != ERROR_OK)
-                       return result;
+       if (tdata1 != tdata1_rb) {
+               LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
+                               PRIx64 " to tdata1 it contains 0x%" PRIx64,
+                               tdata1, tdata1_rb);
+               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
-       assert(first_hart >= 0);
+
+       riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+
+       return ERROR_OK;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+       RISCV_INFO(r);
+
+       if (riscv_enumerate_triggers(target) != ERROR_OK)
+               return ERROR_FAIL;
+
+       riscv_reg_t tselect;
+       if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+               return ERROR_FAIL;
 
        unsigned int i;
-       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+       for (i = 0; i < r->trigger_count; i++) {
                if (r->trigger_unique_id[i] != -1)
                        continue;
 
-               riscv_set_register_on_hart(target, first_hart, GDB_REGNO_TSELECT, i);
+               riscv_set_register(target, GDB_REGNO_TSELECT, i);
 
                uint64_t tdata1;
-               int result = riscv_get_register_on_hart(target, &tdata1, first_hart,
-                               GDB_REGNO_TDATA1);
+               int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
                if (result != ERROR_OK)
                        return result;
                int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
 
                result = ERROR_OK;
-               for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-                       if (!riscv_hart_enabled(target, hartid))
-                               continue;
-                       if (hartid > first_hart)
-                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
-                       switch (type) {
-                               case 1:
-                                       result = maybe_add_trigger_t1(target, hartid, trigger, tdata1);
-                                       break;
-                               case 2:
-                                       result = maybe_add_trigger_t2(target, hartid, trigger, tdata1);
-                                       break;
-                               default:
-                                       LOG_DEBUG("trigger %d has unknown type %d", i, type);
-                                       continue;
-                       }
-
-                       if (result != ERROR_OK)
+               switch (type) {
+                       case 1:
+                               result = maybe_add_trigger_t1(target, trigger, tdata1);
+                               break;
+                       case 2:
+                               result = maybe_add_trigger_t2(target, trigger, tdata1);
+                               break;
+                       case 6:
+                               result = maybe_add_trigger_t6(target, trigger, tdata1);
+                               break;
+                       default:
+                               LOG_DEBUG("trigger %d has unknown type %d", i, type);
                                continue;
                }
 
@@ -674,14 +728,9 @@ static int add_trigger(struct target *target, struct trigger *trigger)
                break;
        }
 
-       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT,
-                               tselect[hartid]);
-       }
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
 
-       if (i >= r->trigger_count[first_hart]) {
+       if (i >= r->trigger_count) {
                LOG_ERROR("Couldn't find an available hardware trigger.");
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
        }
@@ -689,6 +738,124 @@ static int add_trigger(struct target *target, struct trigger *trigger)
        return ERROR_OK;
 }
 
+/**
+ * Write one memory item of given "size". Use memory access of given "access_size".
+ * Utilize read-modify-write, if needed.
+ * */
+static int write_by_given_size(struct target *target, target_addr_t address,
+               uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_write_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Modify and write back */
+       memcpy(helper_buf + offset_head, buffer, size);
+       return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf);
+}
+
+/**
+ * Read one memory item of given "size". Use memory access of given "access_size".
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+static int read_by_given_size(struct target *target, target_addr_t address,
+       uint32_t size, uint8_t *buffer, uint32_t access_size)
+{
+       assert(size == 1 || size == 2 || size == 4 || size == 8);
+       assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8);
+
+       if (access_size <= size && address % access_size == 0)
+               /* Can do the memory access directly without a helper buffer. */
+               return target_read_memory(target, address, access_size, size / access_size, buffer);
+
+       unsigned int offset_head = address % access_size;
+       unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2;
+       uint8_t helper_buf[n_blocks * access_size];
+
+       /* Read from memory */
+       if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK)
+               return ERROR_FAIL;
+
+       /* Pick the requested portion from the buffer */
+       memcpy(buffer, helper_buf + offset_head, size);
+       return ERROR_OK;
+}
+
+/**
+ * Write one memory item using any memory access size that will work.
+ * Utilize read-modify-write, if needed.
+ * */
+int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
+/**
+ * Read one memory item using any memory access size that will work.
+ * Read larger section of memory and pick out the required portion, if needed.
+ * */
+int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer)
+{
+       assert(size == 1 || size == 2 ||  size == 4 || size == 8);
+
+       /* Find access size that correspond to data size and the alignment. */
+       unsigned int preferred_size = size;
+       while (address % preferred_size != 0)
+               preferred_size /= 2;
+
+       /* First try the preferred (most natural) access size. */
+       if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK)
+               return ERROR_OK;
+
+       /* On failure, try other access sizes.
+          Minimize the number of accesses by trying first the largest size. */
+       for (unsigned int access_size = 8; access_size > 0; access_size /= 2) {
+               if (access_size == preferred_size)
+                       /* Already tried this size. */
+                       continue;
+
+               if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK)
+                       return ERROR_OK;
+       }
+
+       /* No access attempt succeeded. */
+       return ERROR_FAIL;
+}
+
 int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 {
        LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
@@ -705,8 +872,9 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
                        return ERROR_FAIL;
                }
 
-               if (target_read_memory(target, breakpoint->address, 2, breakpoint->length / 2,
-                                       breakpoint->orig_instr) != ERROR_OK) {
+               /* Read the original instruction. */
+               if (riscv_read_by_any_size(
+                               target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
                        LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
                                        breakpoint->address);
                        return ERROR_FAIL;
@@ -714,9 +882,8 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
 
                uint8_t buff[4] = { 0 };
                buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
-               int const retval = target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2, buff);
-
-               if (retval != ERROR_OK) {
+               /* Write the ebreak instruction. */
+               if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) {
                        LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
                                        TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
                        return ERROR_FAIL;
@@ -744,40 +911,26 @@ static int remove_trigger(struct target *target, struct trigger *trigger)
        if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       int first_hart = -1;
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               if (first_hart < 0) {
-                       first_hart = hartid;
-                       break;
-               }
-       }
-       assert(first_hart >= 0);
-
        unsigned int i;
-       for (i = 0; i < r->trigger_count[first_hart]; i++) {
+       for (i = 0; i < r->trigger_count; i++) {
                if (r->trigger_unique_id[i] == trigger->unique_id)
                        break;
        }
-       if (i >= r->trigger_count[first_hart]) {
+       if (i >= r->trigger_count) {
                LOG_ERROR("Couldn't find the hardware resources used by hardware "
                                "trigger.");
                return ERROR_FAIL;
        }
        LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
                        trigger->unique_id);
-       for (int hartid = first_hart; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
-               riscv_reg_t tselect;
-               int result = riscv_get_register_on_hart(target, &tselect, hartid, GDB_REGNO_TSELECT);
-               if (result != ERROR_OK)
-                       return result;
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, i);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
-       }
+
+       riscv_reg_t tselect;
+       int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+       if (result != ERROR_OK)
+               return result;
+       riscv_set_register(target, GDB_REGNO_TSELECT, i);
+       riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
        r->trigger_unique_id[i] = -1;
 
        return ERROR_OK;
@@ -787,8 +940,9 @@ int riscv_remove_breakpoint(struct target *target,
                struct breakpoint *breakpoint)
 {
        if (breakpoint->type == BKPT_SOFT) {
-               if (target_write_memory(target, breakpoint->address, 2, breakpoint->length / 2,
-                                       breakpoint->orig_instr) != ERROR_OK) {
+               /* Write the original instruction. */
+               if (riscv_write_by_any_size(
+                               target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
                        LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
                                        "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
                        return ERROR_FAIL;
@@ -864,8 +1018,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi
 {
        struct watchpoint *wp = target->watchpoints;
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
        LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
 
        /*TODO instead of disassembling the instruction that we think caused the
@@ -968,7 +1120,7 @@ static int riscv_examine(struct target *target)
 
        /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
 
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
+       RISCV_INFO(info);
        uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
        LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
        info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
@@ -1000,44 +1152,42 @@ static int old_or_new_riscv_poll(struct target *target)
                return riscv_openocd_poll(target);
 }
 
+int riscv_select_current_hart(struct target *target)
+{
+       return riscv_set_current_hartid(target, target->coreid);
+}
+
 int halt_prep(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
-                                 target->debug_reason);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
+                               target->debug_reason);
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               LOG_DEBUG("[%s] Hart is already halted (reason=%d).",
+                               target_name(target), target->debug_reason);
+       } else {
+               if (r->halt_prep(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       LOG_DEBUG("Hart %d is already halted (reason=%d).", i,
-                                         target->debug_reason);
-               } else {
-                       if (r->halt_prep(target) != ERROR_OK)
-                               return ERROR_FAIL;
-                       r->prepped = true;
-               }
+               r->prepped = true;
        }
+
        return ERROR_OK;
 }
 
 int riscv_halt_go_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               LOG_DEBUG("[%s] Hart is already halted.", target_name(target));
+       } else {
+               if (r->halt_go(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       LOG_DEBUG("Hart %d is already halted.", i);
-               } else {
-                       if (r->halt_go(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               }
        }
 
        riscv_invalidate_register_cache(target);
@@ -1110,15 +1260,6 @@ int riscv_halt(struct target *target)
                        return ERROR_FAIL;
        }
 
-       if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid != -1) {
-                       LOG_DEBUG("halt requested on RTOS hartid %d", r->rtos_hartid);
-                       target->rtos->current_threadid = r->rtos_hartid + 1;
-                       target->rtos->current_thread = r->rtos_hartid + 1;
-               } else
-                       LOG_DEBUG("halt requested, but no known RTOS hartid");
-       }
-
        return result;
 }
 
@@ -1140,22 +1281,19 @@ static int riscv_deassert_reset(struct target *target)
 int riscv_resume_prep_all_harts(struct target *target)
 {
        RISCV_INFO(r);
-       for (int i = 0; i < riscv_count_harts(target); ++i) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
 
-               LOG_DEBUG("prep hart %d", i);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] prep hart", target_name(target));
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               if (r->resume_prep(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       if (r->resume_prep(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               } else {
-                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
-               }
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
        }
 
-       LOG_DEBUG("[%d] mark as prepped", target->coreid);
+       LOG_DEBUG("[%s] mark as prepped", target_name(target));
        r->prepped = true;
 
        return ERROR_OK;
@@ -1171,13 +1309,12 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
        if (riscv_enumerate_triggers(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       int hartid = riscv_current_hartid(target);
        if (r->manual_hwbp_set) {
                /* Look at every trigger that may have been set. */
                riscv_reg_t tselect;
                if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
                        return ERROR_FAIL;
-               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+               for (unsigned int t = 0; t < r->trigger_count; t++) {
                        if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
                                return ERROR_FAIL;
                        riscv_reg_t tdata1;
@@ -1215,14 +1352,12 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
 {
        RISCV_INFO(r);
 
-       int hartid = riscv_current_hartid(target);
-
        if (r->manual_hwbp_set) {
                /* Look at every trigger that may have been set. */
                riscv_reg_t tselect;
                if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
                        return ERROR_FAIL;
-               for (unsigned t = 0; t < r->trigger_count[hartid]; t++) {
+               for (unsigned int t = 0; t < r->trigger_count; t++) {
                        if (state[t] != 0) {
                                if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
                                        return ERROR_FAIL;
@@ -1376,17 +1511,6 @@ static int riscv_target_resume(struct target *target, int current, target_addr_t
                        debug_execution, false);
 }
 
-static int riscv_select_current_hart(struct target *target)
-{
-       RISCV_INFO(r);
-       if (riscv_rtos_enabled(target)) {
-               if (r->rtos_hartid == -1)
-                       r->rtos_hartid = target->rtos->current_threadid - 1;
-               return riscv_set_current_hartid(target, r->rtos_hartid);
-       } else
-               return riscv_set_current_hartid(target, target->coreid);
-}
-
 static int riscv_mmu(struct target *target, int *enabled)
 {
        if (!riscv_enable_virt2phys) {
@@ -1394,9 +1518,6 @@ static int riscv_mmu(struct target *target, int *enabled)
                return ERROR_OK;
        }
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
-
        /* Don't use MMU in explicit or effective M (machine) mode */
        riscv_reg_t priv;
        if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
@@ -1443,13 +1564,10 @@ static int riscv_address_translate(struct target *target,
        int mode;
        uint64_t ppn_value;
        target_addr_t table_address;
-       virt2phys_info_t *info;
+       const virt2phys_info_t *info;
        uint64_t pte = 0;
        int i;
 
-       if (riscv_rtos_enabled(target))
-               riscv_set_current_hartid(target, target->rtos->current_thread - 1);
-
        int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
        if (result != ERROR_OK)
                return result;
@@ -1631,8 +1749,8 @@ static int riscv_get_gdb_reg_list_internal(struct target *target,
                enum target_register_class reg_class, bool read)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("rtos_hartid=%d, current_hartid=%d, reg_class=%d, read=%d",
-                       r->rtos_hartid, r->current_hartid, reg_class, read);
+       LOG_DEBUG("current_hartid=%d, reg_class=%d, read=%d",
+                       r->current_hartid, reg_class, read);
 
        if (!target->reg_cache) {
                LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
@@ -1702,8 +1820,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                struct reg_param *reg_params, target_addr_t entry_point,
                target_addr_t exit_point, int timeout_ms, void *arch_info)
 {
-       riscv_info_t *info = (riscv_info_t *) target->arch_info;
-       int hartid = riscv_current_hartid(target);
+       RISCV_INFO(info);
 
        if (num_mem_params > 0) {
                LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
@@ -1768,7 +1885,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        reg_mstatus->type->get(reg_mstatus);
        current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
        uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
-       buf_set_u64(mstatus_bytes, 0, info->xlen[0], set_field(current_mstatus,
+       buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
                                ie_mask, 0));
 
        reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
@@ -1814,7 +1931,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
        }
 
        /* The current hart id might have been changed in poll(). */
-       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
 
        if (reg_pc->type->get(reg_pc) != ERROR_OK)
@@ -1828,12 +1945,12 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
 
        /* Restore Interrupts */
        LOG_DEBUG("Restoring Interrupts");
-       buf_set_u64(mstatus_bytes, 0, info->xlen[0], current_mstatus);
+       buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
        reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
 
        /* Restore registers */
        uint8_t buf[8] = { 0 };
-       buf_set_u64(buf, 0, info->xlen[0], saved_pc);
+       buf_set_u64(buf, 0, info->xlen, saved_pc);
        if (reg_pc->type->set(reg_pc, buf) != ERROR_OK)
                return ERROR_FAIL;
 
@@ -1849,7 +1966,7 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
                }
                LOG_DEBUG("restore %s", reg_params[i].reg_name);
                struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
-               buf_set_u64(buf, 0, info->xlen[0], saved_regs[r->number]);
+               buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
                if (r->type->set(r, buf) != ERROR_OK) {
                        LOG_ERROR("set(%s) failed", r->name);
                        return ERROR_FAIL;
@@ -2004,58 +2121,66 @@ int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
        return ERROR_OK;
 }
 
-/*** OpenOCD Interface ***/
-int riscv_openocd_poll(struct target *target)
+int sample_memory(struct target *target)
 {
-       LOG_DEBUG("polling all harts");
-       int halted_hart = -1;
-       if (riscv_rtos_enabled(target)) {
-               /* Check every hart for an event. */
-               for (int i = 0; i < riscv_count_harts(target); ++i) {
-                       enum riscv_poll_hart out = riscv_poll_hart(target, i);
-                       switch (out) {
-                       case RPH_NO_CHANGE:
-                       case RPH_DISCOVERED_RUNNING:
-                               continue;
-                       case RPH_DISCOVERED_HALTED:
-                               halted_hart = i;
-                               break;
-                       case RPH_ERROR:
-                               return ERROR_FAIL;
+       RISCV_INFO(r);
+
+       if (!r->sample_buf.buf || !r->sample_config.enabled)
+               return ERROR_OK;
+
+       LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size);
+
+       uint64_t start = timeval_ms();
+       riscv_sample_buf_maybe_add_timestamp(target, true);
+       int result = ERROR_OK;
+       if (r->sample_memory) {
+               result = r->sample_memory(target, &r->sample_buf, &r->sample_config,
+                                                                         start + TARGET_DEFAULT_POLLING_INTERVAL);
+               if (result != ERROR_NOT_IMPLEMENTED)
+                       goto exit;
+       }
+
+       /* Default slow path. */
+       while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) {
+               for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) {
+                       if (r->sample_config.bucket[i].enabled &&
+                                       r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) {
+                               assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
+                               r->sample_buf.buf[r->sample_buf.used] = i;
+                               result = riscv_read_phys_memory(
+                                       target, r->sample_config.bucket[i].address,
+                                       r->sample_config.bucket[i].size_bytes, 1,
+                                       r->sample_buf.buf + r->sample_buf.used + 1);
+                               if (result == ERROR_OK)
+                                       r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes;
+                               else
+                                       goto exit;
                        }
                }
-               if (halted_hart == -1) {
-                       LOG_DEBUG("  no harts just halted, target->state=%d", target->state);
-                       return ERROR_OK;
-               }
-               LOG_DEBUG("  hart %d halted", halted_hart);
-
-               target->state = TARGET_HALTED;
-               enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
-               if (set_debug_reason(target, halt_reason) != ERROR_OK)
-                       return ERROR_FAIL;
+       }
 
-               target->rtos->current_threadid = halted_hart + 1;
-               target->rtos->current_thread = halted_hart + 1;
-               riscv_set_rtos_hartid(target, halted_hart);
+exit:
+       riscv_sample_buf_maybe_add_timestamp(target, false);
+       if (result != ERROR_OK) {
+               LOG_INFO("Turning off memory sampling because it failed.");
+               r->sample_config.enabled = false;
+       }
+       return result;
+}
 
-               /* If we're here then at least one hart triggered.  That means we want
-                * to go and halt _every_ hart (configured with -rtos riscv) in the
-                * system, as that's the invariant we hold here.  Some harts might have
-                * already halted (as we're either in single-step mode or they also
-                * triggered a breakpoint), so don't attempt to halt those harts.
-                * riscv_halt() will do all that for us. */
-               riscv_halt(target);
+/*** OpenOCD Interface ***/
+int riscv_openocd_poll(struct target *target)
+{
+       LOG_DEBUG("polling all harts");
+       int halted_hart = -1;
 
-       } else if (target->smp) {
+       if (target->smp) {
                unsigned halts_discovered = 0;
-               unsigned total_targets = 0;
                unsigned should_remain_halted = 0;
                unsigned should_resume = 0;
                unsigned i = 0;
                for (struct target_list *list = target->head; list;
                                list = list->next, i++) {
-                       total_targets++;
                        struct target *t = list->target;
                        riscv_info_t *r = riscv_info(t);
                        enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
@@ -2113,15 +2238,29 @@ int riscv_openocd_poll(struct target *target)
                        LOG_DEBUG("resume all");
                        riscv_resume(target, true, 0, 0, 0, false);
                }
+
+               /* Sample memory if any target is running. */
+               for (struct target_list *list = target->head; list;
+                               list = list->next, i++) {
+                       struct target *t = list->target;
+                       if (t->state == TARGET_RUNNING) {
+                               sample_memory(target);
+                               break;
+                       }
+               }
+
                return ERROR_OK;
 
        } else {
                enum riscv_poll_hart out = riscv_poll_hart(target,
                                riscv_current_hartid(target));
-               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING)
+               if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) {
+                       if (target->state == TARGET_RUNNING)
+                               sample_memory(target);
                        return ERROR_OK;
-               else if (out == RPH_ERROR)
+               } else if (out == RPH_ERROR) {
                        return ERROR_FAIL;
+               }
 
                halted_hart = riscv_current_hartid(target);
                LOG_DEBUG("  hart %d halted", halted_hart);
@@ -2218,32 +2357,86 @@ COMMAND_HANDLER(riscv_set_reset_timeout_sec)
        return ERROR_OK;
 }
 
-COMMAND_HANDLER(riscv_test_compliance) {
-
+COMMAND_HANDLER(riscv_set_prefer_sba)
+{
        struct target *target = get_current_target(CMD_CTX);
-
        RISCV_INFO(r);
-
-       if (CMD_ARGC > 0) {
-               LOG_ERROR("Command does not take any parameters.");
+       bool prefer_sba;
+       LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead.");
+       if (CMD_ARGC != 1) {
+               LOG_ERROR("Command takes exactly 1 parameter");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
-
-       if (r->test_compliance) {
-               return r->test_compliance(target);
+       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba);
+       if (prefer_sba) {
+               /* Use system bus with highest priority */
+               r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS;
+               r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF;
+               r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
        } else {
-               LOG_ERROR("This target does not support this command (may implement an older version of the spec).");
-               return ERROR_FAIL;
+               /* Use progbuf with highest priority */
+               r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+               r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+               r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
        }
+
+       /* Reset warning flags */
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
+       return ERROR_OK;
 }
 
-COMMAND_HANDLER(riscv_set_prefer_sba)
+COMMAND_HANDLER(riscv_set_mem_access)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+       int progbuf_cnt = 0;
+       int sysbus_cnt = 0;
+       int abstract_cnt = 0;
+
+       if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) {
+               LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS);
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
-       COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_prefer_sba);
+
+       /* Check argument validity */
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               if (strcmp("progbuf", CMD_ARGV[i]) == 0) {
+                       progbuf_cnt++;
+               } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) {
+                       sysbus_cnt++;
+               } else if (strcmp("abstract", CMD_ARGV[i]) == 0) {
+                       abstract_cnt++;
+               } else {
+                       LOG_ERROR("Unknown argument '%s'. "
+                               "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               }
+       }
+       if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) {
+               LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`.");
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       /* Args are valid, store them */
+       for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++)
+               r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED;
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               if (strcmp("progbuf", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF;
+               else if (strcmp("sysbus", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS;
+               else if (strcmp("abstract", CMD_ARGV[i]) == 0)
+                       r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT;
+       }
+
+       /* Reset warning flags */
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
        return ERROR_OK;
 }
 
@@ -2257,109 +2450,180 @@ COMMAND_HANDLER(riscv_set_enable_virtual)
        return ERROR_OK;
 }
 
-void parse_error(const char *string, char c, unsigned position)
+int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val)
 {
-       char buf[position+2];
-       for (unsigned i = 0; i < position; i++)
-               buf[i] = ' ';
-       buf[position] = '^';
-       buf[position + 1] = 0;
-
-       LOG_ERROR("Parse error at character %c in:", c);
-       LOG_ERROR("%s", string);
-       LOG_ERROR("%s", buf);
-}
+       char *args = strdup(tcl_arg);
+       if (!args)
+               return ERROR_FAIL;
 
-int parse_ranges(range_t **ranges, const char **argv)
-{
-       for (unsigned pass = 0; pass < 2; pass++) {
-               unsigned range = 0;
+       /* For backward compatibility, allow multiple parameters within one TCL argument, separated by ',' */
+       char *arg = strtok(args, ",");
+       while (arg) {
                unsigned low = 0;
-               bool parse_low = true;
                unsigned high = 0;
-               for (unsigned i = 0; i == 0 || argv[0][i-1]; i++) {
-                       char c = argv[0][i];
-                       if (isspace(c)) {
-                               /* Ignore whitespace. */
-                               continue;
+               char *name = NULL;
+
+               char *dash = strchr(arg, '-');
+               char *equals = strchr(arg, '=');
+               unsigned int pos;
+
+               if (!dash && !equals) {
+                       /* Expecting single register number. */
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+               } else if (dash && !equals) {
+                       /* Expecting register range - two numbers separated by a dash: ##-## */
+                       *dash = 0;
+                       dash++;
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+                       if (sscanf(dash, "%u%n", &high, &pos) != 1 || pos != strlen(dash)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", dash);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
+                       }
+                       if (high < low) {
+                               LOG_ERROR("Incorrect range encountered [%u, %u].", low, high);
+                               free(args);
+                               return ERROR_FAIL;
+                       }
+               } else if (!dash && equals) {
+                       /* Expecting single register number with textual name specified: ##=name */
+                       *equals = 0;
+                       equals++;
+                       if (sscanf(arg, "%u%n", &low, &pos) != 1 || pos != strlen(arg)) {
+                               LOG_ERROR("Failed to parse single register number from '%s'.", arg);
+                               free(args);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
 
-                       if (parse_low) {
-                               if (isdigit(c)) {
-                                       low *= 10;
-                                       low += c - '0';
-                               } else if (c == '-') {
-                                       parse_low = false;
-                               } else if (c == ',' || c == 0) {
-                                       if (pass == 1) {
-                                               (*ranges)[range].low = low;
-                                               (*ranges)[range].high = low;
-                                       }
-                                       low = 0;
-                                       range++;
-                               } else {
-                                       parse_error(argv[0], c, i);
-                                       return ERROR_COMMAND_SYNTAX_ERROR;
-                               }
+                       name = calloc(1, strlen(equals) + strlen(reg_type) + 2);
+                       if (!name) {
+                               LOG_ERROR("Failed to allocate register name.");
+                               free(args);
+                               return ERROR_FAIL;
+                       }
 
-                       } else {
-                               if (isdigit(c)) {
-                                       high *= 10;
-                                       high += c - '0';
-                               } else if (c == ',' || c == 0) {
-                                       parse_low = true;
-                                       if (pass == 1) {
-                                               (*ranges)[range].low = low;
-                                               (*ranges)[range].high = high;
-                                       }
-                                       low = 0;
-                                       high = 0;
-                                       range++;
-                               } else {
-                                       parse_error(argv[0], c, i);
-                                       return ERROR_COMMAND_SYNTAX_ERROR;
-                               }
+                       /* Register prefix: "csr_" or "custom_" */
+                       strcpy(name, reg_type);
+                       name[strlen(reg_type)] = '_';
+
+                       if (sscanf(equals, "%[_a-zA-Z0-9]%n", name + strlen(reg_type) + 1, &pos) != 1 || pos != strlen(equals)) {
+                               LOG_ERROR("Failed to parse register name from '%s'.", equals);
+                               free(args);
+                               free(name);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
+               } else {
+                       LOG_ERROR("Invalid argument '%s'.", arg);
+                       free(args);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
                }
 
-               if (pass == 0) {
-                       free(*ranges);
-                       *ranges = calloc(range + 2, sizeof(range_t));
-                       if (!*ranges)
+               high = high > low ? high : low;
+
+               if (high > max_val) {
+                       LOG_ERROR("Cannot expose %s register number %u, maximum allowed value is %u.", reg_type, high, max_val);
+                       free(name);
+                       free(args);
+                       return ERROR_FAIL;
+               }
+
+               /* Check for overlap, name uniqueness. */
+               range_list_t *entry;
+               list_for_each_entry(entry, ranges, list) {
+                       if ((entry->low <= high) && (low <= entry->high)) {
+                               if (low == high)
+                                       LOG_WARNING("Duplicate %s register number - "
+                                                       "Register %u has already been exposed previously", reg_type, low);
+                               else
+                                       LOG_WARNING("Overlapping register ranges - Register range starting from %u overlaps "
+                                                       "with already exposed register/range at %u.", low, entry->low);
+                       }
+
+                       if (entry->name && name && (strcasecmp(entry->name, name) == 0)) {
+                               LOG_ERROR("Duplicate register name \"%s\" found.", name);
+                               free(name);
+                               free(args);
                                return ERROR_FAIL;
-               } else {
-                       (*ranges)[range].low = 1;
-                       (*ranges)[range].high = 0;
+                       }
+               }
+
+               range_list_t *range = calloc(1, sizeof(range_list_t));
+               if (!range) {
+                       LOG_ERROR("Failed to allocate range list.");
+                       free(name);
+                       free(args);
+                       return ERROR_FAIL;
                }
+
+               range->low = low;
+               range->high = high;
+               range->name = name;
+               list_add(&range->list, ranges);
+
+               arg = strtok(NULL, ",");
        }
 
+       free(args);
        return ERROR_OK;
 }
 
 COMMAND_HANDLER(riscv_set_expose_csrs)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       if (CMD_ARGC == 0) {
+               LOG_ERROR("Command expects parameters");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       return parse_ranges(&expose_csr, CMD_ARGV);
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(info);
+       int ret = ERROR_OK;
+
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               ret = parse_ranges(&info->expose_csr, CMD_ARGV[i], "csr", 0xfff);
+               if (ret != ERROR_OK)
+                       break;
+       }
+
+       return ret;
 }
 
 COMMAND_HANDLER(riscv_set_expose_custom)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 parameter");
+       if (CMD_ARGC == 0) {
+               LOG_ERROR("Command expects parameters");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
-       return parse_ranges(&expose_custom, CMD_ARGV);
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(info);
+       int ret = ERROR_OK;
+
+       for (unsigned int i = 0; i < CMD_ARGC; i++) {
+               ret = parse_ranges(&info->expose_custom, CMD_ARGV[i], "custom", 0x3fff);
+               if (ret != ERROR_OK)
+                       break;
+       }
+
+       return ret;
 }
 
 COMMAND_HANDLER(riscv_authdata_read)
 {
-       if (CMD_ARGC != 0) {
-               LOG_ERROR("Command takes no parameters");
+       unsigned int index = 0;
+       if (CMD_ARGC == 0) {
+               /* nop */
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+       } else {
+               LOG_ERROR("Command takes at most one parameter");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
@@ -2377,7 +2641,7 @@ COMMAND_HANDLER(riscv_authdata_read)
 
        if (r->authdata_read) {
                uint32_t value;
-               if (r->authdata_read(target, &value) != ERROR_OK)
+               if (r->authdata_read(target, &value, index) != ERROR_OK)
                        return ERROR_FAIL;
                command_print_sameline(CMD, "0x%08" PRIx32, value);
                return ERROR_OK;
@@ -2389,19 +2653,26 @@ COMMAND_HANDLER(riscv_authdata_read)
 
 COMMAND_HANDLER(riscv_authdata_write)
 {
-       if (CMD_ARGC != 1) {
-               LOG_ERROR("Command takes exactly 1 argument");
+       uint32_t value;
+       unsigned int index = 0;
+
+       if (CMD_ARGC == 0) {
+               /* nop */
+       } else if (CMD_ARGC == 1) {
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
+       } else if (CMD_ARGC == 2) {
+               COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
+               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+       } else {
+               LOG_ERROR("Command takes at most 2 arguments");
                return ERROR_COMMAND_SYNTAX_ERROR;
        }
 
        struct target *target = get_current_target(CMD_CTX);
        RISCV_INFO(r);
 
-       uint32_t value;
-       COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value);
-
        if (r->authdata_write) {
-               return r->authdata_write(target, value);
+               return r->authdata_write(target, value, index);
        } else {
                LOG_ERROR("authdata_write is not implemented for this target.");
                return ERROR_FAIL;
@@ -2617,13 +2888,40 @@ COMMAND_HANDLER(riscv_set_ebreaku)
        return ERROR_OK;
 }
 
+COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
+                          unsigned int value)
+{
+       char full_key[80];
+       snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
+       command_print(CMD, "%-21s %3d", full_key, value);
+       return 0;
+}
+
+COMMAND_HANDLER(handle_info)
+{
+       struct target *target = get_current_target(CMD_CTX);
+       RISCV_INFO(r);
+
+       /* This output format can be fed directly into TCL's "array set". */
+
+       riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target));
+       riscv_enumerate_triggers(target);
+       riscv_print_info_line(CMD, "hart", "trigger_count",
+                                                 r->trigger_count);
+
+       if (r->print_info)
+               return CALL_COMMAND_HANDLER(r->print_info, target);
+
+       return 0;
+}
+
 static const struct command_registration riscv_exec_command_handlers[] = {
        {
-               .name = "test_compliance",
-               .handler = riscv_test_compliance,
-               .usage = "",
+               .name = "info",
+               .handler = handle_info,
                .mode = COMMAND_EXEC,
-               .help = "Runs a basic compliance test suite against the RISC-V Debug Spec."
+               .usage = "",
+               .help = "Displays some information OpenOCD detected about the target."
        },
        {
                .name = "set_command_timeout_sec",
@@ -2647,6 +2945,14 @@ static const struct command_registration riscv_exec_command_handlers[] = {
                .help = "When on, prefer to use System Bus Access to access memory. "
                        "When off (default), prefer to use the Program Buffer to access memory."
        },
+       {
+               .name = "set_mem_access",
+               .handler = riscv_set_mem_access,
+               .mode = COMMAND_ANY,
+               .usage = "method1 [method2] [method3]",
+               .help = "Set which memory access methods shall be used and in which order "
+                       "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'."
+       },
        {
                .name = "set_enable_virtual",
                .handler = riscv_set_enable_virtual,
@@ -2659,8 +2965,8 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "expose_csrs",
                .handler = riscv_set_expose_csrs,
-               .mode = COMMAND_ANY,
-               .usage = "n0[-m0][,n1[-m1]]...",
+               .mode = COMMAND_CONFIG,
+               .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
                .help = "Configure a list of inclusive ranges for CSRs to expose in "
                                "addition to the standard ones. This must be executed before "
                                "`init`."
@@ -2668,8 +2974,8 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "expose_custom",
                .handler = riscv_set_expose_custom,
-               .mode = COMMAND_ANY,
-               .usage = "n0[-m0][,n1[-m1]]...",
+               .mode = COMMAND_CONFIG,
+               .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...",
                .help = "Configure a list of inclusive ranges for custom registers to "
                        "expose. custom0 is accessed as abstract register number 0xc000, "
                        "etc. This must be executed before `init`."
@@ -2677,16 +2983,18 @@ static const struct command_registration riscv_exec_command_handlers[] = {
        {
                .name = "authdata_read",
                .handler = riscv_authdata_read,
-               .usage = "",
+               .usage = "[index]",
                .mode = COMMAND_ANY,
-               .help = "Return the 32-bit value read from authdata."
+               .help = "Return the 32-bit value read from authdata or authdata0 "
+                               "(index=0), or authdata1 (index=1)."
        },
        {
                .name = "authdata_write",
                .handler = riscv_authdata_write,
                .mode = COMMAND_ANY,
-               .usage = "value",
-               .help = "Write the 32-bit value to authdata."
+               .usage = "[index] value",
+               .help = "Write the 32-bit value to authdata or authdata0 (index=0), "
+                               "or authdata1 (index=1)."
        },
        {
                .name = "dmi_read",
@@ -2836,6 +3144,7 @@ static unsigned int riscv_data_bits(struct target *target)
 struct target_type riscv_target = {
        .name = "riscv",
 
+       .target_create = riscv_create_target,
        .init_target = riscv_init_target,
        .deinit_target = riscv_deinit_target,
        .examine = riscv_examine,
@@ -2889,49 +3198,37 @@ void riscv_info_init(struct target *target, riscv_info_t *r)
        r->dtm_version = 1;
        r->registers_initialized = false;
        r->current_hartid = target->coreid;
+       r->version_specific = NULL;
 
        memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
 
-       for (size_t h = 0; h < RISCV_MAX_HARTS; ++h)
-               r->xlen[h] = -1;
+       r->xlen = -1;
+
+       r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
+       r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
+       r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
+
+       r->mem_access_progbuf_warn = true;
+       r->mem_access_sysbus_warn = true;
+       r->mem_access_abstract_warn = true;
+
+       INIT_LIST_HEAD(&r->expose_csr);
+       INIT_LIST_HEAD(&r->expose_custom);
 }
 
 static int riscv_resume_go_all_harts(struct target *target)
 {
        RISCV_INFO(r);
 
-       /* Dummy variables to make mingw32-gcc happy. */
-       int first = 0;
-       int last = 1;
-       int step = 1;
-       switch (resume_order) {
-               case RO_NORMAL:
-                       first = 0;
-                       last = riscv_count_harts(target) - 1;
-                       step = 1;
-                       break;
-               case RO_REVERSED:
-                       first = riscv_count_harts(target) - 1;
-                       last = 0;
-                       step = -1;
-                       break;
-               default:
-                       assert(0);
-       }
-
-       for (int i = first; i != last + step; i += step) {
-               if (!riscv_hart_enabled(target, i))
-                       continue;
-
-               LOG_DEBUG("resuming hart %d", i);
-               if (riscv_set_current_hartid(target, i) != ERROR_OK)
+       LOG_DEBUG("[%s] resuming hart", target_name(target));
+       if (riscv_select_current_hart(target) != ERROR_OK)
+               return ERROR_FAIL;
+       if (riscv_is_halted(target)) {
+               if (r->resume_go(target) != ERROR_OK)
                        return ERROR_FAIL;
-               if (riscv_is_halted(target)) {
-                       if (r->resume_go(target) != ERROR_OK)
-                               return ERROR_FAIL;
-               } else {
-                       LOG_DEBUG("  hart %d requested resume, but was already resumed", i);
-               }
+       } else {
+               LOG_DEBUG("[%s] hart requested resume, but was already resumed",
+                               target_name(target));
        }
 
        riscv_invalidate_register_cache(target);
@@ -2941,17 +3238,9 @@ static int riscv_resume_go_all_harts(struct target *target)
 int riscv_step_rtos_hart(struct target *target)
 {
        RISCV_INFO(r);
-       int hartid = r->current_hartid;
-       if (riscv_rtos_enabled(target)) {
-               hartid = r->rtos_hartid;
-               if (hartid == -1) {
-                       LOG_DEBUG("GDB has asked me to step \"any\" thread, so I'm stepping hart 0.");
-                       hartid = 0;
-               }
-       }
-       if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
+       if (riscv_select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
-       LOG_DEBUG("stepping hart %d", hartid);
+       LOG_DEBUG("[%s] stepping", target_name(target));
 
        if (!riscv_is_halted(target)) {
                LOG_ERROR("Hart isn't halted before single step!");
@@ -2970,7 +3259,7 @@ int riscv_step_rtos_hart(struct target *target)
        return ERROR_OK;
 }
 
-bool riscv_supports_extension(struct target *target, int hartid, char letter)
+bool riscv_supports_extension(struct target *target, char letter)
 {
        RISCV_INFO(r);
        unsigned num;
@@ -2980,24 +3269,13 @@ bool riscv_supports_extension(struct target *target, int hartid, char letter)
                num = letter - 'A';
        else
                return false;
-       return r->misa[hartid] & (1 << num);
+       return r->misa & BIT(num);
 }
 
 unsigned riscv_xlen(const struct target *target)
-{
-       return riscv_xlen_of_hart(target, riscv_current_hartid(target));
-}
-
-int riscv_xlen_of_hart(const struct target *target, int hartid)
 {
        RISCV_INFO(r);
-       assert(r->xlen[hartid] != -1);
-       return r->xlen[hartid];
-}
-
-bool riscv_rtos_enabled(const struct target *target)
-{
-       return false;
+       return r->xlen;
 }
 
 int riscv_set_current_hartid(struct target *target, int hartid)
@@ -3008,16 +3286,10 @@ int riscv_set_current_hartid(struct target *target, int hartid)
 
        int previous_hartid = riscv_current_hartid(target);
        r->current_hartid = hartid;
-       assert(riscv_hart_enabled(target, hartid));
        LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
        if (r->select_current_hart(target) != ERROR_OK)
                return ERROR_FAIL;
 
-       /* This might get called during init, in which case we shouldn't be
-        * setting up the register cache. */
-       if (target_was_examined(target) && riscv_rtos_enabled(target))
-               riscv_invalidate_register_cache(target);
-
        return ERROR_OK;
 }
 
@@ -3041,19 +3313,6 @@ int riscv_current_hartid(const struct target *target)
        return r->current_hartid;
 }
 
-void riscv_set_all_rtos_harts(struct target *target)
-{
-       RISCV_INFO(r);
-       r->rtos_hartid = -1;
-}
-
-void riscv_set_rtos_hartid(struct target *target, int hartid)
-{
-       LOG_DEBUG("setting RTOS hartid %d", hartid);
-       RISCV_INFO(r);
-       r->rtos_hartid = hartid;
-}
-
 int riscv_count_harts(struct target *target)
 {
        if (!target)
@@ -3064,11 +3323,6 @@ int riscv_count_harts(struct target *target)
        return r->hart_count(target);
 }
 
-bool riscv_has_register(struct target *target, int hartid, int regid)
-{
-       return 1;
-}
-
 /**
  * If write is true:
  *   return true iff we are guaranteed that the register will contain exactly
@@ -3085,7 +3339,7 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
                        (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31))
                return true;
 
-       /* Most CSRs won't change value on us, but we can't assume it about rbitrary
+       /* Most CSRs won't change value on us, but we can't assume it about arbitrary
         * CSRs. */
        switch (regno) {
                case GDB_REGNO_DPC:
@@ -3122,75 +3376,67 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
  * This function is called when the debug user wants to change the value of a
  * register. The new value may be cached, and may not be written until the hart
  * is resumed. */
-int riscv_set_register(struct target *target, enum gdb_regno r, riscv_reg_t v)
-{
-       return riscv_set_register_on_hart(target, riscv_current_hartid(target), r, v);
-}
-
-int riscv_set_register_on_hart(struct target *target, int hartid,
-               enum gdb_regno regid, uint64_t value)
+int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value)
 {
        RISCV_INFO(r);
-       LOG_DEBUG("{%d} %s <- %" PRIx64, hartid, gdb_regno_name(regid), value);
+       LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value);
        assert(r->set_register);
 
+       keep_alive();
+
        /* TODO: Hack to deal with gdb that thinks these registers still exist. */
        if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
-                       riscv_supports_extension(target, hartid, 'E'))
+                       riscv_supports_extension(target, 'E'))
                return ERROR_OK;
 
        struct reg *reg = &target->reg_cache->reg_list[regid];
        buf_set_u64(reg->value, 0, reg->size, value);
 
-       int result = r->set_register(target, hartid, regid, value);
+       int result = r->set_register(target, regid, value);
        if (result == ERROR_OK)
                reg->valid = gdb_regno_cacheable(regid, true);
        else
                reg->valid = false;
-       LOG_DEBUG("[%s]{%d} wrote 0x%" PRIx64 " to %s valid=%d",
-                         target_name(target), hartid, value, reg->name, reg->valid);
+       LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
+                         target_name(target), value, reg->name, reg->valid);
        return result;
 }
 
 int riscv_get_register(struct target *target, riscv_reg_t *value,
-               enum gdb_regno r)
-{
-       return riscv_get_register_on_hart(target, value,
-                       riscv_current_hartid(target), r);
-}
-
-int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
-               int hartid, enum gdb_regno regid)
+               enum gdb_regno regid)
 {
        RISCV_INFO(r);
 
+       keep_alive();
+
        struct reg *reg = &target->reg_cache->reg_list[regid];
        if (!reg->exist) {
-               LOG_DEBUG("[%s]{%d} %s does not exist.",
-                                 target_name(target), hartid, gdb_regno_name(regid));
+               LOG_DEBUG("[%s] %s does not exist.",
+                                 target_name(target), gdb_regno_name(regid));
                return ERROR_FAIL;
        }
 
-       if (reg && reg->valid && hartid == riscv_current_hartid(target)) {
+       if (reg && reg->valid) {
                *value = buf_get_u64(reg->value, 0, reg->size);
-               LOG_DEBUG("{%d} %s: %" PRIx64 " (cached)", hartid,
+               LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target),
                                  gdb_regno_name(regid), *value);
                return ERROR_OK;
        }
 
        /* TODO: Hack to deal with gdb that thinks these registers still exist. */
        if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
-                       riscv_supports_extension(target, hartid, 'E')) {
+                       riscv_supports_extension(target, 'E')) {
                *value = 0;
                return ERROR_OK;
        }
 
-       int result = r->get_register(target, value, hartid, regid);
+       int result = r->get_register(target, value, regid);
 
        if (result == ERROR_OK)
                reg->valid = gdb_regno_cacheable(regid, false);
 
-       LOG_DEBUG("{%d} %s: %" PRIx64, hartid, gdb_regno_name(regid), *value);
+       LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
+                       gdb_regno_name(regid), *value);
        return result;
 }
 
@@ -3216,7 +3462,7 @@ enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
 size_t riscv_debug_buffer_size(struct target *target)
 {
        RISCV_INFO(r);
-       return r->debug_buffer_size[riscv_current_hartid(target)];
+       return r->debug_buffer_size;
 }
 
 int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
@@ -3262,15 +3508,6 @@ int riscv_dmi_write_u64_bits(struct target *target)
        return r->dmi_write_u64_bits(target);
 }
 
-bool riscv_hart_enabled(struct target *target, int hartid)
-{
-       /* FIXME: Add a hart mask to the RTOS. */
-       if (riscv_rtos_enabled(target))
-               return hartid < riscv_count_harts(target);
-
-       return hartid == target->coreid;
-}
-
 /**
  * Count triggers, and initialize trigger_count for each hart.
  * trigger_count is initialized even if this function fails to discover
@@ -3287,58 +3524,57 @@ int riscv_enumerate_triggers(struct target *target)
 
        r->triggers_enumerated = true;  /* At the very least we tried. */
 
-       for (int hartid = 0; hartid < riscv_count_harts(target); ++hartid) {
-               if (!riscv_hart_enabled(target, hartid))
-                       continue;
+       riscv_reg_t tselect;
+       int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+       /* If tselect is not readable, the trigger module is likely not
+               * implemented. There are no triggers to enumerate then and no error
+               * should be thrown. */
+       if (result != ERROR_OK) {
+               LOG_DEBUG("[%s] Cannot access tselect register. "
+                               "Assuming that triggers are not implemented.", target_name(target));
+               r->trigger_count = 0;
+               return ERROR_OK;
+       }
 
-               riscv_reg_t tselect;
-               int result = riscv_get_register_on_hart(target, &tselect, hartid,
-                               GDB_REGNO_TSELECT);
+       for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
+               r->trigger_count = t;
+
+               /* If we can't write tselect, then this hart does not support triggers. */
+               if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
+                       break;
+               uint64_t tselect_rb;
+               result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
+               if (result != ERROR_OK)
+                       return result;
+               /* Mask off the top bit, which is used as tdrmode in old
+                       * implementations. */
+               tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
+               if (tselect_rb != t)
+                       break;
+               uint64_t tdata1;
+               result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
                if (result != ERROR_OK)
                        return result;
 
-               for (unsigned t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
-                       r->trigger_count[hartid] = t;
-
-                       /* If we can't write tselect, then this hart does not support triggers. */
-                       if (riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, t) != ERROR_OK)
-                               break;
-                       uint64_t tselect_rb;
-                       result = riscv_get_register_on_hart(target, &tselect_rb, hartid,
-                                       GDB_REGNO_TSELECT);
-                       if (result != ERROR_OK)
-                               return result;
-                       /* Mask off the top bit, which is used as tdrmode in old
-                        * implementations. */
-                       tselect_rb &= ~(1ULL << (riscv_xlen(target)-1));
-                       if (tselect_rb != t)
+               int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+               if (type == 0)
+                       break;
+               switch (type) {
+                       case 1:
+                               /* On these older cores we don't support software using
+                                       * triggers. */
+                               riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                                break;
-                       uint64_t tdata1;
-                       result = riscv_get_register_on_hart(target, &tdata1, hartid,
-                                       GDB_REGNO_TDATA1);
-                       if (result != ERROR_OK)
-                               return result;
-
-                       int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
-                       if (type == 0)
+                       case 2:
+                               if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
+                                       riscv_set_register(target, GDB_REGNO_TDATA1, 0);
                                break;
-                       switch (type) {
-                               case 1:
-                                       /* On these older cores we don't support software using
-                                        * triggers. */
-                                       riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-                                       break;
-                               case 2:
-                                       if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
-                                               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TDATA1, 0);
-                                       break;
-                       }
                }
+       }
 
-               riscv_set_register_on_hart(target, hartid, GDB_REGNO_TSELECT, tselect);
+       riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
 
-               LOG_INFO("[%d] Found %d triggers", hartid, r->trigger_count[hartid]);
-       }
+       LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count);
 
        return ERROR_OK;
 }
@@ -3549,8 +3785,8 @@ static int register_get(struct reg *reg)
        }
        reg->valid = gdb_regno_cacheable(reg->number, false);
        char *str = buf_to_hex_str(reg->value, reg->size);
-       LOG_DEBUG("[%d]{%d} read 0x%s from %s (valid=%d)", target->coreid,
-                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
        free(str);
        return ERROR_OK;
 }
@@ -3562,10 +3798,15 @@ static int register_set(struct reg *reg, uint8_t *buf)
        RISCV_INFO(r);
 
        char *str = buf_to_hex_str(buf, reg->size);
-       LOG_DEBUG("[%d]{%d} write 0x%s to %s (valid=%d)", target->coreid,
-                       riscv_current_hartid(target), str, reg->name, reg->valid);
+       LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
+                       str, reg->name, reg->valid);
        free(str);
 
+       /* Exit early for writing x0, which on the hardware would be ignored, and we
+        * don't want to update our cache. */
+       if (reg->number == GDB_REGNO_ZERO)
+               return ERROR_OK;
+
        memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
        reg->valid = gdb_regno_cacheable(reg->number, true);
 
@@ -3625,13 +3866,10 @@ int riscv_init_registers(struct target *target)
        target->reg_cache->name = "RISC-V Registers";
        target->reg_cache->num_regs = GDB_REGNO_COUNT;
 
-       if (expose_custom) {
-               for (unsigned i = 0; expose_custom[i].low <= expose_custom[i].high; i++) {
-                       for (unsigned number = expose_custom[i].low;
-                                       number <= expose_custom[i].high;
-                                       number++)
-                               target->reg_cache->num_regs++;
-               }
+       if (!list_empty(&info->expose_custom)) {
+               range_list_t *entry;
+               list_for_each_entry(entry, &info->expose_custom, list)
+                       target->reg_cache->num_regs += entry->high - entry->low + 1;
        }
 
        LOG_DEBUG("create register cache for %d registers",
@@ -3650,8 +3888,6 @@ int riscv_init_registers(struct target *target)
                return ERROR_FAIL;
        char *reg_name = info->reg_names;
 
-       int hartid = riscv_current_hartid(target);
-
        static struct reg_feature feature_cpu = {
                .name = "org.gnu.gdb.riscv.cpu"
        };
@@ -3709,35 +3945,35 @@ int riscv_init_registers(struct target *target)
         */
 
        info->vector_uint8.type = &type_uint8;
-       info->vector_uint8.count = info->vlenb[hartid];
+       info->vector_uint8.count = info->vlenb;
        info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint8_vector.id = "bytes";
        info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
 
        info->vector_uint16.type = &type_uint16;
-       info->vector_uint16.count = info->vlenb[hartid] / 2;
+       info->vector_uint16.count = info->vlenb / 2;
        info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint16_vector.id = "shorts";
        info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
 
        info->vector_uint32.type = &type_uint32;
-       info->vector_uint32.count = info->vlenb[hartid] / 4;
+       info->vector_uint32.count = info->vlenb / 4;
        info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint32_vector.id = "words";
        info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
 
        info->vector_uint64.type = &type_uint64;
-       info->vector_uint64.count = info->vlenb[hartid] / 8;
+       info->vector_uint64.count = info->vlenb / 8;
        info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint64_vector.id = "longs";
        info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
        info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
 
        info->vector_uint128.type = &type_uint128;
-       info->vector_uint128.count = info->vlenb[hartid] / 16;
+       info->vector_uint128.count = info->vlenb / 16;
        info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
        info->type_uint128_vector.id = "quads";
        info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
@@ -3745,28 +3981,28 @@ int riscv_init_registers(struct target *target)
 
        info->vector_fields[0].name = "b";
        info->vector_fields[0].type = &info->type_uint8_vector;
-       if (info->vlenb[hartid] >= 2) {
+       if (info->vlenb >= 2) {
                info->vector_fields[0].next = info->vector_fields + 1;
                info->vector_fields[1].name = "s";
                info->vector_fields[1].type = &info->type_uint16_vector;
        } else {
                info->vector_fields[0].next = NULL;
        }
-       if (info->vlenb[hartid] >= 4) {
+       if (info->vlenb >= 4) {
                info->vector_fields[1].next = info->vector_fields + 2;
                info->vector_fields[2].name = "w";
                info->vector_fields[2].type = &info->type_uint32_vector;
        } else {
                info->vector_fields[1].next = NULL;
        }
-       if (info->vlenb[hartid] >= 8) {
+       if (info->vlenb >= 8) {
                info->vector_fields[2].next = info->vector_fields + 3;
                info->vector_fields[3].name = "l";
                info->vector_fields[3].type = &info->type_uint64_vector;
        } else {
                info->vector_fields[2].next = NULL;
        }
-       if (info->vlenb[hartid] >= 16) {
+       if (info->vlenb >= 16) {
                info->vector_fields[3].next = info->vector_fields + 4;
                info->vector_fields[4].name = "q";
                info->vector_fields[4].type = &info->type_uint128_vector;
@@ -3791,7 +4027,6 @@ int riscv_init_registers(struct target *target)
        qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info);
        unsigned csr_info_index = 0;
 
-       unsigned custom_range_index = 0;
        int custom_within_range = 0;
 
        riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
@@ -3818,7 +4053,7 @@ int riscv_init_registers(struct target *target)
                 * of other things to break in that case as well. */
                if (number <= GDB_REGNO_XPR31) {
                        r->exist = number <= GDB_REGNO_XPR15 ||
-                               !riscv_supports_extension(target, hartid, 'E');
+                               !riscv_supports_extension(target, 'E');
                        /* TODO: For now we fake that all GPRs exist because otherwise gdb
                         * doesn't work. */
                        r->exist = true;
@@ -3930,13 +4165,13 @@ int riscv_init_registers(struct target *target)
                        r->feature = &feature_cpu;
                } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
                        r->caller_save = true;
-                       if (riscv_supports_extension(target, hartid, 'D')) {
+                       if (riscv_supports_extension(target, 'D')) {
                                r->size = 64;
-                               if (riscv_supports_extension(target, hartid, 'F'))
+                               if (riscv_supports_extension(target, 'F'))
                                        r->reg_data_type = &type_ieee_single_double;
                                else
                                        r->reg_data_type = &type_ieee_double;
-                       } else if (riscv_supports_extension(target, hartid, 'F')) {
+                       } else if (riscv_supports_extension(target, 'F')) {
                                r->reg_data_type = &type_ieee_single;
                                r->size = 32;
                        } else {
@@ -4067,7 +4302,7 @@ int riscv_init_registers(struct target *target)
                                case CSR_FFLAGS:
                                case CSR_FRM:
                                case CSR_FCSR:
-                                       r->exist = riscv_supports_extension(target, hartid, 'F');
+                                       r->exist = riscv_supports_extension(target, 'F');
                                        r->group = "float";
                                        r->feature = &feature_fpu;
                                        break;
@@ -4081,15 +4316,15 @@ int riscv_init_registers(struct target *target)
                                case CSR_SCAUSE:
                                case CSR_STVAL:
                                case CSR_SATP:
-                                       r->exist = riscv_supports_extension(target, hartid, 'S');
+                                       r->exist = riscv_supports_extension(target, 'S');
                                        break;
                                case CSR_MEDELEG:
                                case CSR_MIDELEG:
                                        /* "In systems with only M-mode, or with both M-mode and
                                         * U-mode but without U-mode trap support, the medeleg and
                                         * mideleg registers should not exist." */
-                                       r->exist = riscv_supports_extension(target, hartid, 'S') ||
-                                               riscv_supports_extension(target, hartid, 'N');
+                                       r->exist = riscv_supports_extension(target, 'S') ||
+                                               riscv_supports_extension(target, 'N');
                                        break;
 
                                case CSR_PMPCFG1:
@@ -4166,18 +4401,25 @@ int riscv_init_registers(struct target *target)
                                case CSR_VL:
                                case CSR_VTYPE:
                                case CSR_VLENB:
-                                       r->exist = riscv_supports_extension(target, hartid, 'V');
+                                       r->exist = riscv_supports_extension(target, 'V');
                                        break;
                        }
 
-                       if (!r->exist && expose_csr) {
-                               for (unsigned i = 0; expose_csr[i].low <= expose_csr[i].high; i++) {
-                                       if (csr_number >= expose_csr[i].low && csr_number <= expose_csr[i].high) {
-                                               LOG_INFO("Exposing additional CSR %d", csr_number);
+                       if (!r->exist && !list_empty(&info->expose_csr)) {
+                               range_list_t *entry;
+                               list_for_each_entry(entry, &info->expose_csr, list)
+                                       if ((entry->low <= csr_number) && (csr_number <= entry->high)) {
+                                               if (entry->name) {
+                                                       *reg_name = 0;
+                                                       r->name = entry->name;
+                                               }
+
+                                               LOG_DEBUG("Exposing additional CSR %d (name=%s)",
+                                                               csr_number, entry->name ? entry->name : reg_name);
+
                                                r->exist = true;
                                                break;
                                        }
-                               }
                        }
 
                } else if (number == GDB_REGNO_PRIV) {
@@ -4188,8 +4430,8 @@ int riscv_init_registers(struct target *target)
 
                } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
                        r->caller_save = false;
-                       r->exist = riscv_supports_extension(target, hartid, 'V') && info->vlenb[hartid];
-                       r->size = info->vlenb[hartid] * 8;
+                       r->exist = riscv_supports_extension(target, 'V') && info->vlenb;
+                       r->size = info->vlenb * 8;
                        sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
                        r->group = "vector";
                        r->feature = &feature_vector;
@@ -4197,10 +4439,10 @@ int riscv_init_registers(struct target *target)
 
                } else if (number >= GDB_REGNO_COUNT) {
                        /* Custom registers. */
-                       assert(expose_custom);
+                       assert(!list_empty(&info->expose_custom));
+
+                       range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
 
-                       range_t *range = &expose_custom[custom_range_index];
-                       assert(range->low <= range->high);
                        unsigned custom_number = range->low + custom_within_range;
 
                        r->group = "custom";
@@ -4212,18 +4454,27 @@ int riscv_init_registers(struct target *target)
                        ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
                        sprintf(reg_name, "custom%d", custom_number);
 
+                       if (range->name) {
+                               *reg_name = 0;
+                               r->name = range->name;
+                       }
+
+                       LOG_DEBUG("Exposing additional custom register %d (name=%s)",
+                                       number, range->name ? range->name : reg_name);
+
                        custom_within_range++;
                        if (custom_within_range > range->high - range->low) {
                                custom_within_range = 0;
-                               custom_range_index++;
+                               list_rotate_left(&info->expose_custom);
                        }
                }
 
-               if (reg_name[0])
+               if (reg_name[0]) {
                        r->name = reg_name;
-               reg_name += strlen(reg_name) + 1;
-               assert(reg_name < info->reg_names + target->reg_cache->num_regs *
-                               max_reg_name_len);
+                       reg_name += strlen(reg_name) + 1;
+                       assert(reg_name < info->reg_names + target->reg_cache->num_regs *
+                                       max_reg_name_len);
+               }
                r->value = info->reg_cache_values[number];
        }
 
index d943134e23e46b72f461a46ae8743ba0ec8352aa..d0f4f6ec048bbd5e2b2130680954bbaadd60ad95 100644 (file)
@@ -10,6 +10,7 @@ struct riscv_program;
 #include "gdb_regs.h"
 #include "jtag/jtag.h"
 #include "target/register.h"
+#include <helper/command.h>
 
 /* The register cache is statically allocated. */
 #define RISCV_MAX_HARTS 1024
@@ -26,6 +27,8 @@ struct riscv_program;
 
 # define PG_MAX_LEVEL 4
 
+#define RISCV_NUM_MEM_ACCESS_METHODS  3
+
 extern struct target_type riscv011_target;
 extern struct target_type riscv013_target;
 
@@ -36,6 +39,13 @@ typedef uint64_t riscv_reg_t;
 typedef uint32_t riscv_insn_t;
 typedef uint64_t riscv_addr_t;
 
+enum riscv_mem_access_method {
+       RISCV_MEM_ACCESS_UNSPECIFIED,
+       RISCV_MEM_ACCESS_PROGBUF,
+       RISCV_MEM_ACCESS_SYSBUS,
+       RISCV_MEM_ACCESS_ABSTRACT
+};
+
 enum riscv_halt_reason {
        RISCV_HALT_INTERRUPT,
        RISCV_HALT_BREAKPOINT,
@@ -51,15 +61,35 @@ typedef struct {
        unsigned custom_number;
 } riscv_reg_info_t;
 
+#define RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE      0x80
+#define RISCV_SAMPLE_BUF_TIMESTAMP_AFTER       0x81
+struct riscv_sample_buf {
+       uint8_t *buf;
+       unsigned int used;
+       unsigned int size;
+};
+
+typedef struct {
+       bool enabled;
+       struct {
+               bool enabled;
+               target_addr_t address;
+               uint32_t size_bytes;
+       } bucket[16];
+} riscv_sample_config_t;
+
+typedef struct {
+       struct list_head list;
+       uint16_t low, high;
+       char *name;
+} range_list_t;
+
 typedef struct {
        unsigned dtm_version;
 
        struct command_context *cmd_ctx;
        void *version_specific;
 
-       /* The hart that the RTOS thinks is currently being debugged. */
-       int rtos_hartid;
-
        /* The hart that is currently being debugged.  Note that this is
         * different than the hartid that the RTOS is expected to use.  This
         * one will change all the time, it's more of a global argument to
@@ -76,13 +106,13 @@ typedef struct {
        char *reg_names;
 
        /* It's possible that each core has a different supported ISA set. */
-       int xlen[RISCV_MAX_HARTS];
-       riscv_reg_t misa[RISCV_MAX_HARTS];
+       int xlen;
+       riscv_reg_t misa;
        /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */
-       unsigned vlenb[RISCV_MAX_HARTS];
+       unsigned int vlenb;
 
        /* The number of triggers per hart. */
-       unsigned trigger_count[RISCV_MAX_HARTS];
+       unsigned int trigger_count;
 
        /* For each physical trigger, contains -1 if the hwbp is available, or the
         * unique_id of the breakpoint/watchpoint that is using it.
@@ -91,7 +121,7 @@ typedef struct {
        int trigger_unique_id[RISCV_MAX_HWBPS];
 
        /* The number of entries in the debug buffer. */
-       int debug_buffer_size[RISCV_MAX_HARTS];
+       int debug_buffer_size;
 
        /* This avoids invalidating the register cache too often. */
        bool registers_initialized;
@@ -112,10 +142,8 @@ typedef struct {
 
        /* Helper functions that target the various RISC-V debug spec
         * implementations. */
-       int (*get_register)(struct target *target,
-               riscv_reg_t *value, int hid, int rid);
-       int (*set_register)(struct target *target, int hartid, int regid,
-                       uint64_t value);
+       int (*get_register)(struct target *target, riscv_reg_t *value, int regid);
+       int (*set_register)(struct target *target, int regid, uint64_t value);
        int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
        int (*set_register_buf)(struct target *target, int regno,
                        const uint8_t *buf);
@@ -143,8 +171,8 @@ typedef struct {
        void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
        void (*fill_dmi_nop_u64)(struct target *target, char *buf);
 
-       int (*authdata_read)(struct target *target, uint32_t *value);
-       int (*authdata_write)(struct target *target, uint32_t value);
+       int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index);
+       int (*authdata_write)(struct target *target, uint32_t value, unsigned int index);
 
        int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
        int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
@@ -152,7 +180,10 @@ typedef struct {
        int (*test_sba_config_reg)(struct target *target, target_addr_t legal_address,
                        uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test);
 
-       int (*test_compliance)(struct target *target);
+       int (*sample_memory)(struct target *target,
+                                                struct riscv_sample_buf *buf,
+                                                riscv_sample_config_t *config,
+                                                int64_t until_ms);
 
        int (*read_memory)(struct target *target, target_addr_t address,
                        uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
@@ -161,6 +192,8 @@ typedef struct {
        int (*hart_count)(struct target *target);
        unsigned (*data_bits)(struct target *target);
 
+       COMMAND_HELPER((*print_info), struct target *target);
+
        /* Storage for vector register types. */
        struct reg_data_type_vector vector_uint8;
        struct reg_data_type_vector vector_uint16;
@@ -179,8 +212,31 @@ typedef struct {
        /* Set when trigger registers are changed by the user. This indicates we eed
         * to beware that we may hit a trigger that we didn't realize had been set. */
        bool manual_hwbp_set;
+
+       /* Memory access methods to use, ordered by priority, highest to lowest. */
+       int mem_access_methods[RISCV_NUM_MEM_ACCESS_METHODS];
+
+       /* Different memory regions may need different methods but single configuration is applied
+        * for all. Following flags are used to warn only once about failing memory access method. */
+       bool mem_access_progbuf_warn;
+       bool mem_access_sysbus_warn;
+       bool mem_access_abstract_warn;
+
+       /* In addition to the ones in the standard spec, we'll also expose additional
+        * CSRs in this list. */
+       struct list_head expose_csr;
+       /* Same, but for custom registers.
+        * Custom registers are for non-standard extensions and use abstract register numbers
+        * from range 0xc000 ... 0xffff. */
+       struct list_head expose_custom;
+
+       riscv_sample_config_t sample_config;
+       struct riscv_sample_buf sample_buf;
 } riscv_info_t;
 
+COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
+                          unsigned int value);
+
 typedef struct {
        uint8_t tunneled_dr_width;
        struct scan_field tunneled_dr[4];
@@ -205,8 +261,6 @@ extern int riscv_command_timeout_sec;
 /* Wall-clock timeout after reset. Settable via RISC-V Target commands.*/
 extern int riscv_reset_timeout_sec;
 
-extern bool riscv_prefer_sba;
-
 extern bool riscv_enable_virtual;
 extern bool riscv_ebreakm;
 extern bool riscv_ebreaks;
@@ -216,7 +270,10 @@ extern bool riscv_ebreaku;
  * that provides that. */
 static inline riscv_info_t *riscv_info(const struct target *target) __attribute__((unused));
 static inline riscv_info_t *riscv_info(const struct target *target)
-{ return target->arch_info; }
+{
+       assert(target->arch_info);
+       return target->arch_info;
+}
 #define RISCV_INFO(R) riscv_info_t *R = riscv_info(target);
 
 extern uint8_t ir_dtmcontrol[4];
@@ -269,44 +326,30 @@ void riscv_info_init(struct target *target, riscv_info_t *r);
  * then the only hart. */
 int riscv_step_rtos_hart(struct target *target);
 
-bool riscv_supports_extension(struct target *target, int hartid, char letter);
+bool riscv_supports_extension(struct target *target, char letter);
 
 /* Returns XLEN for the given (or current) hart. */
 unsigned riscv_xlen(const struct target *target);
-int riscv_xlen_of_hart(const struct target *target, int hartid);
-
-bool riscv_rtos_enabled(const struct target *target);
+int riscv_xlen_of_hart(const struct target *target);
 
 /* Sets the current hart, which is the hart that will actually be used when
  * issuing debug commands. */
 int riscv_set_current_hartid(struct target *target, int hartid);
+int riscv_select_current_hart(struct target *target);
 int riscv_current_hartid(const struct target *target);
 
 /*** Support functions for the RISC-V 'RTOS', which provides multihart support
  * without requiring multiple targets.  */
 
-/* When using the RTOS to debug, this selects the hart that is currently being
- * debugged.  This doesn't propagate to the hardware. */
-void riscv_set_all_rtos_harts(struct target *target);
-void riscv_set_rtos_hartid(struct target *target, int hartid);
-
 /* Lists the number of harts in the system, which are assumed to be
  * consecutive and start with mhartid=0. */
 int riscv_count_harts(struct target *target);
 
-/* Returns TRUE if the target has the given register on the given hart.  */
-bool riscv_has_register(struct target *target, int hartid, int regid);
-
 /** Set register, updating the cache. */
 int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
-/** Set register, updating the cache. */
-int riscv_set_register_on_hart(struct target *target, int hid, enum gdb_regno rid, uint64_t v);
 /** Get register, from the cache if it's in there. */
 int riscv_get_register(struct target *target, riscv_reg_t *value,
                enum gdb_regno r);
-/** Get register, from the cache if it's in there. */
-int riscv_get_register_on_hart(struct target *target, riscv_reg_t *value,
-               int hartid, enum gdb_regno regid);
 
 /* Checks the state of the current hart -- "is_halted" checks the actual
  * on-device register. */
@@ -329,9 +372,6 @@ int riscv_dmi_write_u64_bits(struct target *target);
 /* Invalidates the register cache. */
 void riscv_invalidate_register_cache(struct target *target);
 
-/* Returns TRUE when a hart is enabled in this target. */
-bool riscv_hart_enabled(struct target *target, int hartid);
-
 int riscv_enumerate_triggers(struct target *target);
 
 int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint);
@@ -356,4 +396,7 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval);
 void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
                riscv_bscan_tunneled_scan_context_t *ctxt);
 
+int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
+int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
+
 #endif
index 0072b9afe7fbd5fcb6215bd3c2303eeb6fdd7c4e..b347212d3417f9e2fd336788f2b0c17ced310a86 100644 (file)
@@ -85,12 +85,15 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval)
        if (result != ERROR_OK)
                return SEMI_ERROR;
 
-       uint8_t tmp[12];
+       uint8_t tmp_buf[12];
 
-       /* Read the current instruction, including the bracketing */
-       *retval = target_read_memory(target, pc - 4, 2, 6, tmp);
-       if (*retval != ERROR_OK)
-               return SEMI_ERROR;
+       /* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
+       for (int i = 0; i < 3; i++) {
+               /* Instruction memories may not support arbitrary read size. Use any size that will work. */
+               *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
+               if (*retval != ERROR_OK)
+                       return SEMI_ERROR;
+       }
 
        /*
         * The instructions that trigger a semihosting call,
@@ -100,9 +103,9 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval)
         * 00100073              ebreak
         * 40705013              srai    zero,zero,0x7
         */
-       uint32_t pre = target_buffer_get_u32(target, tmp);
-       uint32_t ebreak = target_buffer_get_u32(target, tmp + 4);
-       uint32_t post = target_buffer_get_u32(target, tmp + 8);
+       uint32_t pre = target_buffer_get_u32(target, tmp_buf);
+       uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
+       uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
        LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
 
        if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {