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:
return ERROR_OK;
}
- keep_alive();
-
riscv_batch_add_nop(batch);
for (size_t i = 0; i < batch->used_scans; ++i) {
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)
/*
* 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
/*
* 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
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));
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;
}
/*
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));
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,
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;
}
+
* 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. */
#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
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. ***/
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;
}
/* 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. ***/
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);
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);
}
}
/* 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));
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",
}
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);
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;
}
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;
#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);
/* 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);
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
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.) */
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;
}
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. */
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);
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. */
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));
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) {
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. */
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));
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) {
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;
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;
}
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;
/* 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);
* 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;
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,
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;
{
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;
{
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;
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;
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;
info->abstract_read_fpr_supported = true;
info->abstract_write_fpr_supported = true;
+ info->has_aampostincrement = YNM_MAYBE;
+
return ERROR_OK;
}
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. */
/* 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);
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));
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);
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;
}
}
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. */
{
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;
}
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);
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);
}
}
+ uint32_t sbcs;
+ if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
+ return ERROR_FAIL;
+
return ERROR_OK;
}
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) {
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)
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;
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;
}
/*
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);
/* 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;
/* 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;
}
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;
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;
/* 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;
}
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;
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;
}
/**
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;
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) */
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);
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,
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);
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;
}
}
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)
.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) {
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)
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);
} 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);
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);
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);
}
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;
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;
}
/* 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;
- }
-}
#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)))
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
/* 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;
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,
.pa_ppn_mask = {0x3ff, 0xfff},
};
-virt2phys_info_t sv39 = {
+const virt2phys_info_t sv39 = {
.name = "Sv39",
.va_bits = 39,
.level = 3,
.pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
};
-virt2phys_info_t sv48 = {
+const virt2phys_info_t sv48 = {
.name = "Sv48",
.va_bits = 48,
.level = 4,
.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)
return in;
}
-
-
static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
{
struct scan_field field;
}
}
+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;
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)
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;
}
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);
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);
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);
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)
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);
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;
}
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;
}
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);
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;
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;
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;
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;
{
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
/* 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);
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);
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;
}
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;
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;
{
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;
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) {
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) {
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;
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.");
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.");
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);
}
/* 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)
/* 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;
}
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;
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);
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);
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;
}
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;
}
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;
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;
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",
.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,
{
.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`."
{
.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`."
{
.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",
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,
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);
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!");
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;
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)
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;
}
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)
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
(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:
* 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;
}
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)
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
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;
}
}
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;
}
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);
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",
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"
};
*/
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;
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;
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));
* 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;
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 {
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;
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:
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) {
} 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;
} 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";
((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];
}
#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
# define PG_MAX_LEVEL 4
+#define RISCV_NUM_MEM_ACCESS_METHODS 3
+
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
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,
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
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.
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;
/* 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);
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);
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);
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;
/* 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];
/* 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;
* 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];
* 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. */
/* 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);
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
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,
* 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) {