diff options
author | Palmer Dabbelt <palmer@dabbelt.com> | 2017-05-16 09:33:40 -0700 |
---|---|---|
committer | Palmer Dabbelt <palmer@dabbelt.com> | 2017-05-16 12:35:49 -0700 |
commit | a1f754b2f0ec5fe72c86d6916d7c603e7727e68e (patch) | |
tree | 9a1c281461503845ecd4cde904852335e24dbe52 /riscv | |
parent | e465de145c69c28230b5c73cc58f96fd9bd04419 (diff) | |
parent | 9b539c8f0ee5075cbc3a0505d7fb6be39e9d7352 (diff) | |
download | spike-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.h | 1413 | ||||
-rw-r--r-- | riscv/debug_module.cc | 442 | ||||
-rw-r--r-- | riscv/debug_module.h | 117 | ||||
-rw-r--r-- | riscv/decode.h | 15 | ||||
-rw-r--r-- | riscv/execute.cc | 19 | ||||
-rw-r--r-- | riscv/gdbserver.cc | 2163 | ||||
-rw-r--r-- | riscv/gdbserver.h | 266 | ||||
-rw-r--r-- | riscv/jtag_dtm.cc | 180 | ||||
-rw-r--r-- | riscv/jtag_dtm.h | 61 | ||||
-rw-r--r-- | riscv/opcodes.h | 244 | ||||
-rw-r--r-- | riscv/processor.cc | 50 | ||||
-rw-r--r-- | riscv/processor.h | 9 | ||||
-rw-r--r-- | riscv/remote_bitbang.cc | 180 | ||||
-rw-r--r-- | riscv/remote_bitbang.h | 34 | ||||
-rw-r--r-- | riscv/riscv.mk.in | 6 | ||||
-rw-r--r-- | riscv/sim.cc | 12 | ||||
-rw-r--r-- | riscv/sim.h | 13 |
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*); |