Upstream a whole host of RISC-V changes. 29/6529/4
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) {

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)