aboutsummaryrefslogtreecommitdiff
path: root/riscv
diff options
context:
space:
mode:
authorPalmer Dabbelt <palmer@dabbelt.com>2017-05-16 09:33:40 -0700
committerPalmer Dabbelt <palmer@dabbelt.com>2017-05-16 12:35:49 -0700
commita1f754b2f0ec5fe72c86d6916d7c603e7727e68e (patch)
tree9a1c281461503845ecd4cde904852335e24dbe52 /riscv
parente465de145c69c28230b5c73cc58f96fd9bd04419 (diff)
parent9b539c8f0ee5075cbc3a0505d7fb6be39e9d7352 (diff)
downloadspike-a1f754b2f0ec5fe72c86d6916d7c603e7727e68e.zip
spike-a1f754b2f0ec5fe72c86d6916d7c603e7727e68e.tar.gz
spike-a1f754b2f0ec5fe72c86d6916d7c603e7727e68e.tar.bz2
Merge remote-tracking branch 'origin/debug-0.13' into priv-1.10
Diffstat (limited to 'riscv')
-rw-r--r--riscv/debug_defines.h1413
-rw-r--r--riscv/debug_module.cc442
-rw-r--r--riscv/debug_module.h117
-rw-r--r--riscv/decode.h15
-rw-r--r--riscv/execute.cc19
-rw-r--r--riscv/gdbserver.cc2163
-rw-r--r--riscv/gdbserver.h266
-rw-r--r--riscv/jtag_dtm.cc180
-rw-r--r--riscv/jtag_dtm.h61
-rw-r--r--riscv/opcodes.h244
-rw-r--r--riscv/processor.cc50
-rw-r--r--riscv/processor.h9
-rw-r--r--riscv/remote_bitbang.cc180
-rw-r--r--riscv/remote_bitbang.h34
-rw-r--r--riscv/riscv.mk.in6
-rw-r--r--riscv/sim.cc12
-rw-r--r--riscv/sim.h13
17 files changed, 2696 insertions, 2528 deletions
diff --git a/riscv/debug_defines.h b/riscv/debug_defines.h
new file mode 100644
index 0000000..cb07ec2
--- /dev/null
+++ b/riscv/debug_defines.h
@@ -0,0 +1,1413 @@
+#define DTM_IDCODE 0x01
+/*
+* Identifies the release version of this part.
+ */
+#define DTM_IDCODE_VERSION_OFFSET 28
+#define DTM_IDCODE_VERSION_LENGTH 4
+#define DTM_IDCODE_VERSION (0xf << DTM_IDCODE_VERSION_OFFSET)
+/*
+* Identifies the designer's part number of this part.
+ */
+#define DTM_IDCODE_PARTNUMBER_OFFSET 12
+#define DTM_IDCODE_PARTNUMBER_LENGTH 16
+#define DTM_IDCODE_PARTNUMBER (0xffff << DTM_IDCODE_PARTNUMBER_OFFSET)
+/*
+* Identifies the designer/manufacturer of this part. Bits 6:0 must be
+* bits 6:0 of the designer/manufacturer's Identification Code as
+* assigned by JEDEC Standard JEP106. Bits 10:7 contain the modulo-16
+* count of the number of continuation characters (0x7f) in that same
+* Identification Code.
+ */
+#define DTM_IDCODE_MANUFID_OFFSET 1
+#define DTM_IDCODE_MANUFID_LENGTH 11
+#define DTM_IDCODE_MANUFID (0x7ff << DTM_IDCODE_MANUFID_OFFSET)
+#define DTM_IDCODE_1_OFFSET 0
+#define DTM_IDCODE_1_LENGTH 1
+#define DTM_IDCODE_1 (0x1 << DTM_IDCODE_1_OFFSET)
+#define DTM_DTMCS 0x10
+/*
+* Writing 1 to this bit does a hard reset of the DTM,
+* causing the DTM to forget about any outstanding DMI transactions.
+* In general this should only be used when the Debugger has
+* reason to expect that the outstanding DMI transaction will never
+* complete (e.g. a reset condition caused an inflight DMI transaction to
+* be cancelled).
+ */
+#define DTM_DTMCS_DMIHARDRESET_OFFSET 17
+#define DTM_DTMCS_DMIHARDRESET_LENGTH 1
+#define DTM_DTMCS_DMIHARDRESET (0x1 << DTM_DTMCS_DMIHARDRESET_OFFSET)
+/*
+* Writing 1 to this bit clears the sticky error state
+* and allows the DTM to retry or complete the previous
+* transaction.
+ */
+#define DTM_DTMCS_DMIRESET_OFFSET 16
+#define DTM_DTMCS_DMIRESET_LENGTH 1
+#define DTM_DTMCS_DMIRESET (0x1 << DTM_DTMCS_DMIRESET_OFFSET)
+/*
+* This is a hint to the debugger of the minimum number of
+* cycles a debugger should spend in
+* Run-Test/Idle after every DMI scan to avoid a `busy'
+* return code (\Fdmistat of 3). A debugger must still
+* check \Fdmistat when necessary.
+*
+* 0: It is not necessary to enter Run-Test/Idle at all.
+*
+* 1: Enter Run-Test/Idle and leave it immediately.
+*
+* 2: Enter Run-Test/Idle and stay there for 1 cycle before leaving.
+*
+* And so on.
+ */
+#define DTM_DTMCS_IDLE_OFFSET 12
+#define DTM_DTMCS_IDLE_LENGTH 3
+#define DTM_DTMCS_IDLE (0x7 << DTM_DTMCS_IDLE_OFFSET)
+/*
+* 0: No error.
+*
+* 1: Reserved. Interpret the same as 2.
+*
+* 2: An operation failed (resulted in \Fop of 2).
+*
+* 3: An operation was attempted while a DMI access was still in
+* progress (resulted in \Fop of 3).
+ */
+#define DTM_DTMCS_DMISTAT_OFFSET 10
+#define DTM_DTMCS_DMISTAT_LENGTH 2
+#define DTM_DTMCS_DMISTAT (0x3 << DTM_DTMCS_DMISTAT_OFFSET)
+/*
+* The size of \Faddress in \Rdmi.
+ */
+#define DTM_DTMCS_ABITS_OFFSET 4
+#define DTM_DTMCS_ABITS_LENGTH 6
+#define DTM_DTMCS_ABITS (0x3f << DTM_DTMCS_ABITS_OFFSET)
+/*
+* 0: Version described in spec version 0.11.
+*
+* 1: Version described in spec version 0.12 (and later?), which
+* reduces the DMI data width to 32 bits.
+*
+* Other values are reserved for future use.
+ */
+#define DTM_DTMCS_VERSION_OFFSET 0
+#define DTM_DTMCS_VERSION_LENGTH 4
+#define DTM_DTMCS_VERSION (0xf << DTM_DTMCS_VERSION_OFFSET)
+#define DTM_DMI 0x11
+/*
+* Address used for DMI access. In Update-DR this value is used
+* to access the DM over the DMI.
+ */
+#define DTM_DMI_ADDRESS_OFFSET 34
+#define DTM_DMI_ADDRESS_LENGTH abits
+#define DTM_DMI_ADDRESS (((1L<<abits)-1) << DTM_DMI_ADDRESS_OFFSET)
+/*
+* The data to send to the DM over the DMI during Update-DR, and
+* the data returned from the DM as a result of the previous operation.
+ */
+#define DTM_DMI_DATA_OFFSET 2
+#define DTM_DMI_DATA_LENGTH 32
+#define DTM_DMI_DATA (0xffffffffL << DTM_DMI_DATA_OFFSET)
+/*
+* When the debugger writes this field, it has the following meaning:
+*
+* 0: Ignore \Fdata. (nop)
+*
+* 1: Read from \Faddress. (read)
+*
+* 2: Write \Fdata to \Faddress. (write)
+*
+* 3: Reserved.
+*
+* When the debugger reads this field, it means the following:
+*
+* 0: The previous operation completed successfully.
+*
+* 1: Reserved.
+*
+* 2: A previous operation failed. The data scanned into \Rdmi in
+* this access will be ignored. This status is sticky and can be
+* cleared by writing \Fdmireset in \Rdtmcs.
+*
+* This indicates that the DM itself responded with an error, e.g.
+* in the System Bus and Serial Port overflow/underflow cases.
+*
+* 3: An operation was attempted while a DMI request is still in
+* progress. The data scanned into \Rdmi in this access will be
+* ignored. This status is sticky and can be cleared by writing
+* \Fdmireset in \Rdtmcs. If a debugger sees this status, it
+* needs to give the target more TCK edges between Update-DR and
+* Capture-DR. The simplest way to do that is to add extra transitions
+* in Run-Test/Idle.
+*
+* (The DTM, DM, and/or component may be in different clock domains,
+* so synchronization may be required. Some relatively fixed number of
+* TCK ticks may be needed for the request to reach the DM, complete,
+* and for the response to be synchronized back into the TCK domain.)
+ */
+#define DTM_DMI_OP_OFFSET 0
+#define DTM_DMI_OP_LENGTH 2
+#define DTM_DMI_OP (0x3L << DTM_DMI_OP_OFFSET)
+#define CSR_DCSR 0x7b0
+/*
+* 0: There is no external debug support.
+*
+* 1: External debug support exists as it is described in this document.
+*
+* Other values are reserved for future standards.
+ */
+#define CSR_DCSR_XDEBUGVER_OFFSET 30
+#define CSR_DCSR_XDEBUGVER_LENGTH 2
+#define CSR_DCSR_XDEBUGVER (0x3 << CSR_DCSR_XDEBUGVER_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Machine Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKM_OFFSET 15
+#define CSR_DCSR_EBREAKM_LENGTH 1
+#define CSR_DCSR_EBREAKM (0x1 << CSR_DCSR_EBREAKM_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Hypervisor Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKH_OFFSET 14
+#define CSR_DCSR_EBREAKH_LENGTH 1
+#define CSR_DCSR_EBREAKH (0x1 << CSR_DCSR_EBREAKH_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in Supervisor Mode enter Debug Mode.
+ */
+#define CSR_DCSR_EBREAKS_OFFSET 13
+#define CSR_DCSR_EBREAKS_LENGTH 1
+#define CSR_DCSR_EBREAKS (0x1 << CSR_DCSR_EBREAKS_OFFSET)
+/*
+* When 1, {\tt ebreak} instructions in User/Application Mode enter
+* Debug Mode.
+ */
+#define CSR_DCSR_EBREAKU_OFFSET 12
+#define CSR_DCSR_EBREAKU_LENGTH 1
+#define CSR_DCSR_EBREAKU (0x1 << CSR_DCSR_EBREAKU_OFFSET)
+/*
+* 0: Increment counters as usual.
+*
+* 1: Don't increment any counters while in Debug Mode. This includes
+* the {\tt cycle} and {\tt instret} CSRs. This is preferred for most
+* debugging scenarios.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPCOUNT_OFFSET 10
+#define CSR_DCSR_STOPCOUNT_LENGTH 1
+#define CSR_DCSR_STOPCOUNT (0x1 << CSR_DCSR_STOPCOUNT_OFFSET)
+/*
+* 0: Increment timers as usual.
+*
+* 1: Don't increment any hart-local timers while in Debug Mode.
+*
+* An implementation may choose not to support writing to this bit.
+* The debugger must read back the value it writes to check whether
+* the feature is supported.
+ */
+#define CSR_DCSR_STOPTIME_OFFSET 9
+#define CSR_DCSR_STOPTIME_LENGTH 1
+#define CSR_DCSR_STOPTIME (0x1 << CSR_DCSR_STOPTIME_OFFSET)
+/*
+* Explains why Debug Mode was entered.
+*
+* When there are multiple reasons to enter Debug Mode in a single
+* cycle, the cause with the highest priority is the one written.
+*
+* 1: An {\tt ebreak} instruction was executed. (priority 3)
+*
+* 2: The Trigger Module caused a halt. (priority 4)
+*
+* 3: \Fhaltreq was set. (priority 2)
+*
+* 4: The hart single stepped because \Fstep was set. (priority 1)
+*
+* Other values are reserved for future use.
+ */
+#define CSR_DCSR_CAUSE_OFFSET 6
+#define CSR_DCSR_CAUSE_LENGTH 3
+#define CSR_DCSR_CAUSE (0x7 << CSR_DCSR_CAUSE_OFFSET)
+/*
+* When set and not in Debug Mode, the hart will only execute a single
+* instruction and then enter Debug Mode.
+* Interrupts are disabled when this bit is set.
+* If the instruction does not complete due to an exception,
+* the hart will immediately enter Debug Mode before executing
+* the trap handler, with appropriate exception registers set.
+ */
+#define CSR_DCSR_STEP_OFFSET 2
+#define CSR_DCSR_STEP_LENGTH 1
+#define CSR_DCSR_STEP (0x1 << CSR_DCSR_STEP_OFFSET)
+/*
+* Contains the privilege level 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 privilege level when exiting Debug Mode.
+*
+* Not all privilege levels are supported on all harts. If the
+* encoding written is not supported or the debugger is not allowed to
+* change to it, the hart may change to any supported privilege level.
+ */
+#define CSR_DCSR_PRV_OFFSET 0
+#define CSR_DCSR_PRV_LENGTH 2
+#define CSR_DCSR_PRV (0x3 << CSR_DCSR_PRV_OFFSET)
+#define CSR_DPC 0x7b1
+#define CSR_DPC_DPC_OFFSET 0
+#define CSR_DPC_DPC_LENGTH XLEN
+#define CSR_DPC_DPC (((1L<<XLEN)-1) << CSR_DPC_DPC_OFFSET)
+#define CSR_DSCRATCH0 0x7b2
+#define CSR_DSCRATCH1 0x7b3
+#define CSR_PRIV virtual
+/*
+* Contains the privilege level the hart was operating in when Debug
+* Mode was entered. The encoding is described in Table
+* \ref{tab:privlevel}. A user can write this value to change the
+* hart's privilege level when exiting Debug Mode.
+ */
+#define CSR_PRIV_PRV_OFFSET 0
+#define CSR_PRIV_PRV_LENGTH 2
+#define CSR_PRIV_PRV (0x3 << CSR_PRIV_PRV_OFFSET)
+#define CSR_TSELECT 0x7a0
+#define CSR_TSELECT_INDEX_OFFSET 0
+#define CSR_TSELECT_INDEX_LENGTH XLEN
+#define CSR_TSELECT_INDEX (((1L<<XLEN)-1) << CSR_TSELECT_INDEX_OFFSET)
+#define CSR_TDATA1 0x7a1
+/*
+* 0: There is no trigger at this \Rtselect.
+*
+* 1: The trigger is a legacy SiFive address match trigger. These
+* should not be implemented and aren't further documented here.
+*
+* 2: The trigger is an address/data match trigger. The remaining bits
+* in this register act as described in \Rmcontrol.
+*
+* 3: The trigger is an instruction count trigger. The remaining bits
+* in this register act as described in \Ricount.
+*
+* 15: This trigger exists (so enumeration shouldn't terminate), but
+* is not currently available.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_TDATA1_TYPE_OFFSET XLEN-4
+#define CSR_TDATA1_TYPE_LENGTH 4
+#define CSR_TDATA1_TYPE (0xfL << CSR_TDATA1_TYPE_OFFSET)
+/*
+* 0: Both Debug and M Mode can write the {\tt tdata} registers at the
+* selected \Rtselect.
+*
+* 1: Only Debug Mode can write the {\tt tdata} registers at the
+* selected \Rtselect. Writes from other modes are ignored.
+*
+* This bit is only writable from Debug Mode.
+ */
+#define CSR_TDATA1_HMODE_OFFSET XLEN-5
+#define CSR_TDATA1_HMODE_LENGTH 1
+#define CSR_TDATA1_HMODE (0x1L << CSR_TDATA1_HMODE_OFFSET)
+/*
+* Trigger-specific data.
+ */
+#define CSR_TDATA1_DATA_OFFSET 0
+#define CSR_TDATA1_DATA_LENGTH XLEN - 5
+#define CSR_TDATA1_DATA (((1L<<XLEN - 5)-1) << CSR_TDATA1_DATA_OFFSET)
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA2_DATA_OFFSET 0
+#define CSR_TDATA2_DATA_LENGTH XLEN
+#define CSR_TDATA2_DATA (((1L<<XLEN)-1) << CSR_TDATA2_DATA_OFFSET)
+#define CSR_TDATA3 0x7a3
+#define CSR_TDATA3_DATA_OFFSET 0
+#define CSR_TDATA3_DATA_LENGTH XLEN
+#define CSR_TDATA3_DATA (((1L<<XLEN)-1) << CSR_TDATA3_DATA_OFFSET)
+#define CSR_MCONTROL 0x7a1
+#define CSR_MCONTROL_TYPE_OFFSET XLEN-4
+#define CSR_MCONTROL_TYPE_LENGTH 4
+#define CSR_MCONTROL_TYPE (0xfL << CSR_MCONTROL_TYPE_OFFSET)
+#define CSR_MCONTROL_DMODE_OFFSET XLEN-5
+#define CSR_MCONTROL_DMODE_LENGTH 1
+#define CSR_MCONTROL_DMODE (0x1L << CSR_MCONTROL_DMODE_OFFSET)
+/*
+* Specifies the largest naturally aligned powers-of-two (NAPOT) range
+* supported by the hardware. 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.
+ */
+#define CSR_MCONTROL_MASKMAX_OFFSET XLEN-11
+#define CSR_MCONTROL_MASKMAX_LENGTH 6
+#define CSR_MCONTROL_MASKMAX (0x3fL << CSR_MCONTROL_MASKMAX_OFFSET)
+/*
+* 0: Perform a match on the virtual address.
+*
+* 1: Perform a match on the data value loaded/stored, or the
+* instruction executed.
+ */
+#define CSR_MCONTROL_SELECT_OFFSET 19
+#define CSR_MCONTROL_SELECT_LENGTH 1
+#define CSR_MCONTROL_SELECT (0x1L << 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 are committed.
+*
+* 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 and
+* not implement that suggestion than to not implement them at all.
+*
+* Most hardware will only implement one timing or the other, possibly
+* dependent on \Fselect, \Fexecute, \Fload, and \Fstore. 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 \Ftiming of 0 will result in the same load
+* happening again when the debugger lets the core run. For data load
+* triggers, debuggers must first attempt to set the breakpoint with
+* \Ftiming of 1.
+*
+* A chain of triggers that don't all have the same \Ftiming value
+* will never fire (unless consecutive instructions match the
+* appropriate triggers).
+ */
+#define CSR_MCONTROL_TIMING_OFFSET 18
+#define CSR_MCONTROL_TIMING_LENGTH 1
+#define CSR_MCONTROL_TIMING (0x1L << CSR_MCONTROL_TIMING_OFFSET)
+/*
+* Determines what happens when this trigger matches.
+*
+* 0: Raise a breakpoint exception. (Used when software wants to use
+* the trigger module without an external debugger attached.)
+*
+* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
+*
+* 2: Start tracing.
+*
+* 3: Stop tracing.
+*
+* 4: Emit trace data for this match. If it is a data access match,
+* emit appropriate Load/Store Address/Data. If it is an instruction
+* execution, emit its PC.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_MCONTROL_ACTION_OFFSET 12
+#define CSR_MCONTROL_ACTION_LENGTH 6
+#define CSR_MCONTROL_ACTION (0x3fL << CSR_MCONTROL_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.
+ */
+#define CSR_MCONTROL_CHAIN_OFFSET 11
+#define CSR_MCONTROL_CHAIN_LENGTH 1
+#define CSR_MCONTROL_CHAIN (0x1L << CSR_MCONTROL_CHAIN_OFFSET)
+/*
+* 0: Matches when the value equals \Rtdatatwo.
+*
+* 1: Matches when the top M bits of the value match the top M bits of
+* \Rtdatatwo. M is XLEN-1 minus the index of the least-significant
+* bit containing 0 in \Rtdatatwo.
+*
+* 2: Matches when the value is greater than or equal to \Rtdatatwo.
+*
+* 3: Matches when the value is less than \Rtdatatwo.
+*
+* 4: Matches when the lower half of the value equals the lower half
+* of \Rtdatatwo after the lower half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* 5: Matches when the upper half of the value equals the lower half
+* of \Rtdatatwo after the upper half of the value is ANDed with the
+* upper half of \Rtdatatwo.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_MCONTROL_MATCH_OFFSET 7
+#define CSR_MCONTROL_MATCH_LENGTH 4
+#define CSR_MCONTROL_MATCH (0xfL << CSR_MCONTROL_MATCH_OFFSET)
+/*
+* When set, enable this trigger in M mode.
+ */
+#define CSR_MCONTROL_M_OFFSET 6
+#define CSR_MCONTROL_M_LENGTH 1
+#define CSR_MCONTROL_M (0x1L << CSR_MCONTROL_M_OFFSET)
+/*
+* When set, enable this trigger in H mode.
+ */
+#define CSR_MCONTROL_H_OFFSET 5
+#define CSR_MCONTROL_H_LENGTH 1
+#define CSR_MCONTROL_H (0x1L << CSR_MCONTROL_H_OFFSET)
+/*
+* When set, enable this trigger in S mode.
+ */
+#define CSR_MCONTROL_S_OFFSET 4
+#define CSR_MCONTROL_S_LENGTH 1
+#define CSR_MCONTROL_S (0x1L << CSR_MCONTROL_S_OFFSET)
+/*
+* When set, enable this trigger in U mode.
+ */
+#define CSR_MCONTROL_U_OFFSET 3
+#define CSR_MCONTROL_U_LENGTH 1
+#define CSR_MCONTROL_U (0x1L << CSR_MCONTROL_U_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or opcode of an
+* instruction that is executed.
+ */
+#define CSR_MCONTROL_EXECUTE_OFFSET 2
+#define CSR_MCONTROL_EXECUTE_LENGTH 1
+#define CSR_MCONTROL_EXECUTE (0x1L << CSR_MCONTROL_EXECUTE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a store.
+ */
+#define CSR_MCONTROL_STORE_OFFSET 1
+#define CSR_MCONTROL_STORE_LENGTH 1
+#define CSR_MCONTROL_STORE (0x1L << CSR_MCONTROL_STORE_OFFSET)
+/*
+* When set, the trigger fires on the virtual address or data of a load.
+ */
+#define CSR_MCONTROL_LOAD_OFFSET 0
+#define CSR_MCONTROL_LOAD_LENGTH 1
+#define CSR_MCONTROL_LOAD (0x1L << CSR_MCONTROL_LOAD_OFFSET)
+#define CSR_ICOUNT 0x7a1
+#define CSR_ICOUNT_TYPE_OFFSET XLEN-4
+#define CSR_ICOUNT_TYPE_LENGTH 4
+#define CSR_ICOUNT_TYPE (0xfL << CSR_ICOUNT_TYPE_OFFSET)
+#define CSR_ICOUNT_DMODE_OFFSET XLEN-5
+#define CSR_ICOUNT_DMODE_LENGTH 1
+#define CSR_ICOUNT_DMODE (0x1L << CSR_ICOUNT_DMODE_OFFSET)
+/*
+* When count is decremented to 0, the trigger fires. Instead of
+* changing \Fcount from 1 to 0, it is also acceptable for hardware to
+* clear \Fm, \Fh, \Fs, and \Fu. This allows \Fcount 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 (0x3fffL << CSR_ICOUNT_COUNT_OFFSET)
+/*
+* When set, every instruction completed or exception taken in M mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_M_OFFSET 9
+#define CSR_ICOUNT_M_LENGTH 1
+#define CSR_ICOUNT_M (0x1L << CSR_ICOUNT_M_OFFSET)
+/*
+* When set, every instruction completed or exception taken in in H mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_H_OFFSET 8
+#define CSR_ICOUNT_H_LENGTH 1
+#define CSR_ICOUNT_H (0x1L << CSR_ICOUNT_H_OFFSET)
+/*
+* When set, every instruction completed or exception taken in S mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_S_OFFSET 7
+#define CSR_ICOUNT_S_LENGTH 1
+#define CSR_ICOUNT_S (0x1L << CSR_ICOUNT_S_OFFSET)
+/*
+* When set, every instruction completed or exception taken in U mode decrements \Fcount
+* by 1.
+ */
+#define CSR_ICOUNT_U_OFFSET 6
+#define CSR_ICOUNT_U_LENGTH 1
+#define CSR_ICOUNT_U (0x1L << CSR_ICOUNT_U_OFFSET)
+/*
+* Determines what happens when this trigger matches.
+*
+* 0: Raise a breakpoint exception. (Used when software wants to use the
+* trigger module without an external debugger attached.)
+*
+* 1: Enter Debug Mode. (Only supported when \Fhmode is 1.)
+*
+* 2: Start tracing.
+*
+* 3: Stop tracing.
+*
+* 4: Emit trace data for this match. If it is a data access match,
+* emit appropriate Load/Store Address/Data. If it is an instruction
+* execution, emit its PC.
+*
+* Other values are reserved for future use.
+ */
+#define CSR_ICOUNT_ACTION_OFFSET 0
+#define CSR_ICOUNT_ACTION_LENGTH 6
+#define CSR_ICOUNT_ACTION (0x3fL << CSR_ICOUNT_ACTION_OFFSET)
+#define DMI_DMSTATUS 0x11
+/*
+* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq.
+ */
+#define DMI_DMSTATUS_ALLRESUMEACK_OFFSET 17
+#define DMI_DMSTATUS_ALLRESUMEACK_LENGTH 1
+#define DMI_DMSTATUS_ALLRESUMEACK (0x1 << DMI_DMSTATUS_ALLRESUMEACK_OFFSET)
+/*
+* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq.
+ */
+#define DMI_DMSTATUS_ANYRESUMEACK_OFFSET 16
+#define DMI_DMSTATUS_ANYRESUMEACK_LENGTH 1
+#define DMI_DMSTATUS_ANYRESUMEACK (0x1 << DMI_DMSTATUS_ANYRESUMEACK_OFFSET)
+/*
+* This field is 1 when all currently selected harts do not exist in this system.
+ */
+#define DMI_DMSTATUS_ALLNONEXISTENT_OFFSET 15
+#define DMI_DMSTATUS_ALLNONEXISTENT_LENGTH 1
+#define DMI_DMSTATUS_ALLNONEXISTENT (0x1 << DMI_DMSTATUS_ALLNONEXISTENT_OFFSET)
+/*
+* This field is 1 when any currently selected hart does not exist in this system.
+ */
+#define DMI_DMSTATUS_ANYNONEXISTENT_OFFSET 14
+#define DMI_DMSTATUS_ANYNONEXISTENT_LENGTH 1
+#define DMI_DMSTATUS_ANYNONEXISTENT (0x1 << DMI_DMSTATUS_ANYNONEXISTENT_OFFSET)
+/*
+* This field is 1 when all currently selected harts are unavailable.
+ */
+#define DMI_DMSTATUS_ALLUNAVAIL_OFFSET 13
+#define DMI_DMSTATUS_ALLUNAVAIL_LENGTH 1
+#define DMI_DMSTATUS_ALLUNAVAIL (0x1 << DMI_DMSTATUS_ALLUNAVAIL_OFFSET)
+/*
+* This field is 1 when any currently selected hart is unavailable.
+ */
+#define DMI_DMSTATUS_ANYUNAVAIL_OFFSET 12
+#define DMI_DMSTATUS_ANYUNAVAIL_LENGTH 1
+#define DMI_DMSTATUS_ANYUNAVAIL (0x1 << DMI_DMSTATUS_ANYUNAVAIL_OFFSET)
+/*
+* This field is 1 when all currently selected harts are running.
+ */
+#define DMI_DMSTATUS_ALLRUNNING_OFFSET 11
+#define DMI_DMSTATUS_ALLRUNNING_LENGTH 1
+#define DMI_DMSTATUS_ALLRUNNING (0x1 << DMI_DMSTATUS_ALLRUNNING_OFFSET)
+/*
+* This field is 1 when any currently selected hart is running.
+ */
+#define DMI_DMSTATUS_ANYRUNNING_OFFSET 10
+#define DMI_DMSTATUS_ANYRUNNING_LENGTH 1
+#define DMI_DMSTATUS_ANYRUNNING (0x1 << DMI_DMSTATUS_ANYRUNNING_OFFSET)
+/*
+* This field is 1 when all currently selected harts are halted.
+ */
+#define DMI_DMSTATUS_ALLHALTED_OFFSET 9
+#define DMI_DMSTATUS_ALLHALTED_LENGTH 1
+#define DMI_DMSTATUS_ALLHALTED (0x1 << DMI_DMSTATUS_ALLHALTED_OFFSET)
+/*
+* This field is 1 when any currently selected hart is halted.
+ */
+#define DMI_DMSTATUS_ANYHALTED_OFFSET 8
+#define DMI_DMSTATUS_ANYHALTED_LENGTH 1
+#define DMI_DMSTATUS_ANYHALTED (0x1 << DMI_DMSTATUS_ANYHALTED_OFFSET)
+/*
+* 0 when authentication is required before using the DM. 1 when the
+* authentication check has passed. On components that don't implement
+* authentication, this bit must be preset as 1.
+ */
+#define DMI_DMSTATUS_AUTHENTICATED_OFFSET 7
+#define DMI_DMSTATUS_AUTHENTICATED_LENGTH 1
+#define DMI_DMSTATUS_AUTHENTICATED (0x1 << DMI_DMSTATUS_AUTHENTICATED_OFFSET)
+/*
+* 0: The authentication module is ready to process the next
+* read/write to \Rauthdata.
+*
+* 1: The authentication module is busy. Accessing \Rauthdata results
+* in unspecified behavior.
+*
+* \Fauthbusy only becomes set in immediate response to an access to
+* \Rauthdata.
+ */
+#define DMI_DMSTATUS_AUTHBUSY_OFFSET 6
+#define DMI_DMSTATUS_AUTHBUSY_LENGTH 1
+#define DMI_DMSTATUS_AUTHBUSY (0x1 << DMI_DMSTATUS_AUTHBUSY_OFFSET)
+#define DMI_DMSTATUS_CFGSTRVALID_OFFSET 4
+#define DMI_DMSTATUS_CFGSTRVALID_LENGTH 1
+#define DMI_DMSTATUS_CFGSTRVALID (0x1 << DMI_DMSTATUS_CFGSTRVALID_OFFSET)
+/*
+* Reserved for future use. Reads as 0.
+ */
+#define DMI_DMSTATUS_VERSIONHI_OFFSET 2
+#define DMI_DMSTATUS_VERSIONHI_LENGTH 2
+#define DMI_DMSTATUS_VERSIONHI (0x3 << DMI_DMSTATUS_VERSIONHI_OFFSET)
+/*
+* 00: There is no Debug Module present.
+*
+* 01: There is a Debug Module and it conforms to version 0.11 of this
+* specification.
+*
+* 10: There is a Debug Module and it conforms to version 0.13 of this
+* specification.
+*
+* 11: Reserved for future use.
+ */
+#define DMI_DMSTATUS_VERSIONLO_OFFSET 0
+#define DMI_DMSTATUS_VERSIONLO_LENGTH 2
+#define DMI_DMSTATUS_VERSIONLO (0x3 << DMI_DMSTATUS_VERSIONLO_OFFSET)
+#define DMI_DMCONTROL 0x10
+/*
+* Halt request signal for all currently selected harts. When set to 1, the
+* hart will halt if it is not currently halted.
+* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HALTREQ_OFFSET 31
+#define DMI_DMCONTROL_HALTREQ_LENGTH 1
+#define DMI_DMCONTROL_HALTREQ (0x1 << DMI_DMCONTROL_HALTREQ_OFFSET)
+/*
+* Resume request signal for all currently selected harts. When set to 1,
+* the hart will resume if it is currently halted.
+* Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_RESUMEREQ_OFFSET 30
+#define DMI_DMCONTROL_RESUMEREQ_LENGTH 1
+#define DMI_DMCONTROL_RESUMEREQ (0x1 << DMI_DMCONTROL_RESUMEREQ_OFFSET)
+/*
+* This optional bit controls reset to all the currently selected harts.
+* To perform a reset the debugger writes 1, and then writes 0 to
+* deassert the reset signal.
+*
+* If this feature is not implemented, the bit always stays 0, so
+* after writing 1 the debugger can read the register back to see if
+* the feature is supported.
+*
+* Writes apply to the new value of \Fhartsel and \Fhasel.
+ */
+#define DMI_DMCONTROL_HARTRESET_OFFSET 29
+#define DMI_DMCONTROL_HARTRESET_LENGTH 1
+#define DMI_DMCONTROL_HARTRESET (0x1 << DMI_DMCONTROL_HARTRESET_OFFSET)
+/*
+* Selects the definition of currently selected harts.
+*
+* 0: There is a single currently selected hart, that selected by \Fhartsel.
+*
+* 1: There may be multiple currently selected harts -- that selected by \Fhartsel,
+* plus those selected by the hart array mask register.
+*
+* An implementation which does not implement the hart array mask register
+* should tie this field to 0. A debugger which wishes to use the hart array
+* mask register feature should set this bit and read back to see if the functionality
+* is supported.
+ */
+#define DMI_DMCONTROL_HASEL_OFFSET 26
+#define DMI_DMCONTROL_HASEL_LENGTH 1
+#define DMI_DMCONTROL_HASEL (0x1 << DMI_DMCONTROL_HASEL_OFFSET)
+/*
+* The DM-specific index of the hart to select. This hart is always part of the
+* currently selected harts.
+ */
+#define DMI_DMCONTROL_HARTSEL_OFFSET 16
+#define DMI_DMCONTROL_HARTSEL_LENGTH 10
+#define DMI_DMCONTROL_HARTSEL (0x3ff << DMI_DMCONTROL_HARTSEL_OFFSET)
+/*
+* This bit controls the reset signal from the DM to the rest of the
+* system. To perform a reset the debugger writes 1, and then writes 0
+* to deassert the reset.
+ */
+#define DMI_DMCONTROL_NDMRESET_OFFSET 1
+#define DMI_DMCONTROL_NDMRESET_LENGTH 1
+#define DMI_DMCONTROL_NDMRESET (0x1 << DMI_DMCONTROL_NDMRESET_OFFSET)
+/*
+* This bit serves as a reset signal for the Debug Module itself.
+*
+* 0: The module's state, including authentication mechanism,
+* takes its reset values (the \Fdmactive bit is the only bit which can
+* be written to something other than its reset value).
+*
+* 1: The module functions normally.
+*
+* No other mechanism should exist that may result in resetting the
+* Debug Module after power up, including the platform's system reset
+* or Debug Transport reset signals.
+*
+* A debugger should pulse this bit low to ensure that the Debug
+* Module is fully reset and ready to use.
+*
+* Implementations may use this bit to aid debugging, for example by
+* preventing the Debug Module from being power gated while debugging
+* is active.
+ */
+#define DMI_DMCONTROL_DMACTIVE_OFFSET 0
+#define DMI_DMCONTROL_DMACTIVE_LENGTH 1
+#define DMI_DMCONTROL_DMACTIVE (0x1 << DMI_DMCONTROL_DMACTIVE_OFFSET)
+#define DMI_HARTINFO 0x12
+/*
+* Number of {\tt dscratch} registers available for the debugger
+* to use during program buffer execution, starting from \Rdscratchzero.
+* The debugger can make no assumptions about the contents of these
+* registers between commands.
+ */
+#define DMI_HARTINFO_NSCRATCH_OFFSET 20
+#define DMI_HARTINFO_NSCRATCH_LENGTH 4
+#define DMI_HARTINFO_NSCRATCH (0xf << DMI_HARTINFO_NSCRATCH_OFFSET)
+/*
+* 0: The {\tt data} registers are shadowed in the hart by CSR
+* registers. Each CSR register is XLEN bits in size, and corresponds
+* to a single argument, per Table~\ref{tab:datareg}.
+*
+* 1: The {\tt data} registers are shadowed in the hart's memory map.
+* Each register takes up 4 bytes in the memory map.
+ */
+#define DMI_HARTINFO_DATAACCESS_OFFSET 16
+#define DMI_HARTINFO_DATAACCESS_LENGTH 1
+#define DMI_HARTINFO_DATAACCESS (0x1 << DMI_HARTINFO_DATAACCESS_OFFSET)
+/*
+* If \Fdataaccess is 0: Number of CSR registers dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Number of 32-bit words in the memory map
+* dedicated to shadowing the {\tt data} registers.
+ */
+#define DMI_HARTINFO_DATASIZE_OFFSET 12
+#define DMI_HARTINFO_DATASIZE_LENGTH 4
+#define DMI_HARTINFO_DATASIZE (0xf << DMI_HARTINFO_DATASIZE_OFFSET)
+/*
+* If \Fdataaccess is 0: The number of the first CSR dedicated to
+* shadowing the {\tt data} registers.
+*
+* If \Fdataaccess is 1: Signed address of RAM where the {\tt data}
+* registers are shadowed.
+ */
+#define DMI_HARTINFO_DATAADDR_OFFSET 0
+#define DMI_HARTINFO_DATAADDR_LENGTH 12
+#define DMI_HARTINFO_DATAADDR (0xfff << DMI_HARTINFO_DATAADDR_OFFSET)
+#define DMI_HALTSUM 0x13
+#define DMI_HALTSUM_HALT1023_992_OFFSET 31
+#define DMI_HALTSUM_HALT1023_992_LENGTH 1
+#define DMI_HALTSUM_HALT1023_992 (0x1 << DMI_HALTSUM_HALT1023_992_OFFSET)
+#define DMI_HALTSUM_HALT991_960_OFFSET 30
+#define DMI_HALTSUM_HALT991_960_LENGTH 1
+#define DMI_HALTSUM_HALT991_960 (0x1 << DMI_HALTSUM_HALT991_960_OFFSET)
+#define DMI_HALTSUM_HALT959_928_OFFSET 29
+#define DMI_HALTSUM_HALT959_928_LENGTH 1
+#define DMI_HALTSUM_HALT959_928 (0x1 << DMI_HALTSUM_HALT959_928_OFFSET)
+#define DMI_HALTSUM_HALT927_896_OFFSET 28
+#define DMI_HALTSUM_HALT927_896_LENGTH 1
+#define DMI_HALTSUM_HALT927_896 (0x1 << DMI_HALTSUM_HALT927_896_OFFSET)
+#define DMI_HALTSUM_HALT895_864_OFFSET 27
+#define DMI_HALTSUM_HALT895_864_LENGTH 1
+#define DMI_HALTSUM_HALT895_864 (0x1 << DMI_HALTSUM_HALT895_864_OFFSET)
+#define DMI_HALTSUM_HALT863_832_OFFSET 26
+#define DMI_HALTSUM_HALT863_832_LENGTH 1
+#define DMI_HALTSUM_HALT863_832 (0x1 << DMI_HALTSUM_HALT863_832_OFFSET)
+#define DMI_HALTSUM_HALT831_800_OFFSET 25
+#define DMI_HALTSUM_HALT831_800_LENGTH 1
+#define DMI_HALTSUM_HALT831_800 (0x1 << DMI_HALTSUM_HALT831_800_OFFSET)
+#define DMI_HALTSUM_HALT799_768_OFFSET 24
+#define DMI_HALTSUM_HALT799_768_LENGTH 1
+#define DMI_HALTSUM_HALT799_768 (0x1 << DMI_HALTSUM_HALT799_768_OFFSET)
+#define DMI_HALTSUM_HALT767_736_OFFSET 23
+#define DMI_HALTSUM_HALT767_736_LENGTH 1
+#define DMI_HALTSUM_HALT767_736 (0x1 << DMI_HALTSUM_HALT767_736_OFFSET)
+#define DMI_HALTSUM_HALT735_704_OFFSET 22
+#define DMI_HALTSUM_HALT735_704_LENGTH 1
+#define DMI_HALTSUM_HALT735_704 (0x1 << DMI_HALTSUM_HALT735_704_OFFSET)
+#define DMI_HALTSUM_HALT703_672_OFFSET 21
+#define DMI_HALTSUM_HALT703_672_LENGTH 1
+#define DMI_HALTSUM_HALT703_672 (0x1 << DMI_HALTSUM_HALT703_672_OFFSET)
+#define DMI_HALTSUM_HALT671_640_OFFSET 20
+#define DMI_HALTSUM_HALT671_640_LENGTH 1
+#define DMI_HALTSUM_HALT671_640 (0x1 << DMI_HALTSUM_HALT671_640_OFFSET)
+#define DMI_HALTSUM_HALT639_608_OFFSET 19
+#define DMI_HALTSUM_HALT639_608_LENGTH 1
+#define DMI_HALTSUM_HALT639_608 (0x1 << DMI_HALTSUM_HALT639_608_OFFSET)
+#define DMI_HALTSUM_HALT607_576_OFFSET 18
+#define DMI_HALTSUM_HALT607_576_LENGTH 1
+#define DMI_HALTSUM_HALT607_576 (0x1 << DMI_HALTSUM_HALT607_576_OFFSET)
+#define DMI_HALTSUM_HALT575_544_OFFSET 17
+#define DMI_HALTSUM_HALT575_544_LENGTH 1
+#define DMI_HALTSUM_HALT575_544 (0x1 << DMI_HALTSUM_HALT575_544_OFFSET)
+#define DMI_HALTSUM_HALT543_512_OFFSET 16
+#define DMI_HALTSUM_HALT543_512_LENGTH 1
+#define DMI_HALTSUM_HALT543_512 (0x1 << DMI_HALTSUM_HALT543_512_OFFSET)
+#define DMI_HALTSUM_HALT511_480_OFFSET 15
+#define DMI_HALTSUM_HALT511_480_LENGTH 1
+#define DMI_HALTSUM_HALT511_480 (0x1 << DMI_HALTSUM_HALT511_480_OFFSET)
+#define DMI_HALTSUM_HALT479_448_OFFSET 14
+#define DMI_HALTSUM_HALT479_448_LENGTH 1
+#define DMI_HALTSUM_HALT479_448 (0x1 << DMI_HALTSUM_HALT479_448_OFFSET)
+#define DMI_HALTSUM_HALT447_416_OFFSET 13
+#define DMI_HALTSUM_HALT447_416_LENGTH 1
+#define DMI_HALTSUM_HALT447_416 (0x1 << DMI_HALTSUM_HALT447_416_OFFSET)
+#define DMI_HALTSUM_HALT415_384_OFFSET 12
+#define DMI_HALTSUM_HALT415_384_LENGTH 1
+#define DMI_HALTSUM_HALT415_384 (0x1 << DMI_HALTSUM_HALT415_384_OFFSET)
+#define DMI_HALTSUM_HALT383_352_OFFSET 11
+#define DMI_HALTSUM_HALT383_352_LENGTH 1
+#define DMI_HALTSUM_HALT383_352 (0x1 << DMI_HALTSUM_HALT383_352_OFFSET)
+#define DMI_HALTSUM_HALT351_320_OFFSET 10
+#define DMI_HALTSUM_HALT351_320_LENGTH 1
+#define DMI_HALTSUM_HALT351_320 (0x1 << DMI_HALTSUM_HALT351_320_OFFSET)
+#define DMI_HALTSUM_HALT319_288_OFFSET 9
+#define DMI_HALTSUM_HALT319_288_LENGTH 1
+#define DMI_HALTSUM_HALT319_288 (0x1 << DMI_HALTSUM_HALT319_288_OFFSET)
+#define DMI_HALTSUM_HALT287_256_OFFSET 8
+#define DMI_HALTSUM_HALT287_256_LENGTH 1
+#define DMI_HALTSUM_HALT287_256 (0x1 << DMI_HALTSUM_HALT287_256_OFFSET)
+#define DMI_HALTSUM_HALT255_224_OFFSET 7
+#define DMI_HALTSUM_HALT255_224_LENGTH 1
+#define DMI_HALTSUM_HALT255_224 (0x1 << DMI_HALTSUM_HALT255_224_OFFSET)
+#define DMI_HALTSUM_HALT223_192_OFFSET 6
+#define DMI_HALTSUM_HALT223_192_LENGTH 1
+#define DMI_HALTSUM_HALT223_192 (0x1 << DMI_HALTSUM_HALT223_192_OFFSET)
+#define DMI_HALTSUM_HALT191_160_OFFSET 5
+#define DMI_HALTSUM_HALT191_160_LENGTH 1
+#define DMI_HALTSUM_HALT191_160 (0x1 << DMI_HALTSUM_HALT191_160_OFFSET)
+#define DMI_HALTSUM_HALT159_128_OFFSET 4
+#define DMI_HALTSUM_HALT159_128_LENGTH 1
+#define DMI_HALTSUM_HALT159_128 (0x1 << DMI_HALTSUM_HALT159_128_OFFSET)
+#define DMI_HALTSUM_HALT127_96_OFFSET 3
+#define DMI_HALTSUM_HALT127_96_LENGTH 1
+#define DMI_HALTSUM_HALT127_96 (0x1 << DMI_HALTSUM_HALT127_96_OFFSET)
+#define DMI_HALTSUM_HALT95_64_OFFSET 2
+#define DMI_HALTSUM_HALT95_64_LENGTH 1
+#define DMI_HALTSUM_HALT95_64 (0x1 << DMI_HALTSUM_HALT95_64_OFFSET)
+#define DMI_HALTSUM_HALT63_32_OFFSET 1
+#define DMI_HALTSUM_HALT63_32_LENGTH 1
+#define DMI_HALTSUM_HALT63_32 (0x1 << DMI_HALTSUM_HALT63_32_OFFSET)
+#define DMI_HALTSUM_HALT31_0_OFFSET 0
+#define DMI_HALTSUM_HALT31_0_LENGTH 1
+#define DMI_HALTSUM_HALT31_0 (0x1 << DMI_HALTSUM_HALT31_0_OFFSET)
+#define DMI_HAWINDOWSEL 0x14
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0
+#define DMI_HAWINDOWSEL_HAWINDOWSEL_LENGTH 5
+#define DMI_HAWINDOWSEL_HAWINDOWSEL (0x1f << DMI_HAWINDOWSEL_HAWINDOWSEL_OFFSET)
+#define DMI_HAWINDOW 0x15
+#define DMI_HAWINDOW_MASKDATA_OFFSET 0
+#define DMI_HAWINDOW_MASKDATA_LENGTH 32
+#define DMI_HAWINDOW_MASKDATA (0xffffffff << DMI_HAWINDOW_MASKDATA_OFFSET)
+#define DMI_ABSTRACTCS 0x16
+/*
+* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16.
+*
+* TODO: Explain what can be done with each size of the buffer, to suggest
+* why you would want more or less words.
+ */
+#define DMI_ABSTRACTCS_PROGSIZE_OFFSET 24
+#define DMI_ABSTRACTCS_PROGSIZE_LENGTH 5
+#define DMI_ABSTRACTCS_PROGSIZE (0x1f << DMI_ABSTRACTCS_PROGSIZE_OFFSET)
+/*
+* 1: An abstract command is currently being executed.
+*
+* This bit is set as soon as \Rcommand is written, and is
+* not cleared until that command has completed.
+ */
+#define DMI_ABSTRACTCS_BUSY_OFFSET 12
+#define DMI_ABSTRACTCS_BUSY_LENGTH 1
+#define DMI_ABSTRACTCS_BUSY (0x1 << DMI_ABSTRACTCS_BUSY_OFFSET)
+/*
+* Gets set if an abstract command fails. The bits in this field remain set until
+* they are cleared by writing 1 to them. No abstract command is
+* started until the value is reset to 0.
+*
+* 0 (none): No error.
+*
+* 1 (busy): An abstract command was executing while \Rcommand or one
+* of the {\tt data} registers was accessed.
+*
+* 2 (not supported): The requested command is not supported. A
+* command that is not supported while the hart is running may be
+* supported when it is halted.
+*
+* 3 (exception): An exception occurred while executing the command
+* (eg. while executing the Program Buffer).
+*
+* 4 (halt/resume): An abstract command couldn't execute because the
+* hart wasn't in the expected state (running/halted).
+*
+* 7 (other): The command failed for another reason.
+ */
+#define DMI_ABSTRACTCS_CMDERR_OFFSET 8
+#define DMI_ABSTRACTCS_CMDERR_LENGTH 3
+#define DMI_ABSTRACTCS_CMDERR (0x7 << DMI_ABSTRACTCS_CMDERR_OFFSET)
+/*
+* Number of {\tt data} registers that are implemented as part of the
+* abstract command interface. Valid sizes are 0 - 8.
+ */
+#define DMI_ABSTRACTCS_DATACOUNT_OFFSET 0
+#define DMI_ABSTRACTCS_DATACOUNT_LENGTH 5
+#define DMI_ABSTRACTCS_DATACOUNT (0x1f << DMI_ABSTRACTCS_DATACOUNT_OFFSET)
+#define DMI_COMMAND 0x17
+/*
+* The type determines the overall functionality of this
+* abstract command.
+ */
+#define DMI_COMMAND_CMDTYPE_OFFSET 24
+#define DMI_COMMAND_CMDTYPE_LENGTH 8
+#define DMI_COMMAND_CMDTYPE (0xff << DMI_COMMAND_CMDTYPE_OFFSET)
+/*
+* This field is interpreted in a command-specific manner,
+* described for each abstract command.
+ */
+#define DMI_COMMAND_CONTROL_OFFSET 0
+#define DMI_COMMAND_CONTROL_LENGTH 24
+#define DMI_COMMAND_CONTROL (0xffffff << DMI_COMMAND_CONTROL_OFFSET)
+#define DMI_ABSTRACTAUTO 0x18
+/*
+* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 16
+#define DMI_ABSTRACTAUTO_AUTOEXECPROGBUF (0xffff << DMI_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET)
+/*
+* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word
+* cause the command in \Rcommand to be executed again.
+ */
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 12
+#define DMI_ABSTRACTAUTO_AUTOEXECDATA (0xfff << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET)
+#define DMI_CFGSTRADDR0 0x19
+#define DMI_CFGSTRADDR0_ADDR_OFFSET 0
+#define DMI_CFGSTRADDR0_ADDR_LENGTH 32
+#define DMI_CFGSTRADDR0_ADDR (0xffffffff << DMI_CFGSTRADDR0_ADDR_OFFSET)
+#define DMI_CFGSTRADDR1 0x1a
+#define DMI_CFGSTRADDR2 0x1b
+#define DMI_CFGSTRADDR3 0x1c
+#define DMI_DATA0 0x04
+#define DMI_DATA0_DATA_OFFSET 0
+#define DMI_DATA0_DATA_LENGTH 32
+#define DMI_DATA0_DATA (0xffffffff << DMI_DATA0_DATA_OFFSET)
+#define DMI_DATA11 0x0f
+#define DMI_PROGBUF0 0x20
+#define DMI_PROGBUF0_DATA_OFFSET 0
+#define DMI_PROGBUF0_DATA_LENGTH 32
+#define DMI_PROGBUF0_DATA (0xffffffff << DMI_PROGBUF0_DATA_OFFSET)
+#define DMI_PROGBUF15 0x2f
+#define DMI_AUTHDATA 0x30
+#define DMI_AUTHDATA_DATA_OFFSET 0
+#define DMI_AUTHDATA_DATA_LENGTH 32
+#define DMI_AUTHDATA_DATA (0xffffffff << DMI_AUTHDATA_DATA_OFFSET)
+#define DMI_SERCS 0x34
+/*
+* Number of supported serial ports.
+ */
+#define DMI_SERCS_SERIALCOUNT_OFFSET 28
+#define DMI_SERCS_SERIALCOUNT_LENGTH 4
+#define DMI_SERCS_SERIALCOUNT (0xf << DMI_SERCS_SERIALCOUNT_OFFSET)
+/*
+* Select which serial port is accessed by \Rserrx and \Rsertx.
+ */
+#define DMI_SERCS_SERIAL_OFFSET 24
+#define DMI_SERCS_SERIAL_LENGTH 3
+#define DMI_SERCS_SERIAL (0x7 << DMI_SERCS_SERIAL_OFFSET)
+#define DMI_SERCS_ERROR7_OFFSET 23
+#define DMI_SERCS_ERROR7_LENGTH 1
+#define DMI_SERCS_ERROR7 (0x1 << DMI_SERCS_ERROR7_OFFSET)
+#define DMI_SERCS_VALID7_OFFSET 22
+#define DMI_SERCS_VALID7_LENGTH 1
+#define DMI_SERCS_VALID7 (0x1 << DMI_SERCS_VALID7_OFFSET)
+#define DMI_SERCS_FULL7_OFFSET 21
+#define DMI_SERCS_FULL7_LENGTH 1
+#define DMI_SERCS_FULL7 (0x1 << DMI_SERCS_FULL7_OFFSET)
+#define DMI_SERCS_ERROR6_OFFSET 20
+#define DMI_SERCS_ERROR6_LENGTH 1
+#define DMI_SERCS_ERROR6 (0x1 << DMI_SERCS_ERROR6_OFFSET)
+#define DMI_SERCS_VALID6_OFFSET 19
+#define DMI_SERCS_VALID6_LENGTH 1
+#define DMI_SERCS_VALID6 (0x1 << DMI_SERCS_VALID6_OFFSET)
+#define DMI_SERCS_FULL6_OFFSET 18
+#define DMI_SERCS_FULL6_LENGTH 1
+#define DMI_SERCS_FULL6 (0x1 << DMI_SERCS_FULL6_OFFSET)
+#define DMI_SERCS_ERROR5_OFFSET 17
+#define DMI_SERCS_ERROR5_LENGTH 1
+#define DMI_SERCS_ERROR5 (0x1 << DMI_SERCS_ERROR5_OFFSET)
+#define DMI_SERCS_VALID5_OFFSET 16
+#define DMI_SERCS_VALID5_LENGTH 1
+#define DMI_SERCS_VALID5 (0x1 << DMI_SERCS_VALID5_OFFSET)
+#define DMI_SERCS_FULL5_OFFSET 15
+#define DMI_SERCS_FULL5_LENGTH 1
+#define DMI_SERCS_FULL5 (0x1 << DMI_SERCS_FULL5_OFFSET)
+#define DMI_SERCS_ERROR4_OFFSET 14
+#define DMI_SERCS_ERROR4_LENGTH 1
+#define DMI_SERCS_ERROR4 (0x1 << DMI_SERCS_ERROR4_OFFSET)
+#define DMI_SERCS_VALID4_OFFSET 13
+#define DMI_SERCS_VALID4_LENGTH 1
+#define DMI_SERCS_VALID4 (0x1 << DMI_SERCS_VALID4_OFFSET)
+#define DMI_SERCS_FULL4_OFFSET 12
+#define DMI_SERCS_FULL4_LENGTH 1
+#define DMI_SERCS_FULL4 (0x1 << DMI_SERCS_FULL4_OFFSET)
+#define DMI_SERCS_ERROR3_OFFSET 11
+#define DMI_SERCS_ERROR3_LENGTH 1
+#define DMI_SERCS_ERROR3 (0x1 << DMI_SERCS_ERROR3_OFFSET)
+#define DMI_SERCS_VALID3_OFFSET 10
+#define DMI_SERCS_VALID3_LENGTH 1
+#define DMI_SERCS_VALID3 (0x1 << DMI_SERCS_VALID3_OFFSET)
+#define DMI_SERCS_FULL3_OFFSET 9
+#define DMI_SERCS_FULL3_LENGTH 1
+#define DMI_SERCS_FULL3 (0x1 << DMI_SERCS_FULL3_OFFSET)
+#define DMI_SERCS_ERROR2_OFFSET 8
+#define DMI_SERCS_ERROR2_LENGTH 1
+#define DMI_SERCS_ERROR2 (0x1 << DMI_SERCS_ERROR2_OFFSET)
+#define DMI_SERCS_VALID2_OFFSET 7
+#define DMI_SERCS_VALID2_LENGTH 1
+#define DMI_SERCS_VALID2 (0x1 << DMI_SERCS_VALID2_OFFSET)
+#define DMI_SERCS_FULL2_OFFSET 6
+#define DMI_SERCS_FULL2_LENGTH 1
+#define DMI_SERCS_FULL2 (0x1 << DMI_SERCS_FULL2_OFFSET)
+#define DMI_SERCS_ERROR1_OFFSET 5
+#define DMI_SERCS_ERROR1_LENGTH 1
+#define DMI_SERCS_ERROR1 (0x1 << DMI_SERCS_ERROR1_OFFSET)
+#define DMI_SERCS_VALID1_OFFSET 4
+#define DMI_SERCS_VALID1_LENGTH 1
+#define DMI_SERCS_VALID1 (0x1 << DMI_SERCS_VALID1_OFFSET)
+#define DMI_SERCS_FULL1_OFFSET 3
+#define DMI_SERCS_FULL1_LENGTH 1
+#define DMI_SERCS_FULL1 (0x1 << DMI_SERCS_FULL1_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 has
+* over or underflowed. This bit will remain set until it is reset by
+* writing 1 to this bit.
+ */
+#define DMI_SERCS_ERROR0_OFFSET 2
+#define DMI_SERCS_ERROR0_LENGTH 1
+#define DMI_SERCS_ERROR0 (0x1 << DMI_SERCS_ERROR0_OFFSET)
+/*
+* 1 when the core-to-debugger queue for serial port 0 is not empty.
+ */
+#define DMI_SERCS_VALID0_OFFSET 1
+#define DMI_SERCS_VALID0_LENGTH 1
+#define DMI_SERCS_VALID0 (0x1 << DMI_SERCS_VALID0_OFFSET)
+/*
+* 1 when the debugger-to-core queue for serial port 0 is full.
+ */
+#define DMI_SERCS_FULL0_OFFSET 0
+#define DMI_SERCS_FULL0_LENGTH 1
+#define DMI_SERCS_FULL0 (0x1 << DMI_SERCS_FULL0_OFFSET)
+#define DMI_SERTX 0x35
+#define DMI_SERTX_DATA_OFFSET 0
+#define DMI_SERTX_DATA_LENGTH 32
+#define DMI_SERTX_DATA (0xffffffff << DMI_SERTX_DATA_OFFSET)
+#define DMI_SERRX 0x36
+#define DMI_SERRX_DATA_OFFSET 0
+#define DMI_SERRX_DATA_LENGTH 32
+#define DMI_SERRX_DATA (0xffffffff << DMI_SERRX_DATA_OFFSET)
+#define DMI_SBCS 0x38
+/*
+* When a 1 is written here, triggers a read at the address in {\tt
+* sbaddress} using the access size set by \Fsbaccess.
+ */
+#define DMI_SBCS_SBSINGLEREAD_OFFSET 20
+#define DMI_SBCS_SBSINGLEREAD_LENGTH 1
+#define DMI_SBCS_SBSINGLEREAD (0x1 << DMI_SBCS_SBSINGLEREAD_OFFSET)
+/*
+* Select the access size to use for system bus accesses triggered by
+* writes to the {\tt sbaddress} registers or \Rsbdatazero.
+*
+* 0: 8-bit
+*
+* 1: 16-bit
+*
+* 2: 32-bit
+*
+* 3: 64-bit
+*
+* 4: 128-bit
+*
+* If an unsupported system bus access size is written here,
+* the DM may not perform the access, or may perform the access
+* with any access size.
+ */
+#define DMI_SBCS_SBACCESS_OFFSET 17
+#define DMI_SBCS_SBACCESS_LENGTH 3
+#define DMI_SBCS_SBACCESS (0x7 << DMI_SBCS_SBACCESS_OFFSET)
+/*
+* When 1, the internal address value (used by the system bus master)
+* is incremented by the access size (in bytes) selected in \Fsbaccess
+* after every system bus access.
+ */
+#define DMI_SBCS_SBAUTOINCREMENT_OFFSET 16
+#define DMI_SBCS_SBAUTOINCREMENT_LENGTH 1
+#define DMI_SBCS_SBAUTOINCREMENT (0x1 << DMI_SBCS_SBAUTOINCREMENT_OFFSET)
+/*
+* When 1, every read from \Rsbdatazero automatically triggers a system
+* bus read at the new address.
+ */
+#define DMI_SBCS_SBAUTOREAD_OFFSET 15
+#define DMI_SBCS_SBAUTOREAD_LENGTH 1
+#define DMI_SBCS_SBAUTOREAD (0x1 << DMI_SBCS_SBAUTOREAD_OFFSET)
+/*
+* When the debug module's system bus
+* master causes a bus error, this field gets set. The bits in this
+* field remain set until they are cleared by writing 1 to them.
+* While this field is non-zero, no more system bus accesses can be
+* initiated by the debug module.
+*
+* 0: There was no bus error.
+*
+* 1: There was a timeout.
+*
+* 2: A bad address was accessed.
+*
+* 3: There was some other error (eg. alignment).
+*
+* 4: The system bus master was busy when a one of the
+* {\tt sbaddress} or {\tt sbdata} registers was written,
+* or the {\tt sbdata} register was read when it had
+* stale data.
+ */
+#define DMI_SBCS_SBERROR_OFFSET 12
+#define DMI_SBCS_SBERROR_LENGTH 3
+#define DMI_SBCS_SBERROR (0x7 << DMI_SBCS_SBERROR_OFFSET)
+/*
+* Width of system bus addresses in bits. (0 indicates there is no bus
+* access support.)
+ */
+#define DMI_SBCS_SBASIZE_OFFSET 5
+#define DMI_SBCS_SBASIZE_LENGTH 7
+#define DMI_SBCS_SBASIZE (0x7f << DMI_SBCS_SBASIZE_OFFSET)
+/*
+* 1 when 128-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS128_OFFSET 4
+#define DMI_SBCS_SBACCESS128_LENGTH 1
+#define DMI_SBCS_SBACCESS128 (0x1 << DMI_SBCS_SBACCESS128_OFFSET)
+/*
+* 1 when 64-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS64_OFFSET 3
+#define DMI_SBCS_SBACCESS64_LENGTH 1
+#define DMI_SBCS_SBACCESS64 (0x1 << DMI_SBCS_SBACCESS64_OFFSET)
+/*
+* 1 when 32-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS32_OFFSET 2
+#define DMI_SBCS_SBACCESS32_LENGTH 1
+#define DMI_SBCS_SBACCESS32 (0x1 << DMI_SBCS_SBACCESS32_OFFSET)
+/*
+* 1 when 16-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS16_OFFSET 1
+#define DMI_SBCS_SBACCESS16_LENGTH 1
+#define DMI_SBCS_SBACCESS16 (0x1 << DMI_SBCS_SBACCESS16_OFFSET)
+/*
+* 1 when 8-bit system bus accesses are supported.
+ */
+#define DMI_SBCS_SBACCESS8_OFFSET 0
+#define DMI_SBCS_SBACCESS8_LENGTH 1
+#define DMI_SBCS_SBACCESS8 (0x1 << DMI_SBCS_SBACCESS8_OFFSET)
+#define DMI_SBADDRESS0 0x39
+/*
+* Accesses bits 31:0 of the internal address.
+ */
+#define DMI_SBADDRESS0_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS0_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS0_ADDRESS (0xffffffff << DMI_SBADDRESS0_ADDRESS_OFFSET)
+#define DMI_SBADDRESS1 0x3a
+/*
+* Accesses bits 63:32 of the internal address (if the system address
+* bus is that wide).
+ */
+#define DMI_SBADDRESS1_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS1_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS1_ADDRESS (0xffffffff << DMI_SBADDRESS1_ADDRESS_OFFSET)
+#define DMI_SBADDRESS2 0x3b
+/*
+* Accesses bits 95:64 of the internal address (if the system address
+* bus is that wide).
+ */
+#define DMI_SBADDRESS2_ADDRESS_OFFSET 0
+#define DMI_SBADDRESS2_ADDRESS_LENGTH 32
+#define DMI_SBADDRESS2_ADDRESS (0xffffffff << DMI_SBADDRESS2_ADDRESS_OFFSET)
+#define DMI_SBDATA0 0x3c
+/*
+* Accesses bits 31:0 of the internal data.
+ */
+#define DMI_SBDATA0_DATA_OFFSET 0
+#define DMI_SBDATA0_DATA_LENGTH 32
+#define DMI_SBDATA0_DATA (0xffffffff << DMI_SBDATA0_DATA_OFFSET)
+#define DMI_SBDATA1 0x3d
+/*
+* Accesses bits 63:32 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA1_DATA_OFFSET 0
+#define DMI_SBDATA1_DATA_LENGTH 32
+#define DMI_SBDATA1_DATA (0xffffffff << DMI_SBDATA1_DATA_OFFSET)
+#define DMI_SBDATA2 0x3e
+/*
+* Accesses bits 95:64 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA2_DATA_OFFSET 0
+#define DMI_SBDATA2_DATA_LENGTH 32
+#define DMI_SBDATA2_DATA (0xffffffff << DMI_SBDATA2_DATA_OFFSET)
+#define DMI_SBDATA3 0x3f
+/*
+* Accesses bits 127:96 of the internal data (if the system bus is
+* that wide).
+ */
+#define DMI_SBDATA3_DATA_OFFSET 0
+#define DMI_SBDATA3_DATA_LENGTH 32
+#define DMI_SBDATA3_DATA (0xffffffff << DMI_SBDATA3_DATA_OFFSET)
+#define TRACE 0x728
+/*
+* 1 if the trace buffer has wrapped since the last time \Fdiscard was
+* written. 0 otherwise.
+ */
+#define TRACE_WRAPPED_OFFSET 24
+#define TRACE_WRAPPED_LENGTH 1
+#define TRACE_WRAPPED (0x1 << TRACE_WRAPPED_OFFSET)
+/*
+* Emit Timestamp trace sequences.
+ */
+#define TRACE_EMITTIMESTAMP_OFFSET 23
+#define TRACE_EMITTIMESTAMP_LENGTH 1
+#define TRACE_EMITTIMESTAMP (0x1 << TRACE_EMITTIMESTAMP_OFFSET)
+/*
+* Emit Store Data trace sequences.
+ */
+#define TRACE_EMITSTOREDATA_OFFSET 22
+#define TRACE_EMITSTOREDATA_LENGTH 1
+#define TRACE_EMITSTOREDATA (0x1 << TRACE_EMITSTOREDATA_OFFSET)
+/*
+* Emit Load Data trace sequences.
+ */
+#define TRACE_EMITLOADDATA_OFFSET 21
+#define TRACE_EMITLOADDATA_LENGTH 1
+#define TRACE_EMITLOADDATA (0x1 << TRACE_EMITLOADDATA_OFFSET)
+/*
+* Emit Store Address trace sequences.
+ */
+#define TRACE_EMITSTOREADDR_OFFSET 20
+#define TRACE_EMITSTOREADDR_LENGTH 1
+#define TRACE_EMITSTOREADDR (0x1 << TRACE_EMITSTOREADDR_OFFSET)
+/*
+* Emit Load Address trace sequences.
+ */
+#define TRACE_EMITLOADADDR_OFFSET 19
+#define TRACE_EMITLOADADDR_LENGTH 1
+#define TRACE_EMITLOADADDR (0x1 << TRACE_EMITLOADADDR_OFFSET)
+/*
+* Emit Privilege Level trace sequences.
+ */
+#define TRACE_EMITPRIV_OFFSET 18
+#define TRACE_EMITPRIV_LENGTH 1
+#define TRACE_EMITPRIV (0x1 << TRACE_EMITPRIV_OFFSET)
+/*
+* Emit Branch Taken and Branch Not Taken trace sequences.
+ */
+#define TRACE_EMITBRANCH_OFFSET 17
+#define TRACE_EMITBRANCH_LENGTH 1
+#define TRACE_EMITBRANCH (0x1 << TRACE_EMITBRANCH_OFFSET)
+/*
+* Emit PC trace sequences.
+ */
+#define TRACE_EMITPC_OFFSET 16
+#define TRACE_EMITPC_LENGTH 1
+#define TRACE_EMITPC (0x1 << TRACE_EMITPC_OFFSET)
+/*
+* Determine what happens when the trace buffer is full. 0 means wrap
+* and overwrite. 1 means turn off trace until \Fdiscard is written as 1.
+* 2 means cause a trace full exception. 3 is reserved for future use.
+ */
+#define TRACE_FULLACTION_OFFSET 8
+#define TRACE_FULLACTION_LENGTH 2
+#define TRACE_FULLACTION (0x3 << TRACE_FULLACTION_OFFSET)
+/*
+* 0: Trace to a dedicated on-core RAM (which is not further defined in
+* this spec).
+*
+* 1: Trace to RAM on the system bus.
+*
+* 2: Send trace data to a dedicated off-chip interface (which is not
+* defined in this spec). This does not affect execution speed.
+*
+* 3: Reserved for future use.
+*
+* Options 0 and 1 slow down execution (eg. because of system bus
+* contention).
+ */
+#define TRACE_DESTINATION_OFFSET 4
+#define TRACE_DESTINATION_LENGTH 2
+#define TRACE_DESTINATION (0x3 << TRACE_DESTINATION_OFFSET)
+/*
+* When 1, the trace logic may stall processor execution to ensure it
+* can emit all the trace sequences required. When 0 individual trace
+* sequences may be dropped.
+ */
+#define TRACE_STALL_OFFSET 2
+#define TRACE_STALL_LENGTH 1
+#define TRACE_STALL (0x1 << TRACE_STALL_OFFSET)
+/*
+* Writing 1 to this bit tells the trace logic that any trace
+* collected is no longer required. When tracing to RAM, it resets the
+* trace write pointer to the start of the memory, as well as
+* \Fwrapped.
+ */
+#define TRACE_DISCARD_OFFSET 1
+#define TRACE_DISCARD_LENGTH 1
+#define TRACE_DISCARD (0x1 << TRACE_DISCARD_OFFSET)
+#define TRACE_SUPPORTED_OFFSET 0
+#define TRACE_SUPPORTED_LENGTH 1
+#define TRACE_SUPPORTED (0x1 << TRACE_SUPPORTED_OFFSET)
+#define TBUFSTART 0x729
+#define TBUFEND 0x72a
+#define TBUFWRITE 0x72b
+#define SHORTNAME 0x123
+/*
+* Description of what this field is used for.
+ */
+#define SHORTNAME_FIELD_OFFSET 0
+#define SHORTNAME_FIELD_LENGTH 8
+#define SHORTNAME_FIELD (0xff << SHORTNAME_FIELD_OFFSET)
+#define AC_ACCESS_REGISTER None
+/*
+* This is 0 to indicate Access Register Command.
+ */
+#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 24
+#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8
+#define AC_ACCESS_REGISTER_CMDTYPE (0xff << AC_ACCESS_REGISTER_CMDTYPE_OFFSET)
+/*
+* 2: Access the lowest 32 bits of the register.
+*
+* 3: Access the lowest 64 bits of the register.
+*
+* 4: Access the lowest 128 bits of the register.
+*
+* If \Fsize specifies a size larger than the register's actual size,
+* then the access must fail. If a register is accessible, then reads of \Fsize
+* less than or equal to the register's actual size must be supported.
+ */
+#define AC_ACCESS_REGISTER_SIZE_OFFSET 20
+#define AC_ACCESS_REGISTER_SIZE_LENGTH 3
+#define AC_ACCESS_REGISTER_SIZE (0x7 << AC_ACCESS_REGISTER_SIZE_OFFSET)
+/*
+* When 1, execute the program in the Program Buffer exactly once
+* after performing the transfer, if any.
+ */
+#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 18
+#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1
+#define AC_ACCESS_REGISTER_POSTEXEC (0x1 << AC_ACCESS_REGISTER_POSTEXEC_OFFSET)
+/*
+* 0: Don't do the operation specified by \Fwrite.
+*
+* 1: Do the operation specified by \Fwrite.
+ */
+#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 17
+#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1
+#define AC_ACCESS_REGISTER_TRANSFER (0x1 << AC_ACCESS_REGISTER_TRANSFER_OFFSET)
+/*
+* When \Ftransfer is set:
+* 0: Copy data from the specified register into {\tt arg0} portion
+* of {\tt data}.
+*
+* 1: Copy data from {\tt arg0} portion of {\tt data} into the
+* specified register.
+ */
+#define AC_ACCESS_REGISTER_WRITE_OFFSET 16
+#define AC_ACCESS_REGISTER_WRITE_LENGTH 1
+#define AC_ACCESS_REGISTER_WRITE (0x1 << AC_ACCESS_REGISTER_WRITE_OFFSET)
+/*
+* Number of the register to access, as described in Table~\ref{tab:regno}.
+ */
+#define AC_ACCESS_REGISTER_REGNO_OFFSET 0
+#define AC_ACCESS_REGISTER_REGNO_LENGTH 16
+#define AC_ACCESS_REGISTER_REGNO (0xffff << AC_ACCESS_REGISTER_REGNO_OFFSET)
+#define AC_QUICK_ACCESS None
+/*
+* This is 1 to indicate Quick Access command.
+ */
+#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 24
+#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8
+#define AC_QUICK_ACCESS_CMDTYPE (0xff << AC_QUICK_ACCESS_CMDTYPE_OFFSET)
diff --git a/riscv/debug_module.cc b/riscv/debug_module.cc
index 8bcc60e..db035e3 100644
--- a/riscv/debug_module.cc
+++ b/riscv/debug_module.cc
@@ -1,49 +1,167 @@
#include <cassert>
#include "debug_module.h"
+#include "debug_defines.h"
+#include "opcodes.h"
#include "mmu.h"
#include "debug_rom/debug_rom.h"
+#include "debug_rom/debug_rom_defines.h"
+
+#if 1
+# define D(x) x
+#else
+# define D(x)
+#endif
+
+///////////////////////// debug_module_t
+
+debug_module_t::debug_module_t(sim_t *sim) : sim(sim)
+{
+ dmcontrol = {0};
+
+ dmstatus = {0};
+ dmstatus.authenticated = 1;
+ dmstatus.versionlo = 2;
+
+ abstractcs = {0};
+ abstractcs.progsize = progsize;
+
+ abstractauto = {0};
+
+ memset(halted, 0, sizeof(halted));
+ memset(debug_rom_flags, 0, sizeof(debug_rom_flags));
+ memset(resumeack, 0, sizeof(resumeack));
+ memset(program_buffer, 0, sizeof(program_buffer));
+ memset(dmdata, 0, sizeof(dmdata));
+
+ write32(debug_rom_whereto, 0,
+ jal(ZERO, debug_abstract_start - DEBUG_ROM_WHERETO));
+
+ memset(debug_abstract, 0, sizeof(debug_abstract));
+
+}
+
+void debug_module_t::reset()
+{
+ for (unsigned i = 0; i < sim->nprocs(); i++) {
+ processor_t *proc = sim->get_core(i);
+ if (proc)
+ proc->halt_request = false;
+ }
+
+ dmcontrol = {0};
+
+ dmstatus = {0};
+ dmstatus.authenticated = 1;
+ dmstatus.versionlo = 2;
+
+ abstractcs = {0};
+ abstractcs.datacount = sizeof(dmdata) / 4;
+ abstractcs.progsize = progsize;
+
+ abstractauto = {0};
+}
+
+void debug_module_t::add_device(bus_t *bus) {
+ bus->add_device(DEBUG_START, this);
+}
bool debug_module_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
addr = DEBUG_START + addr;
- if (addr >= DEBUG_RAM_START && addr + len <= DEBUG_RAM_END) {
- memcpy(bytes, debug_ram + addr - DEBUG_RAM_START, len);
+ if (addr >= DEBUG_ROM_ENTRY &&
+ (addr + len) <= (DEBUG_ROM_ENTRY + debug_rom_raw_len)) {
+ memcpy(bytes, debug_rom_raw + addr - DEBUG_ROM_ENTRY, len);
return true;
}
- if (addr >= DEBUG_ROM_START && addr + len <= DEBUG_ROM_END) {
- memcpy(bytes, debug_rom_raw + addr - DEBUG_ROM_START, len);
+ if (addr >= DEBUG_ROM_WHERETO && (addr + len) <= (DEBUG_ROM_WHERETO + 4)) {
+ memcpy(bytes, debug_rom_whereto + addr - DEBUG_ROM_WHERETO, len);
+ return true;
+ }
+
+ if (addr >= DEBUG_ROM_FLAGS && ((addr + len) <= DEBUG_ROM_FLAGS + 1024)) {
+ memcpy(bytes, debug_rom_flags + addr - DEBUG_ROM_FLAGS, len);
+ return true;
+ }
+
+ if (addr >= debug_abstract_start && ((addr + len) <= (debug_abstract_start + sizeof(debug_abstract)))) {
+ memcpy(bytes, debug_abstract + addr - debug_abstract_start, len);
+ return true;
+ }
+
+ if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) {
+ memcpy(bytes, dmdata + addr - debug_data_start, len);
+ return true;
+ }
+
+ if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + sizeof(program_buffer)))) {
+ memcpy(bytes, program_buffer + addr - debug_progbuf_start, len);
return true;
}
fprintf(stderr, "ERROR: invalid load from debug module: %zd bytes at 0x%016"
PRIx64 "\n", len, addr);
+
return false;
}
bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
+
+ uint8_t id_bytes[4];
+ uint32_t id = 0;
+ if (len == 4) {
+ memcpy(id_bytes, bytes, 4);
+ id = read32(id_bytes, 0);
+ }
+
addr = DEBUG_START + addr;
+
+ if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) {
+ memcpy(dmdata + addr - debug_data_start, bytes, len);
+ return true;
+ }
+
+ if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + sizeof(program_buffer)))) {
+ fprintf(stderr, "Successful write to program buffer %d bytes at %x\n", (int) len, (int) addr);
+ memcpy(program_buffer + addr - debug_progbuf_start, bytes, len);
+
+ return true;
+ }
- if (addr & (len-1)) {
- fprintf(stderr, "ERROR: unaligned store to debug module: %zd bytes at 0x%016"
- PRIx64 "\n", len, addr);
- return false;
+ if (addr == DEBUG_ROM_HALTED) {
+ assert (len == 4);
+ halted[id] = true;
+ if (dmcontrol.hartsel == id) {
+ if (0 == (debug_rom_flags[id] & (1 << DEBUG_ROM_FLAG_GO))){
+ if (dmcontrol.hartsel == id) {
+ abstractcs.busy = false;
+ }
+ }
+ }
+ return true;
}
- if (addr >= DEBUG_RAM_START && addr + len <= DEBUG_RAM_END) {
- memcpy(debug_ram + addr - DEBUG_RAM_START, bytes, len);
+ if (addr == DEBUG_ROM_GOING) {
+ debug_rom_flags[dmcontrol.hartsel] &= ~(1 << DEBUG_ROM_FLAG_GO);
return true;
- } else if (len == 4 && addr == DEBUG_CLEARDEBINT) {
- clear_interrupt(bytes[0] | (bytes[1] << 8) |
- (bytes[2] << 16) | (bytes[3] << 24));
+ }
+
+ if (addr == DEBUG_ROM_RESUMING) {
+ assert (len == 4);
+ halted[id] = false;
+ resumeack[id] = true;
+ debug_rom_flags[id] &= ~(1 << DEBUG_ROM_FLAG_RESUME);
return true;
- } else if (len == 4 && addr == DEBUG_SETHALTNOT) {
- set_halt_notification(bytes[0] | (bytes[1] << 8) |
- (bytes[2] << 16) | (bytes[3] << 24));
+ }
+
+ if (addr == DEBUG_ROM_EXCEPTION) {
+ if (abstractcs.cmderr == CMDERR_NONE) {
+ abstractcs.cmderr = CMDERR_EXCEPTION;
+ }
return true;
}
@@ -52,23 +170,303 @@ bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes)
return false;
}
-void debug_module_t::ram_write32(unsigned int index, uint32_t value)
+void debug_module_t::write32(uint8_t *memory, unsigned int index, uint32_t value)
{
- char* base = debug_ram + index * 4;
+ uint8_t* base = memory + index * 4;
base[0] = value & 0xff;
base[1] = (value >> 8) & 0xff;
base[2] = (value >> 16) & 0xff;
base[3] = (value >> 24) & 0xff;
}
-uint32_t debug_module_t::ram_read32(unsigned int index)
+uint32_t debug_module_t::read32(uint8_t *memory, unsigned int index)
{
- // It'd be better for raw_page (and all memory) to be unsigned chars, but mem
- // in sim_t is just chars, so I'm following that convention.
- unsigned char* base = (unsigned char*) (debug_ram + index * 4);
+ uint8_t* base = memory + index * 4;
uint32_t value = ((uint32_t) base[0]) |
(((uint32_t) base[1]) << 8) |
(((uint32_t) base[2]) << 16) |
(((uint32_t) base[3]) << 24);
return value;
}
+
+processor_t *debug_module_t::current_proc() const
+{
+ processor_t *proc = NULL;
+ try {
+ proc = sim->get_core(dmcontrol.hartsel);
+ } catch (const std::out_of_range&) {
+ }
+ return proc;
+}
+
+bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
+{
+ uint32_t result = 0;
+ D(fprintf(stderr, "dmi_read(0x%x) -> ", address));
+ if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) {
+ unsigned i = address - DMI_DATA0;
+ result = read32(dmdata, i);
+ if (abstractcs.busy) {
+ result = -1;
+ fprintf(stderr, "\ndmi_read(0x%02x (data[%d]) -> -1 because abstractcs.busy==true\n", address, i);
+ }
+
+ if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) {
+ abstractcs.cmderr = CMDERR_BUSY;
+ }
+
+ if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) {
+ perform_abstract_command();
+ }
+ } else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progsize) {
+ unsigned i = address - DMI_PROGBUF0;
+ result = read32(program_buffer, i);
+ if (abstractcs.busy) {
+ result = -1;
+ fprintf(stderr, "\ndmi_read(0x%02x (progbuf[%d]) -> -1 because abstractcs.busy==true\n", address, i);
+ }
+ if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) {
+ perform_abstract_command();
+ }
+
+ } else {
+ switch (address) {
+ case DMI_DMCONTROL:
+ {
+ processor_t *proc = current_proc();
+ if (proc)
+ dmcontrol.haltreq = proc->halt_request;
+
+ result = set_field(result, DMI_DMCONTROL_HALTREQ, dmcontrol.haltreq);
+ result = set_field(result, DMI_DMCONTROL_RESUMEREQ, dmcontrol.resumereq);
+ result = set_field(result, DMI_DMCONTROL_HARTSEL, dmcontrol.hartsel);
+ result = set_field(result, DMI_DMCONTROL_HARTRESET, dmcontrol.hartreset);
+ result = set_field(result, DMI_DMCONTROL_NDMRESET, dmcontrol.ndmreset);
+ result = set_field(result, DMI_DMCONTROL_DMACTIVE, dmcontrol.dmactive);
+ }
+ break;
+ case DMI_DMSTATUS:
+ {
+ processor_t *proc = current_proc();
+
+ dmstatus.allnonexistant = false;
+ dmstatus.allunavail = false;
+ dmstatus.allrunning = false;
+ dmstatus.allhalted = false;
+ dmstatus.allresumeack = false;
+ if (proc) {
+ if (halted[dmcontrol.hartsel]) {
+ dmstatus.allhalted = true;
+ } else {
+ dmstatus.allrunning = true;
+ }
+ } else {
+ dmstatus.allnonexistant = true;
+ }
+ dmstatus.anynonexistant = dmstatus.allnonexistant;
+ dmstatus.anyunavail = dmstatus.allunavail;
+ dmstatus.anyrunning = dmstatus.allrunning;
+ dmstatus.anyhalted = dmstatus.allhalted;
+ if (proc) {
+ if (resumeack[dmcontrol.hartsel]) {
+ dmstatus.allresumeack = true;
+ } else {
+ dmstatus.allresumeack = false;
+ }
+ } else {
+ dmstatus.allresumeack = false;
+ }
+
+ result = set_field(result, DMI_DMSTATUS_ALLNONEXISTENT, dmstatus.allnonexistant);
+ result = set_field(result, DMI_DMSTATUS_ALLUNAVAIL, dmstatus.allunavail);
+ result = set_field(result, DMI_DMSTATUS_ALLRUNNING, dmstatus.allrunning);
+ result = set_field(result, DMI_DMSTATUS_ALLHALTED, dmstatus.allhalted);
+ result = set_field(result, DMI_DMSTATUS_ALLRESUMEACK, dmstatus.allresumeack);
+ result = set_field(result, DMI_DMSTATUS_ANYNONEXISTENT, dmstatus.anynonexistant);
+ result = set_field(result, DMI_DMSTATUS_ANYUNAVAIL, dmstatus.anyunavail);
+ result = set_field(result, DMI_DMSTATUS_ANYRUNNING, dmstatus.anyrunning);
+ result = set_field(result, DMI_DMSTATUS_ANYHALTED, dmstatus.anyhalted);
+ result = set_field(result, DMI_DMSTATUS_ANYRESUMEACK, dmstatus.anyresumeack);
+ result = set_field(result, DMI_DMSTATUS_AUTHENTICATED, dmstatus.authenticated);
+ result = set_field(result, DMI_DMSTATUS_AUTHBUSY, dmstatus.authbusy);
+ result = set_field(result, DMI_DMSTATUS_VERSIONHI, dmstatus.versionhi);
+ result = set_field(result, DMI_DMSTATUS_VERSIONLO, dmstatus.versionlo);
+ }
+ break;
+ case DMI_ABSTRACTCS:
+ result = set_field(result, DMI_ABSTRACTCS_CMDERR, abstractcs.cmderr);
+ result = set_field(result, DMI_ABSTRACTCS_BUSY, abstractcs.busy);
+ result = set_field(result, DMI_ABSTRACTCS_DATACOUNT, abstractcs.datacount);
+ result = set_field(result, DMI_ABSTRACTCS_PROGSIZE, abstractcs.progsize);
+ break;
+ case DMI_ABSTRACTAUTO:
+ result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, abstractauto.autoexecprogbuf);
+ result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECDATA, abstractauto.autoexecdata);
+ break;
+ case DMI_COMMAND:
+ result = 0;
+ break;
+ case DMI_HARTINFO:
+ result = set_field(result, DMI_HARTINFO_NSCRATCH, 1);
+ result = set_field(result, DMI_HARTINFO_DATAACCESS, 1);
+ result = set_field(result, DMI_HARTINFO_DATASIZE, abstractcs.datacount);
+ result = set_field(result, DMI_HARTINFO_DATAADDR, debug_data_start);
+ break;
+ default:
+ result = 0;
+ D(fprintf(stderr, "Unexpected. Returning Error."));
+ return false;
+ }
+ }
+ D(fprintf(stderr, "0x%x\n", result));
+ *value = result;
+ return true;
+}
+
+bool debug_module_t::perform_abstract_command()
+{
+ if (abstractcs.cmderr != CMDERR_NONE)
+ return true;
+ if (abstractcs.busy) {
+ abstractcs.cmderr = CMDERR_BUSY;
+ return true;
+ }
+
+ if ((command >> 24) == 0) {
+ // register access
+ unsigned size = get_field(command, AC_ACCESS_REGISTER_SIZE);
+ bool write = get_field(command, AC_ACCESS_REGISTER_WRITE);
+ unsigned regno = get_field(command, AC_ACCESS_REGISTER_REGNO);
+
+ if (!halted[dmcontrol.hartsel]) {
+ abstractcs.cmderr = CMDERR_HALTRESUME;
+ return true;
+ }
+
+ if (get_field(command, AC_ACCESS_REGISTER_TRANSFER)) {
+
+ if (regno < 0x1000 || regno >= 0x1020) {
+ abstractcs.cmderr = CMDERR_NOTSUP;
+ return true;
+ }
+
+ unsigned regnum = regno - 0x1000;
+
+ switch (size) {
+ case 2:
+ if (write)
+ write32(debug_abstract, 0, lw(regnum, ZERO, debug_data_start));
+ else
+ write32(debug_abstract, 0, sw(regnum, ZERO, debug_data_start));
+ break;
+ case 3:
+ if (write)
+ write32(debug_abstract, 0, ld(regnum, ZERO, debug_data_start));
+ else
+ write32(debug_abstract, 0, sd(regnum, ZERO, debug_data_start));
+ break;
+ /*
+ case 4:
+ if (write)
+ write32(debug_rom_code, 0, lq(regnum, ZERO, debug_data_start));
+ else
+ write32(debug_rom_code, 0, sq(regnum, ZERO, debug_data_start));
+ break;
+ */
+ default:
+ abstractcs.cmderr = CMDERR_NOTSUP;
+ return true;
+ }
+ } else {
+ //NOP
+ write32(debug_abstract, 0, addi(ZERO, ZERO, 0));
+ }
+
+ if (get_field(command, AC_ACCESS_REGISTER_POSTEXEC)) {
+ // Since the next instruction is what we will use, just use nother NOP
+ // to get there.
+ write32(debug_abstract, 1, addi(ZERO, ZERO, 0));
+ } else {
+ write32(debug_abstract, 1, ebreak());
+ }
+
+ debug_rom_flags[dmcontrol.hartsel] |= 1 << DEBUG_ROM_FLAG_GO;
+
+ abstractcs.busy = true;
+ } else {
+ abstractcs.cmderr = CMDERR_NOTSUP;
+ }
+ return true;
+}
+
+bool debug_module_t::dmi_write(unsigned address, uint32_t value)
+{
+ D(fprintf(stderr, "dmi_write(0x%x, 0x%x)\n", address, value));
+ if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) {
+ unsigned i = address - DMI_DATA0;
+ if (!abstractcs.busy)
+ write32(dmdata, address - DMI_DATA0, value);
+
+ if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) {
+ abstractcs.cmderr = CMDERR_BUSY;
+ }
+
+ if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) {
+ perform_abstract_command();
+ }
+ return true;
+
+ } else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progsize) {
+ unsigned i = address - DMI_PROGBUF0;
+
+ if (!abstractcs.busy)
+ write32(program_buffer, i, value);
+
+ if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) {
+ perform_abstract_command();
+ }
+ return true;
+
+ } else {
+ switch (address) {
+ case DMI_DMCONTROL:
+ {
+ dmcontrol.dmactive = get_field(value, DMI_DMCONTROL_DMACTIVE);
+ if (dmcontrol.dmactive) {
+ dmcontrol.haltreq = get_field(value, DMI_DMCONTROL_HALTREQ);
+ dmcontrol.resumereq = get_field(value, DMI_DMCONTROL_RESUMEREQ);
+ dmcontrol.ndmreset = get_field(value, DMI_DMCONTROL_NDMRESET);
+ dmcontrol.hartsel = get_field(value, DMI_DMCONTROL_HARTSEL);
+ } else {
+ reset();
+ }
+ processor_t *proc = current_proc();
+ if (proc) {
+ proc->halt_request = dmcontrol.haltreq;
+ if (dmcontrol.resumereq) {
+ debug_rom_flags[dmcontrol.hartsel] |= (1 << DEBUG_ROM_FLAG_RESUME);
+ resumeack[dmcontrol.hartsel] = false;
+ }
+ if (dmcontrol.ndmreset) {
+ proc->reset();
+ }
+ }
+ }
+ return true;
+
+ case DMI_COMMAND:
+ command = value;
+ return perform_abstract_command();
+
+ case DMI_ABSTRACTCS:
+ abstractcs.cmderr = (cmderr_t) (((uint32_t) (abstractcs.cmderr)) & (~(uint32_t)(get_field(value, DMI_ABSTRACTCS_CMDERR))));
+ return true;
+
+ case DMI_ABSTRACTAUTO:
+ abstractauto.autoexecprogbuf = get_field(value, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
+ abstractauto.autoexecdata = get_field(value, DMI_ABSTRACTAUTO_AUTOEXECDATA);
+ break;
+ }
+ }
+ return false;
+}
diff --git a/riscv/debug_module.h b/riscv/debug_module.h
index 53b32db..f0d2a47 100644
--- a/riscv/debug_module.h
+++ b/riscv/debug_module.h
@@ -6,41 +6,104 @@
#include "devices.h"
+class sim_t;
+
+typedef struct {
+ bool haltreq;
+ bool resumereq;
+ unsigned hartsel;
+ bool hartreset;
+ bool dmactive;
+ bool ndmreset;
+} dmcontrol_t;
+
+typedef struct {
+ bool allnonexistant;
+ bool anynonexistant;
+ bool allunavail;
+ bool anyunavail;
+ bool allrunning;
+ bool anyrunning;
+ bool allhalted;
+ bool anyhalted;
+ bool allresumeack;
+ bool anyresumeack;
+ bool authenticated;
+ bool authbusy;
+ bool cfgstrvalid;
+ unsigned versionhi;
+ unsigned versionlo;
+} dmstatus_t;
+
+typedef enum cmderr {
+ CMDERR_NONE = 0,
+ CMDERR_BUSY = 1,
+ CMDERR_NOTSUP = 2,
+ CMDERR_EXCEPTION = 3,
+ CMDERR_HALTRESUME = 4,
+ CMDERR_OTHER = 7
+} cmderr_t;
+
+typedef struct {
+ bool busy;
+ unsigned datacount;
+ unsigned progsize;
+ cmderr_t cmderr;
+} abstractcs_t;
+
+typedef struct {
+ unsigned autoexecprogbuf;
+ unsigned autoexecdata;
+} abstractauto_t;
+
class debug_module_t : public abstract_device_t
{
public:
+ debug_module_t(sim_t *sim);
+
+ void add_device(bus_t *bus);
+
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
- void ram_write32(unsigned int index, uint32_t value);
- uint32_t ram_read32(unsigned int index);
-
- void set_interrupt(uint32_t hartid) {
- interrupt.insert(hartid);
- }
- void clear_interrupt(uint32_t hartid) {
- interrupt.erase(hartid);
- }
- bool get_interrupt(uint32_t hartid) const {
- return interrupt.find(hartid) != interrupt.end();
- }
-
- void set_halt_notification(uint32_t hartid) {
- halt_notification.insert(hartid);
- }
- void clear_halt_notification(uint32_t hartid) {
- halt_notification.erase(hartid);
- }
- bool get_halt_notification(uint32_t hartid) const {
- return halt_notification.find(hartid) != halt_notification.end();
- }
+ // Debug Module Interface that the debugger (in our case through JTAG DTM)
+ // uses to access the DM.
+ // Return true for success, false for failure.
+ bool dmi_read(unsigned address, uint32_t *value);
+ bool dmi_write(unsigned address, uint32_t value);
private:
- // Track which interrupts from module to debugger are set.
- std::set<uint32_t> interrupt;
- // Track which halt notifications from debugger to module are set.
- std::set<uint32_t> halt_notification;
- char debug_ram[DEBUG_RAM_SIZE];
+ static const unsigned datasize = 2;
+ static const unsigned progsize = 16;
+ static const unsigned debug_data_start = 0x380;
+ static const unsigned debug_progbuf_start = debug_data_start - progsize*4;
+
+ static const unsigned debug_abstract_size = 2;
+ static const unsigned debug_abstract_start = debug_progbuf_start - debug_abstract_size*4;
+
+ sim_t *sim;
+
+ uint8_t debug_rom_whereto[4];
+ uint8_t debug_abstract[debug_abstract_size * 4];
+ uint8_t program_buffer[progsize * 4];
+ uint8_t dmdata[datasize * 4];
+
+ bool halted[1024];
+ bool resumeack[1024];
+ uint8_t debug_rom_flags[1024];
+
+ void write32(uint8_t *rom, unsigned int index, uint32_t value);
+ uint32_t read32(uint8_t *rom, unsigned int index);
+
+ dmcontrol_t dmcontrol;
+ dmstatus_t dmstatus;
+ abstractcs_t abstractcs;
+ abstractauto_t abstractauto;
+ uint32_t command;
+
+ processor_t *current_proc() const;
+ void reset();
+ bool perform_abstract_command();
};
#endif
diff --git a/riscv/decode.h b/riscv/decode.h
index c3487b1..9dcd809 100644
--- a/riscv/decode.h
+++ b/riscv/decode.h
@@ -144,7 +144,7 @@ private:
})
# define WRITE_FREG(reg, value) ({ \
freg_t wdata = freg(value); /* value may have side effects */ \
- STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata}; \
+ STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata.v}; \
DO_WRITE_FREG(reg, wdata); \
})
#endif
@@ -246,17 +246,8 @@ inline freg_t freg(freg_t f) { return f; }
throw trap_illegal_instruction(0); \
(which); })
+// Seems that 0x0 doesn't work.
#define DEBUG_START 0x100
-#define DEBUG_ROM_START 0x800
-#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
-#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
-#define DEBUG_ROM_END (DEBUG_ROM_START + debug_rom_raw_len)
-#define DEBUG_RAM_START 0x400
-#define DEBUG_RAM_SIZE 64
-#define DEBUG_RAM_END (DEBUG_RAM_START + DEBUG_RAM_SIZE)
-#define DEBUG_END 0xfff
-#define DEBUG_CLEARDEBINT 0x100
-#define DEBUG_SETHALTNOT 0x10c
-#define DEBUG_SIZE (DEBUG_END - DEBUG_START + 1)
+#define DEBUG_END (0x1000 - 1)
#endif
diff --git a/riscv/execute.cc b/riscv/execute.cc
index 1b53ccf..303effe 100644
--- a/riscv/execute.cc
+++ b/riscv/execute.cc
@@ -63,16 +63,12 @@ bool processor_t::slow_path()
void processor_t::step(size_t n)
{
if (state.dcsr.cause == DCSR_CAUSE_NONE) {
- // TODO: get_interrupt() isn't super fast. Does that matter?
- if (sim->debug_module.get_interrupt(id)) {
+ if (halt_request) {
enter_debug_mode(DCSR_CAUSE_DEBUGINT);
- } else if (state.dcsr.halt) {
+ } // !!!The halt bit in DCSR is deprecated.
+ else if (state.dcsr.halt) {
enter_debug_mode(DCSR_CAUSE_HALT);
}
- } else {
- // In Debug Mode, just do 11 steps at a time. Otherwise we're going to be
- // spinning the rest of the time anyway.
- n = std::min(n, (size_t) 11);
}
while (n > 0) {
@@ -120,6 +116,15 @@ void processor_t::step(size_t n)
// enter_debug_mode changed state.pc, so we can't just continue.
break;
}
+
+ if (unlikely(state.pc >= DEBUG_START &&
+ state.pc < DEBUG_END)) {
+ // We're waiting for the debugger to tell us something.
+ return;
+ }
+
+
+
}
}
else while (instret < n)
diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc
deleted file mode 100644
index 01defda..0000000
--- a/riscv/gdbserver.cc
+++ /dev/null
@@ -1,2163 +0,0 @@
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <cassert>
-#include <cinttypes>
-#include <cstdio>
-#include <vector>
-
-#include "disasm.h"
-#include "sim.h"
-#include "gdbserver.h"
-#include "mmu.h"
-#include "encoding.h"
-
-//////////////////////////////////////// Utility Functions
-
-#undef DEBUG
-#ifdef DEBUG
-# define D(x) x
-#else
-# define D(x)
-#endif // DEBUG
-
-void die(const char* msg)
-{
- fprintf(stderr, "gdbserver code died: %s\n", msg);
- abort();
-}
-
-// gdb's register list is defined in riscv_gdb_reg_names gdb/riscv-tdep.c in
-// its source tree. We must interpret the numbers the same here.
-enum {
- REG_XPR0 = 0,
- REG_XPR31 = 31,
- REG_PC = 32,
- REG_FPR0 = 33,
- REG_FPR31 = 64,
- REG_CSR0 = 65,
- REG_MSTATUS = CSR_MSTATUS + REG_CSR0,
- REG_CSR4095 = 4160,
- REG_PRIV = 4161
-};
-
-//////////////////////////////////////// Functions to generate RISC-V opcodes.
-
-// TODO: Does this already exist somewhere?
-
-#define ZERO 0
-// Using regnames.cc as source. The RVG Calling Convention of the 2.0 RISC-V
-// spec says it should be 2 and 3.
-#define S0 8
-#define S1 9
-static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) {
- return (value >> lo) & ((1 << (hi+1-lo)) - 1);
-}
-
-static uint32_t bit(uint32_t value, unsigned int b) {
- return (value >> b) & 1;
-}
-
-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;
-}
-
-static uint32_t csrsi(unsigned int csr, uint16_t imm) {
- return (csr << 20) |
- (bits(imm, 4, 0) << 15) |
- MATCH_CSRRSI;
-}
-
-static uint32_t csrci(unsigned int csr, uint16_t imm) {
- return (csr << 20) |
- (bits(imm, 4, 0) << 15) |
- MATCH_CSRRCI;
-}
-
-static uint32_t csrr(unsigned int rd, unsigned int csr) {
- return (csr << 20) | (rd << 7) | MATCH_CSRRS;
-}
-
-static uint32_t csrw(unsigned int source, unsigned int csr) {
- return (csr << 20) | (source << 15) | MATCH_CSRRW;
-}
-
-static uint32_t fence_i()
-{
- return MATCH_FENCE_I;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-static uint32_t sd(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_SD;
-}
-
-static uint32_t sq(unsigned int src, unsigned int base, uint16_t offset)
-{
-#if 0
- return (bits(offset, 11, 5) << 25) |
- (bits(src, 4, 0) << 20) |
- (base << 15) |
- (bits(offset, 4, 0) << 7) |
- MATCH_SQ;
-#else
- abort();
-#endif
-}
-
-static uint32_t lq(unsigned int rd, unsigned int base, uint16_t offset)
-{
-#if 0
- return (bits(offset, 11, 0) << 20) |
- (base << 15) |
- (bits(rd, 4, 0) << 7) |
- MATCH_LQ;
-#else
- abort();
-#endif
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm)
-{
- return (bits(imm, 11, 0) << 20) |
- (src << 15) |
- (dest << 7) |
- MATCH_ORI;
-}
-
-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;
-}
-
-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;
-}
-
-
-static uint32_t nop()
-{
- return addi(0, 0, 0);
-}
-
-template <typename T>
-unsigned int circular_buffer_t<T>::size() const
-{
- if (end >= start)
- return end - start;
- else
- return end + capacity - start;
-}
-
-template <typename T>
-void circular_buffer_t<T>::consume(unsigned int bytes)
-{
- start = (start + bytes) % capacity;
-}
-
-template <typename T>
-unsigned int circular_buffer_t<T>::contiguous_empty_size() const
-{
- if (end >= start)
- if (start == 0)
- return capacity - end - 1;
- else
- return capacity - end;
- else
- return start - end - 1;
-}
-
-template <typename T>
-unsigned int circular_buffer_t<T>::contiguous_data_size() const
-{
- if (end >= start)
- return end - start;
- else
- return capacity - start;
-}
-
-template <typename T>
-void circular_buffer_t<T>::data_added(unsigned int bytes)
-{
- end += bytes;
- assert(end <= capacity);
- if (end == capacity)
- end = 0;
-}
-
-template <typename T>
-void circular_buffer_t<T>::reset()
-{
- start = 0;
- end = 0;
-}
-
-template <typename T>
-void circular_buffer_t<T>::append(const T *src, unsigned int count)
-{
- unsigned int copy = std::min(count, contiguous_empty_size());
- memcpy(contiguous_empty(), src, copy * sizeof(T));
- data_added(copy);
- count -= copy;
- if (count > 0) {
- assert(count < contiguous_empty_size());
- memcpy(contiguous_empty(), src+copy, count * sizeof(T));
- data_added(count);
- }
-}
-
-////////////////////////////// Debug Operations
-
-class halt_op_t : public operation_t
-{
- public:
- halt_op_t(gdbserver_t& gdbserver, bool send_status=false) :
- operation_t(gdbserver), send_status(send_status),
- state(ST_ENTER) {};
-
- void write_dpc_program() {
- gs.dr_write32(0, csrsi(CSR_DCSR, DCSR_HALT));
- gs.dr_write32(1, csrr(S0, CSR_DPC));
- gs.dr_write_store(2, S0, SLOT_DATA0);
- gs.dr_write_jump(3);
- gs.set_interrupt(0);
- }
-
- bool perform_step(unsigned int step) {
- switch (state) {
- gs.tselect_valid = false;
- case ST_ENTER:
- if (gs.xlen == 0) {
- gs.dr_write32(0, xori(S1, ZERO, -1));
- gs.dr_write32(1, srli(S1, S1, 31));
- // 0x00000001 0x00000001:ffffffff 0x00000001:ffffffff:ffffffff:ffffffff
- gs.dr_write32(2, sw(S1, ZERO, DEBUG_RAM_START));
- gs.dr_write32(3, srli(S1, S1, 31));
- // 0x00000000 0x00000000:00000003 0x00000000:00000003:ffffffff:ffffffff
- gs.dr_write32(4, sw(S1, ZERO, DEBUG_RAM_START + 4));
- gs.dr_write_jump(5);
- gs.set_interrupt(0);
- state = ST_XLEN;
-
- } else {
- write_dpc_program();
- state = ST_DPC;
- }
- return false;
-
- case ST_XLEN:
- {
- uint32_t word0 = gs.dr_read32(0);
- uint32_t word1 = gs.dr_read32(1);
-
- if (word0 == 1 && word1 == 0) {
- gs.xlen = 32;
- } else if (word0 == 0xffffffff && word1 == 3) {
- gs.xlen = 64;
- } else if (word0 == 0xffffffff && word1 == 0xffffffff) {
- gs.xlen = 128;
- }
-
- write_dpc_program();
- state = ST_DPC;
- return false;
- }
-
- case ST_DPC:
- gs.dpc = gs.dr_read(SLOT_DATA0);
- gs.dr_write32(0, csrr(S0, CSR_MSTATUS));
- gs.dr_write_store(1, S0, SLOT_DATA0);
- gs.dr_write_jump(2);
- gs.set_interrupt(0);
- state = ST_MSTATUS;
- return false;
-
- case ST_MSTATUS:
- gs.mstatus = gs.dr_read(SLOT_DATA0);
- gs.mstatus_dirty = false;
- gs.dr_write32(0, csrr(S0, CSR_DCSR));
- gs.dr_write32(1, sw(S0, 0, (uint16_t) DEBUG_RAM_START + 16));
- gs.dr_write_jump(2);
- gs.set_interrupt(0);
- state = ST_DCSR;
- return false;
-
- case ST_DCSR:
- gs.dcsr = gs.dr_read32(4);
-
- gs.sptbr_valid = false;
- gs.pte_cache.clear();
-
- if (send_status) {
- switch (get_field(gs.dcsr, DCSR_CAUSE)) {
- case DCSR_CAUSE_NONE:
- fprintf(stderr, "Internal error. Processor halted without reason.\n");
- abort();
-
- case DCSR_CAUSE_DEBUGINT:
- gs.send_packet("S02"); // Pretend program received SIGINT.
- break;
-
- case DCSR_CAUSE_HWBP:
- case DCSR_CAUSE_STEP:
- case DCSR_CAUSE_HALT:
- // There's no gdb code for this.
- gs.send_packet("T05");
- break;
- case DCSR_CAUSE_SWBP:
- gs.send_packet("T05swbreak:;");
- break;
- }
- }
-
- return true;
-
- default:
- assert(0);
- }
- }
-
- private:
- bool send_status;
- enum {
- ST_ENTER,
- ST_XLEN,
- ST_DPC,
- ST_MSTATUS,
- ST_DCSR
- } state;
-};
-
-class continue_op_t : public operation_t
-{
- public:
- continue_op_t(gdbserver_t& gdbserver, bool single_step) :
- operation_t(gdbserver), single_step(single_step) {};
-
- bool perform_step(unsigned int step) {
- D(fprintf(stderr, "continue step %d\n", step));
- switch (step) {
- case 0:
- gs.dr_write_load(0, S0, SLOT_DATA0);
- gs.dr_write32(1, csrw(S0, CSR_DPC));
- // TODO: Isn't there a fence.i in Debug ROM already?
- if (gs.fence_i_required) {
- gs.dr_write32(2, fence_i());
- gs.dr_write_jump(3);
- gs.fence_i_required = false;
- } else {
- gs.dr_write_jump(2);
- }
- gs.dr_write(SLOT_DATA0, gs.dpc);
- gs.set_interrupt(0);
- return false;
-
- case 1:
- gs.dr_write_load(0, S0, SLOT_DATA0);
- gs.dr_write32(1, csrw(S0, CSR_MSTATUS));
- gs.dr_write_jump(2);
- gs.dr_write(SLOT_DATA0, gs.mstatus);
- gs.set_interrupt(0);
- return false;
-
- case 2:
- gs.dr_write32(0, lw(S0, 0, (uint16_t) DEBUG_RAM_START+16));
- gs.dr_write32(1, csrw(S0, CSR_DCSR));
- gs.dr_write_jump(2);
-
- reg_t dcsr = set_field(gs.dcsr, DCSR_HALT, 0);
- dcsr = set_field(dcsr, DCSR_STEP, single_step);
- // Software breakpoints should go here.
- dcsr = set_field(dcsr, DCSR_EBREAKM, 1);
- dcsr = set_field(dcsr, DCSR_EBREAKH, 1);
- dcsr = set_field(dcsr, DCSR_EBREAKS, 1);
- dcsr = set_field(dcsr, DCSR_EBREAKU, 1);
- gs.dr_write32(4, dcsr);
-
- gs.set_interrupt(0);
- return true;
- }
- return false;
- }
-
- private:
- bool single_step;
-};
-
-class general_registers_read_op_t : public operation_t
-{
- // Register order that gdb expects is:
- // "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
- // "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
- // "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
- // "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31",
-
- // Each byte of register data is described by two hex digits. The bytes with
- // the register are transmitted in target byte order. The size of each
- // register and their position within the ‘g’ packet are determined by the
- // gdb internal gdbarch functions DEPRECATED_REGISTER_RAW_SIZE and
- // gdbarch_register_name.
-
- public:
- general_registers_read_op_t(gdbserver_t& gdbserver) :
- operation_t(gdbserver) {};
-
- bool perform_step(unsigned int step)
- {
- D(fprintf(stderr, "register_read step %d\n", step));
- if (step == 0) {
- gs.start_packet();
-
- // x0 is always zero.
- if (gs.xlen == 32) {
- gs.send((uint32_t) 0);
- } else {
- gs.send((uint64_t) 0);
- }
-
- gs.dr_write_store(0, 1, SLOT_DATA0);
- gs.dr_write_store(1, 2, SLOT_DATA1);
- gs.dr_write_jump(2);
- gs.set_interrupt(0);
- return false;
- }
-
- if (gs.xlen == 32) {
- gs.send((uint32_t) gs.dr_read(SLOT_DATA0));
- } else {
- gs.send((uint64_t) gs.dr_read(SLOT_DATA0));
- }
- if (step >= 16) {
- gs.end_packet();
- return true;
- }
-
- if (gs.xlen == 32) {
- gs.send((uint32_t) gs.dr_read(SLOT_DATA1));
- } else {
- gs.send((uint64_t) gs.dr_read(SLOT_DATA1));
- }
-
- unsigned int current_reg = 2 * step + 1;
- unsigned int i = 0;
- if (current_reg == S1) {
- gs.dr_write_load(i++, S1, SLOT_DATA_LAST);
- }
- gs.dr_write_store(i++, current_reg, SLOT_DATA0);
- if (current_reg + 1 == S0) {
- gs.dr_write32(i++, csrr(S0, CSR_DSCRATCH));
- }
- if (step < 15) {
- gs.dr_write_store(i++, current_reg+1, SLOT_DATA1);
- }
- gs.dr_write_jump(i);
- gs.set_interrupt(0);
-
- return false;
- }
-};
-
-class register_read_op_t : public operation_t
-{
- public:
- register_read_op_t(gdbserver_t& gdbserver, unsigned int reg) :
- operation_t(gdbserver), reg(reg) {};
-
- bool perform_step(unsigned int step)
- {
- switch (step) {
- case 0:
- if (reg >= REG_XPR0 && reg <= REG_XPR31) {
- unsigned int i = 0;
- if (reg == S0) {
- gs.dr_write32(i++, csrr(S0, CSR_DSCRATCH));
- }
- if (gs.xlen == 32) {
- gs.dr_write32(i++, sw(reg - REG_XPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- } else {
- gs.dr_write32(i++, sd(reg - REG_XPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- }
- gs.dr_write_jump(i);
- } else if (reg == REG_PC) {
- gs.start_packet();
- if (gs.xlen == 32) {
- gs.send((uint32_t) gs.dpc);
- } else {
- gs.send(gs.dpc);
- }
- gs.end_packet();
- return true;
- } else if (reg >= REG_FPR0 && reg <= REG_FPR31) {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write(SLOT_DATA1, set_field(gs.mstatus, MSTATUS_FS, 1));
- gs.dr_write32(1, csrw(S0, CSR_MSTATUS));
- gs.mstatus_dirty = true;
- if (gs.xlen == 32) {
- gs.dr_write32(2, fsw(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- } else {
- gs.dr_write32(2, fsd(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- }
- gs.dr_write_jump(3);
- } else if (reg == REG_MSTATUS) {
- gs.start_packet();
- if (gs.xlen == 32) {
- gs.send((uint32_t) gs.mstatus);
- } else {
- gs.send(gs.mstatus);
- }
- gs.end_packet();
- return true;
- } else if (reg >= REG_CSR0 && reg <= REG_CSR4095) {
- gs.dr_write32(0, csrr(S0, reg - REG_CSR0));
- gs.dr_write_store(1, S0, SLOT_DATA0);
- gs.dr_write_jump(2);
- // If we hit an exception reading the CSR, we'll end up returning ~0 as
- // the register's value, which is what we want. (Right?)
- gs.dr_write(SLOT_DATA0, ~(uint64_t) 0);
- } else if (reg == REG_PRIV) {
- gs.start_packet();
- gs.send((uint8_t) get_field(gs.dcsr, DCSR_PRV));
- gs.end_packet();
- return true;
- } else {
- gs.send_packet("E02");
- return true;
- }
- gs.set_interrupt(0);
- return false;
-
- case 1:
- {
- unsigned result = gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1);
- if (result) {
- gs.send_packet("E03");
- return true;
- }
- gs.start_packet();
- if (gs.xlen == 32) {
- gs.send(gs.dr_read32(4));
- } else {
- gs.send(gs.dr_read(SLOT_DATA0));
- }
- gs.end_packet();
- return true;
- }
- }
- return false;
- }
-
- private:
- unsigned int reg;
-};
-
-class register_write_op_t : public operation_t
-{
- public:
- register_write_op_t(gdbserver_t& gdbserver, unsigned int reg, reg_t value) :
- operation_t(gdbserver), reg(reg), value(value) {};
-
- bool perform_step(unsigned int step)
- {
- switch (step) {
- case 0:
- gs.dr_write_load(0, S0, SLOT_DATA0);
- gs.dr_write(SLOT_DATA0, value);
- if (reg == S0) {
- gs.dr_write32(1, csrw(S0, CSR_DSCRATCH));
- gs.dr_write_jump(2);
- } else if (reg == S1) {
- gs.dr_write_store(1, S0, SLOT_DATA_LAST);
- gs.dr_write_jump(2);
- } else if (reg >= REG_XPR0 && reg <= REG_XPR31) {
- gs.dr_write32(1, addi(reg, S0, 0));
- gs.dr_write_jump(2);
- } else if (reg == REG_PC) {
- gs.dpc = value;
- return true;
- } else if (reg >= REG_FPR0 && reg <= REG_FPR31) {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write(SLOT_DATA1, set_field(gs.mstatus, MSTATUS_FS, 1));
- gs.dr_write32(1, csrw(S0, CSR_MSTATUS));
- gs.mstatus_dirty = true;
- if (gs.xlen == 32) {
- gs.dr_write32(2, flw(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- } else {
- gs.dr_write32(2, fld(reg - REG_FPR0, 0, (uint16_t) DEBUG_RAM_START + 16));
- }
- gs.dr_write_jump(3);
- } else if (reg == REG_MSTATUS) {
- gs.mstatus = value;
- gs.mstatus_dirty = true;
- return true;
- } else if (reg >= REG_CSR0 && reg <= REG_CSR4095) {
- gs.dr_write32(1, csrw(S0, reg - REG_CSR0));
- gs.dr_write_jump(2);
- if (reg == REG_CSR0 + CSR_SPTBR) {
- gs.sptbr = value;
- gs.sptbr_valid = true;
- }
- } else if (reg == REG_PRIV) {
- gs.dcsr = set_field(gs.dcsr, DCSR_PRV, value);
- return true;
- } else {
- gs.send_packet("E02");
- return true;
- }
- gs.set_interrupt(0);
- return false;
-
- case 1:
- {
- unsigned result = gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1);
- if (result) {
- gs.send_packet("E03");
- return true;
- }
- gs.send_packet("OK");
- return true;
- }
- }
-
- assert(0);
- }
-
- private:
- unsigned int reg;
- reg_t value;
-};
-
-class memory_read_op_t : public operation_t
-{
- public:
- // Read length bytes from vaddr, storing the result into data.
- // If data is NULL, send the result straight to gdb.
- memory_read_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length,
- unsigned char *data=NULL) :
- operation_t(gdbserver), vaddr(vaddr), length(length), data(data), index(0)
- {
- buf = new uint8_t[length];
- };
-
- ~memory_read_op_t()
- {
- delete[] buf;
- }
-
- bool perform_step(unsigned int step)
- {
- if (step == 0) {
- // address goes in S0
- paddr = gs.translate(vaddr);
- access_size = gs.find_access_size(paddr, length);
-
- gs.dr_write_load(0, S0, SLOT_DATA0);
- switch (access_size) {
- case 1:
- gs.dr_write32(1, lb(S1, S0, 0));
- break;
- case 2:
- gs.dr_write32(1, lh(S1, S0, 0));
- break;
- case 4:
- gs.dr_write32(1, lw(S1, S0, 0));
- break;
- case 8:
- gs.dr_write32(1, ld(S1, S0, 0));
- break;
- }
- gs.dr_write_store(2, S1, SLOT_DATA1);
- gs.dr_write_jump(3);
- gs.dr_write(SLOT_DATA0, paddr);
- gs.set_interrupt(0);
-
- return false;
- }
-
- if (gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1)) {
- // Note that OpenOCD doesn't report this error to gdb by default. They
- // think it can mess up stack tracing. So far I haven't seen any
- // problems.
- gs.send_packet("E99");
- return true;
- }
-
- reg_t value = gs.dr_read(SLOT_DATA1);
- for (unsigned int i = 0; i < access_size; i++) {
- if (data) {
- *(data++) = value & 0xff;
- D(fprintf(stderr, "%02x", (unsigned int) (value & 0xff)));
- } else {
- buf[index++] = value & 0xff;
- }
- value >>= 8;
- }
- if (data) {
- D(fprintf(stderr, "\n"));
- }
- length -= access_size;
- paddr += access_size;
-
- if (length == 0) {
- if (!data) {
- gs.start_packet();
- char buffer[3];
- for (unsigned int i = 0; i < index; i++) {
- sprintf(buffer, "%02x", (unsigned int) buf[i]);
- gs.send(buffer);
- }
- gs.end_packet();
- }
- return true;
- } else {
- gs.dr_write(SLOT_DATA0, paddr);
- gs.set_interrupt(0);
- return false;
- }
- }
-
- private:
- reg_t vaddr;
- unsigned int length;
- unsigned char* data;
- reg_t paddr;
- unsigned int access_size;
- unsigned int index;
- uint8_t *buf;
-};
-
-class memory_write_op_t : public operation_t
-{
- public:
- memory_write_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length,
- const unsigned char *data) :
- operation_t(gdbserver), vaddr(vaddr), offset(0), length(length), data(data) {};
-
- ~memory_write_op_t() {
- delete[] data;
- }
-
- bool perform_step(unsigned int step)
- {
- reg_t paddr = gs.translate(vaddr);
-
- unsigned int data_offset;
- switch (gs.xlen) {
- case 32:
- data_offset = slot_offset32[SLOT_DATA1];
- break;
- case 64:
- data_offset = slot_offset64[SLOT_DATA1];
- break;
- case 128:
- data_offset = slot_offset128[SLOT_DATA1];
- break;
- default:
- abort();
- }
-
- if (step == 0) {
- access_size = gs.find_access_size(paddr, length);
-
- D(fprintf(stderr, "write to 0x%" PRIx64 " -> 0x%" PRIx64 " (access=%d): ",
- vaddr, paddr, access_size));
- for (unsigned int i = 0; i < length; i++) {
- D(fprintf(stderr, "%02x", data[i]));
- }
- D(fprintf(stderr, "\n"));
-
- // address goes in S0
- gs.dr_write_load(0, S0, SLOT_DATA0);
- switch (access_size) {
- case 1:
- gs.dr_write32(1, lb(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset));
- gs.dr_write32(2, sb(S1, S0, 0));
- gs.dr_write32(data_offset, data[0]);
- break;
- case 2:
- gs.dr_write32(1, lh(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset));
- gs.dr_write32(2, sh(S1, S0, 0));
- gs.dr_write32(data_offset, data[0] | (data[1] << 8));
- break;
- case 4:
- gs.dr_write32(1, lw(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset));
- gs.dr_write32(2, sw(S1, S0, 0));
- gs.dr_write32(data_offset, data[0] | (data[1] << 8) |
- (data[2] << 16) | (data[3] << 24));
- break;
- case 8:
- gs.dr_write32(1, ld(S1, 0, (uint16_t) DEBUG_RAM_START + 4*data_offset));
- gs.dr_write32(2, sd(S1, S0, 0));
- gs.dr_write32(data_offset, data[0] | (data[1] << 8) |
- (data[2] << 16) | (data[3] << 24));
- gs.dr_write32(data_offset+1, data[4] | (data[5] << 8) |
- (data[6] << 16) | (data[7] << 24));
- break;
- default:
- fprintf(stderr, "gdbserver error: write %d bytes to 0x%016" PRIx64
- " -> 0x%016" PRIx64 "; access_size=%d\n",
- length, vaddr, paddr, access_size);
- gs.send_packet("E12");
- return true;
- }
- gs.dr_write_jump(3);
- gs.dr_write(SLOT_DATA0, paddr);
- gs.set_interrupt(0);
-
- return false;
- }
-
- if (gs.dr_read32(DEBUG_RAM_SIZE / 4 - 1)) {
- gs.send_packet("E98");
- return true;
- }
-
- offset += access_size;
- if (offset >= length) {
- gs.send_packet("OK");
- return true;
- } else {
- const unsigned char *d = data + offset;
- switch (access_size) {
- case 1:
- gs.dr_write32(data_offset, d[0]);
- break;
- case 2:
- gs.dr_write32(data_offset, d[0] | (d[1] << 8));
- break;
- case 4:
- gs.dr_write32(data_offset, d[0] | (d[1] << 8) |
- (d[2] << 16) | (d[3] << 24));
- break;
- case 8:
- gs.dr_write32(data_offset, d[0] | (d[1] << 8) |
- (d[2] << 16) | (d[3] << 24));
- gs.dr_write32(data_offset+1, d[4] | (d[5] << 8) |
- (d[6] << 16) | (d[7] << 24));
- break;
- default:
- gs.send_packet("E13");
- return true;
- }
- gs.dr_write(SLOT_DATA0, paddr + offset);
- gs.set_interrupt(0);
- return false;
- }
- }
-
- private:
- reg_t vaddr;
- unsigned int offset;
- unsigned int length;
- unsigned int access_size;
- const unsigned char *data;
-};
-
-class collect_translation_info_op_t : public operation_t
-{
- public:
- // Read sufficient information from the target into gdbserver structures so
- // that it's possible to translate vaddr, vaddr+length, and all addresses
- // in between to physical addresses.
- collect_translation_info_op_t(gdbserver_t& gdbserver, reg_t vaddr, size_t length) :
- operation_t(gdbserver), state(STATE_START), vaddr(vaddr), length(length) {};
-
- bool perform_step(unsigned int step)
- {
- // Perform any reads from the just-completed action.
- switch (state) {
- case STATE_START:
- break;
- case STATE_READ_SPTBR:
- gs.sptbr = gs.dr_read(SLOT_DATA0);
- gs.sptbr_valid = true;
- vm = decode_vm_info(gs.xlen, gs.privilege_mode(), gs.sptbr);
- if (vm.levels == 0)
- return true;
- break;
- case STATE_READ_PTE:
- if (vm.ptesize == 4) {
- gs.pte_cache[pte_addr] = gs.dr_read32(4);
- } else {
- gs.pte_cache[pte_addr] = ((uint64_t) gs.dr_read32(5) << 32) |
- gs.dr_read32(4);
- }
- D(fprintf(stderr, "pte_cache[0x%" PRIx64 "] = 0x%" PRIx64 "\n", pte_addr,
- gs.pte_cache[pte_addr]));
- break;
- }
-
- // Set up the next action.
- // We only get here for VM_SV32/39/38.
-
- if (!gs.sptbr_valid) {
- state = STATE_READ_SPTBR;
- gs.dr_write32(0, csrr(S0, CSR_SPTBR));
- gs.dr_write_store(1, S0, SLOT_DATA0);
- gs.dr_write_jump(2);
- gs.set_interrupt(0);
- return false;
- }
-
- reg_t base = vm.ptbase;
- for (int i = vm.levels - 1; i >= 0; i--) {
- int ptshift = i * vm.idxbits;
- reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
-
- pte_addr = base + idx * vm.ptesize;
- auto it = gs.pte_cache.find(pte_addr);
- if (it == gs.pte_cache.end()) {
- state = STATE_READ_PTE;
- if (vm.ptesize == 4) {
- gs.dr_write32(0, lw(S0, 0, (uint16_t) DEBUG_RAM_START + 16));
- gs.dr_write32(1, lw(S1, S0, 0));
- gs.dr_write32(2, sw(S1, 0, (uint16_t) DEBUG_RAM_START + 16));
- } else {
- assert(gs.xlen >= 64);
- gs.dr_write32(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START + 16));
- gs.dr_write32(1, ld(S1, S0, 0));
- gs.dr_write32(2, sd(S1, 0, (uint16_t) DEBUG_RAM_START + 16));
- }
- gs.dr_write_jump(3);
- gs.dr_write32(4, pte_addr);
- gs.dr_write32(5, pte_addr >> 32);
- gs.set_interrupt(0);
- return false;
- }
-
- reg_t pte = gs.pte_cache[pte_addr];
- reg_t ppn = pte >> PTE_PPN_SHIFT;
-
- if (PTE_TABLE(pte)) { // next level of page table
- base = ppn << PGSHIFT;
- } else {
- // We've collected all the data required for the translation.
- return true;
- }
- }
- fprintf(stderr,
- "ERROR: gdbserver couldn't find appropriate PTEs to translate 0x%016" PRIx64 "\n",
- vaddr);
- return true;
- }
-
- private:
- enum {
- STATE_START,
- STATE_READ_SPTBR,
- STATE_READ_PTE
- } state;
- reg_t vaddr;
- size_t length;
- vm_info vm;
- reg_t pte_addr;
-};
-
-class hardware_breakpoint_insert_op_t : public operation_t
-{
- public:
- hardware_breakpoint_insert_op_t(gdbserver_t& gdbserver,
- hardware_breakpoint_t bp) :
- operation_t(gdbserver), state(STATE_START), bp(bp) {};
-
- void write_new_index_program()
- {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write32(1, csrw(S0, CSR_TSELECT));
- gs.dr_write32(2, csrr(S0, CSR_TSELECT));
- gs.dr_write_store(3, S0, SLOT_DATA1);
- gs.dr_write_jump(4);
- gs.dr_write(SLOT_DATA1, bp.index);
- }
-
- bool perform_step(unsigned int step)
- {
- switch (state) {
- case STATE_START:
- bp.index = 0;
- write_new_index_program();
- state = STATE_CHECK_INDEX;
- break;
-
- case STATE_CHECK_INDEX:
- if (gs.dr_read(SLOT_DATA1) != bp.index) {
- // We've exhausted breakpoints without finding an appropriate one.
- gs.send_packet("E58");
- return true;
- }
-
- gs.dr_write32(0, csrr(S0, CSR_TDATA1));
- gs.dr_write_store(1, S0, SLOT_DATA0);
- gs.dr_write_jump(2);
- state = STATE_CHECK_MCONTROL;
- break;
-
- case STATE_CHECK_MCONTROL:
- {
- reg_t mcontrol = gs.dr_read(SLOT_DATA0);
- unsigned int type = mcontrol >> (gs.xlen - 4);
- if (type == 0) {
- // We've exhausted breakpoints without finding an appropriate one.
- gs.send_packet("E58");
- return true;
- }
-
- if (type == 2 &&
- !get_field(mcontrol, MCONTROL_EXECUTE) &&
- !get_field(mcontrol, MCONTROL_LOAD) &&
- !get_field(mcontrol, MCONTROL_STORE)) {
- // Found an unused trigger.
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write32(1, csrw(S0, CSR_TDATA1));
- gs.dr_write_jump(2);
- mcontrol = set_field(0, MCONTROL_ACTION, MCONTROL_ACTION_DEBUG_MODE);
- mcontrol = set_field(mcontrol, MCONTROL_DMODE(gs.xlen), 1);
- mcontrol = set_field(mcontrol, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
- mcontrol = set_field(mcontrol, MCONTROL_M, 1);
- mcontrol = set_field(mcontrol, MCONTROL_H, 1);
- mcontrol = set_field(mcontrol, MCONTROL_S, 1);
- mcontrol = set_field(mcontrol, MCONTROL_U, 1);
- mcontrol = set_field(mcontrol, MCONTROL_EXECUTE, bp.execute);
- mcontrol = set_field(mcontrol, MCONTROL_LOAD, bp.load);
- mcontrol = set_field(mcontrol, MCONTROL_STORE, bp.store);
- // For store triggers it's nicer to fire just before the
- // instruction than just after. However, gdb doesn't clear the
- // breakpoints and step before resuming from a store trigger.
- // That means that without extra code, you'll keep hitting the
- // same watchpoint over and over again. That's not useful at all.
- // Instead of fixing this the right way, just set timing=1 for
- // those triggers.
- if (bp.load || bp.store)
- mcontrol = set_field(mcontrol, MCONTROL_TIMING, 1);
-
- gs.dr_write(SLOT_DATA1, mcontrol);
- state = STATE_WRITE_ADDRESS;
- } else {
- bp.index++;
- write_new_index_program();
- state = STATE_CHECK_INDEX;
- }
- }
- break;
-
- case STATE_WRITE_ADDRESS:
- {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write32(1, csrw(S0, CSR_TDATA2));
- gs.dr_write_jump(2);
- gs.dr_write(SLOT_DATA1, bp.vaddr);
- gs.set_interrupt(0);
- gs.send_packet("OK");
-
- gs.hardware_breakpoints.insert(bp);
-
- return true;
- }
- }
-
- gs.set_interrupt(0);
- return false;
- }
-
- private:
- enum {
- STATE_START,
- STATE_CHECK_INDEX,
- STATE_CHECK_MCONTROL,
- STATE_WRITE_ADDRESS
- } state;
- hardware_breakpoint_t bp;
-};
-
-class maybe_save_tselect_op_t : public operation_t
-{
- public:
- maybe_save_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {};
- bool perform_step(unsigned int step) {
- if (gs.tselect_valid)
- return true;
-
- switch (step) {
- case 0:
- gs.dr_write32(0, csrr(S0, CSR_TDATA1));
- gs.dr_write_store(1, S0, SLOT_DATA0);
- gs.dr_write_jump(2);
- gs.set_interrupt(0);
- return false;
- case 1:
- gs.tselect = gs.dr_read(SLOT_DATA0);
- gs.tselect_valid = true;
- break;
- }
- return true;
- }
-};
-
-class maybe_restore_tselect_op_t : public operation_t
-{
- public:
- maybe_restore_tselect_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {};
- bool perform_step(unsigned int step) {
- if (gs.tselect_valid) {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write32(1, csrw(S0, CSR_TSELECT));
- gs.dr_write_jump(2);
- gs.dr_write(SLOT_DATA1, gs.tselect);
- }
- return true;
- }
-};
-
-class maybe_restore_mstatus_op_t : public operation_t
-{
- public:
- maybe_restore_mstatus_op_t(gdbserver_t& gdbserver) : operation_t(gdbserver) {};
- bool perform_step(unsigned int step) {
- if (gs.mstatus_dirty) {
- gs.dr_write_load(0, S0, SLOT_DATA1);
- gs.dr_write32(1, csrw(S0, CSR_MSTATUS));
- gs.dr_write_jump(2);
- gs.dr_write(SLOT_DATA1, gs.mstatus);
- }
- return true;
- }
-};
-
-class hardware_breakpoint_remove_op_t : public operation_t
-{
- public:
- hardware_breakpoint_remove_op_t(gdbserver_t& gdbserver,
- hardware_breakpoint_t bp) :
- operation_t(gdbserver), bp(bp) {};
-
- bool perform_step(unsigned int step) {
- gs.dr_write32(0, addi(S0, ZERO, bp.index));
- gs.dr_write32(1, csrw(S0, CSR_TSELECT));
- gs.dr_write32(2, csrw(ZERO, CSR_TDATA1));
- gs.dr_write_jump(3);
- gs.set_interrupt(0);
- return true;
- }
-
- private:
- hardware_breakpoint_t bp;
-};
-
-////////////////////////////// gdbserver itself
-
-gdbserver_t::gdbserver_t(uint16_t port, sim_t *sim) :
- xlen(0),
- sim(sim),
- client_fd(0),
- // gdb likes to send 0x100000 bytes at once when downloading.
- recv_buf(0x180000), send_buf(64 * 1024)
-{
- socket_fd = socket(AF_INET, SOCK_STREAM, 0);
- if (socket_fd == -1) {
- fprintf(stderr, "failed to make socket: %s (%d)\n", strerror(errno), errno);
- abort();
- }
-
- fcntl(socket_fd, F_SETFL, O_NONBLOCK);
- int reuseaddr = 1;
- if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
- sizeof(int)) == -1) {
- fprintf(stderr, "failed setsockopt: %s (%d)\n", strerror(errno), errno);
- abort();
- }
-
- struct sockaddr_in addr;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = INADDR_ANY;
- addr.sin_port = htons(port);
-
- if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
- fprintf(stderr, "failed to bind socket: %s (%d)\n", strerror(errno), errno);
- abort();
- }
-
- if (listen(socket_fd, 1) == -1) {
- fprintf(stderr, "failed to listen on socket: %s (%d)\n", strerror(errno), errno);
- abort();
- }
-}
-
-unsigned int gdbserver_t::find_access_size(reg_t address, int length)
-{
- reg_t composite = address | length;
- if ((composite & 0x7) == 0 && xlen >= 64)
- return 8;
- if ((composite & 0x3) == 0)
- return 4;
- return 1;
-}
-
-reg_t gdbserver_t::translate(reg_t vaddr)
-{
- vm_info vm = decode_vm_info(xlen, privilege_mode(), sptbr);
- if (vm.levels == 0)
- return vaddr;
-
- // Handle page tables here. There's a bunch of duplicated code with
- // collect_translation_info_op_t. :-(
- reg_t base = vm.ptbase;
- for (int i = vm.levels - 1; i >= 0; i--) {
- int ptshift = i * vm.idxbits;
- reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << vm.idxbits) - 1);
-
- reg_t pte_addr = base + idx * vm.ptesize;
- auto it = pte_cache.find(pte_addr);
- if (it == pte_cache.end()) {
- fprintf(stderr, "ERROR: gdbserver tried to translate 0x%016" PRIx64
- " without first collecting the relevant PTEs.\n", vaddr);
- die("gdbserver_t::translate()");
- }
-
- reg_t pte = pte_cache[pte_addr];
- reg_t ppn = pte >> PTE_PPN_SHIFT;
-
- if (PTE_TABLE(pte)) { // next level of page table
- base = ppn << PGSHIFT;
- } else {
- // We've collected all the data required for the translation.
- reg_t vpn = vaddr >> PGSHIFT;
- reg_t paddr = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT;
- paddr += vaddr & (PGSIZE-1);
- D(fprintf(stderr, "gdbserver translate 0x%" PRIx64 " -> 0x%" PRIx64 "\n", vaddr, paddr));
- return paddr;
- }
- }
-
- fprintf(stderr, "ERROR: gdbserver tried to translate 0x%016" PRIx64
- " but the relevant PTEs are invalid.\n", vaddr);
- // TODO: Is it better to throw an exception here?
- return -1;
-}
-
-unsigned int gdbserver_t::privilege_mode()
-{
- unsigned int mode = get_field(dcsr, DCSR_PRV);
- if (get_field(mstatus, MSTATUS_MPRV))
- mode = get_field(mstatus, MSTATUS_MPP);
- return mode;
-}
-
-void gdbserver_t::dr_write32(unsigned int index, uint32_t value)
-{
- sim->debug_module.ram_write32(index, value);
-}
-
-void gdbserver_t::dr_write64(unsigned int index, uint64_t value)
-{
- dr_write32(index, value);
- dr_write32(index+1, value >> 32);
-}
-
-void gdbserver_t::dr_write(enum slot slot, uint64_t value)
-{
- switch (xlen) {
- case 32:
- dr_write32(slot_offset32[slot], value);
- break;
- case 64:
- dr_write64(slot_offset64[slot], value);
- break;
- case 128:
- default:
- abort();
- }
-}
-
-void gdbserver_t::dr_write_jump(unsigned int index)
-{
- dr_write32(index, jal(0,
- (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*index))));
-}
-
-void gdbserver_t::dr_write_store(unsigned int index, unsigned int reg, enum slot slot)
-{
- assert(slot != SLOT_INST0 || index > 2);
- assert(slot != SLOT_DATA0 || index < 4 || index > 6);
- assert(slot != SLOT_DATA1 || index < 5 || index > 10);
- assert(slot != SLOT_DATA_LAST || index < 6 || index > 14);
- switch (xlen) {
- case 32:
- return dr_write32(index,
- sw(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset32[slot]));
- case 64:
- return dr_write32(index,
- sd(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset64[slot]));
- case 128:
- return dr_write32(index,
- sq(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset128[slot]));
- default:
- fprintf(stderr, "xlen is %d!\n", xlen);
- abort();
- }
-}
-
-void gdbserver_t::dr_write_load(unsigned int index, unsigned int reg, enum slot slot)
-{
- switch (xlen) {
- case 32:
- return dr_write32(index,
- lw(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset32[slot]));
- case 64:
- return dr_write32(index,
- ld(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset64[slot]));
- case 128:
- return dr_write32(index,
- lq(reg, 0, (uint16_t) DEBUG_RAM_START + 4 * slot_offset128[slot]));
- default:
- fprintf(stderr, "xlen is %d!\n", xlen);
- abort();
- }
-}
-
-uint32_t gdbserver_t::dr_read32(unsigned int index)
-{
- uint32_t value = sim->debug_module.ram_read32(index);
- D(fprintf(stderr, "read32(%d) -> 0x%x\n", index, value));
- return value;
-}
-
-uint64_t gdbserver_t::dr_read64(unsigned int index)
-{
- return ((uint64_t) dr_read32(index+1) << 32) | dr_read32(index);
-}
-
-uint64_t gdbserver_t::dr_read(enum slot slot)
-{
- switch (xlen) {
- case 32:
- return dr_read32(slot_offset32[slot]);
- case 64:
- return dr_read64(slot_offset64[slot]);
- case 128:
- abort();
- default:
- abort();
- }
-}
-
-void gdbserver_t::add_operation(operation_t* operation)
-{
- operation_queue.push(operation);
-}
-
-void gdbserver_t::accept()
-{
- client_fd = ::accept(socket_fd, NULL, NULL);
- if (client_fd == -1) {
- if (errno == EAGAIN) {
- // No client waiting to connect right now.
- } else {
- fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno),
- errno);
- abort();
- }
- } else {
- fcntl(client_fd, F_SETFL, O_NONBLOCK);
-
- expect_ack = false;
- extended_mode = false;
-
- // gdb wants the core to be halted when it attaches.
- add_operation(new halt_op_t(*this));
- }
-}
-
-void gdbserver_t::read()
-{
- // Reading from a non-blocking socket still blocks if there is no data
- // available.
-
- size_t count = recv_buf.contiguous_empty_size();
- ssize_t bytes = ::read(client_fd, recv_buf.contiguous_empty(), count);
- if (bytes == -1) {
- if (errno == EAGAIN) {
- // We'll try again the next call.
- } else {
- fprintf(stderr, "failed to read on socket: %s (%d)\n", strerror(errno), errno);
- abort();
- }
- } else if (bytes == 0) {
- // The remote disconnected.
- client_fd = 0;
- processor_t *p = sim->get_core(0);
- // TODO p->set_halted(false, HR_NONE);
- recv_buf.reset();
- send_buf.reset();
- } else {
- recv_buf.data_added(bytes);
- }
-}
-
-void gdbserver_t::write()
-{
- if (send_buf.empty())
- return;
-
- while (!send_buf.empty()) {
- unsigned int count = send_buf.contiguous_data_size();
- assert(count > 0);
- ssize_t bytes = ::write(client_fd, send_buf.contiguous_data(), count);
- if (bytes == -1) {
- fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno);
- abort();
- } else if (bytes == 0) {
- // Client can't take any more data right now.
- break;
- } else {
- D(fprintf(stderr, "wrote %zd bytes: ", bytes));
- for (int i = 0; i < bytes; i++) {
- D(fprintf(stderr, "%c", send_buf[i]));
- }
- D(fprintf(stderr, "\n"));
- send_buf.consume(bytes);
- }
- }
-}
-
-void print_packet(const std::vector<uint8_t> &packet)
-{
- for (uint8_t c : packet) {
- if (c >= ' ' and c <= '~')
- fprintf(stderr, "%c", c);
- else
- fprintf(stderr, "\\x%02x", c);
- }
- fprintf(stderr, "\n");
-}
-
-uint8_t compute_checksum(const std::vector<uint8_t> &packet)
-{
- uint8_t checksum = 0;
- for (auto i = packet.begin() + 1; i != packet.end() - 3; i++ ) {
- checksum += *i;
- }
- return checksum;
-}
-
-uint8_t character_hex_value(uint8_t character)
-{
- if (character >= '0' && character <= '9')
- return character - '0';
- if (character >= 'a' && character <= 'f')
- return 10 + character - 'a';
- if (character >= 'A' && character <= 'F')
- return 10 + character - 'A';
- return 0xff;
-}
-
-uint8_t extract_checksum(const std::vector<uint8_t> &packet)
-{
- return character_hex_value(*(packet.end() - 1)) +
- 16 * character_hex_value(*(packet.end() - 2));
-}
-
-void gdbserver_t::process_requests()
-{
- // See https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html
-
- while (!recv_buf.empty()) {
- std::vector<uint8_t> packet;
- for (unsigned int i = 0; i < recv_buf.size(); i++) {
- uint8_t b = recv_buf[i];
-
- if (packet.empty() && expect_ack && b == '+') {
- recv_buf.consume(1);
- break;
- }
-
- if (packet.empty() && b == 3) {
- D(fprintf(stderr, "Received interrupt\n"));
- recv_buf.consume(1);
- handle_interrupt();
- break;
- }
-
- if (b == '$') {
- // Start of new packet.
- if (!packet.empty()) {
- fprintf(stderr, "Received malformed %zd-byte packet from debug client: ",
- packet.size());
- print_packet(packet);
- recv_buf.consume(i);
- break;
- }
- }
-
- packet.push_back(b);
-
- // Packets consist of $<packet-data>#<checksum>
- // where <checksum> is
- if (packet.size() >= 4 &&
- packet[packet.size()-3] == '#') {
- handle_packet(packet);
- recv_buf.consume(i+1);
- break;
- }
- }
- // There's a partial packet in the buffer. Wait until we get more data to
- // process it.
- if (packet.size()) {
- break;
- }
- }
-
- if (recv_buf.full()) {
- fprintf(stderr,
- "Receive buffer is full, but no complete packet was found!\n");
- for (unsigned line = 0; line < 8; line++) {
- for (unsigned i = 0; i < 16; i++) {
- fprintf(stderr, "%02x ", recv_buf.entry(line * 16 + i));
- }
- for (unsigned i = 0; i < 16; i++) {
- uint8_t e = recv_buf.entry(line * 16 + i);
- if (e >= ' ' && e <= '~')
- fprintf(stderr, "%c", e);
- else
- fprintf(stderr, ".");
- }
- fprintf(stderr, "\n");
- }
- assert(!recv_buf.full());
- }
-}
-
-void gdbserver_t::handle_halt_reason(const std::vector<uint8_t> &packet)
-{
- send_packet("S00");
-}
-
-void gdbserver_t::handle_general_registers_read(const std::vector<uint8_t> &packet)
-{
- add_operation(new general_registers_read_op_t(*this));
-}
-
-void gdbserver_t::set_interrupt(uint32_t hartid) {
- sim->debug_module.set_interrupt(hartid);
-}
-
-// First byte is the most-significant one.
-// Eg. "08675309" becomes 0x08675309.
-uint64_t consume_hex_number(std::vector<uint8_t>::const_iterator &iter,
- std::vector<uint8_t>::const_iterator end)
-{
- uint64_t value = 0;
-
- while (iter != end) {
- uint8_t c = *iter;
- uint64_t c_value = character_hex_value(c);
- if (c_value > 15)
- break;
- iter++;
- value <<= 4;
- value += c_value;
- }
- return value;
-}
-
-// First byte is the least-significant one.
-// Eg. "08675309" becomes 0x09536708
-uint64_t gdbserver_t::consume_hex_number_le(
- std::vector<uint8_t>::const_iterator &iter,
- std::vector<uint8_t>::const_iterator end)
-{
- uint64_t value = 0;
- unsigned int shift = 4;
-
- while (iter != end) {
- uint8_t c = *iter;
- uint64_t c_value = character_hex_value(c);
- if (c_value > 15)
- break;
- iter++;
- value |= c_value << shift;
- if ((shift % 8) == 0)
- shift += 12;
- else
- shift -= 4;
- }
- if (shift > (xlen+4)) {
- fprintf(stderr,
- "gdb sent too many data bytes. That means it thinks XLEN is greater "
- "than %d.\nTo fix that, tell gdb: set arch riscv:rv%d\n",
- xlen, xlen);
- }
- return value;
-}
-
-void consume_string(std::string &str, std::vector<uint8_t>::const_iterator &iter,
- std::vector<uint8_t>::const_iterator end, uint8_t separator)
-{
- while (iter != end && *iter != separator) {
- str.append(1, (char) *iter);
- iter++;
- }
-}
-
-void gdbserver_t::handle_register_read(const std::vector<uint8_t> &packet)
-{
- // p n
-
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- unsigned int n = consume_hex_number(iter, packet.end());
- if (*iter != '#')
- return send_packet("E01");
-
- add_operation(new register_read_op_t(*this, n));
-}
-
-void gdbserver_t::handle_register_write(const std::vector<uint8_t> &packet)
-{
- // P n...=r...
-
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- unsigned int n = consume_hex_number(iter, packet.end());
- if (*iter != '=')
- return send_packet("E05");
- iter++;
-
- reg_t value = consume_hex_number_le(iter, packet.end());
- if (*iter != '#')
- return send_packet("E06");
-
- processor_t *p = sim->get_core(0);
-
- add_operation(new register_write_op_t(*this, n, value));
-}
-
-void gdbserver_t::handle_memory_read(const std::vector<uint8_t> &packet)
-{
- // m addr,length
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- reg_t address = consume_hex_number(iter, packet.end());
- if (*iter != ',')
- return send_packet("E10");
- iter++;
- reg_t length = consume_hex_number(iter, packet.end());
- if (*iter != '#')
- return send_packet("E11");
-
- add_operation(new collect_translation_info_op_t(*this, address, length));
- add_operation(new memory_read_op_t(*this, address, length));
-}
-
-void gdbserver_t::handle_memory_binary_write(const std::vector<uint8_t> &packet)
-{
- // X addr,length:XX...
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- reg_t address = consume_hex_number(iter, packet.end());
- if (*iter != ',')
- return send_packet("E20");
- iter++;
- reg_t length = consume_hex_number(iter, packet.end());
- if (*iter != ':')
- return send_packet("E21");
- iter++;
-
- if (length == 0) {
- return send_packet("OK");
- }
-
- unsigned char *data = new unsigned char[length];
- for (unsigned int i = 0; i < length; i++) {
- if (iter == packet.end()) {
- return send_packet("E22");
- }
- uint8_t c = *iter;
- iter++;
- if (c == '}') {
- // The binary data representation uses 7d (ascii ‘}’) as an escape
- // character. Any escaped byte is transmitted as the escape character
- // followed by the original character XORed with 0x20. For example, the
- // byte 0x7d would be transmitted as the two bytes 0x7d 0x5d. The bytes
- // 0x23 (ascii ‘#’), 0x24 (ascii ‘$’), and 0x7d (ascii ‘}’) must always
- // be escaped.
- if (iter == packet.end()) {
- return send_packet("E23");
- }
- c = (*iter) ^ 0x20;
- iter++;
- }
- data[i] = c;
- }
- if (*iter != '#')
- return send_packet("E4b"); // EOVERFLOW
-
- add_operation(new collect_translation_info_op_t(*this, address, length));
- add_operation(new memory_write_op_t(*this, address, length, data));
-}
-
-void gdbserver_t::handle_continue(const std::vector<uint8_t> &packet)
-{
- // c [addr]
- processor_t *p = sim->get_core(0);
- if (packet[2] != '#') {
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- dpc = consume_hex_number(iter, packet.end());
- if (*iter != '#')
- return send_packet("E30");
- }
-
- add_operation(new maybe_restore_tselect_op_t(*this));
- add_operation(new maybe_restore_mstatus_op_t(*this));
- add_operation(new continue_op_t(*this, false));
-}
-
-void gdbserver_t::handle_step(const std::vector<uint8_t> &packet)
-{
- // s [addr]
- if (packet[2] != '#') {
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- die("handle_step");
- //p->state.pc = consume_hex_number(iter, packet.end());
- if (*iter != '#')
- return send_packet("E40");
- }
-
- add_operation(new maybe_restore_tselect_op_t(*this));
- add_operation(new continue_op_t(*this, true));
-}
-
-void gdbserver_t::handle_kill(const std::vector<uint8_t> &packet)
-{
- // k
- // The exact effect of this packet is not specified.
- // Looks like OpenOCD disconnects?
- // TODO
-}
-
-void gdbserver_t::handle_extended(const std::vector<uint8_t> &packet)
-{
- // Enable extended mode. In extended mode, the remote server is made
- // persistent. The ‘R’ packet is used to restart the program being debugged.
- send_packet("OK");
- extended_mode = true;
-}
-
-void gdbserver_t::software_breakpoint_insert(reg_t vaddr, unsigned int size)
-{
- fence_i_required = true;
- add_operation(new collect_translation_info_op_t(*this, vaddr, size));
- unsigned char* inst = new unsigned char[4];
- if (size == 2) {
- inst[0] = MATCH_C_EBREAK & 0xff;
- inst[1] = (MATCH_C_EBREAK >> 8) & 0xff;
- } else {
- inst[0] = MATCH_EBREAK & 0xff;
- inst[1] = (MATCH_EBREAK >> 8) & 0xff;
- inst[2] = (MATCH_EBREAK >> 16) & 0xff;
- inst[3] = (MATCH_EBREAK >> 24) & 0xff;
- }
-
- software_breakpoint_t bp = {
- .vaddr = vaddr,
- .size = size
- };
- software_breakpoints[vaddr] = bp;
- add_operation(new memory_read_op_t(*this, bp.vaddr, bp.size,
- software_breakpoints[bp.vaddr].instruction));
- add_operation(new memory_write_op_t(*this, bp.vaddr, bp.size, inst));
-}
-
-void gdbserver_t::software_breakpoint_remove(reg_t vaddr, unsigned int size)
-{
- fence_i_required = true;
- add_operation(new collect_translation_info_op_t(*this, vaddr, size));
-
- software_breakpoint_t found_bp = software_breakpoints[vaddr];
- unsigned char* instruction = new unsigned char[4];
- memcpy(instruction, found_bp.instruction, 4);
- add_operation(new memory_write_op_t(*this, found_bp.vaddr,
- found_bp.size, instruction));
- software_breakpoints.erase(vaddr);
-}
-
-void gdbserver_t::hardware_breakpoint_insert(const hardware_breakpoint_t &bp)
-{
- add_operation(new maybe_save_tselect_op_t(*this));
- add_operation(new hardware_breakpoint_insert_op_t(*this, bp));
-}
-
-void gdbserver_t::hardware_breakpoint_remove(const hardware_breakpoint_t &bp)
-{
- add_operation(new maybe_save_tselect_op_t(*this));
- hardware_breakpoint_t found = *hardware_breakpoints.find(bp);
- add_operation(new hardware_breakpoint_remove_op_t(*this, found));
-}
-
-void gdbserver_t::handle_breakpoint(const std::vector<uint8_t> &packet)
-{
- // insert: Z type,addr,length
- // remove: z type,addr,length
-
- // type: 0 - software breakpoint, 1 - hardware breakpoint, 2 - write
- // watchpoint, 3 - read watchpoint, 4 - access watchpoint; addr is address;
- // length is in bytes. For a software breakpoint, length specifies the size
- // of the instruction to be patched. For hardware breakpoints and watchpoints
- // length specifies the memory region to be monitored. To avoid potential
- // problems with duplicate packets, the operations should be implemented in
- // an idempotent way.
-
- bool insert = (packet[1] == 'Z');
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
- gdb_breakpoint_type_t type = static_cast<gdb_breakpoint_type_t>(
- consume_hex_number(iter, packet.end()));
- if (*iter != ',')
- return send_packet("E50");
- iter++;
- reg_t address = consume_hex_number(iter, packet.end());
- if (*iter != ',')
- return send_packet("E51");
- iter++;
- unsigned int size = consume_hex_number(iter, packet.end());
- // There may be more options after a ; here, but we don't support that.
- if (*iter != '#')
- return send_packet("E52");
-
- switch (type) {
- case GB_SOFTWARE:
- if (size != 2 && size != 4) {
- return send_packet("E53");
- }
- if (insert) {
- software_breakpoint_insert(address, size);
- } else {
- software_breakpoint_remove(address, size);
- }
- break;
-
- case GB_HARDWARE:
- case GB_WRITE:
- case GB_READ:
- case GB_ACCESS:
- {
- hardware_breakpoint_t bp = {
- .vaddr = address,
- .size = size
- };
- bp.load = (type == GB_READ || type == GB_ACCESS);
- bp.store = (type == GB_WRITE || type == GB_ACCESS);
- bp.execute = (type == GB_HARDWARE || type == GB_ACCESS);
- if (insert) {
- hardware_breakpoint_insert(bp);
- // Insert might fail if there's no space, so the insert operation will
- // send its own OK (or not).
- return;
- } else {
- hardware_breakpoint_remove(bp);
- }
- }
- break;
-
- default:
- return send_packet("E56");
- }
-
- return send_packet("OK");
-}
-
-void gdbserver_t::handle_query(const std::vector<uint8_t> &packet)
-{
- std::string name;
- std::vector<uint8_t>::const_iterator iter = packet.begin() + 2;
-
- consume_string(name, iter, packet.end(), ':');
- if (iter != packet.end())
- iter++;
- if (name == "Supported") {
- start_packet();
- while (iter != packet.end()) {
- std::string feature;
- consume_string(feature, iter, packet.end(), ';');
- if (iter != packet.end())
- iter++;
- if (feature == "swbreak+") {
- send("swbreak+;");
- }
- }
- send("PacketSize=131072;");
- return end_packet();
- }
-
- D(fprintf(stderr, "Unsupported query %s\n", name.c_str()));
- return send_packet("");
-}
-
-void gdbserver_t::handle_packet(const std::vector<uint8_t> &packet)
-{
- if (compute_checksum(packet) != extract_checksum(packet)) {
- fprintf(stderr, "Received %zd-byte packet with invalid checksum\n", packet.size());
- fprintf(stderr, "Computed checksum: %x\n", compute_checksum(packet));
- print_packet(packet);
- send("-");
- return;
- }
-
- D(fprintf(stderr, "Received %zd-byte packet from debug client: ", packet.size()));
- D(print_packet(packet));
- send("+");
-
- switch (packet[1]) {
- case '!':
- return handle_extended(packet);
- case '?':
- return handle_halt_reason(packet);
- case 'g':
- return handle_general_registers_read(packet);
-// case 'k':
-// return handle_kill(packet);
- case 'm':
- return handle_memory_read(packet);
-// case 'M':
-// return handle_memory_write(packet);
- case 'X':
- return handle_memory_binary_write(packet);
- case 'p':
- return handle_register_read(packet);
- case 'P':
- return handle_register_write(packet);
- case 'c':
- return handle_continue(packet);
- case 's':
- return handle_step(packet);
- case 'z':
- case 'Z':
- return handle_breakpoint(packet);
- case 'q':
- case 'Q':
- return handle_query(packet);
- }
-
- // Not supported.
- D(fprintf(stderr, "** Unsupported packet: "));
- D(print_packet(packet));
- send_packet("");
-}
-
-void gdbserver_t::handle_interrupt()
-{
- processor_t *p = sim->get_core(0);
- add_operation(new halt_op_t(*this, true));
-}
-
-void gdbserver_t::handle()
-{
- if (client_fd > 0) {
- processor_t *p = sim->get_core(0);
-
- bool interrupt = sim->debug_module.get_interrupt(0);
-
- if (!interrupt && !operation_queue.empty()) {
- operation_t *operation = operation_queue.front();
- if (operation->step()) {
- operation_queue.pop();
- delete operation;
- }
- }
-
- bool halt_notification = sim->debug_module.get_halt_notification(0);
- if (halt_notification) {
- sim->debug_module.clear_halt_notification(0);
- add_operation(new halt_op_t(*this, true));
- }
-
- this->read();
- this->write();
-
- } else {
- this->accept();
- }
-
- if (operation_queue.empty()) {
- this->process_requests();
- }
-}
-
-void gdbserver_t::send(const char* msg)
-{
- unsigned int length = strlen(msg);
- for (const char *c = msg; *c; c++)
- running_checksum += *c;
- send_buf.append((const uint8_t *) msg, length);
-}
-
-void gdbserver_t::send(uint64_t value)
-{
- char buffer[3];
- for (unsigned int i = 0; i < 8; i++) {
- sprintf(buffer, "%02x", (int) (value & 0xff));
- send(buffer);
- value >>= 8;
- }
-}
-
-void gdbserver_t::send(uint32_t value)
-{
- char buffer[3];
- for (unsigned int i = 0; i < 4; i++) {
- sprintf(buffer, "%02x", (int) (value & 0xff));
- send(buffer);
- value >>= 8;
- }
-}
-
-void gdbserver_t::send(uint8_t value)
-{
- char buffer[3];
- sprintf(buffer, "%02x", (int) value);
- send(buffer);
-}
-
-void gdbserver_t::send_packet(const char* data)
-{
- start_packet();
- send(data);
- end_packet();
- expect_ack = true;
-}
-
-void gdbserver_t::start_packet()
-{
- send("$");
- running_checksum = 0;
-}
-
-void gdbserver_t::end_packet(const char* data)
-{
- if (data) {
- send(data);
- }
-
- char checksum_string[4];
- sprintf(checksum_string, "#%02x", running_checksum);
- send(checksum_string);
- expect_ack = true;
-}
diff --git a/riscv/gdbserver.h b/riscv/gdbserver.h
deleted file mode 100644
index 8584215..0000000
--- a/riscv/gdbserver.h
+++ /dev/null
@@ -1,266 +0,0 @@
-#ifndef _RISCV_GDBSERVER_H
-#define _RISCV_GDBSERVER_H
-
-#include <map>
-#include <queue>
-
-#include <stdint.h>
-
-class sim_t;
-
-template <typename T>
-class circular_buffer_t
-{
-public:
- // The buffer can store capacity-1 data elements.
- circular_buffer_t(unsigned int capacity) : data(new T[capacity]),
- start(0), end(0), capacity(capacity) {}
- circular_buffer_t() : start(0), end(0), capacity(0) {}
- ~circular_buffer_t() { delete[] data; }
-
- T *data;
- unsigned int start; // Data start, inclusive.
- unsigned int end; // Data end, exclusive.
- unsigned int capacity; // Size of the buffer.
- unsigned int size() const;
- bool empty() const { return start == end; }
- bool full() const { return ((end+1) % capacity) == start; }
- T entry(unsigned index) { return data[(start + index) % capacity]; }
-
- // Return size and address of the block of RAM where more data can be copied
- // to be added to the buffer.
- unsigned int contiguous_empty_size() const;
- T *contiguous_empty() { return data + end; }
- void data_added(unsigned int bytes);
-
- unsigned int contiguous_data_size() const;
- T *contiguous_data() { return data + start; }
- // Tell the buffer that some bytes were consumed from the start of the
- // buffer.
- void consume(unsigned int bytes);
-
- void reset();
-
- T operator[](unsigned int i) const { return data[(start + i) % capacity]; }
-
- void append(const T *src, unsigned int count);
-};
-
-// Class to track software breakpoints that we set.
-class software_breakpoint_t
-{
- public:
- reg_t vaddr;
- unsigned int size;
- unsigned char instruction[4];
-};
-
-class hardware_breakpoint_t
-{
- public:
- reg_t vaddr;
- unsigned int size;
- unsigned int index;
- bool load, store, execute;
-};
-
-struct hardware_breakpoint_compare_t {
- bool operator()(const hardware_breakpoint_t& a, const hardware_breakpoint_t& b) const {
- if (a.vaddr != b.vaddr)
- return a.vaddr < b.vaddr;
- return a.size < b.size;
- }
-};
-
-class gdbserver_t;
-
-class operation_t
-{
- public:
- operation_t(gdbserver_t& gdbserver) : gs(gdbserver), current_step(0) {}
- virtual ~operation_t() {}
-
- bool step() {
- bool result = perform_step(current_step);
- current_step++;
- return result;
- }
-
- // Perform the next step of this operation (which is probably to write to
- // Debug RAM and assert the debug interrupt).
- // Return true if this operation is complete. In that case the object will
- // be deleted.
- // Return false if more steps are required the next time the debug
- // interrupt is clear.
- virtual bool perform_step(unsigned int step) = 0;
-
- protected:
- gdbserver_t& gs;
- unsigned int current_step;
-};
-
-/*
- * word 32 64 128
- * 0 inst/0 inst/0 inst/0
- * 1 inst inst/0 inst/0
- * 2 inst inst inst/0
- * 3 inst inst inst/0
- * 4 data0 data0 data0
- * 5 data1 data0 data0
- * 6 data2 data1 data0
- * 7 data1 data0
- * 8 data2 data1
- * 9 data2 data1
- * 10 data1
- * 11 data1
- * 12 data2
- * 13 data2
- * 14 data2
- * 15 data2
- */
-enum slot {
- SLOT_INST0,
- SLOT_DATA0,
- SLOT_DATA1,
- SLOT_DATA_LAST,
-};
-
-static const unsigned int slot_offset32[] = {0, 4, 5, DEBUG_RAM_SIZE/4 - 1};
-static const unsigned int slot_offset64[] = {0, 4, 6, DEBUG_RAM_SIZE/4 - 2};
-static const unsigned int slot_offset128[] = {0, 4, 8, DEBUG_RAM_SIZE/4 - 4};
-
-typedef enum {
- GB_SOFTWARE = 0,
- GB_HARDWARE = 1,
- GB_WRITE = 2,
- GB_READ = 3,
- GB_ACCESS = 4,
-} gdb_breakpoint_type_t;
-
-class gdbserver_t
-{
-public:
- // Create a new server, listening for connections from localhost on the given
- // port.
- gdbserver_t(uint16_t port, sim_t *sim);
-
- // Process all pending messages from a client.
- void handle();
-
- void handle_packet(const std::vector<uint8_t> &packet);
- void handle_interrupt();
-
- void software_breakpoint_remove(reg_t vaddr, unsigned int size);
- void software_breakpoint_insert(reg_t vaddr, unsigned int size);
- void hardware_breakpoint_remove(const hardware_breakpoint_t &bp);
- void hardware_breakpoint_insert(const hardware_breakpoint_t &bp);
-
- void handle_breakpoint(const std::vector<uint8_t> &packet);
- void handle_continue(const std::vector<uint8_t> &packet);
- void handle_extended(const std::vector<uint8_t> &packet);
- void handle_general_registers_read(const std::vector<uint8_t> &packet);
- void continue_general_registers_read();
- void handle_halt_reason(const std::vector<uint8_t> &packet);
- void handle_kill(const std::vector<uint8_t> &packet);
- void handle_memory_binary_write(const std::vector<uint8_t> &packet);
- void handle_memory_read(const std::vector<uint8_t> &packet);
- void handle_query(const std::vector<uint8_t> &packet);
- void handle_register_read(const std::vector<uint8_t> &packet);
- void continue_register_read();
- void handle_register_write(const std::vector<uint8_t> &packet);
- void handle_step(const std::vector<uint8_t> &packet);
-
- bool connected() const { return client_fd > 0; }
-
- // TODO: Move this into its own packet sending class?
- // Add the given message to send_buf.
- void send(const char* msg);
- // Hex-encode a 64-bit value, and send it to gcc in target byte order (little
- // endian).
- void send(uint64_t value);
- // Hex-encode a 32-bit value, and send it to gcc in target byte order (little
- // endian).
- void send(uint32_t value);
- // Hex-encode an 8-bit value, and send it to gcc.
- void send(uint8_t value);
- void send_packet(const char* data);
- uint8_t running_checksum;
- // Send "$" and clear running checksum.
- void start_packet();
- // Send "#" and checksum.
- void end_packet(const char* data=NULL);
-
- // Write value to the index'th word in Debug RAM.
- void dr_write32(unsigned int index, uint32_t value);
- void dr_write64(unsigned int index, uint64_t value);
- void dr_write(enum slot slot, uint64_t value);
- // Write jump-to-resume instruction to the index'th word in Debug RAM.
- void dr_write_jump(unsigned int index);
- // Write an xlen-bit store instruction.
- void dr_write_store(unsigned int index, unsigned int reg, enum slot);
- void dr_write_load(unsigned int index, unsigned int reg, enum slot);
- uint32_t dr_read32(unsigned int index);
- uint64_t dr_read64(unsigned int index);
- uint64_t dr_read(enum slot slot);
-
- uint64_t consume_hex_number_le(std::vector<uint8_t>::const_iterator &iter,
- std::vector<uint8_t>::const_iterator end);
-
- // Return access size to use when writing length bytes to address, so that
- // every write will be aligned.
- unsigned int find_access_size(reg_t address, int length);
-
- void set_interrupt(uint32_t hartid);
-
- // Members that ought to be privated, but that we'd really like to access
- // from operation classes.
- reg_t dpc;
- reg_t dcsr;
- reg_t mstatus;
- bool mstatus_dirty;
- reg_t sptbr;
- bool sptbr_valid;
- reg_t tselect;
- bool tselect_valid;
- bool fence_i_required;
-
- std::map<reg_t, reg_t> pte_cache;
-
- reg_t translate(reg_t vaddr);
- // Return the PRV_x that is used when the code under debug performs a memory
- // access.
- unsigned int privilege_mode();
-
- unsigned int xlen;
-
- std::set<hardware_breakpoint_t, hardware_breakpoint_compare_t>
- hardware_breakpoints;
-
-private:
- sim_t *sim;
- int socket_fd;
- int client_fd;
- circular_buffer_t<uint8_t> recv_buf;
- circular_buffer_t<uint8_t> send_buf;
-
- bool expect_ack;
- bool extended_mode;
- // Used to track whether we think the target is running. If we think it is
- // but it isn't, we need to tell gdb about it.
- bool running;
-
- std::map<reg_t, software_breakpoint_t> software_breakpoints;
-
- // Read pending data from the client.
- void read();
- void write();
- // Accept a new client if there isn't one already connected.
- void accept();
- // Process all complete requests in recv_buf.
- void process_requests();
-
- std::queue<operation_t*> operation_queue;
- void add_operation(operation_t* operation);
-};
-
-#endif
diff --git a/riscv/jtag_dtm.cc b/riscv/jtag_dtm.cc
new file mode 100644
index 0000000..cd3f3ee
--- /dev/null
+++ b/riscv/jtag_dtm.cc
@@ -0,0 +1,180 @@
+#include <stdio.h>
+
+#include "decode.h"
+#include "jtag_dtm.h"
+#include "debug_module.h"
+#include "debug_defines.h"
+
+#if 0
+# define D(x) x
+#else
+# define D(x)
+#endif
+
+enum {
+ IR_IDCODE=1,
+ IR_DTMCONTROL=0x10,
+ IR_DBUS=0x11
+};
+
+#define DTMCONTROL_VERSION 0xf
+#define DTMCONTROL_ABITS (0x3f << 4)
+#define DTMCONTROL_DBUSSTAT (3<<10)
+#define DTMCONTROL_IDLE (7<<12)
+#define DTMCONTROL_DBUSRESET (1<<16)
+
+#define DMI_OP 3
+#define DMI_DATA (0xffffffffL<<2)
+#define DMI_ADDRESS ((1L<<(abits+34)) - (1L<<34))
+
+#define DMI_OP_STATUS_SUCCESS 0
+#define DMI_OP_STATUS_RESERVED 1
+#define DMI_OP_STATUS_FAILED 2
+#define DMI_OP_STATUS_BUSY 3
+
+#define DMI_OP_NOP 0
+#define DMI_OP_READ 1
+#define DMI_OP_WRITE 2
+#define DMI_OP_RESERVED 3
+
+jtag_dtm_t::jtag_dtm_t(debug_module_t *dm) :
+ dm(dm),
+ _tck(false), _tms(false), _tdi(false), _tdo(false),
+ dtmcontrol((abits << DTM_DTMCS_ABITS_OFFSET) | 1),
+ dmi(DMI_OP_STATUS_FAILED << DTM_DMI_OP_OFFSET),
+ _state(TEST_LOGIC_RESET)
+{
+}
+
+void jtag_dtm_t::reset() {
+ _state = TEST_LOGIC_RESET;
+}
+
+void jtag_dtm_t::set_pins(bool tck, bool tms, bool tdi) {
+ const jtag_state_t next[16][2] = {
+ /* TEST_LOGIC_RESET */ { RUN_TEST_IDLE, TEST_LOGIC_RESET },
+ /* RUN_TEST_IDLE */ { RUN_TEST_IDLE, SELECT_DR_SCAN },
+ /* SELECT_DR_SCAN */ { CAPTURE_DR, SELECT_IR_SCAN },
+ /* CAPTURE_DR */ { SHIFT_DR, EXIT1_DR },
+ /* SHIFT_DR */ { SHIFT_DR, EXIT1_DR },
+ /* EXIT1_DR */ { PAUSE_DR, UPDATE_DR },
+ /* PAUSE_DR */ { PAUSE_DR, EXIT2_DR },
+ /* EXIT2_DR */ { SHIFT_DR, UPDATE_DR },
+ /* UPDATE_DR */ { RUN_TEST_IDLE, SELECT_DR_SCAN },
+ /* SELECT_IR_SCAN */ { CAPTURE_IR, TEST_LOGIC_RESET },
+ /* CAPTURE_IR */ { SHIFT_IR, EXIT1_IR },
+ /* SHIFT_IR */ { SHIFT_IR, EXIT1_IR },
+ /* EXIT1_IR */ { PAUSE_IR, UPDATE_IR },
+ /* PAUSE_IR */ { PAUSE_IR, EXIT2_IR },
+ /* EXIT2_IR */ { SHIFT_IR, UPDATE_IR },
+ /* UPDATE_IR */ { RUN_TEST_IDLE, SELECT_DR_SCAN }
+ };
+
+ if (!_tck && tck) {
+ // Positive clock edge.
+
+ switch (_state) {
+ case SHIFT_DR:
+ dr >>= 1;
+ dr |= (uint64_t) _tdi << (dr_length-1);
+ break;
+ case SHIFT_IR:
+ ir >>= 1;
+ ir |= _tdi << (ir_length-1);
+ break;
+ default:
+ break;
+ }
+ _state = next[_state][_tms];
+ switch (_state) {
+ case TEST_LOGIC_RESET:
+ ir = IR_IDCODE;
+ break;
+ case CAPTURE_DR:
+ capture_dr();
+ break;
+ case SHIFT_DR:
+ _tdo = dr & 1;
+ break;
+ case UPDATE_DR:
+ update_dr();
+ break;
+ case CAPTURE_IR:
+ break;
+ case SHIFT_IR:
+ _tdo = ir & 1;
+ break;
+ case UPDATE_IR:
+ break;
+ default:
+ break;
+ }
+ }
+
+ D(fprintf(stderr, "state=%2d, tdi=%d, tdo=%d, tms=%d, tck=%d, ir=0x%02x, "
+ "dr=0x%lx\n",
+ _state, _tdi, _tdo, _tms, _tck, ir, dr));
+
+ _tck = tck;
+ _tms = tms;
+ _tdi = tdi;
+}
+
+void jtag_dtm_t::capture_dr()
+{
+ switch (ir) {
+ case IR_IDCODE:
+ dr = idcode;
+ dr_length = 32;
+ break;
+ case IR_DTMCONTROL:
+ dr = dtmcontrol;
+ dr_length = 32;
+ break;
+ case IR_DBUS:
+ dr = dmi;
+ dr_length = abits + 34;
+ break;
+ default:
+ D(fprintf(stderr, "Unsupported IR: 0x%x\n", ir));
+ break;
+ }
+ D(fprintf(stderr, "Capture DR; IR=0x%x, DR=0x%lx (%d bits)\n",
+ ir, dr, dr_length));
+}
+
+void jtag_dtm_t::update_dr()
+{
+ D(fprintf(stderr, "Update DR; IR=0x%x, DR=0x%lx (%d bits)\n",
+ ir, dr, dr_length));
+ switch (ir) {
+ case IR_DBUS:
+ {
+ unsigned op = get_field(dr, DMI_OP);
+ uint32_t data = get_field(dr, DMI_DATA);
+ unsigned address = get_field(dr, DMI_ADDRESS);
+
+ dmi = dr;
+
+ bool success = true;
+ if (op == DMI_OP_READ) {
+ uint32_t value;
+ if (dm->dmi_read(address, &value)) {
+ dmi = set_field(dmi, DMI_DATA, value);
+ } else {
+ success = false;
+ }
+ } else if (op == DMI_OP_WRITE) {
+ success = dm->dmi_write(address, data);
+ }
+
+ if (success) {
+ dmi = set_field(dmi, DMI_OP, DMI_OP_STATUS_SUCCESS);
+ } else {
+ dmi = set_field(dmi, DMI_OP, DMI_OP_STATUS_FAILED);
+ }
+ D(fprintf(stderr, "dmi=0x%lx\n", dmi));
+ }
+ break;
+ }
+}
diff --git a/riscv/jtag_dtm.h b/riscv/jtag_dtm.h
new file mode 100644
index 0000000..063e3f4
--- /dev/null
+++ b/riscv/jtag_dtm.h
@@ -0,0 +1,61 @@
+#ifndef JTAG_DTM_H
+#define JTAG_DTM_H
+
+#include <stdint.h>
+
+class debug_module_t;
+
+typedef enum {
+ TEST_LOGIC_RESET,
+ RUN_TEST_IDLE,
+ SELECT_DR_SCAN,
+ CAPTURE_DR,
+ SHIFT_DR,
+ EXIT1_DR,
+ PAUSE_DR,
+ EXIT2_DR,
+ UPDATE_DR,
+ SELECT_IR_SCAN,
+ CAPTURE_IR,
+ SHIFT_IR,
+ EXIT1_IR,
+ PAUSE_IR,
+ EXIT2_IR,
+ UPDATE_IR
+} jtag_state_t;
+
+class jtag_dtm_t
+{
+ static const unsigned idcode = 0xdeadbeef;
+
+ public:
+ jtag_dtm_t(debug_module_t *dm);
+ void reset();
+
+ void set_pins(bool tck, bool tms, bool tdi);
+
+ bool tdo() const { return _tdo; }
+
+ jtag_state_t state() const { return _state; }
+
+ private:
+ debug_module_t *dm;
+ bool _tck, _tms, _tdi, _tdo;
+ uint32_t ir;
+ const unsigned ir_length = 5;
+ uint64_t dr;
+ unsigned dr_length;
+
+ // abits must come before dtmcontrol so it can easily be used in the
+ // constructor.
+ const unsigned abits = 6;
+ uint32_t dtmcontrol;
+ uint64_t dmi;
+
+ jtag_state_t _state;
+
+ void capture_dr();
+ void update_dr();
+};
+
+#endif
diff --git a/riscv/opcodes.h b/riscv/opcodes.h
new file mode 100644
index 0000000..34c089e
--- /dev/null
+++ b/riscv/opcodes.h
@@ -0,0 +1,244 @@
+#include "encoding.h"
+
+#define ZERO 0
+#define T0 5
+#define S0 8
+#define S1 9
+
+static uint32_t bits(uint32_t value, unsigned int hi, unsigned int lo) {
+ return (value >> lo) & ((1 << (hi+1-lo)) - 1);
+}
+
+static uint32_t bit(uint32_t value, unsigned int b) {
+ return (value >> b) & 1;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static uint32_t ebreak(void) __attribute__ ((unused));
+static uint32_t ebreak(void) { return MATCH_EBREAK; }
+static uint32_t ebreak_c(void) __attribute__ ((unused));
+static uint32_t ebreak_c(void) { return MATCH_C_EBREAK; }
+
+static uint32_t dret(void) __attribute__ ((unused));
+static uint32_t dret(void) { return MATCH_DRET; }
+
+static uint32_t fence_i(void) __attribute__ ((unused));
+static uint32_t fence_i(void)
+{
+ return MATCH_FENCE_I;
+}
+
+/*
+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;
+}
+
+static uint32_t csrci(unsigned int csr, uint16_t imm) __attribute__ ((unused));
+static uint32_t csrci(unsigned int csr, uint16_t imm) {
+ return (csr << 20) |
+ (bits(imm, 4, 0) << 15) |
+ MATCH_CSRRCI;
+}
+
+static uint32_t li(unsigned int dest, uint16_t imm) __attribute__ ((unused));
+static uint32_t li(unsigned int dest, uint16_t imm)
+{
+ return addi(dest, 0, imm);
+}
+
+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;
+}
+
+static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm) __attribute__ ((unused));
+static uint32_t ori(unsigned int dest, unsigned int src, uint16_t imm)
+{
+ return (bits(imm, 11, 0) << 20) |
+ (src << 15) |
+ (dest << 7) |
+ MATCH_ORI;
+}
+
+static uint32_t nop(void) __attribute__ ((unused));
+static uint32_t nop(void)
+{
+ return addi(0, 0, 0);
+}
+*/
+
+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;
+}
+
+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;
+}
diff --git a/riscv/processor.cc b/riscv/processor.cc
index 664b44d..1e3573d 100644
--- a/riscv/processor.cc
+++ b/riscv/processor.cc
@@ -7,7 +7,6 @@
#include "sim.h"
#include "mmu.h"
#include "disasm.h"
-#include "gdbserver.h"
#include <cinttypes>
#include <cmath>
#include <cstdlib>
@@ -22,7 +21,8 @@
processor_t::processor_t(const char* isa, sim_t* sim, uint32_t id,
bool halt_on_reset)
- : debug(false), sim(sim), ext(NULL), id(id), halt_on_reset(halt_on_reset)
+ : debug(false), halt_request(false), sim(sim), ext(NULL), id(id),
+ halt_on_reset(halt_on_reset)
{
parse_isa_string(isa);
register_base_instructions();
@@ -202,7 +202,7 @@ void processor_t::enter_debug_mode(uint8_t cause)
state.dcsr.prv = state.prv;
set_privilege(PRV_M);
state.dpc = state.pc;
- state.pc = DEBUG_ROM_START;
+ state.pc = debug_rom_entry();
}
void processor_t::take_trap(trap_t& t, reg_t epc)
@@ -215,6 +215,15 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
t.get_badaddr());
}
+ if (state.dcsr.cause) {
+ if (t.cause() == CAUSE_BREAKPOINT) {
+ state.pc = debug_rom_entry();
+ } else {
+ state.pc = DEBUG_ROM_TVEC;
+ }
+ return;
+ }
+
if (t.cause() == CAUSE_BREAKPOINT && (
(state.prv == PRV_M && state.dcsr.ebreakm) ||
(state.prv == PRV_H && state.dcsr.ebreakh) ||
@@ -224,11 +233,6 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
return;
}
- if (state.dcsr.cause) {
- state.pc = DEBUG_ROM_EXCEPTION;
- return;
- }
-
// by default, trap to M-mode, unless delegated to S-mode
reg_t bit = t.cause();
reg_t deleg = state.medeleg;
@@ -270,9 +274,23 @@ void processor_t::take_trap(trap_t& t, reg_t epc)
void processor_t::disasm(insn_t insn)
{
+ static uint64_t last_pc = 1, last_bits;
+ static uint64_t executions = 1;
+
uint64_t bits = insn.bits() & ((1ULL << (8 * insn_length(insn.bits()))) - 1);
- fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
- id, state.pc, bits, disassembler->disassemble(insn).c_str());
+ if (last_pc != state.pc || last_bits != bits) {
+ if (executions != 1) {
+ fprintf(stderr, "core %3d: Executed %" PRIx64 " times\n", id, executions);
+ }
+
+ fprintf(stderr, "core %3d: 0x%016" PRIx64 " (0x%08" PRIx64 ") %s\n",
+ id, state.pc, bits, disassembler->disassemble(insn).c_str());
+ last_pc = state.pc;
+ last_bits = bits;
+ executions = 1;
+ } else {
+ executions++;
+ }
}
int processor_t::paddr_bits()
@@ -597,19 +615,15 @@ reg_t processor_t::get_csr(int which)
{
uint32_t v = 0;
v = set_field(v, DCSR_XDEBUGVER, 1);
- v = set_field(v, DCSR_NDRESET, 0);
- v = set_field(v, DCSR_FULLRESET, 0);
- v = set_field(v, DCSR_PRV, state.dcsr.prv);
- v = set_field(v, DCSR_STEP, state.dcsr.step);
- v = set_field(v, DCSR_DEBUGINT, sim->debug_module.get_interrupt(id));
- v = set_field(v, DCSR_STOPCYCLE, 0);
- v = set_field(v, DCSR_STOPTIME, 0);
v = set_field(v, DCSR_EBREAKM, state.dcsr.ebreakm);
v = set_field(v, DCSR_EBREAKH, state.dcsr.ebreakh);
v = set_field(v, DCSR_EBREAKS, state.dcsr.ebreaks);
v = set_field(v, DCSR_EBREAKU, state.dcsr.ebreaku);
- v = set_field(v, DCSR_HALT, state.dcsr.halt);
+ v = set_field(v, DCSR_STOPCYCLE, 0);
+ v = set_field(v, DCSR_STOPTIME, 0);
v = set_field(v, DCSR_CAUSE, state.dcsr.cause);
+ v = set_field(v, DCSR_STEP, state.dcsr.step);
+ v = set_field(v, DCSR_PRV, state.dcsr.prv);
return v;
}
case CSR_DPC:
diff --git a/riscv/processor.h b/riscv/processor.h
index 7e9e932..071f458 100644
--- a/riscv/processor.h
+++ b/riscv/processor.h
@@ -8,6 +8,7 @@
#include <string>
#include <vector>
#include <map>
+#include "debug_rom/debug_rom_defines.h"
class processor_t;
class mmu_t;
@@ -191,6 +192,14 @@ public:
bool debug;
// When true, take the slow simulation path.
bool slow_path();
+ bool halted() { return state.dcsr.cause ? true : false; }
+ bool halt_request;
+ // The unique debug rom address that this hart jumps to when entering debug
+ // mode. Rely on the fact that spike hart IDs start at 0 and are consecutive.
+ uint32_t debug_rom_entry() {
+ fprintf(stderr, "Debug_rom_entry called for id %d = %x\n", id, DEBUG_ROM_ENTRY + 4*id);
+ return DEBUG_ROM_ENTRY + 4 * id;
+ }
// Return the index of a trigger that matched, or -1.
inline int trigger_match(trigger_operation_t operation, reg_t address, reg_t data)
diff --git a/riscv/remote_bitbang.cc b/riscv/remote_bitbang.cc
new file mode 100644
index 0000000..21306dd
--- /dev/null
+++ b/riscv/remote_bitbang.cc
@@ -0,0 +1,180 @@
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstdio>
+
+#include "remote_bitbang.h"
+
+#if 1
+# define D(x) x
+#else
+# define D(x)
+#endif
+
+/////////// remote_bitbang_t
+
+remote_bitbang_t::remote_bitbang_t(uint16_t port, jtag_dtm_t *tap) :
+ tap(tap),
+ socket_fd(0),
+ client_fd(0),
+ recv_start(0),
+ recv_end(0)
+{
+ socket_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (socket_fd == -1) {
+ fprintf(stderr, "remote_bitbang failed to make socket: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+
+ fcntl(socket_fd, F_SETFL, O_NONBLOCK);
+ int reuseaddr = 1;
+ if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
+ sizeof(int)) == -1) {
+ fprintf(stderr, "remote_bitbang failed setsockopt: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(port);
+
+ if (bind(socket_fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+ fprintf(stderr, "remote_bitbang failed to bind socket: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+
+ if (listen(socket_fd, 1) == -1) {
+ fprintf(stderr, "remote_bitbang failed to listen on socket: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+
+ socklen_t addrlen = sizeof(addr);
+ if (getsockname(socket_fd, (struct sockaddr *) &addr, &addrlen) == -1) {
+ fprintf(stderr, "remote_bitbang getsockname failed: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+
+ printf("Listening for remote bitbang connection on port %d.\n",
+ ntohs(addr.sin_port));
+ fflush(stdout);
+}
+
+void remote_bitbang_t::accept()
+{
+ client_fd = ::accept(socket_fd, NULL, NULL);
+ if (client_fd == -1) {
+ if (errno == EAGAIN) {
+ // No client waiting to connect right now.
+ } else {
+ fprintf(stderr, "failed to accept on socket: %s (%d)\n", strerror(errno),
+ errno);
+ abort();
+ }
+ } else {
+ fcntl(client_fd, F_SETFL, O_NONBLOCK);
+ }
+}
+
+void remote_bitbang_t::tick()
+{
+ if (client_fd > 0) {
+ execute_commands();
+ } else {
+ this->accept();
+ }
+}
+
+void remote_bitbang_t::execute_commands()
+{
+ static char send_buf[buf_size];
+ unsigned total_processed = 0;
+ bool quit = false;
+ bool in_rti = tap->state() == RUN_TEST_IDLE;
+ bool entered_rti = false;
+ while (1) {
+ if (recv_start < recv_end) {
+ unsigned send_offset = 0;
+ while (recv_start < recv_end) {
+ uint8_t command = recv_buf[recv_start];
+
+ switch (command) {
+ case 'B': /* fprintf(stderr, "*BLINK*\n"); */ break;
+ case 'b': /* fprintf(stderr, "_______\n"); */ break;
+ case 'r': tap->reset(); break;
+ case '0': tap->set_pins(0, 0, 0); break;
+ case '1': tap->set_pins(0, 0, 1); break;
+ case '2': tap->set_pins(0, 1, 0); break;
+ case '3': tap->set_pins(0, 1, 1); break;
+ case '4': tap->set_pins(1, 0, 0); break;
+ case '5': tap->set_pins(1, 0, 1); break;
+ case '6': tap->set_pins(1, 1, 0); break;
+ case '7': tap->set_pins(1, 1, 1); break;
+ case 'R': send_buf[send_offset++] = tap->tdo() ? '1' : '0'; break;
+ case 'Q': quit = true; break;
+ default:
+ fprintf(stderr, "remote_bitbang got unsupported command '%c'\n",
+ command);
+ }
+ recv_start++;
+ total_processed++;
+ if (!in_rti && tap->state() == RUN_TEST_IDLE) {
+ entered_rti = true;
+ break;
+ }
+ in_rti = false;
+ }
+ unsigned sent = 0;
+ while (sent < send_offset) {
+ ssize_t bytes = write(client_fd, send_buf + sent, send_offset);
+ if (bytes == -1) {
+ fprintf(stderr, "failed to write to socket: %s (%d)\n", strerror(errno), errno);
+ abort();
+ }
+ sent += bytes;
+ }
+ }
+
+ if (total_processed > buf_size || quit || entered_rti) {
+ // Don't go forever, because that could starve the main simulation.
+ break;
+ }
+
+ recv_start = 0;
+ recv_end = read(client_fd, recv_buf, buf_size);
+
+ if (recv_end == -1) {
+ if (errno == EAGAIN) {
+ break;
+ } else {
+ fprintf(stderr, "remote_bitbang failed to read on socket: %s (%d)\n",
+ strerror(errno), errno);
+ abort();
+ }
+ }
+
+ if (quit) {
+ fprintf(stderr, "Remote Bitbang received 'Q'\n");
+ }
+
+ if (recv_end == 0 || quit) {
+ // The remote disconnected.
+ fprintf(stderr, "Received nothing. Quitting.\n");
+ close(client_fd);
+ client_fd = 0;
+ break;
+ }
+ }
+}
diff --git a/riscv/remote_bitbang.h b/riscv/remote_bitbang.h
new file mode 100644
index 0000000..1db4d55
--- /dev/null
+++ b/riscv/remote_bitbang.h
@@ -0,0 +1,34 @@
+#ifndef REMOTE_BITBANG_H
+#define REMOTE_BITBANG_H
+
+#include <stdint.h>
+
+#include "jtag_dtm.h"
+
+class remote_bitbang_t
+{
+public:
+ // Create a new server, listening for connections from localhost on the given
+ // port.
+ remote_bitbang_t(uint16_t port, jtag_dtm_t *tap);
+
+ // Do a bit of work.
+ void tick();
+
+private:
+ jtag_dtm_t *tap;
+
+ int socket_fd;
+ int client_fd;
+
+ static const ssize_t buf_size = 64 * 1024;
+ char recv_buf[buf_size];
+ ssize_t recv_start, recv_end;
+
+ // Check for a client connecting, and accept if there is one.
+ void accept();
+ // Execute any commands the client has for us.
+ void execute_commands();
+};
+
+#endif
diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in
index 5508a29..05e316a 100644
--- a/riscv/riscv.mk.in
+++ b/riscv/riscv.mk.in
@@ -23,8 +23,9 @@ riscv_hdrs = \
rocc.h \
insn_template.h \
mulhi.h \
- gdbserver.h \
debug_module.h \
+ remote_bitbang.h \
+ jtag_dtm.h \
riscv_precompiled_hdrs = \
insn_template.h \
@@ -45,8 +46,9 @@ riscv_srcs = \
devices.cc \
rom.cc \
clint.cc \
- gdbserver.cc \
debug_module.cc \
+ remote_bitbang.cc \
+ jtag_dtm.cc \
$(riscv_gen_srcs) \
riscv_test_srcs =
diff --git a/riscv/sim.cc b/riscv/sim.cc
index edf0819..42d60a1 100644
--- a/riscv/sim.cc
+++ b/riscv/sim.cc
@@ -2,7 +2,7 @@
#include "sim.h"
#include "mmu.h"
-#include "gdbserver.h"
+#include "remote_bitbang.h"
#include <map>
#include <iostream>
#include <sstream>
@@ -26,16 +26,16 @@ static void handle_signal(int sig)
sim_t::sim_t(const char* isa, size_t nprocs, bool halted, reg_t start_pc,
std::vector<std::pair<reg_t, mem_t*>> mems,
const std::vector<std::string>& args)
- : htif_t(args), mems(mems), procs(std::max(nprocs, size_t(1))),
+ : htif_t(args), debug_module(this), mems(mems), procs(std::max(nprocs, size_t(1))),
start_pc(start_pc),
- current_step(0), current_proc(0), debug(false), gdbserver(NULL)
+ current_step(0), current_proc(0), debug(false), remote_bitbang(NULL)
{
signal(SIGINT, &handle_signal);
for (auto& x : mems)
bus.add_device(x.first, x.second);
- bus.add_device(DEBUG_START, &debug_module);
+ debug_module.add_device(&bus);
debug_mmu = new mmu_t(this, NULL);
@@ -70,8 +70,8 @@ void sim_t::main()
interactive();
else
step(INTERLEAVE);
- if (gdbserver) {
- gdbserver->handle();
+ if (remote_bitbang) {
+ remote_bitbang->tick();
}
}
}
diff --git a/riscv/sim.h b/riscv/sim.h
index 421f5c2..9372cc1 100644
--- a/riscv/sim.h
+++ b/riscv/sim.h
@@ -13,7 +13,7 @@
#include <memory>
class mmu_t;
-class gdbserver_t;
+class remote_bitbang_t;
// this class encapsulates the processors and memory in a RISC-V machine.
class sim_t : public htif_t
@@ -30,9 +30,14 @@ public:
void set_log(bool value);
void set_histogram(bool value);
void set_procs_debug(bool value);
- void set_gdbserver(gdbserver_t* gdbserver) { this->gdbserver = gdbserver; }
+ void set_remote_bitbang(remote_bitbang_t* remote_bitbang) {
+ this->remote_bitbang = remote_bitbang;
+ }
const char* get_dts() { if (dts.empty()) reset(); return dts.c_str(); }
processor_t* get_core(size_t i) { return procs.at(i); }
+ unsigned nprocs() const { return procs.size(); }
+
+ debug_module_t debug_module;
private:
std::vector<std::pair<reg_t, mem_t*>> mems;
@@ -43,7 +48,6 @@ private:
std::unique_ptr<rom_device_t> boot_rom;
std::unique_ptr<clint_t> clint;
bus_t bus;
- debug_module_t debug_module;
processor_t* get_core(const std::string& i);
void step(size_t n); // step through simulation
@@ -55,7 +59,7 @@ private:
bool debug;
bool log;
bool histogram_enabled; // provide a histogram of PCs
- gdbserver_t* gdbserver;
+ remote_bitbang_t* remote_bitbang;
// memory-mapped I/O routines
char* addr_to_mem(reg_t addr);
@@ -87,7 +91,6 @@ private:
friend class processor_t;
friend class mmu_t;
- friend class gdbserver_t;
// htif
friend void sim_thread_main(void*);