aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/checkpatch.yml32
-rw-r--r--.github/workflows/linux-build.yml61
-rw-r--r--.github/workflows/snapshot.yml6
-rw-r--r--.github/workflows/spike-openocd-tests.yml136
-rw-r--r--HACKING26
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac3
-rw-r--r--doc/openocd.texi290
-rwxr-xr-xgit-hooks/commit-msg191
-rwxr-xr-xgit-hooks/pre-push3
-rw-r--r--src/flash/nor/tcl.c3
-rw-r--r--src/helper/Makefile.am2
-rw-r--r--src/helper/base64.c157
-rw-r--r--src/helper/base64.h17
-rw-r--r--src/jtag/drivers/ftdi.c448
-rw-r--r--src/rtos/FreeRTOS.c763
-rw-r--r--src/rtos/hwthread.c49
-rw-r--r--src/rtos/rtos.c266
-rw-r--r--src/rtos/rtos.h52
-rw-r--r--src/rtos/rtos_standard_stackings.c213
-rw-r--r--src/rtos/rtos_standard_stackings.h5
-rw-r--r--src/server/gdb_server.c176
-rw-r--r--src/server/server.h1
-rw-r--r--src/target/breakpoints.c180
-rw-r--r--src/target/breakpoints.h2
-rw-r--r--src/target/dsp563xx.c3
-rw-r--r--src/target/image.c3
-rw-r--r--src/target/riscv/Makefile.am5
-rw-r--r--src/target/riscv/batch.c219
-rw-r--r--src/target/riscv/batch.h61
-rw-r--r--src/target/riscv/debug_defines.c3852
-rw-r--r--src/target/riscv/debug_defines.h1859
-rw-r--r--src/target/riscv/debug_reg_printer.c109
-rw-r--r--src/target/riscv/debug_reg_printer.h35
-rw-r--r--src/target/riscv/encoding.h255
-rw-r--r--src/target/riscv/field_helpers.h47
-rw-r--r--src/target/riscv/gdb_regs.h13
-rw-r--r--src/target/riscv/opcodes.h13
-rw-r--r--src/target/riscv/program.c101
-rw-r--r--src/target/riscv/program.h27
-rw-r--r--src/target/riscv/riscv-011.c176
-rw-r--r--src/target/riscv/riscv-013.c4532
-rw-r--r--src/target/riscv/riscv.c5910
-rw-r--r--src/target/riscv/riscv.h233
-rw-r--r--src/target/riscv/riscv_semihosting.c60
-rw-r--r--src/target/target.c116
-rw-r--r--src/target/target.h12
-rw-r--r--tcl/board/esp32c2-ftdi.cfg21
-rw-r--r--tcl/board/esp32c3-builtin.cfg15
-rw-r--r--tcl/board/esp32c3-ftdi.cfg21
-rw-r--r--tcl/board/esp32c6-builtin.cfg15
-rw-r--r--tcl/board/esp32h2-builtin.cfg15
-rw-r--r--tcl/board/sifive-e31arty-cjtag.cfg23
-rw-r--r--tcl/board/sifive-e31arty-onboard-ftdi.cfg25
-rw-r--r--tcl/board/sifive-hifive1-revb.cfg3
-rw-r--r--tcl/interface/ftdi/arty-onboard-ftdi.cfg7
-rw-r--r--tcl/interface/ftdi/digilent-hs2-cjtag.cfg17
-rw-r--r--tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg27
-rw-r--r--tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg22
-rw-r--r--tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg27
-rw-r--r--tcl/target/1986Be1T.cfg (renamed from tcl/target/1986ве1т.cfg)0
-rw-r--r--tcl/target/K1879x61R.cfg (renamed from tcl/target/к1879xб1я.cfg)0
-rw-r--r--tcl/target/esp32c2.cfg117
-rw-r--r--tcl/target/esp32c3.cfg81
-rw-r--r--tcl/target/esp32c6.cfg142
-rw-r--r--tcl/target/esp32h2.cfg122
-rw-r--r--tcl/target/esp_common.cfg29
-rw-r--r--tcl/target/gd32vf103.cfg90
-rwxr-xr-xtools/filter_openocd_log.py120
69 files changed, 16440 insertions, 5228 deletions
diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml
new file mode 100644
index 0000000..00d1d71
--- /dev/null
+++ b/.github/workflows/checkpatch.yml
@@ -0,0 +1,32 @@
+on: pull_request
+
+name: Check Code Style (checkpatch)
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ env:
+ DL_DIR: ../downloads
+ BUILD_DIR: ../build
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Checkout Base
+ run: |
+ git fetch origin ${{ github.event.pull_request.base.ref }}
+ echo "The current base for checkpatch is: $(git show FETCH_HEAD --oneline --raw)"
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install patchutils python3-ply python3-git
+ - name: Run checkpatch
+ run: |
+ git diff --patch FETCH_HEAD \
+ | filterdiff \
+ -x "a/src/jtag/drivers/libjaylink/*" \
+ -x "a/tools/git2cl/*" \
+ -x "a/.github/*" \
+ -x "a/HACKING" \
+ | ./tools/scripts/checkpatch.pl --no-signoff -
diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml
new file mode 100644
index 0000000..0a3334f
--- /dev/null
+++ b/.github/workflows/linux-build.yml
@@ -0,0 +1,61 @@
+on: pull_request
+
+name: Linux Build
+
+jobs:
+ # 32-bit, clang
+ build32:
+ runs-on: ubuntu-20.04
+ env:
+ CFLAGS: -m32
+ CC: clang
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install clang gcc-multilib
+ - run: ./bootstrap
+ - run: ./configure --enable-remote-bitbang --enable-jtag_vpi --disable-target64
+ - run: make -j`nproc`
+ - run: file src/openocd | grep 32-bit
+ - run: src/openocd --version
+
+ # 64-bit, gcc
+ build64:
+ runs-on: ubuntu-latest
+ env:
+ CFLAGS: -m64
+ CC: gcc
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+ - name: Configure environment
+ run: |
+ TAG=$(git rev-parse --short HEAD)
+ echo "TAG=$TAG" >> $GITHUB_OUTPUT
+ echo "NAME=openocd64-$TAG" >> $GITHUB_ENV
+ - name: Install required packages (apt-get)
+ run: |
+ sudo apt-get update
+ sudo apt-get install libusb-1.0-0 libusb-1.0-0-dev
+ - run: ./bootstrap
+ - run: ./configure --enable-remote-bitbang --enable-jtag_vpi --enable-ftdi-cjtag --prefix /tmp/${{ env.NAME }}
+ - run: make -j`nproc`
+ - name: Check that we built something
+ run: |
+ file src/openocd | grep 64-bit
+ src/openocd --version
+ - name: Package
+ # Package into tgz so that github stores a compressed artifact, even
+ # though it zips that artifact again before it sends it back to be
+ # downloaded.
+ run: |
+ make install
+ tar zcvf ${{ env.NAME }}.tgz -C /tmp ${{ env.NAME }}
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ env.NAME }}
+ path: ${{ env.NAME }}.tgz
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index f5cf564..4a1866b 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -18,7 +18,7 @@ jobs:
sudo apt-get update
sudo apt-get install autotools-dev autoconf automake libtool pkg-config cmake texinfo texlive g++-mingw-w64-i686
- name: Checkout Code
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
- run: ./bootstrap
- name: Prepare libusb1
env:
@@ -102,11 +102,11 @@ jobs:
echo "IS_PRE_RELEASE=$IS_PRE_RELEASE" >> $GITHUB_ENV
echo "ARTIFACT_PATH=$PWD/$ARTIFACT" >> $GITHUB_ENV
- name: Publish OpenOCD packaged for windows
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
path: ${{ env.ARTIFACT_PATH }}
- name: Delete 'latest' Release
- uses: dev-drprasad/delete-tag-and-release@v0.2.1
+ uses: dev-drprasad/delete-tag-and-release@v1.1
with:
delete_release: true
tag_name: ${{ env.RELEASE_NAME }}
diff --git a/.github/workflows/spike-openocd-tests.yml b/.github/workflows/spike-openocd-tests.yml
new file mode 100644
index 0000000..71bf29a
--- /dev/null
+++ b/.github/workflows/spike-openocd-tests.yml
@@ -0,0 +1,136 @@
+# Build Spike and run a couple of debug tests.
+
+name: Test OpenOCD against 2 spike configurations
+
+env:
+ SPIKE_REPO: https://github.com/riscv-software-src/riscv-isa-sim.git
+ SPIKE_REV: master
+ RISCV_TESTS_REPO: https://github.com/riscv-software-src/riscv-tests.git
+ RISCV_TESTS_REV: master
+ TOOLCHAIN_URL: https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v12.2.0-1/xpack-riscv-none-elf-gcc-12.2.0-1-linux-x64.tar.gz
+
+on:
+ # Run on merges to master to populate the cache with entities that are
+ # accessible by every pull request.
+ push:
+ branches:
+ - riscv
+ pull_request:
+ types: [synchronize, opened, reopened]
+
+# There is some commented out code below that would be useful in adding this
+# workflow to other repos. Ideally we can come up with something that would
+# leave this file almost identical between repos, so they can all easily run
+# this test suite.
+
+jobs:
+ test:
+ name: Test debug (Ubuntu)
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install packages
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y device-tree-compiler build-essential
+
+ - name: Get revisions of dependencies
+ run: |
+ SPIKE_COMMIT=$( git ls-remote "$SPIKE_REPO" master | awk '{ print $1; }' )
+ RISC_V_TESTS_COMMIT=$( git ls-remote "$RISCV_TESTS_REPO" master | awk '{ print $1; }' )
+ echo "Revison of Spike: $SPIKE_COMMIT"
+ echo "Revision of RISC-V tests: $RISC_V_TESTS_COMMIT"
+ # Save for later use
+ echo "SPIKE_COMMIT=$SPIKE_COMMIT" >> $GITHUB_ENV
+ echo "RISC_V_TESTS_COMMIT=$RISC_V_TESTS_COMMIT" >> $GITHUB_ENV
+
+ - name: Get the toolchain from cache (if available)
+ id: cache-toolchain
+ uses: actions/cache@v4
+ with:
+ path: /opt/riscv/toolchain
+ key: "toolchain-${{env.TOOLCHAIN_URL}}"
+
+ - name: Get spike from cache (if available)
+ id: cache-spike
+ uses: actions/cache@v4
+ with:
+ path: /opt/riscv/spike
+ key: "spike-${{env.SPIKE_COMMIT}}"
+
+ - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }}
+ name: Download Toolchain (if not cached)
+ run: |
+ mkdir -p /opt/riscv/toolchain
+ wget --progress=dot:giga $TOOLCHAIN_URL -O /tmp/toolchain.tar.gz
+
+ - if: ${{ steps.cache-toolchain.outputs.cache-hit != 'true' }}
+ name: Install Toolchain (if not cached)
+ run: tar zxf /tmp/toolchain.tar.gz --strip-components=1 -C /opt/riscv/toolchain
+
+ - if: ${{ steps.cache-spike.outputs.cache-hit != 'true' }}
+ name: Download Spike source (if not cached)
+ run: |
+ git clone "$SPIKE_REPO"
+ cd riscv-isa-sim
+ git checkout "$SPIKE_COMMIT"
+ git submodule update --init --recursive
+
+ - if: ${{ steps.cache-spike.outputs.cache-hit != 'true' }}
+ name: Build Spike (if not cached)
+ run: |
+ cd riscv-isa-sim
+ mkdir build && cd build
+ ../configure --prefix=/opt/riscv/spike
+ make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+ make install
+
+ - name: Build OpenOCD
+ run: |
+ #cd riscv-openocd
+ ./bootstrap
+ ./configure --prefix=/opt/riscv
+ make -j"$(nproc 2> /dev/null || sysctl -n hw.ncpu)"
+ ls -l src/openocd
+
+# - name: Download OpenOCD
+# run: |
+# git clone --recurse-submodules https://github.com/riscv/riscv-openocd.git
+# cd riscv-openocd
+# git checkout 43ea20dfbb6c815004a51106a3b2009d7f6c4940
+
+ - name: Download Tests
+ run: |
+ git clone "$RISCV_TESTS_REPO"
+ cd riscv-tests
+ git checkout "$RISCV_TESTS_REV"
+ git submodule update --init --recursive
+
+ - name: Run Spike32 Tests
+ id: spike32-tests
+ run: |
+ cd riscv-tests/debug
+ ./gdbserver.py targets/RISC-V/spike32.py --print-failures \
+ --gcc /opt/riscv/toolchain/bin/riscv-none-elf-gcc \
+ --gdb /opt/riscv/toolchain/bin/riscv-none-elf-gdb \
+ --sim_cmd /opt/riscv/spike/bin/spike \
+ --server_cmd $GITHUB_WORKSPACE/src/openocd
+
+ - name: Run Spike64-2 Tests
+ if: success() || steps.spike32-tests.conclusion == 'failure'
+ run: |
+ cd riscv-tests/debug
+ ./gdbserver.py targets/RISC-V/spike64-2.py --print-failures \
+ --gcc /opt/riscv/toolchain/bin/riscv-none-elf-gcc \
+ --gdb /opt/riscv/toolchain/bin/riscv-none-elf-gdb \
+ --sim_cmd /opt/riscv/spike/bin/spike \
+ --server_cmd $GITHUB_WORKSPACE/src/openocd
+
+ - name: Archive test logs
+ # Proceed even if there was a failed test
+ if: ${{ success() || failure() }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-logs
+ path: riscv-tests/debug/logs
diff --git a/HACKING b/HACKING
index 74cbe02..46db3b8 100644
--- a/HACKING
+++ b/HACKING
@@ -77,6 +77,32 @@ patch:
src/openocd -s ../tcl -f /path/to/openocd.cfg
@endcode
+- Runtime coverage testing
+
+ Apply the following patch to prevent OpenOCD from killing itself:
+ @code
+--- a/src/openocd.c
++++ b/src/openocd.c
+@@ -372,8 +372,6 @@ int openocd_main(int argc, char *argv[])
+
+ if (ERROR_FAIL == ret)
+ return EXIT_FAILURE;
+- else if (ERROR_OK != ret)
+- exit_on_signal(ret);
+
+ return ret;
+ }
+ @endcode
+
+ Configure your OpenOCD binary with coverage support as follows:
+ @code
+ LDFLAGS="-fprofile-arcs -ftest-coverage"
+ CFLAGS="-fprofile-arcs -ftest-coverage" ./configure
+ @endcode
+
+ Now every time OpenOCD is run, coverage info in your build directory is
+ updated. Running `gcov src/path/file.c` will generate a report.
+
- Sparse Static Analyzer
Using this tool allows identifying some bug in C code.
diff --git a/Makefile.am b/Makefile.am
index 647b571..f9e0d6b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,13 +37,15 @@ DISTCLEANFILES += jimtcl/jsmn/jsmn.o
endif
# common flags used in openocd build
-AM_CFLAGS = $(GCC_WARNINGS)
+AM_CFLAGS = $(GCC_WARNINGS)\
+ -DFD_SETSIZE=128
AM_CPPFLAGS = $(HOST_CPPFLAGS)\
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
-DPKGDATADIR=\"$(pkgdatadir)\" \
- -DBINDIR=\"$(bindir)\"
+ -DBINDIR=\"$(bindir)\"\
+ -DFD_SETSIZE=128
if INTERNAL_JIMTCL
AM_CPPFLAGS += -I$(top_srcdir)/jimtcl \
diff --git a/configure.ac b/configure.ac
index becc531..c6e02b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -115,6 +115,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
+ [[ftdi_cjtag], [cJTAG (OScan1, JScan3) tunneled thru MPSSE], [FTDI_CJTAG]],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
@@ -386,7 +387,7 @@ AC_ARG_ENABLE([internal-libjaylink],
AC_ARG_ENABLE([remote-bitbang],
AS_HELP_STRING([--enable-remote-bitbang], [Enable building support for the Remote Bitbang driver]),
- [build_remote_bitbang=$enableval], [build_remote_bitbang=no])
+ [build_remote_bitbang=$enableval], [build_remote_bitbang=yes])
AS_CASE(["${host_cpu}"],
[i?86|x86*], [],
diff --git a/doc/openocd.texi b/doc/openocd.texi
index 5eef81e..8ffbcf3 100644
--- a/doc/openocd.texi
+++ b/doc/openocd.texi
@@ -2671,6 +2671,35 @@ minimal impact on the target system. Avoid floating inputs, conflicting outputs
and initially asserted reset signals.
@end deffn
+@deffn {Command} {ftdi oscan1_mode} on|off
+Enable or disable OScan1 mode. This mode is intended for use with an adapter,
+such as the ARM-JTAG-SWD by Olimex, that sits in between the FTDI chip and the
+target. The cJTAG prococol is composed of two wires: TCKC (clock) and TMSC (data).
+TMSC is a bidirectional signal which is time-multiplexed alternating TDI, TMS and
+TDO. The multiplexing is achieved by a tri-state buffer which puts TMSC in Hi-Z
+when the device is supposed to take the control of the line (TDO phase).
+
+The ARM-JTAG-SWD adapter uses standard TRST and TMS signals to control TMSC
+direction. TRST is used by the adapter as selector for the multiplexers which set
+the JTAG probe in 2-wire mode. Whatever signal is used for this purpose, it must
+be defined with the name JTAG_SEL using @command{ftdi layout_signal}. JTAG_SEL is
+set to 0 during OScan1 initialization.
+
+Some JTAG probes like the Digilent JTAG-HS2, support cJTAG by using a
+separate pin to control when TMS is driven onto TMSC. You can use such
+probes by defining the signal TMSC_EN using
+@command{ftdi layout_signal TMSC_EN -data <mask>}.
+@end deffn
+
+@deffn {Command} {ftdi jscan3_mode} on|off
+Enable or disable JScan3 mode. This mode uses the classic 4-wire JTAG protocol
+in chips whose JTAG port is only compliant with the cJTAG standard (IEEE 1149.7).
+
+Since cJTAG needs a 2-wire escape sequence to select the operating mode,
+a cJTAG adapter like ARM-JTAG-SWD by Olimex is still required. This means
+that a cJTAG probe configuration script must be used too.
+@end deffn
+
@deffn {Command} {ftdi layout_signal} name [@option{-data}|@option{-ndata} data_mask] [@option{-input}|@option{-ninput} input_mask] [@option{-oe}|@option{-noe} oe_mask] [@option{-alias}|@option{-nalias} name]
Creates a signal with the specified @var{name}, controlled by one or more FTDI
GPIO pins via a range of possible buffer connections. The masks are FTDI GPIO
@@ -11107,11 +11136,9 @@ an error if current CPU does not support DSP.
@section RISC-V Architecture
@uref{http://riscv.org/, RISC-V} is a free and open ISA. OpenOCD supports JTAG
-debug of RV32 and RV64 cores in heterogeneous multicore systems of up to 32
-harts. (It's possible to increase this limit to 1024 by changing
-RISCV_MAX_HARTS in riscv.h.) OpenOCD primarily supports 0.13 of the RISC-V
-Debug Specification, but there is also support for legacy targets that
-implement version 0.11.
+debug of RV32 and RV64 cores in heterogeneous multicore systems of up to 2^20
+harts. OpenOCD primarily supports 0.13 of the RISC-V Debug Specification,
+but there is also support for legacy targets that implement version 0.11.
@subsection RISC-V Terminology
@@ -11158,6 +11185,13 @@ follows:
@subsection RISC-V Debug Configuration Commands
+@deffn {Command} {riscv dump_sample_buf} [base64]
+Dump and clear the contents of the sample buffer. Which samples are collected
+is configured with @code{riscv memory_sample}. If the optional base64
+argument is passed, the raw buffer is dumped in base64 format, so that
+external tools can gather the data efficiently.
+@end deffn
+
@deffn {Config Command} {riscv expose_csrs} n[-m|=name] [...]
Configure which CSRs to expose in addition to the standard ones. The CSRs to expose
can be specified as individual register numbers or register ranges (inclusive). For the
@@ -11172,13 +11206,13 @@ CSRs.
@example
# Expose a single RISC-V CSR number 128 under the name "csr128":
-$_TARGETNAME expose_csrs 128
+riscv expose_csrs 128
# Expose multiple RISC-V CSRs 128..132 under names "csr128" through "csr132":
-$_TARGETNAME expose_csrs 128-132
+riscv expose_csrs 128-132
# Expose a single RISC-V CSR number 1996 under custom name "csr_myregister":
-$_TARGETNAME expose_csrs 1996=myregister
+riscv expose_csrs 1996=myregister
@end example
@end deffn
@@ -11206,8 +11240,43 @@ $_TARGETNAME expose_custom 32=myregister
@end example
@end deffn
+@deffn {Config Command} {riscv hide_csrs} n[-m] [,n1[-m1]] [...]
+The RISC-V Specification defines many CSRs, and we may want to avoid showing
+each CSR to the user, as they may not be relevant to the task at hand. For
+example, we may choose not to show trigger or PMU registers for simple
+debugging scenarios. This command allows to mark individual registers or
+register ranges (inclusive) as "hidden". Such hidden registers won't be
+displayed in GDB or @code{reg} command output.
+
+@example
+
+# Hide range of RISC-V CSRs
+# CSR_TSELECT - 1952 and CSR_TDATA1 - 1953
+$_TARGETNAME riscv hide_csrs 1952-1953
+
+@end example
+@end deffn
+
+@deffn {Command} {riscv memory_sample} bucket address|clear [size=4]
+Configure OpenOCD to frequently read size bytes at the given addresses.
+Execute the command with no arguments to see the current configuration. Use
+clear to stop using a given bucket.
+
+OpenOCD will allocate a 1MB sample buffer, and when it fills up no more
+samples will be collected until it is emptied with @code{riscv
+dump_sample_buf}.
+@end deffn
+
+@deffn {Command} {riscv repeat_read} count address [size=4]
+Quickly read count words of the given size from address. This can be useful
+to read out a buffer that's memory-mapped to be accessed through a single
+address, or to sample a changing value in a memory-mapped device.
+@end deffn
+
@deffn {Command} {riscv info}
-Displays some information OpenOCD detected about the target.
+Displays some information OpenOCD detected about the target. Output's format
+allows to use it directly with TCL's `array set` function. In case obtaining an
+info point failed, the corresponding value is displayed as "unavailable".
@end deffn
@deffn {Command} {riscv reset_delays} [wait]
@@ -11278,26 +11347,92 @@ When utilizing version 0.11 of the RISC-V Debug Specification,
and DBUS registers, respectively.
@end deffn
+@deffn {Command} {riscv smp} [on|off]
+Display, enable or disable SMP handling mode. This command is needed only if
+user wants to temporary @b{disable} SMP handling for an existing SMP group
+(see @code{aarch64 smp} for additional information). To define an SMP
+group the command @code{target smp} should be used.
+@end deffn
+
+@deffn {Command} {riscv smp_gdb} [core_id]
+Display/set the current core displayed in GDB. This is needed only if
+@code{riscv smp} was used.
+@end deffn
+
@deffn {Command} {riscv use_bscan_tunnel} value
-Enable or disable use of a BSCAN tunnel to reach DM. Supply the width of
-the DM transport TAP's instruction register to enable. Supply a value of 0 to disable.
+Enable or disable use of a BSCAN tunnel to reach the Debug Module. Supply the
+width of the DM transport TAP's instruction register to enable. Supply a
+value of 0 to disable.
+
+This BSCAN tunnel interface is specific to SiFive IP. Anybody may implement
+it, but currently there is no good documentation on it. In a nutshell, this
+feature scans USER4 into a Xilinx TAP to select the tunnel device (assuming
+hardware is present and it is hooked up to the Xilinx USER4 IR) and
+encapsulates a tunneled scan directive into a DR scan into the Xilinx TAP. A
+tunneled DR scan consists of:
+@enumerate
+@item 1 bit that selects IR when 0, or DR when 1
+@item 7 bits that encode the width of the desired tunneled scan
+@item A width+1 stream of bits for the tunneled TDI. The plus one is because there is a one-clock skew between TDI of Xilinx chain and TDO from tunneled chain.
+@item 3 bits of zero that the tunnel uses to go back to idle state.
+@end enumerate
+
+@end deffn
+
+@deffn {Command} {riscv set_bscan_tunnel_ir} value
+Allows the use_bscan_tunnel feature to target non Xilinx device by
+specifying the JTAG TAP IR used to access the bscan tunnel.
+@end deffn
+
+@deffn {Command} {riscv set_maskisr} [@option{off}|@option{steponly}]
+Selects whether interrupts will be disabled when single stepping. The default configuration is @option{off}.
+This feature is only useful on hardware that always steps into interrupts and doesn't support dcsr.stepie=0.
+Keep in mind, disabling the option does not guarantee that single stepping will go into interrupt handlers.
+To make that happen, dcsr.stepie would have to be written to 1 as well.
@end deffn
-@deffn {Command} {riscv set_ebreakm} on|off
+@deffn {Command} {riscv set_ebreakm} [on|off]
Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
-@deffn {Command} {riscv set_ebreaks} on|off
+@deffn {Command} {riscv set_ebreaks} [on|off]
Control dcsr.ebreaks. When on (default), S-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
-@deffn {Command} {riscv set_ebreaku} on|off
+@deffn {Command} {riscv set_ebreaku} [on|off]
Control dcsr.ebreaku. When on (default), U-mode ebreak instructions trap to
OpenOCD. When off, they generate a breakpoint exception handled internally.
@end deffn
+The commands below can be used to prevent OpenOCD from using certain RISC-V trigger features.
+For example in cases when there are known issues in the target hardware.
+
+@deffn {Command} {riscv set_enable_trigger_feature} [(@option{eq}|@option{napot}|@option{ge_lt}|@option{all}) (@option{wp}|@option{none})]
+Control which RISC-V trigger features can be used by OpenOCD placing watchpoints.
+All trigger features are allowed by default. Only new watchpoints, inserted after this command,
+are affected (watchpoints that were already placed before are not changed).
+
+The first argument selects one of the configurable RISC-V trigger features:
+
+@itemize @minus
+@item @option{eq}: Equality match trigger
+@item @option{napot}: NAPOT trigger
+@item @option{ge_lt}: Chained pair of `greater-equal` and `less-than` triggers
+@item @option{all}: All trigger features which were described above
+@end itemize
+
+The second argument configures how OpenOCD should use the selected trigger feature:
+
+@itemize @minus
+@item @option{wp}: Enable this trigger feature for watchpoints - allow OpenOCD to use it. (Default.)
+@item @option{none}: Disable the use of this trigger feature. OpenOCD will not attempt to use it.
+@end itemize
+
+With no parameters, prints current trigger features configuration.
+@end deffn
+
@subsection RISC-V Authentication Commands
The following commands can be used to authenticate to a RISC-V system. Eg. a
@@ -11308,27 +11443,130 @@ set challenge [riscv authdata_read]
riscv authdata_write [expr @{$challenge + 1@}]
@end example
-@deffn {Command} {riscv authdata_read}
-Return the 32-bit value read from authdata.
+@deffn {Command} {riscv authdata_read} [index=0]
+Return the 32-bit value read from authdata or authdata0 (index=0), or
+authdata1 (index=1).
@end deffn
-@deffn {Command} {riscv authdata_write} value
-Write the 32-bit value to authdata.
+@deffn {Command} {riscv authdata_write} [index=0] value
+Write the 32-bit value to authdata or authdata0 (index=0), or authdata1
+(index=1).
@end deffn
@subsection RISC-V DMI Commands
-The following commands allow direct access to the Debug Module Interface, which
-can be used to interact with custom debug features.
+The following commands allow for direct low-level access to the registers
+of the Debug Module (DM). They can be useful to access custom features in the DM.
+
+@deffn {Command} {riscv dm_read} reg_address
+Perform a 32-bit read from the register indicated by reg_address from the DM of the
+current target.
+@end deffn
+
+@deffn {Command} {riscv dm_write} reg_address value
+Write the 32-bit value to the register indicated by reg_address from the DM of the
+current target.
+@end deffn
+
+The following commands allow for direct low-level access to the Debug Module
+Interface (DMI). They can be useful to access any device that resides on the DMI.
@deffn {Command} {riscv dmi_read} address
-Perform a 32-bit DMI read at address, returning the value.
+Perform a 32-bit read from the given DMI address, returning the value.
@end deffn
@deffn {Command} {riscv dmi_write} address value
-Perform a 32-bit DMI write of value at address.
+Perform a 32-bit write to the given DMI address.
@end deffn
+@subsection RISC-V Trigger Commands
+
+The RISC-V Debug Specification defines several optional trigger types that don't
+map cleanly onto OpenOCD's notion of hardware breakpoints. For the types that
+the target supports, these commands let you
+set those triggers directly. (It's also possible to do so by writing the
+appropriate CSRs.)
+
+@deffn {Command} {riscv etrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] exception_codes
+Set an exception trigger (type 5) on the current target, which halts the target when it
+fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
+which execution modes the trigger fires in. @var{exception_codes} is a bit
+field, where each bit corresponds to an exception code in mcause (defined in the
+RISC-V Privileged Spec). The etrigger will fire on the exceptions whose bits are
+set in @var{exception_codes}.
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv etrigger clear}
+Clear the type 5 trigger that was set using @command{riscv etrigger set}.
+@end deffn
+
+@deffn {Command} {riscv icount set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{pending}] count
+Set an instruction count
+trigger (type 3) on the current target, which halts the target when it fires.
+@option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control which
+execution modes the trigger fires in. If [@option{pending}] is passed then the
+pending bit is set, which is unlikely to be useful unless you're debugging the
+hardware implementation of this trigger.
+@var{count} sets the number of instructions to execute before the trigger is
+taken.
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv icount clear}
+Clear the type 3 trigger that was set using @command{riscv icount set}.
+@end deffn
+
+@deffn {Command} {riscv itrigger set} [@option{m}] [@option{s}] [@option{u}] [@option{vs}] [@option{vu}] [@option{nmi}] mie_bits
+Set an interrupt trigger (type 4) on the current target, which halts the target when it
+fires. @option{m}, @option{s}, @option{u}, @option{vs}, and @option{vu} control
+which execution modes the trigger fires in. If [@option{nmi}] is passed then
+the trigger will fire on non-maskable interrupts in those modes. @var{mie_bits}
+controls which interrupts the trigger fires on, using the same bit assignments
+as in the mie CSR (defined in the RISC-V Privileged Spec).
+
+For details on this trigger type, see the RISC-V Debug Specification.
+@end deffn
+
+@deffn {Command} {riscv itrigger clear}
+Clear the type 4 trigger that was set using @command{riscv itrigger set}.
+@end deffn
+
+@subsection RISC-V Program Buffer Commands
+
+Program Buffer is an optional feature of RISC-V targets - it is a mechanism that debuggers
+can use to execute sequences of arbitrary instructions (small programs) on the target.
+For details on the Program Buffer, please refer to the RISC-V Debug Specification.
+
+@deffn {Command} {riscv exec_progbuf} inst1 [inst2 [... inst16]]
+Execute the given sequence of instructions on the target using the Program Buffer.
+The command can only be used on halted targets.
+
+The instructions @var{inst1} .. @var{inst16} shall be specified in their binary form
+(as 32-bit integers). In case a pair of compressed (16-bit) instructions is used,
+the first instruction should be placed to the lower 16-bits of the 32-bit value.
+The terminating @var{ebreak} instruction needs not be specified - it is added
+automatically if needed.
+@end deffn
+
+Examples:
+
+@example
+# Execute 32-bit instructions "fence rw,rw" (0x0330000f)
+# and "fence.i" (0x0000100f) using the Program Buffer,
+# in this order:
+
+riscv exec_progbuf 0x0330000f 0x0000100f
+
+# Execute 16-bit instructions "c.addi s0,s0,1" (0x0405)
+# and "c.add s1,s1,s0" (0x94a2) using the Program Buffer,
+# in this order:
+
+riscv exec_progbuf 0x94a20405
+@end example
+
@section ARC Architecture
@cindex ARC
@@ -12627,6 +12865,14 @@ contrib/rtos-helpers/FreeRTOS-openocd.c
contrib/rtos-helpers/uCOS-III-openocd.c
@end table
+@section RTOS Commands
+@cindex RTOS Commands
+
+@deffn {Config Command} {freertos_ticktype_size} (2|4|8)
+Pass the size (in bytes) of FreeRTOS TickType_t to OpenOCD. To make sure the
+calculation of offsets and sizes is correct. Defaults to 4.
+@end deffn
+
@anchor{usingopenocdsmpwithgdb}
@section Using OpenOCD SMP with GDB
@cindex SMP
diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg
new file mode 100755
index 0000000..99d96cd
--- /dev/null
+++ b/git-hooks/commit-msg
@@ -0,0 +1,191 @@
+#!/bin/sh
+# From Gerrit Code Review 2.13.5
+#
+# Part of Gerrit Code Review (https://www.gerritcodereview.com/)
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+unset GREP_OPTIONS
+
+CHANGE_ID_AFTER="Bug|Issue|Test|Feature|Fixes|Fixed"
+MSG="$1"
+
+# Check for, and add if missing, a unique Change-Id
+#
+add_ChangeId() {
+ clean_message=`sed -e '
+ /^diff --git .*/{
+ s///
+ q
+ }
+ /^Signed-off-by:/d
+ /^#/d
+ ' "$MSG" | git stripspace`
+ if test -z "$clean_message"
+ then
+ return
+ fi
+
+ # Do not add Change-Id to temp commits
+ if echo "$clean_message" | head -1 | grep -q '^\(fixup\|squash\)!'
+ then
+ return
+ fi
+
+ if test "false" = "`git config --bool --get gerrit.createChangeId`"
+ then
+ return
+ fi
+
+ # Does Change-Id: already exist? if so, exit (no change).
+ if grep -i '^Change-Id:' "$MSG" >/dev/null
+ then
+ return
+ fi
+
+ id=`_gen_ChangeId`
+ T="$MSG.tmp.$$"
+ AWK=awk
+ if [ -x /usr/xpg4/bin/awk ]; then
+ # Solaris AWK is just too broken
+ AWK=/usr/xpg4/bin/awk
+ fi
+
+ # Get core.commentChar from git config or use default symbol
+ commentChar=`git config --get core.commentChar`
+ commentChar=${commentChar:-#}
+
+ # How this works:
+ # - parse the commit message as (textLine+ blankLine*)*
+ # - assume textLine+ to be a footer until proven otherwise
+ # - exception: the first block is not footer (as it is the title)
+ # - read textLine+ into a variable
+ # - then count blankLines
+ # - once the next textLine appears, print textLine+ blankLine* as these
+ # aren't footer
+ # - in END, the last textLine+ block is available for footer parsing
+ $AWK '
+ BEGIN {
+ # while we start with the assumption that textLine+
+ # is a footer, the first block is not.
+ isFooter = 0
+ footerComment = 0
+ blankLines = 0
+ }
+
+ # Skip lines starting with commentChar without any spaces before it.
+ /^'"$commentChar"'/ { next }
+
+ # Skip the line starting with the diff command and everything after it,
+ # up to the end of the file, assuming it is only patch data.
+ # If more than one line before the diff was empty, strip all but one.
+ /^diff --git / {
+ blankLines = 0
+ while (getline) { }
+ next
+ }
+
+ # Count blank lines outside footer comments
+ /^$/ && (footerComment == 0) {
+ blankLines++
+ next
+ }
+
+ # Catch footer comment
+ /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
+ footerComment = 1
+ }
+
+ /]$/ && (footerComment == 1) {
+ footerComment = 2
+ }
+
+ # We have a non-blank line after blank lines. Handle this.
+ (blankLines > 0) {
+ print lines
+ for (i = 0; i < blankLines; i++) {
+ print ""
+ }
+
+ lines = ""
+ blankLines = 0
+ isFooter = 1
+ footerComment = 0
+ }
+
+ # Detect that the current block is not the footer
+ (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
+ isFooter = 0
+ }
+
+ {
+ # We need this information about the current last comment line
+ if (footerComment == 2) {
+ footerComment = 0
+ }
+ if (lines != "") {
+ lines = lines "\n";
+ }
+ lines = lines $0
+ }
+
+ # Footer handling:
+ # If the last block is considered a footer, splice in the Change-Id at the
+ # right place.
+ # Look for the right place to inject Change-Id by considering
+ # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
+ # then Change-Id, then everything else (eg. Signed-off-by:).
+ #
+ # Otherwise just print the last block, a new line and the Change-Id as a
+ # block of its own.
+ END {
+ unprinted = 1
+ if (isFooter == 0) {
+ print lines "\n"
+ lines = ""
+ }
+ changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
+ numlines = split(lines, footer, "\n")
+ for (line = 1; line <= numlines; line++) {
+ if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
+ unprinted = 0
+ print "Change-Id: I'"$id"'"
+ }
+ print footer[line]
+ }
+ if (unprinted) {
+ print "Change-Id: I'"$id"'"
+ }
+ }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T"
+}
+_gen_ChangeIdInput() {
+ echo "tree `git write-tree`"
+ if parent=`git rev-parse "HEAD^0" 2>/dev/null`
+ then
+ echo "parent $parent"
+ fi
+ echo "author `git var GIT_AUTHOR_IDENT`"
+ echo "committer `git var GIT_COMMITTER_IDENT`"
+ echo
+ printf '%s' "$clean_message"
+}
+_gen_ChangeId() {
+ _gen_ChangeIdInput |
+ git hash-object -t commit --stdin
+}
+
+
+add_ChangeId
diff --git a/git-hooks/pre-push b/git-hooks/pre-push
new file mode 100755
index 0000000..a7e1266
--- /dev/null
+++ b/git-hooks/pre-push
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./tools/checkpatch.sh
diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c
index 720fb60..1c4e154 100644
--- a/src/flash/nor/tcl.c
+++ b/src/flash/nor/tcl.c
@@ -720,7 +720,8 @@ COMMAND_HANDLER(handle_flash_md_command)
retval = flash_driver_read(bank, buffer, offset, sizebytes);
if (retval == ERROR_OK)
- target_handle_md_output(CMD, target, address, wordsize, count, buffer);
+ target_handle_md_output(CMD, target, address, wordsize, count,
+ buffer, true);
free(buffer);
diff --git a/src/helper/Makefile.am b/src/helper/Makefile.am
index e0fa331..39d93d6 100644
--- a/src/helper/Makefile.am
+++ b/src/helper/Makefile.am
@@ -34,6 +34,8 @@ noinst_LTLIBRARIES += %D%/libhelper.la
%D%/jep106.h \
%D%/jep106.inc \
%D%/jim-nvp.h \
+ %D%/base64.c \
+ %D%/base64.h \
%D%/nvp.h \
%D%/compiler.h
diff --git a/src/helper/base64.c b/src/helper/base64.c
new file mode 100644
index 0000000..effa8ac
--- /dev/null
+++ b/src/helper/base64.c
@@ -0,0 +1,157 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char *base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = count / 4 * 3;
+ pos = out = malloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
diff --git a/src/helper/base64.h b/src/helper/base64.h
new file mode 100644
index 0000000..dbabb60
--- /dev/null
+++ b/src/helper/base64.h
@@ -0,0 +1,17 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+unsigned char *base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c
index 58f83af..a0b120b 100644
--- a/src/jtag/drivers/ftdi.c
+++ b/src/jtag/drivers/ftdi.c
@@ -75,6 +75,16 @@
/* FTDI access library includes */
#include "mpsse.h"
+#if BUILD_FTDI_CJTAG == 1
+#define DO_CLOCK_DATA clock_data
+#define DO_CLOCK_TMS_CS clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT clock_tms_cs_out
+#else
+#define DO_CLOCK_DATA mpsse_clock_data
+#define DO_CLOCK_TMS_CS mpsse_clock_tms_cs
+#define DO_CLOCK_TMS_CS_OUT mpsse_clock_tms_cs_out
+#endif
+
#define JTAG_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
#define JTAG_MODE_ALT (LSB_FIRST | NEG_EDGE_IN | NEG_EDGE_OUT)
#define SWD_MODE (LSB_FIRST | POS_EDGE_IN | NEG_EDGE_OUT)
@@ -85,6 +95,39 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE;
static bool swd_mode;
+#if BUILD_FTDI_CJTAG == 1
+#define ESCAPE_SEQ_OAC_BIT2 28
+
+static void cjtag_reset_online_activate(void);
+
+/*
+ The cJTAG 2-wire OScan1 protocol, in lieu of 4-wire JTAG, is a configuration option
+ for some SoCs. An FTDI-based adapter that can be configured to appropriately drive
+ the bidirectional pin TMSC is able to drive OScan1 protocol. For example, an Olimex
+ ARM-USB-TINY-H with the ARM-JTAG-SWD adapter, connected to a cJTAG-enabled
+ target board is such a topology. A TCK cycle with TMS=1/TDI=N translates to a TMSC
+ output of N, and a TCK cycle with TMS=0 translates to a TMSC input from the target back
+ to the adapter/probe. The OScan1 protocol uses 3 TCK cycles to generate the data flow
+ that is equivalent to that of a single TCK cycle in 4-wire JTAG. The OScan1-related
+ code in this module translates IR/DR scan commanads and JTAG state traversal commands
+ to the two-wire clocking and signaling of OScan1 protocol, if placed into OScan1 mode
+ during initialization.
+*/
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode);
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode);
+
+static bool oscan1_mode;
+
+/*
+ The cJTAG 4-wire JScan3 allows to use standard JTAG protocol with cJTAG hardware
+*/
+static bool jscan3_mode;
+#endif
+
#define MAX_USB_IDS 8
/* vid = pid = 0 marks the end of the list */
static uint16_t ftdi_vid[MAX_USB_IDS + 1] = { 0 };
@@ -230,6 +273,35 @@ static int ftdi_get_signal(const struct signal *s, uint16_t *value_out)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+ else
+ mpsse_clock_data(ctx, out, out_offset, in, in_offset, length, mode);
+}
+
+static void clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs(ctx, out, out_offset, in, in_offset, length, tdi, mode);
+}
+
+static void clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ if (oscan1_mode)
+ oscan1_mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+ else
+ mpsse_clock_tms_cs_out(ctx, out, out_offset, length, tdi, mode);
+}
+#endif
+
/**
* Function move_to_state
* moves the TAP controller from the current state to a
@@ -258,7 +330,7 @@ static void move_to_state(tap_state_t goal_state)
for (int i = 0; i < tms_count; i++)
tap_set_state(tap_state_transition(tap_get_state(), (tms_bits >> i) & 1));
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
0,
tms_count,
@@ -312,7 +384,7 @@ static void ftdi_end_state(tap_state_t state)
static void ftdi_execute_runtest(struct jtag_command *cmd)
{
int i;
- uint8_t zero = 0;
+ static const uint8_t zero;
LOG_DEBUG_IO("runtest %i cycles, end in %s",
cmd->cmd.runtest->num_cycles,
@@ -326,7 +398,7 @@ static void ftdi_execute_runtest(struct jtag_command *cmd)
while (i > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = i > 7 ? 7 : i;
- mpsse_clock_tms_cs_out(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &zero, 0, this_len, false, ftdi_jtag_mode);
i -= this_len;
}
@@ -361,7 +433,7 @@ static void ftdi_execute_tms(struct jtag_command *cmd)
LOG_DEBUG_IO("TMS: %d bits", cmd->cmd.tms->num_bits);
/* TODO: Missing tap state tracking, also missing from ft2232.c! */
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
cmd->cmd.tms->bits,
0,
cmd->cmd.tms->num_bits,
@@ -408,7 +480,7 @@ static void ftdi_execute_pathmove(struct jtag_command *cmd)
state_count++;
if (bit_count == 7 || num_states == 0) {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_byte,
0,
bit_count,
@@ -462,7 +534,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
if (i == cmd->cmd.scan->num_fields - 1 && tap_get_state() != tap_get_end_state()) {
/* Last field, and we're leaving IRSHIFT/DRSHIFT. Clock last bit during tap
* movement. This last field can't have length zero, it was checked above. */
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -472,12 +544,8 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
uint8_t last_bit = 0;
if (field->out_value)
bit_copy(&last_bit, 0, field->out_value, field->num_bits - 1, 1);
-
- /* If endstate is TAP_IDLE, clock out 1-1-0 (->EXIT1 ->UPDATE ->IDLE)
- * Otherwise, clock out 1-0 (->EXIT1 ->PAUSE)
- */
uint8_t tms_bits = 0x03;
- mpsse_clock_tms_cs(mpsse_ctx,
+ DO_CLOCK_TMS_CS(mpsse_ctx,
&tms_bits,
0,
field->in_value,
@@ -487,7 +555,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
ftdi_jtag_mode);
tap_set_state(tap_state_transition(tap_get_state(), 1));
if (tap_get_end_state() == TAP_IDLE) {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
1,
2,
@@ -496,7 +564,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
tap_set_state(tap_state_transition(tap_get_state(), 1));
tap_set_state(tap_state_transition(tap_get_state(), 0));
} else {
- mpsse_clock_tms_cs_out(mpsse_ctx,
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx,
&tms_bits,
2,
1,
@@ -505,7 +573,7 @@ static void ftdi_execute_scan(struct jtag_command *cmd)
tap_set_state(tap_state_transition(tap_get_state(), 0));
}
} else
- mpsse_clock_data(mpsse_ctx,
+ DO_CLOCK_DATA(mpsse_ctx,
field->out_value,
0,
field->in_value,
@@ -586,7 +654,7 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
while (num_cycles > 0) {
/* there are no state transitions in this code, so omit state tracking */
unsigned this_len = num_cycles > 7 ? 7 : num_cycles;
- mpsse_clock_tms_cs_out(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
+ DO_CLOCK_TMS_CS_OUT(mpsse_ctx, &tms, 0, this_len, false, ftdi_jtag_mode);
num_cycles -= this_len;
}
@@ -598,10 +666,19 @@ static void ftdi_execute_stableclocks(struct jtag_command *cmd)
static void ftdi_execute_command(struct jtag_command *cmd)
{
switch (cmd->type) {
+#if BUILD_FTDI_CJTAG == 1
+ case JTAG_RESET:
+ if (cmd->cmd.reset->trst)
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+ break;
+#endif
case JTAG_RUNTEST:
ftdi_execute_runtest(cmd);
break;
case JTAG_TLR_RESET:
+#if BUILD_FTDI_CJTAG == 1
+ cjtag_reset_online_activate(); /* put the target (back) into selected cJTAG mode */
+#endif
ftdi_execute_statemove(cmd);
break;
case JTAG_PATHMOVE:
@@ -676,6 +753,21 @@ static int ftdi_initialize(void)
/* A dummy SWD_EN would have zero mask */
if (sig->data_mask)
ftdi_set_signal(sig, '1');
+#if BUILD_FTDI_CJTAG == 1
+ } else if (oscan1_mode || jscan3_mode) {
+ struct signal *sig = find_signal_by_name("JTAG_SEL");
+ if (!sig) {
+ LOG_ERROR("A cJTAG mode is active but JTAG_SEL signal is not defined");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+ /* A dummy JTAG_SEL would have zero mask */
+ if (sig->data_mask)
+ ftdi_set_signal(sig, '0');
+ else if (jscan3_mode) {
+ LOG_ERROR("In JScan3 mode JTAG_SEL signal cannot be dummy, data mask needed");
+ return ERROR_JTAG_INIT_FAILED;
+ }
+#endif
}
mpsse_set_data_bits_low_byte(mpsse_ctx, output & 0xff, direction & 0xff);
@@ -707,6 +799,290 @@ static int ftdi_quit(void)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("oscan1_mpsse_clock_data: %sout %d bits", in ? "in" : "", length);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t bit;
+
+ /* OScan1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ bitnum = out_offset + i;
+ bit = out ? ((out[bitnum/8] >> (bitnum%8)) & 0x1) : 0;
+
+ /* Try optimized case first: if desired TDI bit is 1, then we
+ can fuse what would otherwise be the first two MPSSE commands */
+ if (bit) {
+ const uint8_t tmsbits = 0x3; /* 1, 1 */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, false, mode);
+ } else {
+ /* Can't fuse because TDI varies; less efficient */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, bit ? 0 : 1, mode);
+
+ /* drive TMSC to desired TMS value (always zero in this context) */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, false, mode);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset, uint8_t *in,
+ unsigned in_offset, unsigned length, bool tdi, uint8_t mode)
+{
+ static const uint8_t zero;
+ static const uint8_t one = 1;
+
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+
+ LOG_DEBUG_IO("oscan1_mpsse_clock_tms_cs: %sout %d bits, tdi=%d", in ? "in" : "", length, tdi);
+
+ for (unsigned i = 0; i < length; i++) {
+ int bitnum;
+ uint8_t tmsbit;
+ uint8_t tdibit;
+
+ /* OScan1 uses 3 separate clocks */
+
+ /* drive TMSC to the *negation* of the desired TDI value */
+ tdibit = tdi ? 0 : 1;
+
+ /* drive TMSC to desired TMS value */
+ bitnum = out_offset + i;
+ tmsbit = ((out[bitnum/8] >> (bitnum%8)) & 0x1);
+
+ if (tdibit == tmsbit) {
+ /* Can squash into a single MPSSE command */
+ const uint8_t tmsbits = 0x3;
+ mpsse_clock_tms_cs_out(mpsse_ctx, &tmsbits, 0, 2, tdibit, mode);
+ } else {
+ /* Unoptimized case, can't formulate with a single command */
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, tdibit, mode);
+ mpsse_clock_tms_cs_out(mpsse_ctx, &one, 0, 1, (tmsbit != 0), mode);
+ }
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '0'); /* put TMSC in high impedance */
+
+ /* drive another TCK without driving TMSC (TDO cycle) */
+ mpsse_clock_tms_cs(mpsse_ctx, &zero, 0, in, in_offset+i, 1, false, mode);
+
+ if (tmsc_en)
+ ftdi_set_signal(tmsc_en, '1'); /* drive again TMSC */
+ }
+}
+
+
+static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t *out, unsigned out_offset,
+ unsigned length, bool tdi, uint8_t mode)
+{
+ oscan1_mpsse_clock_tms_cs(ctx, out, out_offset, 0, 0, length, tdi, mode);
+}
+
+
+static void cjtag_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct signal *tms,
+ char tmsvalue, struct signal *tdi, char tdivalue)
+{
+ ftdi_set_signal(tms, tmsvalue);
+ ftdi_set_signal(tdi, tdivalue);
+ ftdi_set_signal(tck, tckvalue);
+}
+
+static void cjtag_reset_online_activate(void)
+{
+ /* After TAP reset, the cJTAG-to-JTAG adapter is in offline and
+ non-activated state. Escape sequences are needed to bring the
+ TAP online and activated into the desired working mode. */
+
+ struct signal *tck = find_signal_by_name("TCK");
+ struct signal *tdi = find_signal_by_name("TDI");
+ struct signal *tms = find_signal_by_name("TMS");
+ struct signal *tdo = find_signal_by_name("TDO");
+ struct signal *tmsc_en = find_signal_by_name("TMSC_EN");
+ uint16_t tdovalue;
+
+ static struct {
+ int8_t tck;
+ int8_t tms;
+ int8_t tdi;
+ } sequence[] = {
+ /* TCK=0, TMS=1, TDI=0 (drive TMSC to 0 baseline) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for TAP reset - 8 TMSC edges */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* 3 TCK pulses for padding */
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (drive rising TCK edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (drive falling TCK edge) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for SELECT */
+ /* TCK=1, TMS=1, TDI=0 (rising edge of TCK with TMSC still 0, TAP reset that was just setup occurs here too) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=1, TMS=1, TDI=1 (drive rising TMSC edge) */
+ {'1', '1', '1'},
+ /* TCK=1, TMS=1, TDI=0 (drive falling TMSC edge) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK with TMSC still 0) */
+ {'0', '1', '0'},
+
+ /* Drive cJTAG escape sequence for OScan1 activation -- OAC = 1100 -> 2 wires -- */
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK with TMSC still 0... online mode activated... also OAC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... OAC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit2==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK, TMSC stays high) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... OAC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... EC bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=1 (falling edge TCK) */
+ {'0', '1', '1'},
+ /* TCK=1, TMS=1, TDI=1 (rising edge TCK... EC bit3==1) */
+ {'1', '1', '1'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit0==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit1==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit2==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ /* TCK=1, TMS=1, TDI=0 (rising edge TCK... CP bit3==0) */
+ {'1', '1', '0'},
+ /* TCK=0, TMS=1, TDI=0 (falling edge TCK) */
+ {'0', '1', '0'},
+ };
+
+ if (!oscan1_mode && !jscan3_mode)
+ return; /* Nothing to do */
+
+ if (oscan1_mode && jscan3_mode) {
+ LOG_ERROR("Both oscan1_mode and jscan3_mode are \"on\". At most one of them can be enabled.");
+ return;
+ }
+
+ if (!tck) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TCK signal is not defined");
+ return;
+ }
+
+ if (!tdi) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDI signal is not defined");
+ return;
+ }
+
+ if (!tms) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TMS signal is not defined");
+ return;
+ }
+
+ if (!tdo) {
+ LOG_ERROR("Can't run cJTAG online/activate escape sequences: TDO signal is not defined");
+ return;
+ }
+
+ if (jscan3_mode) {
+ /* Update the sequence above to enable JScan3 instead of OScan1 */
+ sequence[ESCAPE_SEQ_OAC_BIT2].tdi = '0';
+ sequence[ESCAPE_SEQ_OAC_BIT2+1].tdi = '0';
+ }
+
+ /* if defined TMSC_EN, replace tms with it */
+ if (tmsc_en)
+ tms = tmsc_en;
+
+ /* Send the sequence to the adapter */
+ for (size_t i = 0; i < sizeof(sequence)/sizeof(sequence[0]); i++)
+ cjtag_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi);
+
+ /* If JScan3 mode, configure cJTAG adapter to 4-wire */
+ if (jscan3_mode)
+ ftdi_set_signal(find_signal_by_name("JTAG_SEL"), '1');
+
+ ftdi_get_signal(tdo, &tdovalue); /* Just to force a flush */
+}
+
+#endif /* #if BUILD_FTDI_CJTAG == 1 */
+
COMMAND_HANDLER(ftdi_handle_device_desc_command)
{
if (CMD_ARGC == 1) {
@@ -918,6 +1294,32 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command)
return ERROR_OK;
}
+#if BUILD_FTDI_CJTAG == 1
+COMMAND_HANDLER(ftdi_handle_oscan1_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], oscan1_mode);
+
+ command_print(CMD, "oscan1 mode: %s.", oscan1_mode ? "on" : "off");
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(ftdi_handle_jscan3_mode_command)
+{
+ if (CMD_ARGC > 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (CMD_ARGC == 1)
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], jscan3_mode);
+
+ command_print(CMD, "jscan3 mode: %s.", jscan3_mode ? "on" : "off");
+ return ERROR_OK;
+}
+#endif
+
static const struct command_registration ftdi_subcommand_handlers[] = {
{
.name = "device_desc",
@@ -979,6 +1381,22 @@ static const struct command_registration ftdi_subcommand_handlers[] = {
"allow signalling speed increase)",
.usage = "(rising|falling)",
},
+#if BUILD_FTDI_CJTAG == 1
+ {
+ .name = "oscan1_mode",
+ .handler = &ftdi_handle_oscan1_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use OScan1 mode for signaling, otherwise 'off' (default is 'off')",
+ .usage = "(on|off)",
+ },
+ {
+ .name = "jscan3_mode",
+ .handler = &ftdi_handle_jscan3_mode_command,
+ .mode = COMMAND_ANY,
+ .help = "set to 'on' to use JScan3 mode for signaling, otherwise 'off' (default is 'off')",
+ .usage = "(on|off)",
+ },
+#endif
COMMAND_REGISTRATION_DONE
};
diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c
index 20977e0..02409a5 100644
--- a/src/rtos/FreeRTOS.c
+++ b/src/rtos/FreeRTOS.c
@@ -20,63 +20,219 @@
#include "target/cortex_m.h"
#define FREERTOS_MAX_PRIORITIES 63
-
-/* FIXME: none of the _width parameters are actually observed properly!
- * you WILL need to edit more if you actually attempt to target a 8/16/64
- * bit target!
- */
+#define FREERTOS_THREAD_NAME_STR_SIZE 200
+#define FREERTOS_CURRENT_EXECUTION_ID 1
struct freertos_params {
const char *target_name;
- const unsigned char thread_count_width;
- const unsigned char pointer_width;
- const unsigned char list_next_offset;
- const unsigned char list_width;
- const unsigned char list_elem_next_offset;
- const unsigned char list_elem_content_offset;
- const unsigned char thread_stack_offset;
- const unsigned char thread_name_offset;
- const struct rtos_register_stacking *stacking_info_cm3;
- const struct rtos_register_stacking *stacking_info_cm4f;
- const struct rtos_register_stacking *stacking_info_cm4f_fpu;
+ int (*stacking)(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr);
+ const struct command_registration *commands;
+};
+
+struct freertos_thread_entry {
+ struct list_head list;
+ threadid_t threadid;
+ target_addr_t tcb;
+};
+
+struct FreeRTOS {
+ const struct freertos_params *param;
+ threadid_t last_threadid;
+ /* Keep track of which threadid we're using for which TCB. We cannot use a
+ * 1:1 mapping because TCB's can be 64 bits, and the gdb protocol doesn't
+ * work well with thread id's that are greater than 32 bits.
+ */
+ struct list_head thread_entry_list;
+ /* sizeof(UBaseType_t) */
+ unsigned ubasetype_size;
+ /* sizeof(void *) */
+ unsigned pointer_size;
+ /* sizeof(TickType_t) */
+ unsigned ticktype_size;
+ unsigned list_width;
+ unsigned list_item_width;
+ unsigned list_elem_next_offset;
+ unsigned list_elem_next_size;
+ unsigned list_elem_content_offset;
+ unsigned list_elem_content_size;
+ unsigned list_uxNumberOfItems_offset;
+ unsigned list_uxNumberOfItems_size;
+ unsigned list_next_offset;
+ unsigned list_next_size;
+ unsigned thread_stack_offset;
+ unsigned thread_stack_size;
+ unsigned thread_name_offset;
+};
+
+static int cortex_m_stacking(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr)
+{
+ /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
+ int cm4_fpu_enabled = 0;
+ struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
+ if (is_armv7m(armv7m_target)) {
+ if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) ||
+ (armv7m_target->fp_feature == FPV5_DP)) {
+ /* Found ARM v7m target which includes a FPU */
+ uint32_t cpacr;
+
+ int retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("Could not read CPACR register to check FPU state");
+ return retval;
+ }
+
+ /* Check if CP10 and CP11 are set to full access. */
+ if (cpacr & 0x00F00000) {
+ /* Found target with enabled FPU */
+ cm4_fpu_enabled = 1;
+ }
+ }
+ }
+
+ if (cm4_fpu_enabled == 1) {
+ /* Read the LR to decide between stacking with or without FPU */
+ uint32_t LR_svc = 0;
+ int retval = target_read_u32(rtos->target,
+ stack_ptr + 0x20,
+ &LR_svc);
+ if (retval != ERROR_OK) {
+ LOG_OUTPUT("Error reading stack frame from FreeRTOS thread");
+ return retval;
+ }
+ if ((LR_svc & 0x10) == 0)
+ *stacking = &rtos_standard_cortex_m4f_fpu_stacking;
+ else
+ *stacking = &rtos_standard_cortex_m4f_stacking;
+ } else {
+ *stacking = &rtos_standard_cortex_m3_stacking;
+ }
+
+ return ERROR_OK;
+}
+
+/* take 4 bytes (32 bits) as the default size,
+ * which is suitable for most 32-bit targets and
+ * configuration of configUSE_16_BIT_TICKS = 0. */
+static unsigned int freertos_ticktype_size = 4;
+COMMAND_HANDLER(handle_freertos_ticktype_size)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ unsigned int size;
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], size);
+ switch (size) {
+ case 2:
+ case 4:
+ case 8:
+ freertos_ticktype_size = size;
+ break;
+ default:
+ LOG_ERROR("Invalid ticktype size. Should be 2, 4 or 8.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ return ERROR_OK;
+}
+
+static const struct command_registration freertos_commands[] = {
+ {
+ .name = "freertos_ticktype_size",
+ .handler = handle_freertos_ticktype_size,
+ .mode = COMMAND_ANY,
+ .usage = "(2|4|8)",
+ .help = "Pass the size (in bytes) of TickType_t to OpenOCD. To make sure the "
+ "calculation of offsets and sizes is correct. Defaults to 4."
+ },
+ COMMAND_REGISTRATION_DONE
};
+static enum {
+ STACKING_MAINLINE,
+ STACKING_METAL
+} riscv_freertos_stacking;
+COMMAND_HANDLER(handle_riscv_freertos_stacking)
+{
+ if (CMD_ARGC != 1) {
+ LOG_ERROR("Command takes exactly 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (!strcmp(CMD_ARGV[0], "mainline")) {
+ riscv_freertos_stacking = STACKING_MAINLINE;
+ } else if (!strcmp(CMD_ARGV[0], "metal")) {
+ riscv_freertos_stacking = STACKING_METAL;
+ } else {
+ LOG_ERROR("Only two arguments are supported: mainline and metal");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ return ERROR_OK;
+}
+
+static const struct command_registration riscv_commands[] = {
+ {
+ .name = "riscv_freertos_stacking",
+ .handler = handle_riscv_freertos_stacking,
+ .mode = COMMAND_ANY,
+ .usage = "mainline|metal",
+ .help = "Select which FreeRTOS branch is being used. OpenOCD needs to "
+ "know because different branches save thread registers on the stack "
+ "in different orders. It is likely that this order on both branches will "
+ "change in the future, so make sure to seek out the very latest OpenOCD if "
+ "debugging is not working right."
+ },
+ COMMAND_REGISTRATION_DONE
+};
+
+static int riscv_stacking(struct rtos *rtos, const struct rtos_register_stacking **stacking,
+ target_addr_t stack_ptr)
+{
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+ LOG_DEBUG("riscv_freertos_stacking=%d", riscv_freertos_stacking);
+ switch (riscv_freertos_stacking) {
+ case STACKING_MAINLINE:
+ if (freertos->pointer_size == 4)
+ *stacking = &rtos_standard_rv32_stacking;
+ else if (freertos->pointer_size == 8)
+ *stacking = &rtos_standard_rv64_stacking;
+ break;
+ case STACKING_METAL:
+ if (freertos->pointer_size == 4)
+ *stacking = &rtos_metal_rv32_stacking;
+ else if (freertos->pointer_size == 8)
+ *stacking = &rtos_metal_rv64_stacking;
+ break;
+ }
+ return ERROR_OK;
+}
+
static const struct freertos_params freertos_params_list[] = {
{
- "cortex_m", /* target_name */
- 4, /* thread_count_width; */
- 4, /* pointer_width; */
- 16, /* list_next_offset; */
- 20, /* list_width; */
- 8, /* list_elem_next_offset; */
- 12, /* list_elem_content_offset */
- 0, /* thread_stack_offset; */
- 52, /* thread_name_offset; */
- &rtos_standard_cortex_m3_stacking, /* stacking_info */
- &rtos_standard_cortex_m4f_stacking,
- &rtos_standard_cortex_m4f_fpu_stacking,
+ .target_name = "cortex_m",
+ .stacking = cortex_m_stacking
+ },
+ {
+ .target_name = "hla_target",
+ .stacking = cortex_m_stacking
},
{
- "hla_target", /* target_name */
- 4, /* thread_count_width; */
- 4, /* pointer_width; */
- 16, /* list_next_offset; */
- 20, /* list_width; */
- 8, /* list_elem_next_offset; */
- 12, /* list_elem_content_offset */
- 0, /* thread_stack_offset; */
- 52, /* thread_name_offset; */
- &rtos_standard_cortex_m3_stacking, /* stacking_info */
- &rtos_standard_cortex_m4f_stacking,
- &rtos_standard_cortex_m4f_fpu_stacking,
+ .target_name = "riscv",
+ .stacking = riscv_stacking,
+ .commands = riscv_commands,
},
};
static bool freertos_detect_rtos(struct target *target);
static int freertos_create(struct target *target);
static int freertos_update_threads(struct rtos *rtos);
-static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+static int freertos_get_thread_reg_list(struct rtos *rtos, threadid_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
+static int freertos_get_thread_reg_value(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
+static int freertos_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
const struct rtos_type freertos_rtos = {
@@ -86,6 +242,8 @@ const struct rtos_type freertos_rtos = {
.create = freertos_create,
.update_threads = freertos_update_threads,
.get_thread_reg_list = freertos_get_thread_reg_list,
+ .get_thread_reg_value = freertos_get_thread_reg_value,
+ .set_reg = freertos_set_reg,
.get_symbol_list_to_lookup = freertos_get_symbol_list_to_lookup,
};
@@ -130,32 +288,202 @@ static const struct symbols freertos_symbol_list[] = {
/* may be problems reading if sizes are not 32 bit long integers. */
/* test mallocs for failure */
+static int freertos_read_struct_value(
+ struct target *target, target_addr_t base_address, unsigned offset,
+ unsigned size_bytes, uint64_t *value)
+{
+ uint8_t buf[size_bytes];
+ int retval = target_read_buffer(target, base_address + offset, size_bytes, buf);
+ *value = buf_get_u64(buf, 0, size_bytes * 8);
+ return retval;
+}
+
+typedef struct {
+ enum {
+ TYPE_POINTER,
+ TYPE_UBASE,
+ TYPE_TICKTYPE,
+ TYPE_LIST_ITEM,
+ TYPE_CHAR_ARRAY
+ } type;
+ unsigned offset;
+ unsigned size;
+} type_offset_size_t;
+
+static unsigned populate_offset_size(struct FreeRTOS *freertos,
+ type_offset_size_t *info, unsigned count)
+{
+ unsigned offset = 0;
+ unsigned largest = 0;
+ for (unsigned i = 0; i < count; i++) {
+ unsigned align = 0;
+ switch (info[i].type) {
+ case TYPE_UBASE:
+ info[i].size = freertos->ubasetype_size;
+ align = freertos->ubasetype_size;
+ break;
+ case TYPE_POINTER:
+ info[i].size = freertos->pointer_size;
+ align = freertos->pointer_size;
+ break;
+ case TYPE_TICKTYPE:
+ info[i].size = freertos->ticktype_size;
+ align = freertos->ticktype_size;
+ break;
+ case TYPE_LIST_ITEM:
+ info[i].size = freertos->list_item_width;
+ align = MAX(freertos->ticktype_size, freertos->pointer_size);
+ break;
+ case TYPE_CHAR_ARRAY:
+ /* size is set by the caller. */
+ align = 1;
+ break;
+ }
+
+ assert(info[i].size > 0);
+ assert(align > 0);
+
+ largest = MAX(largest, align);
+
+ if (offset & (align - 1)) {
+ offset = offset & ~(align - 1);
+ offset += align;
+ }
+
+ info[i].offset = offset;
+ offset += info[i].size;
+ }
+
+ /* Now align offset to the largest type used, and return that as the width
+ * of the structure. */
+
+ if (offset & (largest - 1)) {
+ offset = offset & ~(largest - 1);
+ offset += largest;
+ }
+ return offset;
+}
+
+static void freertos_compute_offsets(struct rtos *rtos)
+{
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+
+ if (freertos->ticktype_size == freertos_ticktype_size)
+ return;
+
+ freertos->pointer_size = DIV_ROUND_UP(target_address_bits(rtos->target), 8);
+ freertos->ubasetype_size = DIV_ROUND_UP(target_data_bits(rtos->target), 8);
+ freertos->ticktype_size = freertos_ticktype_size;
+
+ /*
+ * FreeRTOS can be compiled with configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES
+ * in which case extra data is inserted and OpenOCD won't work right.
+ */
+
+ /* struct xLIST */
+ type_offset_size_t struct_list_info[] = {
+ {TYPE_UBASE, 0, 0}, /* uxNumberOfItems */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxIndex */
+ {TYPE_TICKTYPE, 0, 0}, /* xItemValue */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxNext */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxPrevious */
+ };
+
+ /* struct xLIST_ITEM */
+ type_offset_size_t struct_list_item_info[] = {
+ {TYPE_TICKTYPE, 0, 0}, /* xItemValue */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxNext */
+ {TYPE_POINTER, 0, 0}, /* ListItem_t *pxPrevious */
+ {TYPE_POINTER, 0, 0}, /* void *pvOwner */
+ {TYPE_POINTER, 0, 0}, /* List_t *pvContainer */
+ };
+
+ /* struct tskTaskControlBlock */
+ type_offset_size_t task_control_block_info[] = {
+ {TYPE_POINTER, 0, 0}, /* StackType_t *pxTopOfStack */
+ {TYPE_LIST_ITEM, 0, 0}, /* ListItem_t xStateListItem */
+ {TYPE_LIST_ITEM, 0, 0}, /* ListItem_t xEventListItem */
+ {TYPE_UBASE, 0, 0}, /* uxPriority */
+ {TYPE_POINTER, 0, 0}, /* StackType_t *pxStack */
+ /* configMAX_TASK_NAME_LEN varies a lot between targets, but luckily the
+ * name is NULL_terminated and we don't need to read anything else in
+ * the TCB. */
+ {TYPE_CHAR_ARRAY, 0, FREERTOS_THREAD_NAME_STR_SIZE}, /* char pcTaskName[configMAX_TASK_NAME_LEN] */
+ /* Lots of more optional stuff, but is is irrelevant to us. */
+ };
+
+ freertos->list_width = populate_offset_size(
+ freertos, struct_list_info, ARRAY_SIZE(struct_list_info));
+ freertos->list_uxNumberOfItems_offset = struct_list_info[0].offset;
+ freertos->list_uxNumberOfItems_size = struct_list_info[0].size;
+ freertos->list_next_offset = struct_list_info[3].offset;
+ freertos->list_next_size = struct_list_info[3].size;
+
+ freertos->list_item_width = populate_offset_size(
+ freertos, struct_list_item_info, ARRAY_SIZE(struct_list_item_info));
+ freertos->list_elem_next_offset = struct_list_item_info[1].offset;
+ freertos->list_elem_next_size = struct_list_item_info[1].size;
+ freertos->list_elem_content_offset = struct_list_item_info[3].offset;
+ freertos->list_elem_content_size = struct_list_item_info[3].size;
+
+ populate_offset_size(
+ freertos, task_control_block_info, ARRAY_SIZE(task_control_block_info));
+ freertos->thread_stack_offset = task_control_block_info[0].offset;
+ freertos->thread_stack_size = task_control_block_info[0].size;
+ freertos->thread_name_offset = task_control_block_info[5].offset;
+}
+
+struct freertos_thread_entry *thread_entry_list_find_by_tcb(
+ struct list_head *list, target_addr_t tcb)
+{
+ struct freertos_thread_entry *t;
+ list_for_each_entry(t, list, list) {
+ if (t->tcb == tcb)
+ return t;
+ }
+ return NULL;
+}
+
+struct freertos_thread_entry *thread_entry_list_find_by_id(
+ struct list_head *list, threadid_t threadid)
+{
+ struct freertos_thread_entry *t;
+ list_for_each_entry(t, list, list) {
+ if (t->threadid == threadid)
+ return t;
+ }
+ return NULL;
+}
+
static int freertos_update_threads(struct rtos *rtos)
{
int retval;
unsigned int tasks_found = 0;
- const struct freertos_params *param;
if (!rtos->rtos_specific_params)
- return -1;
+ return ERROR_FAIL;
- param = (const struct freertos_params *) rtos->rtos_specific_params;
+ freertos_compute_offsets(rtos);
+
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
if (!rtos->symbols) {
LOG_ERROR("No symbols for FreeRTOS");
- return -3;
+ return ERROR_FAIL;
}
if (rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address == 0) {
LOG_ERROR("Don't have the number of threads in FreeRTOS");
- return -2;
+ return ERROR_FAIL;
}
- uint32_t thread_list_size = 0;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
- &thread_list_size);
- LOG_DEBUG("FreeRTOS: Read uxCurrentNumberOfTasks at 0x%" PRIx64 ", value %" PRIu32,
+ uint64_t thread_list_size;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
+ 0,
+ freertos->ubasetype_size,
+ &thread_list_size);
+ LOG_DEBUG("FreeRTOS: Read uxCurrentNumberOfTasks at 0x%" PRIx64 ", value %" PRIu64,
rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
thread_list_size);
@@ -168,33 +496,36 @@ static int freertos_update_threads(struct rtos *rtos)
rtos_free_threadlist(rtos);
/* read the current thread */
- uint32_t pointer_casts_are_bad;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
- &pointer_casts_are_bad);
+ target_addr_t px_current_tcb;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
+ 0,
+ freertos->pointer_size,
+ &px_current_tcb);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading current thread in FreeRTOS thread list");
return retval;
}
- rtos->current_thread = pointer_casts_are_bad;
LOG_DEBUG("FreeRTOS: Read pxCurrentTCB at 0x%" PRIx64 ", value 0x%" PRIx64,
rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
- rtos->current_thread);
+ px_current_tcb);
/* read scheduler running */
- uint32_t scheduler_running;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
- &scheduler_running);
+ uint64_t scheduler_running;
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
+ 0,
+ freertos->ubasetype_size,
+ &scheduler_running);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading FreeRTOS scheduler state");
return retval;
}
- LOG_DEBUG("FreeRTOS: Read xSchedulerRunning at 0x%" PRIx64 ", value 0x%" PRIx32,
+ LOG_DEBUG("FreeRTOS: Read xSchedulerRunning at 0x%" PRIx64 ", value 0x%" PRIx64,
rtos->symbols[FREERTOS_VAL_X_SCHEDULER_RUNNING].address,
scheduler_running);
- if ((thread_list_size == 0) || (rtos->current_thread == 0) || (scheduler_running != 1)) {
+ if (thread_list_size == 0 || px_current_tcb == 0 || scheduler_running != 1) {
/* Either : No RTOS threads - there is always at least the current execution though */
/* OR : No current thread - all threads suspended - show the current execution
* of idling */
@@ -204,10 +535,10 @@ static int freertos_update_threads(struct rtos *rtos)
rtos->thread_details = malloc(
sizeof(struct thread_detail) * thread_list_size);
if (!rtos->thread_details) {
- LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
+ LOG_ERROR("Error allocating memory for %" PRIu64 " threads", thread_list_size);
return ERROR_FAIL;
}
- rtos->current_thread = 1;
+ rtos->current_thread = FREERTOS_CURRENT_EXECUTION_ID;
rtos->thread_details->threadid = rtos->current_thread;
rtos->thread_details->exists = true;
rtos->thread_details->extra_info_str = NULL;
@@ -223,27 +554,33 @@ static int freertos_update_threads(struct rtos *rtos)
rtos->thread_details = malloc(
sizeof(struct thread_detail) * thread_list_size);
if (!rtos->thread_details) {
- LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
+ LOG_ERROR("Error allocating memory for %" PRId64 " threads", thread_list_size);
return ERROR_FAIL;
}
}
/* Find out how many lists are needed to be read from pxReadyTasksLists, */
+ uint64_t top_used_priority = 0;
if (rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address == 0) {
- LOG_ERROR("FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around");
- return ERROR_FAIL;
+ LOG_WARNING("FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around");
+ /* This is a hack specific to the binary I'm debugging.
+ * Ideally we get https://github.com/FreeRTOS/FreeRTOS-Kernel/issues/33
+ * into our FreeRTOS source. */
+ top_used_priority = 6;
+ } else {
+ retval = freertos_read_struct_value(rtos->target,
+ rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
+ 0,
+ freertos->ubasetype_size,
+ &top_used_priority);
+ if (retval != ERROR_OK)
+ return retval;
+ LOG_DEBUG("FreeRTOS: Read uxTopUsedPriority at 0x%" PRIx64 ", value %" PRIu64,
+ rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
+ top_used_priority);
}
- uint32_t top_used_priority = 0;
- retval = target_read_u32(rtos->target,
- rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
- &top_used_priority);
- if (retval != ERROR_OK)
- return retval;
- LOG_DEBUG("FreeRTOS: Read uxTopUsedPriority at 0x%" PRIx64 ", value %" PRIu32,
- rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
- top_used_priority);
if (top_used_priority > FREERTOS_MAX_PRIORITIES) {
- LOG_ERROR("FreeRTOS top used priority is unreasonably big, not proceeding: %" PRIu32,
+ LOG_ERROR("FreeRTOS top used priority is unreasonably big, not proceeding: %" PRIu64,
top_used_priority);
return ERROR_FAIL;
}
@@ -265,7 +602,7 @@ static int freertos_update_threads(struct rtos *rtos)
unsigned int num_lists;
for (num_lists = 0; num_lists < config_max_priorities; num_lists++)
list_of_lists[num_lists] = rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address +
- num_lists * param->list_width;
+ num_lists * freertos->list_width;
list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST1].address;
list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST2].address;
@@ -278,60 +615,87 @@ static int freertos_update_threads(struct rtos *rtos)
continue;
/* Read the number of threads in this list */
- uint32_t list_thread_count = 0;
- retval = target_read_u32(rtos->target,
- list_of_lists[i],
- &list_thread_count);
+ uint64_t list_thread_count = 0;
+ retval = freertos_read_struct_value(rtos->target,
+ list_of_lists[i],
+ freertos->list_uxNumberOfItems_offset,
+ freertos->list_uxNumberOfItems_size,
+ &list_thread_count);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading number of threads in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read thread count for list %u at 0x%" PRIx64 ", value %" PRIu32,
- i, list_of_lists[i], list_thread_count);
+ LOG_DEBUG("FreeRTOS: Read thread count for list %u at 0x%" PRIx64 ", value %" PRIu64,
+ i, list_of_lists[i] + freertos->list_uxNumberOfItems_offset, list_thread_count);
if (list_thread_count == 0)
continue;
/* Read the location of first list item */
- uint32_t prev_list_elem_ptr = -1;
- uint32_t list_elem_ptr = 0;
- retval = target_read_u32(rtos->target,
- list_of_lists[i] + param->list_next_offset,
- &list_elem_ptr);
+ target_addr_t prev_list_elem_ptr = -1;
+ target_addr_t list_elem_ptr = 0;
+ retval = freertos_read_struct_value(rtos->target,
+ list_of_lists[i],
+ freertos->list_next_offset,
+ freertos->list_next_size,
+ &list_elem_ptr);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading first thread item location in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read first item for list %u at 0x%" PRIx64 ", value 0x%" PRIx32,
- i, list_of_lists[i] + param->list_next_offset, list_elem_ptr);
+ LOG_DEBUG("FreeRTOS: Read first item for list %u at 0x%" PRIx64 ", value 0x%" PRIx64,
+ i, list_of_lists[i] + freertos->list_next_offset, list_elem_ptr);
while ((list_thread_count > 0) && (list_elem_ptr != 0) &&
(list_elem_ptr != prev_list_elem_ptr) &&
(tasks_found < thread_list_size)) {
/* Get the location of the thread structure. */
- retval = target_read_u32(rtos->target,
- list_elem_ptr + param->list_elem_content_offset,
- &pointer_casts_are_bad);
+ rtos->thread_details[tasks_found].threadid = 0;
+ target_addr_t tcb;
+ retval = freertos_read_struct_value(rtos->target,
+ list_elem_ptr,
+ freertos->list_elem_content_offset,
+ freertos->list_elem_content_size,
+ &tcb);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading thread list item object in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- rtos->thread_details[tasks_found].threadid = pointer_casts_are_bad;
- LOG_DEBUG("FreeRTOS: Read Thread ID at 0x%" PRIx32 ", value 0x%" PRIx64,
- list_elem_ptr + param->list_elem_content_offset,
- rtos->thread_details[tasks_found].threadid);
+
+ const struct freertos_thread_entry *value =
+ thread_entry_list_find_by_tcb(&freertos->thread_entry_list, tcb);
+
+ if (!value) {
+ struct freertos_thread_entry *new_value = calloc(1, sizeof(struct freertos_thread_entry));
+ new_value->tcb = tcb;
+ /* threadid can't be 0.
+ * plus 1 to avoid duplication with "Current Execution" */
+ new_value->threadid = ++freertos->last_threadid + FREERTOS_CURRENT_EXECUTION_ID;
+
+ LOG_DEBUG("FreeRTOS: new thread created, tcb=0x%" PRIx64 ", threadid=0x%" PRIx64,
+ new_value->tcb, new_value->threadid);
+
+ list_add_tail(&new_value->list, &freertos->thread_entry_list);
+ value = new_value;
+ }
+
+ rtos->thread_details[tasks_found].threadid = value->threadid;
+
+ LOG_DEBUG("FreeRTOS: Thread %" PRId64 " has TCB 0x%" TARGET_PRIxADDR
+ "; read from 0x%" PRIx64,
+ value->threadid, value->tcb,
+ list_elem_ptr + freertos->list_elem_content_offset);
/* get thread name */
- #define FREERTOS_THREAD_NAME_STR_SIZE (200)
char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE];
/* Read the thread name */
retval = target_read_buffer(rtos->target,
- rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
+ value->tcb + freertos->thread_name_offset,
FREERTOS_THREAD_NAME_STR_SIZE,
(uint8_t *)&tmp_str);
if (retval != ERROR_OK) {
@@ -341,7 +705,7 @@ static int freertos_update_threads(struct rtos *rtos)
}
tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00';
LOG_DEBUG("FreeRTOS: Read Thread Name at 0x%" PRIx64 ", value '%s'",
- rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
+ value->tcb + freertos->thread_name_offset,
tmp_str);
if (tmp_str[0] == '\x00')
@@ -352,8 +716,9 @@ static int freertos_update_threads(struct rtos *rtos)
strcpy(rtos->thread_details[tasks_found].thread_name_str, tmp_str);
rtos->thread_details[tasks_found].exists = true;
- if (rtos->thread_details[tasks_found].threadid == rtos->current_thread) {
+ if (value->tcb == px_current_tcb && rtos->current_thread != FREERTOS_CURRENT_EXECUTION_ID) {
char running_str[] = "State: Running";
+ rtos->current_thread = value->threadid;
rtos->thread_details[tasks_found].extra_info_str = malloc(
sizeof(running_str));
strcpy(rtos->thread_details[tasks_found].extra_info_str,
@@ -367,17 +732,20 @@ static int freertos_update_threads(struct rtos *rtos)
prev_list_elem_ptr = list_elem_ptr;
list_elem_ptr = 0;
- retval = target_read_u32(rtos->target,
- prev_list_elem_ptr + param->list_elem_next_offset,
- &list_elem_ptr);
+ retval = freertos_read_struct_value(rtos->target,
+ prev_list_elem_ptr,
+ freertos->list_elem_next_offset,
+ freertos->list_elem_next_size,
+ &list_elem_ptr);
if (retval != ERROR_OK) {
LOG_ERROR("Error reading next thread item location in FreeRTOS thread list");
free(list_of_lists);
return retval;
}
- LOG_DEBUG("FreeRTOS: Read next thread location at 0x%" PRIx32 ", value 0x%" PRIx32,
- prev_list_elem_ptr + param->list_elem_next_offset,
- list_elem_ptr);
+ LOG_DEBUG("FreeRTOS: Read next thread location at " TARGET_ADDR_FMT
+ ", value " TARGET_ADDR_FMT,
+ prev_list_elem_ptr + freertos->list_elem_next_offset,
+ list_elem_ptr);
}
}
@@ -385,77 +753,106 @@ static int freertos_update_threads(struct rtos *rtos)
return 0;
}
-static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
- struct rtos_reg **reg_list, int *num_regs)
+static int freertos_get_stacking_info(struct rtos *rtos, threadid_t thread_id,
+ const struct rtos_register_stacking **stacking_info,
+ target_addr_t *stack_ptr)
{
- int retval;
- const struct freertos_params *param;
- int64_t stack_ptr = 0;
-
- if (!rtos)
- return -1;
+ if (!rtos->rtos_specific_params) {
+ LOG_ERROR("rtos_specific_params is NULL!");
+ return ERROR_FAIL;
+ }
- if (thread_id == 0)
- return -2;
+ freertos_compute_offsets(rtos);
- if (!rtos->rtos_specific_params)
- return -1;
+ struct FreeRTOS *freertos = (struct FreeRTOS *) rtos->rtos_specific_params;
+ const struct freertos_params *param = freertos->param;
- param = (const struct freertos_params *) rtos->rtos_specific_params;
+ const struct freertos_thread_entry *entry =
+ thread_entry_list_find_by_id(&freertos->thread_entry_list, thread_id);
+ if (!entry) {
+ LOG_ERROR("Unknown thread id: %" PRId64, thread_id);
+ return ERROR_FAIL;
+ }
/* Read the stack pointer */
- uint32_t pointer_casts_are_bad;
- retval = target_read_u32(rtos->target,
- thread_id + param->thread_stack_offset,
- &pointer_casts_are_bad);
+ int retval = freertos_read_struct_value(rtos->target,
+ entry->tcb,
+ freertos->thread_stack_offset,
+ freertos->thread_stack_size,
+ stack_ptr);
if (retval != ERROR_OK) {
- LOG_ERROR("Error reading stack frame from FreeRTOS thread");
+ LOG_ERROR("Error reading stack frame from FreeRTOS thread %" PRIx64, thread_id);
return retval;
}
- stack_ptr = pointer_casts_are_bad;
- LOG_DEBUG("FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,
- thread_id + param->thread_stack_offset,
- stack_ptr);
+ LOG_DEBUG("[%" PRId64 "] FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,
+ thread_id, entry->tcb + freertos->thread_stack_offset, *stack_ptr);
- /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
- int cm4_fpu_enabled = 0;
- struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
- if (is_armv7m(armv7m_target)) {
- if ((armv7m_target->fp_feature == FPV4_SP) || (armv7m_target->fp_feature == FPV5_SP) ||
- (armv7m_target->fp_feature == FPV5_DP)) {
- /* Found ARM v7m target which includes a FPU */
- uint32_t cpacr;
+ if (param->stacking(rtos, stacking_info, *stack_ptr) != ERROR_OK) {
+ LOG_ERROR("No stacking info found for %s!", param->target_name);
+ return ERROR_FAIL;
+ }
- retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
- if (retval != ERROR_OK) {
- LOG_ERROR("Could not read CPACR register to check FPU state");
- return -1;
- }
+ return ERROR_OK;
+}
- /* Check if CP10 and CP11 are set to full access. */
- if (cpacr & 0x00F00000) {
- /* Found target with enabled FPU */
- cm4_fpu_enabled = 1;
- }
- }
+static int freertos_get_thread_reg_list(struct rtos *rtos, threadid_t thread_id,
+ struct rtos_reg **reg_list, int *num_regs)
+{
+ /* Let the caller read registers directly for the current thread. */
+ if (thread_id == 0)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, thread_id, &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return rtos_generic_stack_read(rtos->target, stacking_info, stack_ptr, reg_list, num_regs);
+}
+
+static int freertos_get_thread_reg_value(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value)
+{
+ LOG_DEBUG("reg_num=%d", reg_num);
+ /* Let the caller read registers directly for the current thread. */
+ if (thread_id == 0)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, thread_id, &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct rtos_reg reg;
+ reg.number = reg_num;
+ int result = rtos_generic_stack_read_reg(rtos->target, stacking_info,
+ stack_ptr, reg_num, &reg);
+ *size = reg.size;
+ *value = malloc(DIV_ROUND_UP(reg.size, 8));
+ if (!*value) {
+ LOG_ERROR("Failed to allocate memory for %d-bit register.", reg.size);
+ return ERROR_FAIL;
}
+ memcpy(*value, reg.value, DIV_ROUND_UP(reg.size, 8));
+ return result;
+}
- if (cm4_fpu_enabled == 1) {
- /* Read the LR to decide between stacking with or without FPU */
- uint32_t lr_svc = 0;
- retval = target_read_u32(rtos->target,
- stack_ptr + 0x20,
- &lr_svc);
- if (retval != ERROR_OK) {
- LOG_OUTPUT("Error reading stack frame from FreeRTOS thread");
- return retval;
- }
- if ((lr_svc & 0x10) == 0)
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs);
- else
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs);
- } else
- return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs);
+static int freertos_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value)
+{
+ LOG_DEBUG("[%" PRId64 "] reg_num=%" PRId32, rtos->current_threadid, reg_num);
+
+ /* Let the caller write registers directly for the current thread. */
+ if (rtos->current_threadid == rtos->current_thread)
+ return ERROR_FAIL;
+
+ const struct rtos_register_stacking *stacking_info;
+ target_addr_t stack_ptr;
+ if (freertos_get_stacking_info(rtos, rtos->current_threadid,
+ &stacking_info, &stack_ptr) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return rtos_generic_stack_write_reg(rtos->target, stacking_info, stack_ptr,
+ reg_num, reg_value);
}
static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
@@ -531,12 +928,32 @@ static bool freertos_detect_rtos(struct target *target)
static int freertos_create(struct target *target)
{
- for (unsigned int i = 0; i < ARRAY_SIZE(freertos_params_list); i++)
- if (strcmp(freertos_params_list[i].target_name, target_type_name(target)) == 0) {
- target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];
- return 0;
- }
+ unsigned int i = 0;
+ while (i < ARRAY_SIZE(freertos_params_list) &&
+ strcmp(freertos_params_list[i].target_name, target_type_name(target)) != 0) {
+ i++;
+ }
+ if (i >= ARRAY_SIZE(freertos_params_list)) {
+ LOG_ERROR("Could not find target in FreeRTOS compatibility list");
+ return ERROR_FAIL;
+ }
+
+ target->rtos->rtos_specific_params = calloc(1, sizeof(struct FreeRTOS));
+ if (!target->rtos->rtos_specific_params) {
+ LOG_ERROR("calloc failed");
+ return ERROR_FAIL;
+ }
+
+ struct FreeRTOS *freertos = (struct FreeRTOS *) target->rtos->rtos_specific_params;
+ INIT_LIST_HEAD(&freertos->thread_entry_list);
+
+ freertos->param = &freertos_params_list[i];
+
+ if (freertos->param->commands) {
+ if (register_commands(target->rtos->cmd_ctx, NULL,
+ freertos->param->commands) != ERROR_OK)
+ return ERROR_FAIL;
+ }
- LOG_ERROR("Could not find target in FreeRTOS compatibility list");
- return -1;
+ return register_commands(target->rtos->cmd_ctx, NULL, freertos_commands);
}
diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c
index 937d01b..f256bc2 100644
--- a/src/rtos/hwthread.c
+++ b/src/rtos/hwthread.c
@@ -17,17 +17,20 @@
static bool hwthread_detect_rtos(struct target *target);
static int hwthread_create(struct target *target);
static int hwthread_update_threads(struct rtos *rtos);
-static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *rtos_reg);
+static int hwthread_get_thread_reg_value(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
static int hwthread_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
static int hwthread_smp_init(struct target *target);
static int hwthread_set_reg(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
+static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id);
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, uint8_t *buffer);
static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, const uint8_t *buffer);
+struct target *hwthread_swbp_target(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
#define HW_THREAD_NAME_STR_SIZE (32)
@@ -42,12 +45,14 @@ const struct rtos_type hwthread_rtos = {
.create = hwthread_create,
.update_threads = hwthread_update_threads,
.get_thread_reg_list = hwthread_get_thread_reg_list,
- .get_thread_reg = hwthread_get_thread_reg,
+ .get_thread_reg_value = hwthread_get_thread_reg_value,
.get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
.smp_init = hwthread_smp_init,
.set_reg = hwthread_set_reg,
+ .needs_fake_step = hwthread_needs_fake_step,
.read_buffer = hwthread_read_buffer,
.write_buffer = hwthread_write_buffer,
+ .swbp_target = hwthread_swbp_target
};
struct hwthread_params {
@@ -83,7 +88,7 @@ static int hwthread_update_threads(struct rtos *rtos)
enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
if (!rtos)
- return -1;
+ return ERROR_FAIL;
target = rtos->target;
@@ -95,7 +100,8 @@ static int hwthread_update_threads(struct rtos *rtos)
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
- if (!target_was_examined(curr))
+ if (!target_was_examined(curr) ||
+ curr->state == TARGET_UNAVAILABLE)
continue;
++thread_list_size;
@@ -120,7 +126,8 @@ static int hwthread_update_threads(struct rtos *rtos)
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
- if (!target_was_examined(curr))
+ if (!target_was_examined(curr) ||
+ curr->state == TARGET_UNAVAILABLE)
continue;
threadid_t tid = threadid_from_target(curr);
@@ -197,7 +204,7 @@ static int hwthread_update_threads(struct rtos *rtos)
else
rtos->current_thread = threadid_from_target(target);
- LOG_DEBUG("%s current_thread=%i", __func__, (int)rtos->current_thread);
+ LOG_DEBUG("current_thread=%i, threads_found=%d", (int)rtos->current_thread, threads_found);
return 0;
}
@@ -282,8 +289,8 @@ static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
return ERROR_OK;
}
-static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *rtos_reg)
+static int hwthread_get_thread_reg_value(struct rtos *rtos, int64_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value)
{
if (!rtos)
return ERROR_FAIL;
@@ -311,11 +318,14 @@ static int hwthread_get_thread_reg(struct rtos *rtos, int64_t thread_id,
if (reg->type->get(reg) != ERROR_OK)
return ERROR_FAIL;
- rtos_reg->number = reg->number;
- rtos_reg->size = reg->size;
- unsigned bytes = (reg->size + 7) / 8;
- assert(bytes <= sizeof(rtos_reg->value));
- memcpy(rtos_reg->value, reg->value, bytes);
+ *size = reg->size;
+ unsigned bytes = DIV_ROUND_UP(reg->size, 8);
+ *value = malloc(bytes);
+ if (!*value) {
+ LOG_ERROR("Failed to allocate memory for %d-bit register.", reg->size);
+ return ERROR_FAIL;
+ }
+ memcpy(*value, reg->value, bytes);
return ERROR_OK;
}
@@ -407,6 +417,11 @@ static int hwthread_create(struct target *target)
return 0;
}
+static bool hwthread_needs_fake_step(struct target *target, int64_t thread_id)
+{
+ return false;
+}
+
static int hwthread_read_buffer(struct rtos *rtos, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
@@ -436,3 +451,9 @@ static int hwthread_write_buffer(struct rtos *rtos, target_addr_t address,
return target_write_buffer(curr, address, size, buffer);
}
+
+struct target *hwthread_swbp_target(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type)
+{
+ return hwthread_find_thread(rtos->target, rtos->current_thread);
+}
diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c
index 0df1182..b5e8e9a 100644
--- a/src/rtos/rtos.c
+++ b/src/rtos/rtos.c
@@ -11,6 +11,7 @@
#include "rtos.h"
#include "target/target.h"
+#include "target/smp.h"
#include "helper/log.h"
#include "helper/binarybuffer.h"
#include "server/gdb_server.h"
@@ -43,7 +44,9 @@ int rtos_smp_init(struct target *target)
return ERROR_TARGET_INIT_FAILED;
}
-static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t)
+static int rtos_target_for_threadid(struct connection *connection,
+ threadid_t threadid,
+ struct target **t)
{
struct target *curr = get_target_from_connection(connection);
if (t)
@@ -52,7 +55,8 @@ static int rtos_target_for_threadid(struct connection *connection, int64_t threa
return ERROR_OK;
}
-static int os_alloc(struct target *target, const struct rtos_type *ostype)
+static int os_alloc(struct target *target, const struct rtos_type *ostype,
+ struct command_context *cmd_ctx)
{
struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos));
@@ -68,6 +72,7 @@ static int os_alloc(struct target *target, const struct rtos_type *ostype)
/* RTOS drivers can override the packet handler in _create(). */
os->gdb_thread_packet = rtos_thread_packet;
os->gdb_target_for_threadid = rtos_target_for_threadid;
+ os->cmd_ctx = cmd_ctx;
return JIM_OK;
}
@@ -83,9 +88,10 @@ static void os_free(struct target *target)
target->rtos = NULL;
}
-static int os_alloc_create(struct target *target, const struct rtos_type *ostype)
+static int os_alloc_create(struct target *target, const struct rtos_type *ostype,
+ struct command_context *cmd_ctx)
{
- int ret = os_alloc(target, ostype);
+ int ret = os_alloc(target, ostype, cmd_ctx);
if (ret == JIM_OK) {
ret = target->rtos->type->create(target);
@@ -108,6 +114,8 @@ int rtos_create(struct jim_getopt_info *goi, struct target *target)
return JIM_ERR;
}
+ struct command_context *cmd_ctx = current_command_context(goi->interp);
+
os_free(target);
e = jim_getopt_string(goi, &cp, NULL);
@@ -125,12 +133,12 @@ int rtos_create(struct jim_getopt_info *goi, struct target *target)
/* rtos_qsymbol() will iterate over all RTOSes. Allocate
* target->rtos here, and set it to the first RTOS type. */
- return os_alloc(target, rtos_types[0]);
+ return os_alloc(target, rtos_types[0], cmd_ctx);
}
for (x = 0; rtos_types[x]; x++)
if (strcmp(cp, rtos_types[x]->name) == 0)
- return os_alloc_create(target, rtos_types[x]);
+ return os_alloc_create(target, rtos_types[x], cmd_ctx);
Jim_SetResultFormatted(goi->interp, "Unknown RTOS type %s, try one of: ", cp);
res = Jim_GetResult(goi->interp);
@@ -503,51 +511,71 @@ static int rtos_put_gdb_reg_list(struct connection *connection,
int rtos_get_gdb_reg(struct connection *connection, int reg_num)
{
struct target *target = get_target_from_connection(connection);
- int64_t current_threadid = target->rtos->current_threadid;
- if ((target->rtos) && (current_threadid != -1) &&
- (current_threadid != 0) &&
- ((current_threadid != target->rtos->current_thread) ||
- (target->smp))) { /* in smp several current thread are possible */
- struct rtos_reg *reg_list;
- int num_regs;
+ threadid_t current_threadid = target->rtos->current_threadid;
+ if (!target->rtos ||
+ current_threadid == -1 ||
+ current_threadid == 0 ||
+ (current_threadid == target->rtos->current_thread &&
+ !target->smp)) { /* in smp several current thread are possible */
+ return ERROR_NOT_IMPLEMENTED;
+ }
- LOG_DEBUG("getting register %d for thread 0x%" PRIx64
- ", target->rtos->current_thread=0x%" PRIx64,
- reg_num,
- current_threadid,
- target->rtos->current_thread);
-
- int retval;
- if (target->rtos->type->get_thread_reg) {
- reg_list = calloc(1, sizeof(*reg_list));
- num_regs = 1;
- retval = target->rtos->type->get_thread_reg(target->rtos,
- current_threadid, reg_num, &reg_list[0]);
- if (retval != ERROR_OK) {
- LOG_ERROR("RTOS: failed to get register %d", reg_num);
- return retval;
- }
- } else {
- retval = target->rtos->type->get_thread_reg_list(target->rtos,
- current_threadid,
- &reg_list,
- &num_regs);
- if (retval != ERROR_OK) {
- LOG_ERROR("RTOS: failed to get register list");
- return retval;
- }
+ struct rtos_reg *reg_list;
+ int num_regs;
+
+ LOG_TARGET_DEBUG(target, "getting register %d for thread 0x%" PRIx64
+ ", target->rtos->current_thread=0x%" PRIx64,
+ reg_num, current_threadid, target->rtos->current_thread);
+
+ int retval;
+ if (target->rtos->type->get_thread_reg_value) {
+ uint32_t reg_size;
+ uint8_t *reg_value;
+ retval = target->rtos->type->get_thread_reg_value(target->rtos,
+ current_threadid, reg_num, &reg_size, &reg_value);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("RTOS: failed to get register %d", reg_num);
+ return retval;
}
- for (int i = 0; i < num_regs; ++i) {
- if (reg_list[i].number == (uint32_t)reg_num) {
- rtos_put_gdb_reg_list(connection, reg_list + i, 1);
- free(reg_list);
- return ERROR_OK;
- }
+ /* Create a reg_list with one register that can
+ * accommodate the full size of the one we just got the
+ * value for. To do that we allocate extra space off the
+ * end of the struct, relying on the fact that
+ * rtos_reg.value is the last element in the struct. */
+ reg_list = calloc(1, sizeof(*reg_list) + DIV_ROUND_UP(reg_size, 8));
+ if (!reg_list) {
+ free(reg_value);
+ LOG_ERROR("Failed to allocated reg_list for %d-byte register.",
+ reg_size);
+ return ERROR_FAIL;
}
+ reg_list[0].number = reg_num;
+ reg_list[0].size = reg_size;
+ memcpy(&reg_list[0].value, reg_value, DIV_ROUND_UP(reg_size, 8));
+ free(reg_value);
+ num_regs = 1;
+ } else {
+ retval = target->rtos->type->get_thread_reg_list(target->rtos,
+ current_threadid,
+ &reg_list,
+ &num_regs);
+ if (retval != ERROR_OK) {
+ LOG_ERROR("RTOS: failed to get register list");
+ return retval;
+ }
+ }
- free(reg_list);
+ for (int i = 0; i < num_regs; ++i) {
+ if (reg_list[i].number == (uint32_t)reg_num) {
+ rtos_put_gdb_reg_list(connection, reg_list + i, 1);
+ free(reg_list);
+ return ERROR_OK;
+ }
}
+
+ free(reg_list);
+
return ERROR_FAIL;
}
@@ -563,10 +591,9 @@ int rtos_get_gdb_reg_list(struct connection *connection)
struct rtos_reg *reg_list;
int num_regs;
- LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
- ", target->rtos->current_thread=0x%" PRIx64 "\r\n",
- current_threadid,
- target->rtos->current_thread);
+ LOG_TARGET_DEBUG(target, "RTOS: getting register list for thread 0x%" PRIx64
+ ", target->rtos->current_thread=0x%" PRIx64,
+ current_threadid, target->rtos->current_thread);
int retval = target->rtos->type->get_thread_reg_list(target->rtos,
current_threadid,
@@ -601,7 +628,7 @@ int rtos_set_reg(struct connection *connection, int reg_num,
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
- int64_t stack_ptr,
+ target_addr_t stack_ptr,
struct rtos_reg **reg_list,
int *num_regs)
{
@@ -613,7 +640,7 @@ int rtos_generic_stack_read(struct target *target,
}
/* Read the stack */
uint8_t *stack_data = malloc(stacking->stack_registers_size);
- uint32_t address = stack_ptr;
+ target_addr_t address = stack_ptr;
if (stacking->stack_growth_direction == 1)
address -= stacking->stack_registers_size;
@@ -626,7 +653,7 @@ int rtos_generic_stack_read(struct target *target,
LOG_ERROR("Error reading stack frame from thread");
return retval;
}
- LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32, address);
+ LOG_DEBUG("RTOS: Read stack frame at " TARGET_ADDR_FMT, address);
#if 0
LOG_OUTPUT("Stack Data :");
@@ -656,6 +683,9 @@ int rtos_generic_stack_read(struct target *target,
buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size);
else if (offset != -1)
buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size);
+
+ LOG_DEBUG("register %d has value 0x%" PRIx64, (*reg_list)[i].number,
+ buf_get_u64((*reg_list)[i].value, 0, 64));
}
free(stack_data);
@@ -663,6 +693,100 @@ int rtos_generic_stack_read(struct target *target,
return ERROR_OK;
}
+/* Read an individual register from the RTOS stack. */
+int rtos_generic_stack_read_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, struct rtos_reg *reg)
+{
+ LOG_DEBUG("stack_ptr=" TARGET_ADDR_FMT ", reg_num=%d", stack_ptr, reg_num);
+ unsigned total_count = MAX(stacking->total_register_count, stacking->num_output_registers);
+ unsigned i;
+ for (i = 0; i < total_count; i++) {
+ if (stacking->register_offsets[i].number == reg_num)
+ break;
+ }
+ if (i >= total_count) {
+ /* This register is not on the stack. Return error so a caller somewhere
+ * will just read the register directly from the target. */
+ return ERROR_FAIL;
+ }
+
+ const struct stack_register_offset *offsets = &stacking->register_offsets[i];
+ reg->size = offsets->width_bits;
+
+ unsigned width_bytes = DIV_ROUND_UP(offsets->width_bits, 8);
+ if (offsets->offset >= 0) {
+ target_addr_t address = stack_ptr;
+
+ if (stacking->stack_growth_direction == 1)
+ address -= stacking->stack_registers_size;
+
+ if (target_read_buffer(
+ target, address + offsets->offset,
+ width_bytes, reg->value) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_DEBUG("register %d has value 0x%" PRIx64, reg->number,
+ buf_get_u64(reg->value, 0, 64));
+ } else {
+ memset(reg->value, 0, width_bytes);
+ }
+
+ return ERROR_OK;
+}
+
+int rtos_generic_stack_write_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, uint8_t *reg_value)
+{
+ LOG_DEBUG("stack_ptr=" TARGET_ADDR_FMT ", reg_num=%d", stack_ptr, reg_num);
+ unsigned total_count = MAX(stacking->total_register_count, stacking->num_output_registers);
+ unsigned i;
+ for (i = 0; i < total_count; i++) {
+ if (stacking->register_offsets[i].number == reg_num)
+ break;
+ }
+ if (i >= total_count) {
+ /* This register is not on the stack. Return error so a caller somewhere
+ * will just read the register directly from the target. */
+ return ERROR_FAIL;
+ }
+
+ const struct stack_register_offset *offsets = &stacking->register_offsets[i];
+
+ unsigned width_bytes = DIV_ROUND_UP(offsets->width_bits, 8);
+ if (offsets->offset >= 0) {
+ target_addr_t address = stack_ptr;
+
+ if (stacking->stack_growth_direction == 1)
+ address -= stacking->stack_registers_size;
+
+ LOG_DEBUG("write 0x%" PRIx64 " to register %d",
+ buf_get_u64(reg_value, 0, offsets->width_bits), reg_num);
+ if (target_write_buffer(
+ target, address + offsets->offset,
+ width_bytes, reg_value) != ERROR_OK)
+ return ERROR_FAIL;
+ } else if (offsets->offset == -1) {
+ /* This register isn't on the stack, but is listed as one of those. We
+ * read it as 0, and ignore writes. */
+ } else if (offsets->offset == -2) {
+ /* This register requires computation when we "read" it. I'm not sure
+ * how to handle writes. We can't simply return error here because then
+ * the higher level code will end up writing the register in the halted
+ * core, which is definitely not the same as writing it for a thread. */
+ LOG_ERROR("Don't know how to write register %d with offset -2 in a thread.",
+ reg_num);
+ assert(0);
+ } else {
+ LOG_ERROR("Don't know how to handle offset <2.");
+ assert(0);
+ }
+
+ return ERROR_OK;
+}
+
static int rtos_try_next(struct target *target)
{
struct rtos *os = target->rtos;
@@ -685,10 +809,29 @@ static int rtos_try_next(struct target *target)
return 1;
}
-int rtos_update_threads(struct target *target)
+struct rtos *rtos_of_target(struct target *target)
{
+ /* Primarily consider the rtos field of the target itself, secondarily consider
+ * rtos field SMP leader target, then consider rtos field of any other target in the SMP group.
+ * Otherwise NULL return means that no associated non-zero rtos field could be found. */
+
+ struct target_list *pos;
+
if ((target->rtos) && (target->rtos->type))
- target->rtos->type->update_threads(target->rtos);
+ return target->rtos;
+
+ foreach_smp_target(pos, target->smp_targets)
+ if ((pos->target->rtos) && (pos->target->rtos->type))
+ return pos->target->rtos;
+
+ return NULL;
+}
+
+int rtos_update_threads(struct target *target)
+{
+ struct rtos *rtos = rtos_of_target(target);
+ if (rtos)
+ rtos->type->update_threads(rtos);
return ERROR_OK;
}
@@ -710,6 +853,13 @@ void rtos_free_threadlist(struct rtos *rtos)
}
}
+bool rtos_needs_fake_step(struct target *target, int64_t thread_id)
+{
+ if (target->rtos->type->needs_fake_step)
+ return target->rtos->type->needs_fake_step(target, thread_id);
+ return target->rtos->current_thread != thread_id;
+}
+
int rtos_read_buffer(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
@@ -725,3 +875,11 @@ int rtos_write_buffer(struct target *target, target_addr_t address,
return target->rtos->type->write_buffer(target->rtos, address, size, buffer);
return ERROR_NOT_IMPLEMENTED;
}
+
+struct target *rtos_swbp_target(struct target *target, target_addr_t address,
+ uint32_t length, enum breakpoint_type type)
+{
+ if (target->rtos->type->swbp_target)
+ return target->rtos->type->swbp_target(target->rtos, address, length, type);
+ return target;
+}
diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h
index 5ba8b26..210f4fc 100644
--- a/src/rtos/rtos.h
+++ b/src/rtos/rtos.h
@@ -9,6 +9,7 @@
#define OPENOCD_RTOS_RTOS_H
#include "server/server.h"
+#include "target/breakpoints.h"
#include "target/target.h"
#include <helper/jim-nvp.h>
@@ -46,14 +47,18 @@ struct rtos {
struct thread_detail *thread_details;
int thread_count;
int (*gdb_thread_packet)(struct connection *connection, char const *packet, int packet_size);
- int (*gdb_target_for_threadid)(struct connection *connection, int64_t thread_id, struct target **p_target);
+ int (*gdb_target_for_threadid)(struct connection *connection, threadid_t thread_id, struct target **p_target);
void *rtos_specific_params;
+ /* Populated in rtos.c, so that individual RTOSes can register commands. */
+ struct command_context *cmd_ctx;
};
struct rtos_reg {
uint32_t number;
uint32_t size;
uint8_t value[16];
+ /* WARNING: rtos_get_gdb_reg() relies on the fact that value is the last
+ * element of this struct. Any new fields should be added *before* value. */
};
struct rtos_type {
@@ -63,14 +68,23 @@ struct rtos_type {
int (*smp_init)(struct target *target);
int (*update_threads)(struct rtos *rtos);
/** Return a list of general registers, with their values filled out. */
- int (*get_thread_reg_list)(struct rtos *rtos, int64_t thread_id,
+ int (*get_thread_reg_list)(struct rtos *rtos, threadid_t thread_id,
struct rtos_reg **reg_list, int *num_regs);
- int (*get_thread_reg)(struct rtos *rtos, int64_t thread_id,
- uint32_t reg_num, struct rtos_reg *reg);
+ /** Return the size and value of the specified reg_num. The value is
+ * allocated by the callee and freed by the caller. */
+ int (*get_thread_reg_value)(struct rtos *rtos, threadid_t thread_id,
+ uint32_t reg_num, uint32_t *size, uint8_t **value);
int (*get_symbol_list_to_lookup)(struct symbol_table_elem *symbol_list[]);
int (*clean)(struct target *target);
char * (*ps_command)(struct target *target);
int (*set_reg)(struct rtos *rtos, uint32_t reg_num, uint8_t *reg_value);
+ /**
+ * Possibly work around an annoying gdb behaviour: when the current thread
+ * is changed in gdb, it assumes that the target can follow and also make
+ * the thread current. This is an assumption that cannot hold for a real
+ * target running a multi-threading OS. If an RTOS can do this, override
+ * needs_fake_step(). */
+ bool (*needs_fake_step)(struct target *target, threadid_t thread_id);
/* Implement these if different threads in the RTOS can see memory
* differently (for instance because address translation might be different
* for each thread). */
@@ -78,6 +92,12 @@ struct rtos_type {
uint8_t *buffer);
int (*write_buffer)(struct rtos *rtos, target_addr_t address, uint32_t size,
const uint8_t *buffer);
+ /* When a software breakpoint is set, it is set on only one target,
+ * because we assume memory is shared across them. By default this is the
+ * first target in the SMP group. Override this function to have
+ * breakpoint_add() use a different target. */
+ struct target * (*swbp_target)(struct rtos *rtos, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
};
struct stack_register_offset {
@@ -89,8 +109,9 @@ struct stack_register_offset {
};
struct rtos_register_stacking {
- unsigned char stack_registers_size;
- signed char stack_growth_direction;
+ unsigned stack_registers_size;
+ int stack_growth_direction;
+ /* The number of gdb general registers, in order. */
unsigned char num_output_registers;
/* Some targets require evaluating the stack to determine the
* actual stack pointer for a process. If this field is NULL,
@@ -102,6 +123,11 @@ struct rtos_register_stacking {
const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr);
const struct stack_register_offset *register_offsets;
+ /* Total number of registers on the stack, including the general ones. This
+ * may be 0 if there are no additional registers on the stack beyond the
+ * general ones. */
+ unsigned int total_register_count;
+
/* Optional field for targets which may have to implement their own stack read function.
* Because stack format can be weird or stack data needed to be edited before passing to the gdb.
*/
@@ -119,9 +145,17 @@ int rtos_set_reg(struct connection *connection, int reg_num,
uint8_t *reg_value);
int rtos_generic_stack_read(struct target *target,
const struct rtos_register_stacking *stacking,
- int64_t stack_ptr,
+ target_addr_t stack_ptr,
struct rtos_reg **reg_list,
int *num_regs);
+int rtos_generic_stack_read_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, struct rtos_reg *reg);
+int rtos_generic_stack_write_reg(struct target *target,
+ const struct rtos_register_stacking *stacking,
+ target_addr_t stack_ptr,
+ uint32_t reg_num, uint8_t *reg_value);
int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size);
int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
int rtos_get_gdb_reg(struct connection *connection, int reg_num);
@@ -131,10 +165,14 @@ void rtos_free_threadlist(struct rtos *rtos);
int rtos_smp_init(struct target *target);
/* function for handling symbol access */
int rtos_qsymbol(struct connection *connection, char const *packet, int packet_size);
+bool rtos_needs_fake_step(struct target *target, threadid_t thread_id);
int rtos_read_buffer(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer);
int rtos_write_buffer(struct target *target, target_addr_t address,
uint32_t size, const uint8_t *buffer);
+struct target *rtos_swbp_target(struct target *target, target_addr_t address,
+ uint32_t length, enum breakpoint_type type);
+struct rtos *rtos_of_target(struct target *target);
extern const struct rtos_type chibios_rtos;
extern const struct rtos_type chromium_ec_rtos;
diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c
index 5478080..0ca664f 100644
--- a/src/rtos/rtos_standard_stackings.c
+++ b/src/rtos/rtos_standard_stackings.c
@@ -11,6 +11,7 @@
#include "rtos.h"
#include "target/armv7m.h"
+#include "target/riscv/riscv.h"
#include "rtos_standard_stackings.h"
static const struct stack_register_offset rtos_standard_cortex_m3_stack_offsets[ARMV7M_NUM_CORE_REGS] = {
@@ -103,15 +104,181 @@ static const struct stack_register_offset rtos_standard_cortex_r4_stack_offsets[
{ 26, 0x04, 32 }, /* CSPR */
};
+static const struct stack_register_offset rtos_metal_rv32_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 32 },
+ { GDB_REGNO_RA, 0x04, 32 },
+ { GDB_REGNO_SP, 0x08, 32 },
+ { GDB_REGNO_GP, 0x0c, 32 },
+ { GDB_REGNO_TP, 0x10, 32 },
+ { GDB_REGNO_T0, 0x14, 32 },
+ { GDB_REGNO_T1, 0x18, 32 },
+ { GDB_REGNO_T2, 0x1c, 32 },
+ { GDB_REGNO_FP, 0x20, 32 },
+ { GDB_REGNO_S1, 0x24, 32 },
+ { GDB_REGNO_A0, 0x28, 32 },
+ { GDB_REGNO_A1, 0x2c, 32 },
+ { GDB_REGNO_A2, 0x30, 32 },
+ { GDB_REGNO_A3, 0x34, 32 },
+ { GDB_REGNO_A4, 0x38, 32 },
+ { GDB_REGNO_A5, 0x3c, 32 },
+ { GDB_REGNO_A6, 0x40, 32 },
+ { GDB_REGNO_A7, 0x44, 32 },
+ { GDB_REGNO_S2, 0x48, 32 },
+ { GDB_REGNO_S3, 0x4c, 32 },
+ { GDB_REGNO_S4, 0x50, 32 },
+ { GDB_REGNO_S5, 0x54, 32 },
+ { GDB_REGNO_S6, 0x58, 32 },
+ { GDB_REGNO_S7, 0x5c, 32 },
+ { GDB_REGNO_S8, 0x60, 32 },
+ { GDB_REGNO_S9, 0x64, 32 },
+ { GDB_REGNO_S10, 0x68, 32 },
+ { GDB_REGNO_S11, 0x6c, 32 },
+ { GDB_REGNO_T3, 0x70, 32 },
+ { GDB_REGNO_T4, 0x74, 32 },
+ { GDB_REGNO_T5, 0x78, 32 },
+ { GDB_REGNO_T6, 0x7c, 32 },
+ { GDB_REGNO_PC, 0x80, 32 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 0x84, 32 },
+};
+
+static const struct stack_register_offset rtos_metal_rv64_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 64 },
+ { GDB_REGNO_RA, 2 * 0x04, 64 },
+ { GDB_REGNO_SP, 2 * 0x08, 64 },
+ { GDB_REGNO_GP, 2 * 0x0c, 64 },
+ { GDB_REGNO_TP, 2 * 0x10, 64 },
+ { GDB_REGNO_T0, 2 * 0x14, 64 },
+ { GDB_REGNO_T1, 2 * 0x18, 64 },
+ { GDB_REGNO_T2, 2 * 0x1c, 64 },
+ { GDB_REGNO_FP, 2 * 0x20, 64 },
+ { GDB_REGNO_S1, 2 * 0x24, 64 },
+ { GDB_REGNO_A0, 2 * 0x28, 64 },
+ { GDB_REGNO_A1, 2 * 0x2c, 64 },
+ { GDB_REGNO_A2, 2 * 0x30, 64 },
+ { GDB_REGNO_A3, 2 * 0x34, 64 },
+ { GDB_REGNO_A4, 2 * 0x38, 64 },
+ { GDB_REGNO_A5, 2 * 0x3c, 64 },
+ { GDB_REGNO_A6, 2 * 0x40, 64 },
+ { GDB_REGNO_A7, 2 * 0x44, 64 },
+ { GDB_REGNO_S2, 2 * 0x48, 64 },
+ { GDB_REGNO_S3, 2 * 0x4c, 64 },
+ { GDB_REGNO_S4, 2 * 0x50, 64 },
+ { GDB_REGNO_S5, 2 * 0x54, 64 },
+ { GDB_REGNO_S6, 2 * 0x58, 64 },
+ { GDB_REGNO_S7, 2 * 0x5c, 64 },
+ { GDB_REGNO_S8, 2 * 0x60, 64 },
+ { GDB_REGNO_S9, 2 * 0x64, 64 },
+ { GDB_REGNO_S10, 2 * 0x68, 64 },
+ { GDB_REGNO_S11, 2 * 0x6c, 64 },
+ { GDB_REGNO_T3, 2 * 0x70, 64 },
+ { GDB_REGNO_T4, 2 * 0x74, 64 },
+ { GDB_REGNO_T5, 2 * 0x78, 64 },
+ { GDB_REGNO_T6, 2 * 0x7c, 64 },
+ { GDB_REGNO_PC, 2 * 0x80, 64 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 2 * 0x84, 64 },
+};
+
+static const struct stack_register_offset rtos_standard_rv32_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 32 },
+ { GDB_REGNO_RA, 0x04, 32 },
+ { GDB_REGNO_SP, -2, 32 },
+ { GDB_REGNO_GP, -2, 32 },
+ { GDB_REGNO_TP, -2, 32 },
+ { GDB_REGNO_T0, 0x08, 32 },
+ { GDB_REGNO_T1, 0x0c, 32 },
+ { GDB_REGNO_T2, 0x10, 32 },
+ { GDB_REGNO_FP, 0x14, 32 },
+ { GDB_REGNO_S1, 0x18, 32 },
+ { GDB_REGNO_A0, 0x1c, 32 },
+ { GDB_REGNO_A1, 0x20, 32 },
+ { GDB_REGNO_A2, 0x24, 32 },
+ { GDB_REGNO_A3, 0x28, 32 },
+ { GDB_REGNO_A4, 0x2c, 32 },
+ { GDB_REGNO_A5, 0x30, 32 },
+ { GDB_REGNO_A6, 0x34, 32 },
+ { GDB_REGNO_A7, 0x38, 32 },
+ { GDB_REGNO_S2, 0x3c, 32 },
+ { GDB_REGNO_S3, 0x40, 32 },
+ { GDB_REGNO_S4, 0x44, 32 },
+ { GDB_REGNO_S5, 0x48, 32 },
+ { GDB_REGNO_S6, 0x4c, 32 },
+ { GDB_REGNO_S7, 0x50, 32 },
+ { GDB_REGNO_S8, 0x54, 32 },
+ { GDB_REGNO_S9, 0x58, 32 },
+ { GDB_REGNO_S10, 0x5c, 32 },
+ { GDB_REGNO_S11, 0x60, 32 },
+ { GDB_REGNO_T3, 0x64, 32 },
+ { GDB_REGNO_T4, 0x68, 32 },
+ { GDB_REGNO_T5, 0x6c, 32 },
+ { GDB_REGNO_T6, 0x70, 32 },
+ { GDB_REGNO_PC, 0, 32 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 29 * 4, 32 },
+};
+
+static const struct stack_register_offset rtos_standard_rv64_stack_offsets[] = {
+ /* zero isn't on the stack. By making its offset -1 we leave the value at 0
+ * inside rtos_generic_stack_read(). */
+ { GDB_REGNO_ZERO, -1, 64 },
+ { GDB_REGNO_RA, 2 * 0x04, 64 },
+ { GDB_REGNO_SP, -2, 64 },
+ { GDB_REGNO_GP, -2, 64 },
+ { GDB_REGNO_TP, -2, 64 },
+ { GDB_REGNO_T0, 2 * 0x08, 64 },
+ { GDB_REGNO_T1, 2 * 0x0c, 64 },
+ { GDB_REGNO_T2, 2 * 0x10, 64 },
+ { GDB_REGNO_FP, 2 * 0x14, 64 },
+ { GDB_REGNO_S1, 2 * 0x18, 64 },
+ { GDB_REGNO_A0, 2 * 0x1c, 64 },
+ { GDB_REGNO_A1, 2 * 0x20, 64 },
+ { GDB_REGNO_A2, 2 * 0x24, 64 },
+ { GDB_REGNO_A3, 2 * 0x28, 64 },
+ { GDB_REGNO_A4, 2 * 0x2c, 64 },
+ { GDB_REGNO_A5, 2 * 0x30, 64 },
+ { GDB_REGNO_A6, 2 * 0x34, 64 },
+ { GDB_REGNO_A7, 2 * 0x38, 64 },
+ { GDB_REGNO_S2, 2 * 0x3c, 64 },
+ { GDB_REGNO_S3, 2 * 0x40, 64 },
+ { GDB_REGNO_S4, 2 * 0x44, 64 },
+ { GDB_REGNO_S5, 2 * 0x48, 64 },
+ { GDB_REGNO_S6, 2 * 0x4c, 64 },
+ { GDB_REGNO_S7, 2 * 0x50, 64 },
+ { GDB_REGNO_S8, 2 * 0x54, 64 },
+ { GDB_REGNO_S9, 2 * 0x58, 64 },
+ { GDB_REGNO_S10, 2 * 0x5c, 64 },
+ { GDB_REGNO_S11, 2 * 0x60, 64 },
+ { GDB_REGNO_T3, 2 * 0x64, 64 },
+ { GDB_REGNO_T4, 2 * 0x68, 64 },
+ { GDB_REGNO_T5, 2 * 0x6c, 64 },
+ { GDB_REGNO_T6, 2 * 0x70, 64 },
+ { GDB_REGNO_PC, 0, 64 },
+ /* Registers below are on the stack, but not what gdb expects to return from
+ * a 'g' packet so are only accessible through get_reg. */
+ { GDB_REGNO_MSTATUS, 2 * 29 * 4, 64 },
+};
+
static target_addr_t rtos_generic_stack_align(struct target *target,
const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr, int align)
{
- target_addr_t new_stack_ptr;
- target_addr_t aligned_stack_ptr;
- new_stack_ptr = stack_ptr - stacking->stack_growth_direction *
- stacking->stack_registers_size;
- aligned_stack_ptr = new_stack_ptr & ~((target_addr_t)align - 1);
+ target_addr_t new_stack_ptr = stack_ptr;
+ if (stacking->stack_growth_direction > 0)
+ new_stack_ptr -= stacking->stack_registers_size;
+ else
+ new_stack_ptr += stacking->stack_registers_size;
+ target_addr_t aligned_stack_ptr = new_stack_ptr & ~((int64_t)align - 1);
+
if (aligned_stack_ptr != new_stack_ptr &&
stacking->stack_growth_direction == -1) {
/* If we have a downward growing stack, the simple alignment code
@@ -230,3 +397,39 @@ const struct rtos_register_stacking rtos_standard_cortex_r4_stacking = {
.calculate_process_stack = rtos_generic_stack_align8,
.register_offsets = rtos_standard_cortex_r4_stack_offsets
};
+
+const struct rtos_register_stacking rtos_metal_rv32_stacking = {
+ .stack_registers_size = (32 + 2) * 4,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_metal_rv32_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_metal_rv32_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_standard_rv32_stacking = {
+ .stack_registers_size = (32 + 2) * 4,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_standard_rv32_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_standard_rv32_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_metal_rv64_stacking = {
+ .stack_registers_size = (32 + 2) * 8,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_metal_rv64_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_metal_rv64_stack_offsets)
+};
+
+const struct rtos_register_stacking rtos_standard_rv64_stacking = {
+ .stack_registers_size = (32 + 2) * 8,
+ .stack_growth_direction = -1,
+ .num_output_registers = 33,
+ .calculate_process_stack = rtos_generic_stack_align8,
+ .register_offsets = rtos_standard_rv64_stack_offsets,
+ .total_register_count = ARRAY_SIZE(rtos_standard_rv64_stack_offsets)
+};
diff --git a/src/rtos/rtos_standard_stackings.h b/src/rtos/rtos_standard_stackings.h
index 99fbe07..40a3503 100644
--- a/src/rtos/rtos_standard_stackings.h
+++ b/src/rtos/rtos_standard_stackings.h
@@ -21,4 +21,9 @@ target_addr_t rtos_cortex_m_stack_align(struct target *target,
const uint8_t *stack_data, const struct rtos_register_stacking *stacking,
target_addr_t stack_ptr, size_t xpsr_offset);
+extern const struct rtos_register_stacking rtos_standard_rv32_stacking;
+extern const struct rtos_register_stacking rtos_standard_rv64_stacking;
+extern const struct rtos_register_stacking rtos_metal_rv32_stacking;
+extern const struct rtos_register_stacking rtos_metal_rv64_stacking;
+
#endif /* OPENOCD_RTOS_RTOS_STANDARD_STACKINGS_H */
diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c
index 5052bf4..dbbf027 100644
--- a/src/server/gdb_server.c
+++ b/src/server/gdb_server.c
@@ -149,6 +149,47 @@ static bool gdb_use_target_description = true;
/* current processing free-run type, used by file-I/O */
static char gdb_running_type;
+/* Find an available target in the SMP group that gdb is connected to. For
+ * commands that affect an entire SMP group (like memory access and run control)
+ * this will give better results than returning the unavailable target and having
+ * the command fail. If gdb was aware that targets can be unavailable we
+ * wouldn't need this logic.
+ */
+struct target *get_available_target_from_connection(struct connection *connection)
+{
+ struct gdb_service *gdb_service = connection->service->priv;
+ struct target *target = gdb_service->target;
+ if (target->state == TARGET_UNAVAILABLE && target->smp) {
+ struct target_list *tlist;
+ foreach_smp_target(tlist, target->smp_targets) {
+ struct target *t = tlist->target;
+ if (t->state != TARGET_UNAVAILABLE)
+ return t;
+ }
+ /* If we can't find an available target, just return the
+ * original. */
+ }
+ return target;
+}
+
+/** Return true iff the given connection includes the given target. */
+static bool gdb_connection_includes_target(struct connection *connection, struct target *target)
+{
+ struct gdb_service *gdb_service = connection->service->priv;
+ struct target *service_target = gdb_service->target;
+ if (service_target->smp) {
+ struct target_list *tlist;
+ foreach_smp_target(tlist, service_target->smp_targets) {
+ struct target *t = tlist->target;
+ if (t == target)
+ return true;
+ }
+ return false;
+ }
+ /* Non-SMP target. */
+ return service_target == target;
+}
+
static int gdb_last_signal(struct target *target)
{
LOG_TARGET_DEBUG(target, "Debug reason is: %s",
@@ -418,6 +459,8 @@ static int gdb_put_packet_inner(struct connection *connection,
* At this point we should have nothing in the input queue from GDB,
* however sometimes '-' is sent even though we've already received
* an ACK (+) for everything we've sent off.
+ *
+ * This code appears to sometimes eat a ^C coming from gdb.
*/
int gotdata;
for (;; ) {
@@ -436,7 +479,7 @@ static int gdb_put_packet_inner(struct connection *connection,
break;
}
- LOG_WARNING("Discard unexpected char %c", reply);
+ LOG_DEBUG("Discard unexpected char %c", reply);
}
#endif
@@ -784,9 +827,12 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "W00");
} else {
struct target *ct;
- if (target->rtos) {
- target->rtos->current_threadid = target->rtos->current_thread;
- target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &ct);
+ struct rtos *rtos;
+
+ rtos = rtos_of_target(target);
+ if (rtos) {
+ rtos->current_threadid = rtos->current_thread;
+ rtos->gdb_target_for_threadid(connection, rtos->current_threadid, &ct);
} else {
ct = target;
}
@@ -824,9 +870,9 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
}
current_thread[0] = '\0';
- if (target->rtos)
+ if (rtos)
snprintf(current_thread, sizeof(current_thread), "thread:%" PRIx64 ";",
- target->rtos->current_thread);
+ rtos->current_thread);
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply), "T%2.2x%s%s",
signal_var, stop_reason, current_thread);
@@ -955,9 +1001,9 @@ static int gdb_target_callback_event_handler(struct target *target,
enum target_event event, void *priv)
{
struct connection *connection = priv;
- struct gdb_service *gdb_service = connection->service->priv;
- if (gdb_service->target != target)
+ /* Propagate this event if it's for any of the targets on this gdb connection. */
+ if (!gdb_connection_includes_target(connection, target))
return ERROR_OK;
switch (event) {
@@ -1379,8 +1425,13 @@ static int gdb_get_register_packet(struct connection *connection,
LOG_DEBUG("-");
#endif
- if ((target->rtos) && (rtos_get_gdb_reg(connection, reg_num) == ERROR_OK))
- return ERROR_OK;
+ if (target->rtos) {
+ retval = rtos_get_gdb_reg(connection, reg_num);
+ if (retval == ERROR_OK)
+ return ERROR_OK;
+ if (retval != ERROR_NOT_IMPLEMENTED)
+ return gdb_error(connection, retval);
+ }
retval = target_get_gdb_reg_list_noread(target, &reg_list, &reg_list_size,
REG_CLASS_ALL);
@@ -1390,7 +1441,7 @@ static int gdb_get_register_packet(struct connection *connection,
if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
!reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
- return ERROR_SERVER_REMOTE_CLOSED;
+ return gdb_error(connection, ERROR_FAIL);
}
reg_packet = calloc(DIV_ROUND_UP(reg_list[reg_num]->size, 8) * 2 + 1, 1); /* plus one for string termination null */
@@ -1493,7 +1544,7 @@ static int gdb_error(struct connection *connection, int retval)
static int gdb_read_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1501,7 +1552,7 @@ static int gdb_read_memory_packet(struct connection *connection,
uint8_t *buffer;
char *hex_buffer;
- int retval = ERROR_OK;
+ int retval;
/* skip command character */
packet++;
@@ -1568,7 +1619,7 @@ static int gdb_read_memory_packet(struct connection *connection,
static int gdb_write_memory_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1619,7 +1670,7 @@ static int gdb_write_memory_packet(struct connection *connection,
static int gdb_write_memory_binary_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
uint64_t addr = 0;
uint32_t len = 0;
@@ -1698,7 +1749,7 @@ static int gdb_write_memory_binary_packet(struct connection *connection,
static int gdb_step_continue_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
int current = 0;
uint64_t address = 0x0;
int retval = ERROR_OK;
@@ -1726,7 +1777,7 @@ static int gdb_step_continue_packet(struct connection *connection,
static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
int type;
enum breakpoint_type bp_type = BKPT_SOFT /* dummy init to avoid warning */;
enum watchpoint_rw wp_type = WPT_READ /* dummy init to avoid warning */;
@@ -1775,6 +1826,12 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
case 0:
case 1:
if (packet[0] == 'Z') {
+ struct target *bp_target = target;
+ if (target->rtos && bp_type == BKPT_SOFT) {
+ bp_target = rtos_swbp_target(target, address, size, bp_type);
+ if (!bp_target)
+ return ERROR_FAIL;
+ }
retval = breakpoint_add(target, address, size, bp_type);
if (retval == ERROR_NOT_IMPLEMENTED) {
/* Send empty reply to report that breakpoints of this type are not supported */
@@ -1908,7 +1965,7 @@ static int gdb_memory_map(struct connection *connection,
* have to regenerate it a couple of times.
*/
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
struct flash_bank *p;
char *xml = NULL;
int size = 0;
@@ -2613,6 +2670,7 @@ static int gdb_target_description_supported(struct target *target, bool *support
&reg_list_size, REG_CLASS_ALL);
if (retval != ERROR_OK) {
LOG_ERROR("get register list failed");
+ reg_list = NULL;
goto error;
}
@@ -2757,6 +2815,7 @@ static int gdb_query_packet(struct connection *connection,
cmd = malloc((packet_size - 6) / 2 + 1);
size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
cmd[len] = 0;
+ LOG_DEBUG("qRcmd: %s", cmd);
/* We want to print all debug output to GDB connection */
gdb_connection->output_flag = GDB_OUTPUT_ALL;
@@ -3006,6 +3065,24 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
/* simple case, a continue packet */
if (parse[0] == 'c') {
gdb_running_type = 'c';
+
+ if (target->state == TARGET_UNAVAILABLE) {
+ struct target *available_target = get_available_target_from_connection(connection);
+ if (target == available_target) {
+ LOG_DEBUG("All targets for this gdb connection "
+ "are unavailable. Fake to gdb that the resume "
+ "succeeded and the target is now running.");
+ gdb_connection->frontend_state = TARGET_RUNNING;
+ gdb_connection->output_flag = GDB_OUTPUT_ALL;
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
+ return true;
+ }
+ LOG_TARGET_DEBUG(target, "Target is unavailable. Resume %s instead.",
+ target_name(available_target));
+ /* Resume an available target. */
+ target = available_target;
+ }
+
LOG_DEBUG("target %s continue", target_name(target));
gdb_connection->output_flag = GDB_OUTPUT_ALL;
retval = target_resume(target, 1, 0, 0, 0);
@@ -3052,7 +3129,23 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
if (target->rtos) {
/* FIXME: why is this necessary? rtos state should be up-to-date here already! */
- rtos_update_threads(target);
+
+ /* Sometimes this results in picking a different thread than
+ * gdb just requested to step. Then we fake it, and now there's
+ * a different thread selected than gdb expects, so register
+ * accesses go to the wrong one!
+ * E.g.:
+ * Hg1$
+ * P8=72101ce197869329$ # write r8 on thread 1
+ * g$
+ * vCont?$
+ * vCont;s:1;c$ # rtos_update_threads changes to other thread
+ * g$
+ * qXfer:threads:read::0,fff$
+ * P8=cc060607eb89ca7f$ # write r8 on other thread
+ * g$
+ * */
+ /* rtos_update_threads(target); */
target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
@@ -3060,8 +3153,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
* check if the thread to be stepped is the current rtos thread
* if not, we must fake the step
*/
- if (target->rtos->current_thread != thread_id)
- fake_step = true;
+ fake_step = rtos_needs_fake_step(target, thread_id);
}
if (parse[0] == ';') {
@@ -3098,19 +3190,15 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
gdb_connection->output_flag = GDB_OUTPUT_ALL;
target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
- /*
- * work around an annoying gdb behaviour: when the current thread
- * is changed in gdb, it assumes that the target can follow and also
- * make the thread current. This is an assumption that cannot hold
- * for a real target running a multi-threading OS. We just fake
- * the step to not trigger an internal error in gdb. See
- * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
- */
if (fake_step) {
+ /* We just fake the step to not trigger an internal error in
+ * gdb. See https://sourceware.org/bugzilla/show_bug.cgi?id=22925
+ * for details. */
int sig_reply_len;
char sig_reply[128];
LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+ target->rtos->current_threadid = thread_id;
sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
"T05thread:%016"PRIx64";", thread_id);
@@ -3134,9 +3222,15 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
return true;
}
- retval = target_step(ct, current_pc, 0, 0);
- if (retval == ERROR_TARGET_NOT_HALTED)
- LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+ if (ct->state == TARGET_UNAVAILABLE) {
+ LOG_TARGET_ERROR(ct, "Target is unavailable, so cannot be stepped. "
+ "Pretending to gdb that it is running until it's available again.");
+ retval = ERROR_FAIL;
+ } else {
+ retval = target_step(ct, current_pc, 0, 0);
+ if (retval == ERROR_TARGET_NOT_HALTED)
+ LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+ }
/* if step was successful send a reply back to gdb */
if (retval == ERROR_OK) {
@@ -3207,7 +3301,7 @@ static void gdb_restart_inferior(struct connection *connection, const char *pack
static bool gdb_handle_vrun_packet(struct connection *connection, const char *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
const char *parse = packet;
/* Skip "vRun" */
@@ -3253,7 +3347,7 @@ static int gdb_v_packet(struct connection *connection,
struct gdb_connection *gdb_connection = connection->priv;
int result;
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
if (strncmp(packet, "vCont", 5) == 0) {
bool handled;
@@ -3432,7 +3526,7 @@ static int gdb_detach(struct connection *connection)
static int gdb_fileio_response_packet(struct connection *connection,
char const *packet, int packet_size)
{
- struct target *target = get_target_from_connection(connection);
+ struct target *target = get_available_target_from_connection(connection);
char *separator;
char *parsing_point;
int fileio_retcode = strtoul(packet + 1, &separator, 16);
@@ -3725,10 +3819,11 @@ static int gdb_input_inner(struct connection *connection)
}
if (gdb_con->ctrl_c) {
- if (target->state == TARGET_RUNNING) {
- struct target *t = target;
- if (target->rtos)
- target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
+ struct target *available_target = get_available_target_from_connection(connection);
+ if (available_target->state == TARGET_RUNNING) {
+ struct target *t = available_target;
+ if (available_target->rtos)
+ available_target->rtos->gdb_target_for_threadid(connection, target->rtos->current_threadid, &t);
retval = target_halt(t);
if (retval == ERROR_OK)
retval = target_poll(t);
@@ -3736,7 +3831,8 @@ static int gdb_input_inner(struct connection *connection)
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
gdb_con->ctrl_c = false;
} else {
- LOG_INFO("The target is not running when halt was requested, stopping GDB.");
+ LOG_TARGET_INFO(target, "Not running when halt was requested, stopping GDB. (state=%d)",
+ target->state);
target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
}
diff --git a/src/server/server.h b/src/server/server.h
index ea1e94e..c9d4698 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -118,6 +118,5 @@ COMMAND_HELPER(server_port_command, unsigned short *out);
#define ERROR_SERVER_REMOTE_CLOSED (-400)
#define ERROR_CONNECTION_REJECTED (-401)
-#define ERROR_SERVER_INTERRUPTED (-402)
#endif /* OPENOCD_SERVER_SERVER_H */
diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c
index c39a980..77f7673 100644
--- a/src/target/breakpoints.c
+++ b/src/target/breakpoints.c
@@ -15,6 +15,7 @@
#include "target.h"
#include <helper/log.h>
#include "breakpoints.h"
+#include "rtos/rtos.h"
#include "smp.h"
enum breakpoint_watchpoint {
@@ -78,7 +79,7 @@ static int breakpoint_add_internal(struct target *target,
reason = "resource not available";
goto fail;
case ERROR_TARGET_NOT_HALTED:
- reason = "target running";
+ reason = "target not halted";
goto fail;
default:
reason = "unknown reason";
@@ -210,16 +211,12 @@ int breakpoint_add(struct target *target,
uint32_t length,
enum breakpoint_type type)
{
- if (target->smp) {
- struct target_list *head;
-
- if (type == BKPT_SOFT) {
- head = list_first_entry(target->smp_targets, struct target_list, lh);
- return breakpoint_add_internal(head->target, address, length, type);
- }
-
- foreach_smp_target(head, target->smp_targets) {
- struct target *curr = head->target;
+ if (target->smp && type == BKPT_HARD) {
+ struct target_list *list_node;
+ foreach_smp_target(list_node, target->smp_targets) {
+ struct target *curr = list_node->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = breakpoint_add_internal(curr, address, length, type);
if (retval != ERROR_OK)
return retval;
@@ -227,6 +224,8 @@ int breakpoint_add(struct target *target,
return ERROR_OK;
} else {
+ /* For software breakpoints on SMP targets, only set them on a
+ * single target. We assume that SMP targets share memory. */
return breakpoint_add_internal(target, address, length, type);
}
}
@@ -241,6 +240,8 @@ int context_breakpoint_add(struct target *target,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = context_breakpoint_add_internal(curr, asid, length, type);
if (retval != ERROR_OK)
return retval;
@@ -263,6 +264,8 @@ int hybrid_breakpoint_add(struct target *target,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type);
if (retval != ERROR_OK)
return retval;
@@ -273,11 +276,17 @@ int hybrid_breakpoint_add(struct target *target,
return hybrid_breakpoint_add_internal(target, address, asid, length, type);
}
-/* free up a breakpoint */
-static int breakpoint_free(struct target *target, struct breakpoint *breakpoint_to_remove)
+/* Free the data structures we use to track a breakpoint on data_target.
+ * Remove the actual breakpoint from breakpoint_target.
+ * This separation is useful when a software breakpoint is tracked on a target
+ * that is currently unavailable, but the breakpoint also affects a target that
+ * is available.
+ */
+static int breakpoint_free(struct target *data_target, struct target *breakpoint_target,
+ struct breakpoint *breakpoint_to_remove)
{
- struct breakpoint *breakpoint = target->breakpoints;
- struct breakpoint **breakpoint_p = &target->breakpoints;
+ struct breakpoint *breakpoint = data_target->breakpoints;
+ struct breakpoint **breakpoint_p = &data_target->breakpoints;
int retval;
while (breakpoint) {
@@ -288,16 +297,16 @@ static int breakpoint_free(struct target *target, struct breakpoint *breakpoint_
}
if (!breakpoint)
- return ERROR_OK;
+ return ERROR_BREAKPOINT_NOT_FOUND;
- retval = target_remove_breakpoint(target, breakpoint);
+ retval = target_remove_breakpoint(breakpoint_target, breakpoint);
if (retval != ERROR_OK) {
- LOG_TARGET_ERROR(target, "could not remove breakpoint #%d on this target",
+ LOG_TARGET_ERROR(breakpoint_target, "could not remove breakpoint #%d on this target",
breakpoint->number);
return retval;
}
- LOG_TARGET_DEBUG(target, "free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
+ LOG_TARGET_DEBUG(data_target, "free BPID: %" PRIu32 " --> %d", breakpoint->unique_id, retval);
(*breakpoint_p) = breakpoint->next;
free(breakpoint->orig_instr);
free(breakpoint);
@@ -305,24 +314,6 @@ static int breakpoint_free(struct target *target, struct breakpoint *breakpoint_
return ERROR_OK;
}
-static int breakpoint_remove_internal(struct target *target, target_addr_t address)
-{
- struct breakpoint *breakpoint = target->breakpoints;
-
- while (breakpoint) {
- if ((breakpoint->address == address) ||
- (breakpoint->address == 0 && breakpoint->asid == address))
- break;
- breakpoint = breakpoint->next;
- }
-
- if (breakpoint) {
- return breakpoint_free(target, breakpoint);
- } else {
- return ERROR_BREAKPOINT_NOT_FOUND;
- }
-}
-
static int breakpoint_remove_all_internal(struct target *target)
{
LOG_TARGET_DEBUG(target, "Delete all breakpoints");
@@ -333,7 +324,7 @@ static int breakpoint_remove_all_internal(struct target *target)
while (breakpoint) {
struct breakpoint *tmp = breakpoint;
breakpoint = breakpoint->next;
- int status = breakpoint_free(target, tmp);
+ int status = breakpoint_free(target, target, tmp);
if (status != ERROR_OK)
retval = status;
}
@@ -343,41 +334,91 @@ static int breakpoint_remove_all_internal(struct target *target)
int breakpoint_remove(struct target *target, target_addr_t address)
{
- int retval = ERROR_OK;
- unsigned int num_found_breakpoints = 0;
- if (target->smp) {
- struct target_list *head;
-
- foreach_smp_target(head, target->smp_targets) {
- struct target *curr = head->target;
- int status = breakpoint_remove_internal(curr, address);
-
- if (status != ERROR_BREAKPOINT_NOT_FOUND) {
- num_found_breakpoints++;
+ if (!target->smp) {
+ struct breakpoint *breakpoint = breakpoint_find(target, address);
+ if (breakpoint)
+ return breakpoint_free(target, target, breakpoint);
+ return ERROR_BREAKPOINT_NOT_FOUND;
+ }
- if (status != ERROR_OK) {
- LOG_TARGET_ERROR(curr, "failed to remove breakpoint at address " TARGET_ADDR_FMT, address);
- retval = status;
- }
+ int retval = ERROR_OK;
+ unsigned int found = 0;
+ struct target_list *head;
+ /* Target where we found a software breakpoint. */
+ struct target *software_breakpoint_target = NULL;
+ struct breakpoint *software_breakpoint = NULL;
+ /* Target that is available. */
+ struct target *available_target = NULL;
+ /* Target that is available and halted. */
+ struct target *halted_target = NULL;
+
+ foreach_smp_target(head, target->smp_targets) {
+ struct target *curr = head->target;
+
+ if (!available_target && curr->state != TARGET_UNAVAILABLE)
+ available_target = curr;
+ if (!halted_target && curr->state == TARGET_HALTED)
+ halted_target = curr;
+
+ struct breakpoint *breakpoint = breakpoint_find(curr, address);
+ if (!breakpoint)
+ continue;
+
+ found++;
+
+ if (breakpoint->type == BKPT_SOFT) {
+ /* Software breakpoints are set on only one of the SMP
+ * targets. We can remove them through any of the SMP
+ * targets. */
+ if (software_breakpoint_target) {
+ LOG_TARGET_WARNING(curr, "Already found software breakpoint at "
+ TARGET_ADDR_FMT " on %s.", address, target_name(software_breakpoint_target));
+ } else {
+ assert(!software_breakpoint_target);
+ software_breakpoint_target = curr;
+ software_breakpoint = breakpoint;
}
- }
-
- } else {
- retval = breakpoint_remove_internal(target, address);
-
- if (retval != ERROR_BREAKPOINT_NOT_FOUND) {
- num_found_breakpoints++;
-
- if (retval != ERROR_OK)
- LOG_TARGET_ERROR(target, "failed to remove breakpoint at address " TARGET_ADDR_FMT, address);
+ } else {
+ int status = breakpoint_free(curr, curr, breakpoint);
+ if (status != ERROR_OK)
+ retval = status;
}
}
- if (num_found_breakpoints == 0) {
- LOG_TARGET_ERROR(target, "no breakpoint at address " TARGET_ADDR_FMT " found", address);
+ if (!found) {
+ LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address);
return ERROR_BREAKPOINT_NOT_FOUND;
}
+ if (software_breakpoint) {
+ struct target *remove_target;
+ if (software_breakpoint_target->state == TARGET_HALTED)
+ remove_target = software_breakpoint_target;
+ else if (halted_target)
+ remove_target = halted_target;
+ else
+ remove_target = available_target;
+
+ if (remove_target) {
+ LOG_DEBUG("Removing software breakpoint found on %s using %s (address="
+ TARGET_ADDR_FMT ").",
+ target_name(software_breakpoint_target),
+ target_name(remove_target),
+ address);
+ /* Remove the software breakpoint through
+ * remove_target, but update the breakpoints structure
+ * of software_breakpoint_target. */
+ int status = breakpoint_free(software_breakpoint_target, remove_target, software_breakpoint);
+ if (status != ERROR_OK)
+ /* TODO: If there is an error, can we try to remove the
+ * same breakpoint from a different target? */
+ retval = status;
+ } else {
+ LOG_WARNING("No halted target found to remove software breakpoint at "
+ TARGET_ADDR_FMT ".", address);
+ }
+ }
+
return retval;
}
@@ -395,7 +436,7 @@ static int watchpoint_free(struct target *target, struct watchpoint *watchpoint_
}
if (!watchpoint)
- return ERROR_OK;
+ return ERROR_WATCHPOINT_NOT_FOUND;
retval = target_remove_watchpoint(target, watchpoint);
if (retval != ERROR_OK) {
LOG_TARGET_ERROR(target, "could not remove watchpoint #%d on this target",
@@ -491,7 +532,8 @@ struct breakpoint *breakpoint_find(struct target *target, target_addr_t address)
struct breakpoint *breakpoint = target->breakpoints;
while (breakpoint) {
- if (breakpoint->address == address)
+ if (breakpoint->address == address ||
+ (breakpoint->address == 0 && breakpoint->asid == address))
return breakpoint;
breakpoint = breakpoint->next;
}
@@ -542,7 +584,7 @@ static int watchpoint_add_internal(struct target *target, target_addr_t address,
reason = "resource not available";
goto bye;
case ERROR_TARGET_NOT_HALTED:
- reason = "target running";
+ reason = "target not halted";
goto bye;
default:
reason = "unrecognized error";
@@ -573,6 +615,8 @@ int watchpoint_add(struct target *target, target_addr_t address,
foreach_smp_target(head, target->smp_targets) {
struct target *curr = head->target;
+ if (curr->state == TARGET_UNAVAILABLE)
+ continue;
int retval = watchpoint_add_internal(curr, address, length, rw, value, mask);
if (retval != ERROR_OK)
return retval;
diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h
index 64c0ce2..0ec65de 100644
--- a/src/target/breakpoints.h
+++ b/src/target/breakpoints.h
@@ -47,7 +47,7 @@ struct watchpoint {
bool is_set;
unsigned int number;
struct watchpoint *next;
- int unique_id;
+ uint32_t unique_id;
};
int breakpoint_clear_target(struct target *target);
diff --git a/src/target/dsp563xx.c b/src/target/dsp563xx.c
index 80cca1e..1e71803 100644
--- a/src/target/dsp563xx.c
+++ b/src/target/dsp563xx.c
@@ -2146,7 +2146,8 @@ COMMAND_HANDLER(dsp563xx_mem_command)
err = dsp563xx_read_memory(target, mem_type, address, sizeof(uint32_t),
count, buffer);
if (err == ERROR_OK)
- target_handle_md_output(CMD, target, address, sizeof(uint32_t), count, buffer);
+ target_handle_md_output(CMD, target, address, sizeof(uint32_t),
+ count, buffer, true);
} else {
b = buffer;
diff --git a/src/target/image.c b/src/target/image.c
index 440fe17..9175c20 100644
--- a/src/target/image.c
+++ b/src/target/image.c
@@ -24,7 +24,6 @@
#include "image.h"
#include "target.h"
#include <helper/log.h>
-#include <server/server.h>
/* convert ELF header field to host endianness */
#define field16(elf, field) \
@@ -1296,8 +1295,6 @@ int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes, uint32_t *c
crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *buffer++) & 255];
}
keep_alive();
- if (openocd_is_shutdown_pending())
- return ERROR_SERVER_INTERRUPTED;
}
LOG_DEBUG("Calculating checksum done; checksum=0x%" PRIx32, crc);
diff --git a/src/target/riscv/Makefile.am b/src/target/riscv/Makefile.am
index 4b6a74f..7f25eca 100644
--- a/src/target/riscv/Makefile.am
+++ b/src/target/riscv/Makefile.am
@@ -5,6 +5,7 @@ noinst_LTLIBRARIES += %D%/libriscv.la
%D%/asm.h \
%D%/batch.h \
%D%/debug_defines.h \
+ %D%/debug_reg_printer.h \
%D%/encoding.h \
%D%/gdb_regs.h \
%D%/opcodes.h \
@@ -15,4 +16,6 @@ noinst_LTLIBRARIES += %D%/libriscv.la
%D%/riscv-011.c \
%D%/riscv-013.c \
%D%/riscv.c \
- %D%/riscv_semihosting.c
+ %D%/riscv_semihosting.c \
+ %D%/debug_defines.c \
+ %D%/debug_reg_printer.c
diff --git a/src/target/riscv/batch.c b/src/target/riscv/batch.c
index d39967e..5109e86 100644
--- a/src/target/riscv/batch.c
+++ b/src/target/riscv/batch.c
@@ -7,66 +7,71 @@
#include "batch.h"
#include "debug_defines.h"
#include "riscv.h"
-
-#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
-#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+#include "field_helpers.h"
#define DTM_DMI_MAX_ADDRESS_LENGTH ((1<<DTM_DTMCS_ABITS_LENGTH)-1)
#define DMI_SCAN_MAX_BIT_LENGTH (DTM_DMI_MAX_ADDRESS_LENGTH + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH)
#define DMI_SCAN_BUF_SIZE (DIV_ROUND_UP(DMI_SCAN_MAX_BIT_LENGTH, 8))
-static void dump_field(int idle, const struct scan_field *field);
+/* Reserve extra room in the batch (needed for the last NOP operation) */
+#define BATCH_RESERVED_SCANS 1
-struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle)
+struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans)
{
- scans += 4;
+ scans += BATCH_RESERVED_SCANS;
struct riscv_batch *out = calloc(1, sizeof(*out));
- if (!out)
- goto error0;
+ if (!out) {
+ LOG_ERROR("Failed to allocate struct riscv_batch");
+ return NULL;
+ }
+
out->target = target;
out->allocated_scans = scans;
- out->idle_count = idle;
- out->data_out = malloc(sizeof(*out->data_out) * (scans) * DMI_SCAN_BUF_SIZE);
+ out->last_scan = RISCV_SCAN_TYPE_INVALID;
+ out->was_run = false;
+ out->used_idle_count = 0;
+
+ out->data_out = NULL;
+ out->data_in = NULL;
+ out->fields = NULL;
+ out->bscan_ctxt = NULL;
+ out->read_keys = NULL;
+
+ /* FIXME: There is potential for memory usage reduction. We could allocate
+ * smaller buffers than DMI_SCAN_BUF_SIZE (that is, buffers that correspond to
+ * the real DR scan length on the given target) */
+ out->data_out = malloc(sizeof(*out->data_out) * scans * DMI_SCAN_BUF_SIZE);
if (!out->data_out) {
LOG_ERROR("Failed to allocate data_out in RISC-V batch.");
- goto error1;
+ goto alloc_error;
};
- out->data_in = malloc(sizeof(*out->data_in) * (scans) * DMI_SCAN_BUF_SIZE);
+ out->data_in = malloc(sizeof(*out->data_in) * scans * DMI_SCAN_BUF_SIZE);
if (!out->data_in) {
LOG_ERROR("Failed to allocate data_in in RISC-V batch.");
- goto error2;
+ goto alloc_error;
}
- out->fields = malloc(sizeof(*out->fields) * (scans));
+ out->fields = malloc(sizeof(*out->fields) * scans);
if (!out->fields) {
LOG_ERROR("Failed to allocate fields in RISC-V batch.");
- goto error3;
+ goto alloc_error;
}
if (bscan_tunnel_ir_width != 0) {
- out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * (scans));
+ out->bscan_ctxt = malloc(sizeof(*out->bscan_ctxt) * scans);
if (!out->bscan_ctxt) {
LOG_ERROR("Failed to allocate bscan_ctxt in RISC-V batch.");
- goto error4;
+ goto alloc_error;
}
}
- out->last_scan = RISCV_SCAN_TYPE_INVALID;
- out->read_keys = malloc(sizeof(*out->read_keys) * (scans));
+ out->read_keys = malloc(sizeof(*out->read_keys) * scans);
if (!out->read_keys) {
LOG_ERROR("Failed to allocate read_keys in RISC-V batch.");
- goto error5;
+ goto alloc_error;
}
+
return out;
-error5:
- free(out->bscan_ctxt);
-error4:
- free(out->fields);
-error3:
- free(out->data_in);
-error2:
- free(out->data_out);
-error1:
- free(out);
-error0:
+alloc_error:
+ riscv_batch_free(out);
return NULL;
}
@@ -82,32 +87,62 @@ void riscv_batch_free(struct riscv_batch *batch)
bool riscv_batch_full(struct riscv_batch *batch)
{
- return batch->used_scans > (batch->allocated_scans - 4);
+ return riscv_batch_available_scans(batch) == 0;
}
-int riscv_batch_run(struct riscv_batch *batch)
+static bool riscv_batch_was_scan_busy(const struct riscv_batch *batch,
+ size_t scan_idx)
{
- if (batch->used_scans == 0) {
- LOG_DEBUG("Ignoring empty batch.");
- return ERROR_OK;
- }
+ assert(batch->was_run);
+ assert(scan_idx < batch->used_scans);
+ const struct scan_field *field = batch->fields + scan_idx;
+ assert(field->in_value);
+ const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+ return get_field(in, DTM_DMI_OP) == DTM_DMI_OP_BUSY;
+}
+
+static void add_idle_if_increased(struct riscv_batch *batch, size_t new_idle_count)
+{
+ if (!batch->was_run)
+ return;
+ if (batch->used_idle_count <= new_idle_count)
+ return;
+ const size_t idle_change = new_idle_count - batch->used_idle_count;
+ LOG_TARGET_DEBUG(batch->target,
+ "Idle count increased. Adding %zu idle cycles before the batch.",
+ idle_change);
+ jtag_add_runtest(idle_change, TAP_IDLE);
+}
- riscv_batch_add_nop(batch);
+int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
+ size_t idle_count, bool resets_delays, size_t reset_delays_after)
+{
+ assert(batch->used_scans);
+ assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
+ assert(!batch->was_run || riscv_batch_was_scan_busy(batch, start_idx));
+ assert(start_idx == 0 || !riscv_batch_was_scan_busy(batch, start_idx - 1));
+
+ add_idle_if_increased(batch, idle_count);
- for (size_t i = 0; i < batch->used_scans; ++i) {
+ LOG_TARGET_DEBUG(batch->target, "Running batch of scans [%zu, %zu)",
+ start_idx, batch->used_scans);
+
+ for (size_t i = start_idx; i < batch->used_scans; ++i) {
if (bscan_tunnel_ir_width != 0)
- riscv_add_bscan_tunneled_scan(batch->target, batch->fields+i, batch->bscan_ctxt+i);
+ riscv_add_bscan_tunneled_scan(batch->target, batch->fields + i, batch->bscan_ctxt + i);
else
jtag_add_dr_scan(batch->target->tap, 1, batch->fields + i, TAP_IDLE);
- if (batch->idle_count > 0)
- jtag_add_runtest(batch->idle_count, TAP_IDLE);
+ const bool delays_were_reset = resets_delays
+ && (i >= reset_delays_after);
+ if (idle_count > 0 && !delays_were_reset)
+ jtag_add_runtest(idle_count, TAP_IDLE);
}
keep_alive();
if (jtag_execute_queue() != ERROR_OK) {
- LOG_ERROR("Unable to execute JTAG queue");
+ LOG_TARGET_ERROR(batch->target, "Unable to execute JTAG queue");
return ERROR_FAIL;
}
@@ -115,40 +150,48 @@ int riscv_batch_run(struct riscv_batch *batch)
if (bscan_tunnel_ir_width != 0) {
/* need to right-shift "in" by one bit, because of clock skew between BSCAN TAP and DM TAP */
- for (size_t i = 0; i < batch->used_scans; ++i) {
+ for (size_t i = start_idx; i < batch->used_scans; ++i) {
if ((batch->fields + i)->in_value)
buffer_shr((batch->fields + i)->in_value, DMI_SCAN_BUF_SIZE, 1);
}
}
- for (size_t i = 0; i < batch->used_scans; ++i)
- dump_field(batch->idle_count, batch->fields + i);
+ for (size_t i = start_idx; i < batch->used_scans; ++i)
+ riscv_log_dmi_scan(batch->target, idle_count, batch->fields + i,
+ /*discard_in*/ false);
+ batch->was_run = true;
+ batch->used_idle_count = idle_count;
return ERROR_OK;
}
-void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data)
+void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
+ bool read_back)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
- field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->num_bits = riscv_get_dmi_scan_length(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
- field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
- riscv_fill_dmi_write_u64(batch->target, (char *)field->out_value, address, data);
- riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ riscv_fill_dm_write(batch->target, (char *)field->out_value, address, data);
+ if (read_back) {
+ field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
+ riscv_fill_dm_nop(batch->target, (char *)field->in_value);
+ } else {
+ field->in_value = NULL;
+ }
batch->last_scan = RISCV_SCAN_TYPE_WRITE;
batch->used_scans++;
}
-size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
+size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
- field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->num_bits = riscv_get_dmi_scan_length(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
- riscv_fill_dmi_read_u64(batch->target, (char *)field->out_value, address);
- riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ riscv_fill_dm_read(batch->target, (char *)field->out_value, address);
+ riscv_fill_dm_nop(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_READ;
batch->used_scans++;
@@ -156,21 +199,21 @@ size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address)
return batch->read_keys_used++;
}
-unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key)
+unsigned int riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
- assert(index <= batch->used_scans);
+ assert(index < batch->used_scans);
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "op" field from the DMI read result */
- return (unsigned)buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
+ return (unsigned int)buf_get_u32(base, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
-uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key)
+uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key)
{
assert(key < batch->read_keys_used);
size_t index = batch->read_keys[key];
- assert(index <= batch->used_scans);
+ assert(index < batch->used_scans);
uint8_t *base = batch->data_in + DMI_SCAN_BUF_SIZE * index;
/* extract "data" field from the DMI read result */
return buf_get_u32(base, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH);
@@ -180,48 +223,38 @@ void riscv_batch_add_nop(struct riscv_batch *batch)
{
assert(batch->used_scans < batch->allocated_scans);
struct scan_field *field = batch->fields + batch->used_scans;
- field->num_bits = riscv_dmi_write_u64_bits(batch->target);
+ field->num_bits = riscv_get_dmi_scan_length(batch->target);
field->out_value = (void *)(batch->data_out + batch->used_scans * DMI_SCAN_BUF_SIZE);
field->in_value = (void *)(batch->data_in + batch->used_scans * DMI_SCAN_BUF_SIZE);
- riscv_fill_dmi_nop_u64(batch->target, (char *)field->out_value);
- riscv_fill_dmi_nop_u64(batch->target, (char *)field->in_value);
+ riscv_fill_dm_nop(batch->target, (char *)field->out_value);
+ riscv_fill_dm_nop(batch->target, (char *)field->in_value);
batch->last_scan = RISCV_SCAN_TYPE_NOP;
batch->used_scans++;
}
-void dump_field(int idle, const struct scan_field *field)
+size_t riscv_batch_available_scans(struct riscv_batch *batch)
{
- static const char * const op_string[] = {"-", "r", "w", "?"};
- static const char * const status_string[] = {"+", "?", "F", "b"};
-
- if (debug_level < LOG_LVL_DEBUG)
- return;
+ assert(batch->allocated_scans >= (batch->used_scans + BATCH_RESERVED_SCANS));
+ return batch->allocated_scans - batch->used_scans - BATCH_RESERVED_SCANS;
+}
- assert(field->out_value);
- uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
- unsigned int out_op = get_field(out, DTM_DMI_OP);
- unsigned int out_data = get_field(out, DTM_DMI_DATA);
- unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
-
- if (field->in_value) {
- uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
- unsigned int in_op = get_field(in, DTM_DMI_OP);
- unsigned int in_data = get_field(in, DTM_DMI_DATA);
- unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
-
- log_printf_lf(LOG_LVL_DEBUG,
- __FILE__, __LINE__, __PRETTY_FUNCTION__,
- "%db %s %08x @%02x -> %s %08x @%02x; %di",
- field->num_bits, op_string[out_op], out_data, out_address,
- status_string[in_op], in_data, in_address, idle);
- } else {
- log_printf_lf(LOG_LVL_DEBUG,
- __FILE__, __LINE__, __PRETTY_FUNCTION__, "%db %s %08x @%02x -> ?; %di",
- field->num_bits, op_string[out_op], out_data, out_address, idle);
- }
+bool riscv_batch_was_batch_busy(const struct riscv_batch *batch)
+{
+ assert(batch->was_run);
+ assert(batch->used_scans);
+ assert(batch->last_scan == RISCV_SCAN_TYPE_NOP);
+ return riscv_batch_was_scan_busy(batch, batch->used_scans - 1);
}
-size_t riscv_batch_available_scans(struct riscv_batch *batch)
+size_t riscv_batch_finished_scans(const struct riscv_batch *batch)
{
- return batch->allocated_scans - batch->used_scans - 4;
+ if (!riscv_batch_was_batch_busy(batch)) {
+ /* Whole batch succeeded. */
+ return batch->used_scans;
+ }
+ assert(batch->used_scans);
+ size_t first_busy = 0;
+ while (!riscv_batch_was_scan_busy(batch, first_busy))
+ ++first_busy;
+ return first_busy;
}
diff --git a/src/target/riscv/batch.h b/src/target/riscv/batch.h
index 9c42ba8..eaf0d1d 100644
--- a/src/target/riscv/batch.h
+++ b/src/target/riscv/batch.h
@@ -24,8 +24,6 @@ struct riscv_batch {
size_t allocated_scans;
size_t used_scans;
- size_t idle_count;
-
uint8_t *data_out;
uint8_t *data_in;
struct scan_field *fields;
@@ -44,29 +42,53 @@ struct riscv_batch {
/* The read keys. */
size_t *read_keys;
size_t read_keys_used;
+
+ /* Flag indicating that the last run of the batch finished without an error
+ * from the underlying JTAG layer of OpenOCD - all scans were performed.
+ * However, RISC-V DMI "busy" condition could still have occurred.
+ */
+ bool was_run;
+ /* Idle count used on the last run. Only valid after `was_run` is set. */
+ size_t used_idle_count;
};
/* Allocates (or frees) a new scan set. "scans" is the maximum number of JTAG
- * scans that can be issued to this object, and idle is the number of JTAG idle
- * cycles between every real scan. */
-struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans, size_t idle);
+ * scans that can be issued to this object. */
+struct riscv_batch *riscv_batch_alloc(struct target *target, size_t scans);
void riscv_batch_free(struct riscv_batch *batch);
/* Checks to see if this batch is full. */
bool riscv_batch_full(struct riscv_batch *batch);
-/* Executes this scan batch. */
-int riscv_batch_run(struct riscv_batch *batch);
-
-/* Adds a DMI write to this batch. */
-void riscv_batch_add_dmi_write(struct riscv_batch *batch, unsigned address, uint64_t data);
-
-/* DMI reads must be handled in two parts: the first one schedules a read and
+/* Executes this batch of JTAG DTM DMI scans, starting form "start" scan.
+ *
+ * If batch is run for the first time, it is expected that "start" is zero.
+ * It is expected that the batch ends with a DMI NOP operation.
+ *
+ * "idle_count" is the number of JTAG Run-Test-Idle cycles to add in-between
+ * the scans.
+ *
+ * If "resets_delays" is true, the algorithm will stop inserting idle cycles
+ * (JTAG Run-Test-Idle) after "reset_delays_after" number of scans is
+ * performed. This is useful for stress-testing of RISC-V algorithms in
+ * OpenOCD that are based on batches.
+ */
+int riscv_batch_run_from(struct riscv_batch *batch, size_t start_idx,
+ size_t idle_count, bool resets_delays, size_t reset_delays_after);
+
+/* Get the number of scans successfully executed form this batch. */
+size_t riscv_batch_finished_scans(const struct riscv_batch *batch);
+
+/* Adds a DM register write to this batch. */
+void riscv_batch_add_dm_write(struct riscv_batch *batch, uint64_t address, uint32_t data,
+ bool read_back);
+
+/* DM register reads must be handled in two parts: the first one schedules a read and
* provides a key, the second one actually obtains the result of the read -
* status (op) and the actual data. */
-size_t riscv_batch_add_dmi_read(struct riscv_batch *batch, unsigned address);
-unsigned riscv_batch_get_dmi_read_op(struct riscv_batch *batch, size_t key);
-uint32_t riscv_batch_get_dmi_read_data(struct riscv_batch *batch, size_t key);
+size_t riscv_batch_add_dm_read(struct riscv_batch *batch, uint64_t address);
+unsigned int riscv_batch_get_dmi_read_op(const struct riscv_batch *batch, size_t key);
+uint32_t riscv_batch_get_dmi_read_data(const struct riscv_batch *batch, size_t key);
/* Scans in a NOP. */
void riscv_batch_add_nop(struct riscv_batch *batch);
@@ -74,4 +96,13 @@ void riscv_batch_add_nop(struct riscv_batch *batch);
/* Returns the number of available scans. */
size_t riscv_batch_available_scans(struct riscv_batch *batch);
+/* Return true iff the last scan in the batch returned DMI_OP_BUSY. */
+bool riscv_batch_was_batch_busy(const struct riscv_batch *batch);
+
+/* TODO: The function is defined in `riscv-013.c`. This is done to reduce the
+ * diff of the commit. The intention is to move the function definition to
+ * a separate module (e.g. `riscv013-jtag-dtm.c/h`) in another commit. */
+void riscv_log_dmi_scan(const struct target *target, int idle, const struct scan_field *field,
+ bool discard_in);
+
#endif
diff --git a/src/target/riscv/debug_defines.c b/src/target/riscv/debug_defines.c
new file mode 100644
index 0000000..d514c6e
--- /dev/null
+++ b/src/target/riscv/debug_defines.c
@@ -0,0 +1,3852 @@
+/*
+ * This file is auto-generated by running 'make debug_defines' in
+ * https://github.com/riscv/riscv-debug-spec/ (40b9a05)
+ */
+
+#include "debug_defines.h"
+#include <stddef.h>
+#include <assert.h>
+static riscv_debug_reg_field_list_t dtm_idcode_get_version(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "Version",
+ .lsb = 0x1c,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_idcode_get_partnumber(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "PartNumber",
+ .lsb = 0xc,
+ .msb = 0x1b,
+ .values = NULL
+ },
+ .get_next = dtm_idcode_get_version
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_idcode_get_manufid(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ManufId",
+ .lsb = 1,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = dtm_idcode_get_partnumber
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_idcode_get_1(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "1",
+ .lsb = 0,
+ .msb = 0,
+ .values = NULL
+ },
+ .get_next = dtm_idcode_get_manufid
+ };
+ return result;
+}
+
+static const char *dtm_dtmcs_errinfo_values[8] = {
+ [0] = "not_implemented",
+ [1] = "dmi_error",
+ [2] = "communication_error",
+ [3] = "device_error",
+ [4] = "unknown"
+};
+static const char *dtm_dtmcs_version_values[16] = {
+ [0] = "0_11",
+ [1] = "1_0",
+ [15] = "custom"
+};
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_abits(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "abits",
+ .lsb = 4,
+ .msb = 9,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_errinfo(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "errinfo",
+ .lsb = 0x12,
+ .msb = 0x14,
+ .values = dtm_dtmcs_errinfo_values
+ },
+ .get_next = dtm_dtmcs_get_abits
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_dtmhardreset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dtmhardreset",
+ .lsb = 0x11,
+ .msb = 0x11,
+ .values = NULL
+ },
+ .get_next = dtm_dtmcs_get_errinfo
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmireset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmireset",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = NULL
+ },
+ .get_next = dtm_dtmcs_get_dtmhardreset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_idle(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "idle",
+ .lsb = 0xc,
+ .msb = 0xe,
+ .values = NULL
+ },
+ .get_next = dtm_dtmcs_get_dmireset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_dmistat(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmistat",
+ .lsb = 0xa,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = dtm_dtmcs_get_idle
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dtmcs_get_version(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "version",
+ .lsb = 0,
+ .msb = 3,
+ .values = dtm_dtmcs_version_values
+ },
+ .get_next = dtm_dtmcs_get_dmistat
+ };
+ return result;
+}
+
+static const char *dtm_dmi_op_values[4] = {};
+static riscv_debug_reg_field_list_t dtm_dmi_get_address(riscv_debug_reg_ctx_t context)
+{
+ assert(context.abits.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "address",
+ .lsb = 0x22,
+ .msb = (context.abits.value + 0x21),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dmi_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 2,
+ .msb = 0x21,
+ .values = NULL
+ },
+ .get_next = dtm_dmi_get_address
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dtm_dmi_get_op(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "op",
+ .lsb = 0,
+ .msb = 1,
+ .values = dtm_dmi_op_values
+ },
+ .get_next = dtm_dmi_get_data
+ };
+ return result;
+}
+
+
+static const char *csr_dcsr_debugver_values[16] = {
+ [0] = "none",
+ [4] = "1_0",
+ [15] = "custom"
+};
+static const char *csr_dcsr_ebreakvs_values[2] = {
+ [0] = "exception",
+ [1] = "debug_mode"
+};
+static const char *csr_dcsr_ebreakvu_values[2] = {
+ [0] = "exception",
+ [1] = "debug_mode"
+};
+static const char *csr_dcsr_ebreakm_values[2] = {
+ [0] = "exception",
+ [1] = "debug_mode"
+};
+static const char *csr_dcsr_ebreaks_values[2] = {
+ [0] = "exception",
+ [1] = "debug_mode"
+};
+static const char *csr_dcsr_ebreaku_values[2] = {
+ [0] = "exception",
+ [1] = "debug_mode"
+};
+static const char *csr_dcsr_stepie_values[2] = {
+ [0] = "interrupts_disabled",
+ [1] = "interrupts_enabled"
+};
+static const char *csr_dcsr_stopcount_values[2] = {
+ [0] = "normal",
+ [1] = "freeze"
+};
+static const char *csr_dcsr_stoptime_values[2] = {
+ [0] = "normal",
+ [1] = "freeze"
+};
+static const char *csr_dcsr_cause_values[8] = {
+ [1] = "ebreak",
+ [2] = "trigger",
+ [3] = "haltreq",
+ [4] = "step",
+ [5] = "resethaltreq",
+ [6] = "group"
+};
+static const char *csr_dcsr_mprven_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static riscv_debug_reg_field_list_t csr_dcsr_get_stoptime(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "stoptime",
+ .lsb = 9,
+ .msb = 9,
+ .values = csr_dcsr_stoptime_values
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_cause(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cause",
+ .lsb = 6,
+ .msb = 8,
+ .values = csr_dcsr_cause_values
+ },
+ .get_next = csr_dcsr_get_stoptime
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_v(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "v",
+ .lsb = 5,
+ .msb = 5,
+ .values = NULL
+ },
+ .get_next = csr_dcsr_get_cause
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_mprven(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mprven",
+ .lsb = 4,
+ .msb = 4,
+ .values = csr_dcsr_mprven_values
+ },
+ .get_next = csr_dcsr_get_v
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_nmip(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "nmip",
+ .lsb = 3,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = csr_dcsr_get_mprven
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_debugver(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "debugver",
+ .lsb = 0x1c,
+ .msb = 0x1f,
+ .values = csr_dcsr_debugver_values
+ },
+ .get_next = csr_dcsr_get_nmip
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_step(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "step",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = csr_dcsr_get_debugver
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvs(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ebreakvs",
+ .lsb = 0x11,
+ .msb = 0x11,
+ .values = csr_dcsr_ebreakvs_values
+ },
+ .get_next = csr_dcsr_get_step
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakvu(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ebreakvu",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = csr_dcsr_ebreakvu_values
+ },
+ .get_next = csr_dcsr_get_ebreakvs
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_ebreakm(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ebreakm",
+ .lsb = 0xf,
+ .msb = 0xf,
+ .values = csr_dcsr_ebreakm_values
+ },
+ .get_next = csr_dcsr_get_ebreakvu
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaks(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ebreaks",
+ .lsb = 0xd,
+ .msb = 0xd,
+ .values = csr_dcsr_ebreaks_values
+ },
+ .get_next = csr_dcsr_get_ebreakm
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_ebreaku(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ebreaku",
+ .lsb = 0xc,
+ .msb = 0xc,
+ .values = csr_dcsr_ebreaku_values
+ },
+ .get_next = csr_dcsr_get_ebreaks
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_stepie(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "stepie",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = csr_dcsr_stepie_values
+ },
+ .get_next = csr_dcsr_get_ebreaku
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_stopcount(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "stopcount",
+ .lsb = 0xa,
+ .msb = 0xa,
+ .values = csr_dcsr_stopcount_values
+ },
+ .get_next = csr_dcsr_get_stepie
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dcsr_get_prv(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "prv",
+ .lsb = 0,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = csr_dcsr_get_stopcount
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dpc_get_dpc(riscv_debug_reg_ctx_t context)
+{
+ assert(context.DXLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dpc",
+ .lsb = 0,
+ .msb = (context.DXLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dscratch0_get_dscratch0(riscv_debug_reg_ctx_t context)
+{
+ assert(context.DXLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dscratch0",
+ .lsb = 0,
+ .msb = (context.DXLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_dscratch1_get_dscratch1(riscv_debug_reg_ctx_t context)
+{
+ assert(context.DXLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dscratch1",
+ .lsb = 0,
+ .msb = (context.DXLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tselect_get_index(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "index",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *csr_tdata1_type_values[16] = {
+ [0] = "none",
+ [1] = "legacy",
+ [2] = "mcontrol",
+ [3] = "icount",
+ [4] = "itrigger",
+ [5] = "etrigger",
+ [6] = "mcontrol6",
+ [7] = "tmexttrigger",
+ [15] = "disabled"
+};
+static const char *csr_tdata1_dmode_values[2] = {
+ [0] = "both",
+ [1] = "dmode"
+};
+static riscv_debug_reg_field_list_t csr_tdata1_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = csr_tdata1_dmode_values
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tdata1_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = csr_tdata1_type_values
+ },
+ .get_next = csr_tdata1_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tdata1_get_data(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -6),
+ .values = NULL
+ },
+ .get_next = csr_tdata1_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tdata2_get_data(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tdata3_get_data(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *csr_tinfo_version_values[256] = {
+ [0] = "0",
+ [1] = "1"
+};
+static riscv_debug_reg_field_list_t csr_tinfo_get_version(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "version",
+ .lsb = 0x18,
+ .msb = 0x1f,
+ .values = csr_tinfo_version_values
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tinfo_get_info(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "info",
+ .lsb = 0,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = csr_tinfo_get_version
+ };
+ return result;
+}
+
+static const char *csr_tcontrol_mte_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static riscv_debug_reg_field_list_t csr_tcontrol_get_mpte(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mpte",
+ .lsb = 7,
+ .msb = 7,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tcontrol_get_mte(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mte",
+ .lsb = 3,
+ .msb = 3,
+ .values = csr_tcontrol_mte_values
+ },
+ .get_next = csr_tcontrol_get_mpte
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_scontext_get_data(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontext_get_hcontext(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hcontext",
+ .lsb = 0,
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *csr_mcontrol_select_values[2] = {
+ [0] = "address",
+ [1] = "data"
+};
+static const char *csr_mcontrol_timing_values[2] = {
+ [0] = "before",
+ [1] = "after"
+};
+static const char *csr_mcontrol_sizelo_values[4] = {};
+static const char *csr_mcontrol_action_values[16] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static const char *csr_mcontrol_chain_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static const char *csr_mcontrol_match_values[16] = {
+ [0] = "equal",
+ [1] = "napot",
+ [2] = "ge",
+ [3] = "lt",
+ [4] = "mask_low",
+ [5] = "mask_high",
+ [8] = "not_equal",
+ [9] = "not_napot",
+ [12] = "not_mask_low",
+ [13] = "not_mask_high"
+};
+static riscv_debug_reg_field_list_t csr_mcontrol_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_maskmax(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "maskmax",
+ .lsb = (context.XLEN.value + -0xb),
+ .msb = (context.XLEN.value + -6),
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_match(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "match",
+ .lsb = 7,
+ .msb = 0xa,
+ .values = csr_mcontrol_match_values
+ },
+ .get_next = csr_mcontrol_get_maskmax
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_m(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "m",
+ .lsb = 6,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_match
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_s(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "s",
+ .lsb = 4,
+ .msb = 4,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_m
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_u(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "u",
+ .lsb = 3,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_s
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_sizehi(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sizehi",
+ .lsb = 0x15,
+ .msb = 0x16,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_u
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_hit(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit",
+ .lsb = 0x14,
+ .msb = 0x14,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_sizehi
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_execute(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "execute",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_hit
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_select(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "select",
+ .lsb = 0x13,
+ .msb = 0x13,
+ .values = csr_mcontrol_select_values
+ },
+ .get_next = csr_mcontrol_get_execute
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_timing(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "timing",
+ .lsb = 0x12,
+ .msb = 0x12,
+ .values = csr_mcontrol_timing_values
+ },
+ .get_next = csr_mcontrol_get_select
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_sizelo(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sizelo",
+ .lsb = 0x10,
+ .msb = 0x11,
+ .values = csr_mcontrol_sizelo_values
+ },
+ .get_next = csr_mcontrol_get_timing
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0xc,
+ .msb = 0xf,
+ .values = csr_mcontrol_action_values
+ },
+ .get_next = csr_mcontrol_get_sizelo
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_chain(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "chain",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = csr_mcontrol_chain_values
+ },
+ .get_next = csr_mcontrol_get_action
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_store(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "store",
+ .lsb = 1,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_chain
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol_get_load(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "load",
+ .lsb = 0,
+ .msb = 0,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol_get_store
+ };
+ return result;
+}
+
+static const char *csr_mcontrol6_uncertain_values[2] = {
+ [0] = "certain",
+ [1] = "uncertain"
+};
+static const char *csr_mcontrol6_hit0_values[2] = {};
+static const char *csr_mcontrol6_select_values[2] = {
+ [0] = "address",
+ [1] = "data"
+};
+static const char *csr_mcontrol6_size_values[8] = {
+ [0] = "any",
+ [1] = "8bit",
+ [2] = "16bit",
+ [3] = "32bit",
+ [4] = "48bit",
+ [5] = "64bit",
+ [6] = "128bit"
+};
+static const char *csr_mcontrol6_action_values[16] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static const char *csr_mcontrol6_chain_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static const char *csr_mcontrol6_match_values[16] = {
+ [0] = "equal",
+ [1] = "napot",
+ [2] = "ge",
+ [3] = "lt",
+ [4] = "mask_low",
+ [5] = "mask_high",
+ [8] = "not_equal",
+ [9] = "not_napot",
+ [12] = "not_mask_low",
+ [13] = "not_mask_high"
+};
+static const char *csr_mcontrol6_uncertainen_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_match(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "match",
+ .lsb = 7,
+ .msb = 0xa,
+ .values = csr_mcontrol6_match_values
+ },
+ .get_next = csr_mcontrol6_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_m(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "m",
+ .lsb = 6,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_match
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertainen(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "uncertainen",
+ .lsb = 5,
+ .msb = 5,
+ .values = csr_mcontrol6_uncertainen_values
+ },
+ .get_next = csr_mcontrol6_get_m
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_s(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "s",
+ .lsb = 4,
+ .msb = 4,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_uncertainen
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_u(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "u",
+ .lsb = 3,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_s
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_uncertain(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "uncertain",
+ .lsb = 0x1a,
+ .msb = 0x1a,
+ .values = csr_mcontrol6_uncertain_values
+ },
+ .get_next = csr_mcontrol6_get_u
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit1(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit1",
+ .lsb = 0x19,
+ .msb = 0x19,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_uncertain
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_vs(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vs",
+ .lsb = 0x18,
+ .msb = 0x18,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_hit1
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_vu(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vu",
+ .lsb = 0x17,
+ .msb = 0x17,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_vs
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_hit0(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit0",
+ .lsb = 0x16,
+ .msb = 0x16,
+ .values = csr_mcontrol6_hit0_values
+ },
+ .get_next = csr_mcontrol6_get_vu
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_select(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "select",
+ .lsb = 0x15,
+ .msb = 0x15,
+ .values = csr_mcontrol6_select_values
+ },
+ .get_next = csr_mcontrol6_get_hit0
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_execute(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "execute",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_select
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_size(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "size",
+ .lsb = 0x10,
+ .msb = 0x12,
+ .values = csr_mcontrol6_size_values
+ },
+ .get_next = csr_mcontrol6_get_execute
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0xc,
+ .msb = 0xf,
+ .values = csr_mcontrol6_action_values
+ },
+ .get_next = csr_mcontrol6_get_size
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_chain(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "chain",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = csr_mcontrol6_chain_values
+ },
+ .get_next = csr_mcontrol6_get_action
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_store(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "store",
+ .lsb = 1,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_chain
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_mcontrol6_get_load(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "load",
+ .lsb = 0,
+ .msb = 0,
+ .values = NULL
+ },
+ .get_next = csr_mcontrol6_get_store
+ };
+ return result;
+}
+
+static const char *csr_icount_action_values[64] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static riscv_debug_reg_field_list_t csr_icount_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_icount_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_m(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "m",
+ .lsb = 9,
+ .msb = 9,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_pending(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "pending",
+ .lsb = 8,
+ .msb = 8,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_m
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_s(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "s",
+ .lsb = 7,
+ .msb = 7,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_pending
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_u(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "u",
+ .lsb = 6,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_s
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_vs(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vs",
+ .lsb = 0x1a,
+ .msb = 0x1a,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_u
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_vu(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vu",
+ .lsb = 0x19,
+ .msb = 0x19,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_vs
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_hit(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit",
+ .lsb = 0x18,
+ .msb = 0x18,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_vu
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_count(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "count",
+ .lsb = 0xa,
+ .msb = 0x17,
+ .values = NULL
+ },
+ .get_next = csr_icount_get_hit
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_icount_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0,
+ .msb = 5,
+ .values = csr_icount_action_values
+ },
+ .get_next = csr_icount_get_count
+ };
+ return result;
+}
+
+static const char *csr_itrigger_action_values[64] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static riscv_debug_reg_field_list_t csr_itrigger_get_hit(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit",
+ .lsb = (context.XLEN.value + -6),
+ .msb = (context.XLEN.value + -6),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_hit
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_m(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "m",
+ .lsb = 9,
+ .msb = 9,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_s(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "s",
+ .lsb = 7,
+ .msb = 7,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_m
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_u(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "u",
+ .lsb = 6,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_s
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_vs(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vs",
+ .lsb = 0xc,
+ .msb = 0xc,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_u
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_vu(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vu",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_vs
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_nmi(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "nmi",
+ .lsb = 0xa,
+ .msb = 0xa,
+ .values = NULL
+ },
+ .get_next = csr_itrigger_get_vu
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_itrigger_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0,
+ .msb = 5,
+ .values = csr_itrigger_action_values
+ },
+ .get_next = csr_itrigger_get_nmi
+ };
+ return result;
+}
+
+static const char *csr_etrigger_action_values[64] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static riscv_debug_reg_field_list_t csr_etrigger_get_hit(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit",
+ .lsb = (context.XLEN.value + -6),
+ .msb = (context.XLEN.value + -6),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_hit
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_m(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "m",
+ .lsb = 9,
+ .msb = 9,
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_s(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "s",
+ .lsb = 7,
+ .msb = 7,
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_m
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_u(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "u",
+ .lsb = 6,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_s
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_vs(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vs",
+ .lsb = 0xc,
+ .msb = 0xc,
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_u
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_vu(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "vu",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = csr_etrigger_get_vs
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_etrigger_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0,
+ .msb = 5,
+ .values = csr_etrigger_action_values
+ },
+ .get_next = csr_etrigger_get_vu
+ };
+ return result;
+}
+
+static const char *csr_tmexttrigger_action_values[64] = {
+ [0] = "breakpoint",
+ [1] = "debug_mode",
+ [2] = "trace_on",
+ [3] = "trace_off",
+ [4] = "trace_notify",
+ [8] = "external0",
+ [9] = "external1"
+};
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_hit(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hit",
+ .lsb = (context.XLEN.value + -6),
+ .msb = (context.XLEN.value + -6),
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_dmode(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmode",
+ .lsb = (context.XLEN.value + -5),
+ .msb = (context.XLEN.value + -5),
+ .values = NULL
+ },
+ .get_next = csr_tmexttrigger_get_hit
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_type(riscv_debug_reg_ctx_t context)
+{
+ assert(context.XLEN.is_set);
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "type",
+ .lsb = (context.XLEN.value + -4),
+ .msb = (context.XLEN.value + -1),
+ .values = NULL
+ },
+ .get_next = csr_tmexttrigger_get_dmode
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_select(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "select",
+ .lsb = 6,
+ .msb = 0x15,
+ .values = NULL
+ },
+ .get_next = csr_tmexttrigger_get_type
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_intctl(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "intctl",
+ .lsb = 0x16,
+ .msb = 0x16,
+ .values = NULL
+ },
+ .get_next = csr_tmexttrigger_get_select
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_tmexttrigger_get_action(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "action",
+ .lsb = 0,
+ .msb = 5,
+ .values = csr_tmexttrigger_action_values
+ },
+ .get_next = csr_tmexttrigger_get_intctl
+ };
+ return result;
+}
+
+static const char *csr_textra32_mhselect_values[8] = {
+ [0] = "ignore",
+ [4] = "mcontext"
+};
+static const char *csr_textra32_sselect_values[4] = {
+ [0] = "ignore",
+ [1] = "scontext",
+ [2] = "asid"
+};
+static riscv_debug_reg_field_list_t csr_textra32_get_mhvalue(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mhvalue",
+ .lsb = 0x1a,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra32_get_mhselect(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mhselect",
+ .lsb = 0x17,
+ .msb = 0x19,
+ .values = csr_textra32_mhselect_values
+ },
+ .get_next = csr_textra32_get_mhvalue
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra32_get_svalue(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "svalue",
+ .lsb = 2,
+ .msb = 0x11,
+ .values = NULL
+ },
+ .get_next = csr_textra32_get_mhselect
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra32_get_sbytemask(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbytemask",
+ .lsb = 0x12,
+ .msb = 0x13,
+ .values = NULL
+ },
+ .get_next = csr_textra32_get_svalue
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra32_get_sselect(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sselect",
+ .lsb = 0,
+ .msb = 1,
+ .values = csr_textra32_sselect_values
+ },
+ .get_next = csr_textra32_get_sbytemask
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra64_get_mhvalue(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mhvalue",
+ .lsb = 0x33,
+ .msb = 0x3f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra64_get_mhselect(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "mhselect",
+ .lsb = 0x30,
+ .msb = 0x32,
+ .values = NULL
+ },
+ .get_next = csr_textra64_get_mhvalue
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra64_get_sbytemask(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbytemask",
+ .lsb = 0x24,
+ .msb = 0x28,
+ .values = NULL
+ },
+ .get_next = csr_textra64_get_mhselect
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra64_get_svalue(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "svalue",
+ .lsb = 2,
+ .msb = 0x23,
+ .values = NULL
+ },
+ .get_next = csr_textra64_get_sbytemask
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t csr_textra64_get_sselect(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sselect",
+ .lsb = 0,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = csr_textra64_get_svalue
+ };
+ return result;
+}
+
+static const char *dm_dmstatus_ndmresetpending_values[2] = {
+ [0] = "false",
+ [1] = "true"
+};
+static const char *dm_dmstatus_stickyunavail_values[2] = {
+ [0] = "current",
+ [1] = "sticky"
+};
+static const char *dm_dmstatus_authenticated_values[2] = {
+ [0] = "false",
+ [1] = "true"
+};
+static const char *dm_dmstatus_authbusy_values[2] = {
+ [0] = "ready",
+ [1] = "busy"
+};
+static const char *dm_dmstatus_confstrptrvalid_values[2] = {
+ [0] = "invalid",
+ [1] = "valid"
+};
+static const char *dm_dmstatus_version_values[16] = {
+ [0] = "none",
+ [1] = "0_11",
+ [2] = "0_13",
+ [3] = "1_0",
+ [15] = "custom"
+};
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allhalted(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allhalted",
+ .lsb = 9,
+ .msb = 9,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhalted(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anyhalted",
+ .lsb = 8,
+ .msb = 8,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allhalted
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_authenticated(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "authenticated",
+ .lsb = 7,
+ .msb = 7,
+ .values = dm_dmstatus_authenticated_values
+ },
+ .get_next = dm_dmstatus_get_anyhalted
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_authbusy(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "authbusy",
+ .lsb = 6,
+ .msb = 6,
+ .values = dm_dmstatus_authbusy_values
+ },
+ .get_next = dm_dmstatus_get_authenticated
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_hasresethaltreq(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hasresethaltreq",
+ .lsb = 5,
+ .msb = 5,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_authbusy
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_confstrptrvalid(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "confstrptrvalid",
+ .lsb = 4,
+ .msb = 4,
+ .values = dm_dmstatus_confstrptrvalid_values
+ },
+ .get_next = dm_dmstatus_get_hasresethaltreq
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_ndmresetpending(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ndmresetpending",
+ .lsb = 0x18,
+ .msb = 0x18,
+ .values = dm_dmstatus_ndmresetpending_values
+ },
+ .get_next = dm_dmstatus_get_confstrptrvalid
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_stickyunavail(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "stickyunavail",
+ .lsb = 0x17,
+ .msb = 0x17,
+ .values = dm_dmstatus_stickyunavail_values
+ },
+ .get_next = dm_dmstatus_get_ndmresetpending
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_impebreak(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "impebreak",
+ .lsb = 0x16,
+ .msb = 0x16,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_stickyunavail
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allhavereset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allhavereset",
+ .lsb = 0x13,
+ .msb = 0x13,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_impebreak
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anyhavereset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anyhavereset",
+ .lsb = 0x12,
+ .msb = 0x12,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allhavereset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allresumeack(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allresumeack",
+ .lsb = 0x11,
+ .msb = 0x11,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_anyhavereset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anyresumeack(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anyresumeack",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allresumeack
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allnonexistent(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allnonexistent",
+ .lsb = 0xf,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_anyresumeack
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anynonexistent(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anynonexistent",
+ .lsb = 0xe,
+ .msb = 0xe,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allnonexistent
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allunavail(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allunavail",
+ .lsb = 0xd,
+ .msb = 0xd,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_anynonexistent
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anyunavail(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anyunavail",
+ .lsb = 0xc,
+ .msb = 0xc,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allunavail
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_allrunning(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "allrunning",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_anyunavail
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_anyrunning(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "anyrunning",
+ .lsb = 0xa,
+ .msb = 0xa,
+ .values = NULL
+ },
+ .get_next = dm_dmstatus_get_allrunning
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmstatus_get_version(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "version",
+ .lsb = 0,
+ .msb = 3,
+ .values = dm_dmstatus_version_values
+ },
+ .get_next = dm_dmstatus_get_anyrunning
+ };
+ return result;
+}
+
+static const char *dm_dmcontrol_ackhavereset_values[2] = {
+ [0] = "nop",
+ [1] = "ack"
+};
+static const char *dm_dmcontrol_ackunavail_values[2] = {
+ [0] = "nop",
+ [1] = "ack"
+};
+static const char *dm_dmcontrol_hasel_values[2] = {
+ [0] = "single",
+ [1] = "multiple"
+};
+static const char *dm_dmcontrol_dmactive_values[2] = {
+ [0] = "inactive",
+ [1] = "active"
+};
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartselhi(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hartselhi",
+ .lsb = 6,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_setkeepalive(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "setkeepalive",
+ .lsb = 5,
+ .msb = 5,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_hartselhi
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrkeepalive(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "clrkeepalive",
+ .lsb = 4,
+ .msb = 4,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_setkeepalive
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_haltreq(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "haltreq",
+ .lsb = 0x1f,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_clrkeepalive
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_resumereq(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "resumereq",
+ .lsb = 0x1e,
+ .msb = 0x1e,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_haltreq
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_setresethaltreq(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "setresethaltreq",
+ .lsb = 3,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_resumereq
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartreset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hartreset",
+ .lsb = 0x1d,
+ .msb = 0x1d,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_setresethaltreq
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackhavereset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ackhavereset",
+ .lsb = 0x1c,
+ .msb = 0x1c,
+ .values = dm_dmcontrol_ackhavereset_values
+ },
+ .get_next = dm_dmcontrol_get_hartreset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_ackunavail(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ackunavail",
+ .lsb = 0x1b,
+ .msb = 0x1b,
+ .values = dm_dmcontrol_ackunavail_values
+ },
+ .get_next = dm_dmcontrol_get_ackhavereset
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_hasel(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hasel",
+ .lsb = 0x1a,
+ .msb = 0x1a,
+ .values = dm_dmcontrol_hasel_values
+ },
+ .get_next = dm_dmcontrol_get_ackunavail
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_clrresethaltreq(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "clrresethaltreq",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_hasel
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_hartsello(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hartsello",
+ .lsb = 0x10,
+ .msb = 0x19,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_clrresethaltreq
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_ndmreset(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "ndmreset",
+ .lsb = 1,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = dm_dmcontrol_get_hartsello
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcontrol_get_dmactive(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmactive",
+ .lsb = 0,
+ .msb = 0,
+ .values = dm_dmcontrol_dmactive_values
+ },
+ .get_next = dm_dmcontrol_get_ndmreset
+ };
+ return result;
+}
+
+static const char *dm_hartinfo_dataaccess_values[2] = {
+ [0] = "csr",
+ [1] = "memory"
+};
+static riscv_debug_reg_field_list_t dm_hartinfo_get_nscratch(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "nscratch",
+ .lsb = 0x14,
+ .msb = 0x17,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaccess(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dataaccess",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = dm_hartinfo_dataaccess_values
+ },
+ .get_next = dm_hartinfo_get_nscratch
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_hartinfo_get_datasize(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "datasize",
+ .lsb = 0xc,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = dm_hartinfo_get_dataaccess
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_hartinfo_get_dataaddr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dataaddr",
+ .lsb = 0,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = dm_hartinfo_get_datasize
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_hawindowsel_get_hawindowsel(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hawindowsel",
+ .lsb = 0,
+ .msb = 0xe,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_hawindow_get_maskdata(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "maskdata",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *dm_abstractcs_busy_values[2] = {
+ [0] = "ready",
+ [1] = "busy"
+};
+static const char *dm_abstractcs_relaxedpriv_values[2] = {
+ [0] = "full_checks",
+ [1] = "relaxed_checks"
+};
+static const char *dm_abstractcs_cmderr_values[8] = {
+ [0] = "none",
+ [1] = "busy",
+ [2] = "not_supported",
+ [3] = "exception",
+ [4] = "halt_resume",
+ [5] = "bus",
+ [6] = "reserved",
+ [7] = "other"
+};
+static riscv_debug_reg_field_list_t dm_abstractcs_get_cmderr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cmderr",
+ .lsb = 8,
+ .msb = 0xa,
+ .values = dm_abstractcs_cmderr_values
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractcs_get_progbufsize(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "progbufsize",
+ .lsb = 0x18,
+ .msb = 0x1c,
+ .values = NULL
+ },
+ .get_next = dm_abstractcs_get_cmderr
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractcs_get_busy(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "busy",
+ .lsb = 0xc,
+ .msb = 0xc,
+ .values = dm_abstractcs_busy_values
+ },
+ .get_next = dm_abstractcs_get_progbufsize
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractcs_get_relaxedpriv(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "relaxedpriv",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = dm_abstractcs_relaxedpriv_values
+ },
+ .get_next = dm_abstractcs_get_busy
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractcs_get_datacount(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "datacount",
+ .lsb = 0,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = dm_abstractcs_get_relaxedpriv
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_command_get_cmdtype(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cmdtype",
+ .lsb = 0x18,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_command_get_control(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "control",
+ .lsb = 0,
+ .msb = 0x17,
+ .values = NULL
+ },
+ .get_next = dm_command_get_cmdtype
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecprogbuf(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "autoexecprogbuf",
+ .lsb = 0x10,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_abstractauto_get_autoexecdata(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "autoexecdata",
+ .lsb = 0,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = dm_abstractauto_get_autoexecprogbuf
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_confstrptr0_get_addr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "addr",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_confstrptr1_get_addr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "addr",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_confstrptr2_get_addr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "addr",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_confstrptr3_get_addr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "addr",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_nextdm_get_addr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "addr",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_data0_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_progbuf0_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_authdata_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *dm_dmcs2_grouptype_values[2] = {
+ [0] = "halt",
+ [1] = "resume"
+};
+static const char *dm_dmcs2_hgselect_values[2] = {
+ [0] = "harts",
+ [1] = "triggers"
+};
+static riscv_debug_reg_field_list_t dm_dmcs2_get_dmexttrigger(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "dmexttrigger",
+ .lsb = 7,
+ .msb = 0xa,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcs2_get_group(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "group",
+ .lsb = 2,
+ .msb = 6,
+ .values = NULL
+ },
+ .get_next = dm_dmcs2_get_dmexttrigger
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcs2_get_grouptype(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "grouptype",
+ .lsb = 0xb,
+ .msb = 0xb,
+ .values = dm_dmcs2_grouptype_values
+ },
+ .get_next = dm_dmcs2_get_group
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcs2_get_hgwrite(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hgwrite",
+ .lsb = 1,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = dm_dmcs2_get_grouptype
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_dmcs2_get_hgselect(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "hgselect",
+ .lsb = 0,
+ .msb = 0,
+ .values = dm_dmcs2_hgselect_values
+ },
+ .get_next = dm_dmcs2_get_hgwrite
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_haltsum0_get_haltsum0(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "haltsum0",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_haltsum1_get_haltsum1(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "haltsum1",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_haltsum2_get_haltsum2(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "haltsum2",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_haltsum3_get_haltsum3(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "haltsum3",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *dm_sbcs_sbversion_values[8] = {
+ [0] = "legacy",
+ [1] = "1_0"
+};
+static const char *dm_sbcs_sbaccess_values[8] = {
+ [0] = "8bit",
+ [1] = "16bit",
+ [2] = "32bit",
+ [3] = "64bit",
+ [4] = "128bit"
+};
+static const char *dm_sbcs_sberror_values[8] = {
+ [0] = "none",
+ [1] = "timeout",
+ [2] = "address",
+ [3] = "alignment",
+ [4] = "size",
+ [7] = "other"
+};
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbasize(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbasize",
+ .lsb = 5,
+ .msb = 0xb,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess128(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess128",
+ .lsb = 4,
+ .msb = 4,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbasize
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess64(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess64",
+ .lsb = 3,
+ .msb = 3,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbaccess128
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbversion(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbversion",
+ .lsb = 0x1d,
+ .msb = 0x1f,
+ .values = dm_sbcs_sbversion_values
+ },
+ .get_next = dm_sbcs_get_sbaccess64
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusyerror(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbbusyerror",
+ .lsb = 0x16,
+ .msb = 0x16,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbversion
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbbusy(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbbusy",
+ .lsb = 0x15,
+ .msb = 0x15,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbbusyerror
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadonaddr(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbreadonaddr",
+ .lsb = 0x14,
+ .msb = 0x14,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbbusy
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess32(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess32",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbreadonaddr
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess",
+ .lsb = 0x11,
+ .msb = 0x13,
+ .values = dm_sbcs_sbaccess_values
+ },
+ .get_next = dm_sbcs_get_sbaccess32
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbautoincrement(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbautoincrement",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbaccess
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbreadondata(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbreadondata",
+ .lsb = 0xf,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbautoincrement
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sberror(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sberror",
+ .lsb = 0xc,
+ .msb = 0xe,
+ .values = dm_sbcs_sberror_values
+ },
+ .get_next = dm_sbcs_get_sbreadondata
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess16(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess16",
+ .lsb = 1,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sberror
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbcs_get_sbaccess8(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "sbaccess8",
+ .lsb = 0,
+ .msb = 0,
+ .values = NULL
+ },
+ .get_next = dm_sbcs_get_sbaccess16
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbaddress0_get_address(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "address",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbaddress1_get_address(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "address",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbaddress2_get_address(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "address",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbaddress3_get_address(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "address",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbdata0_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbdata1_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbdata2_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t dm_sbdata3_get_data(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "data",
+ .lsb = 0,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t shortname_get_field(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "field",
+ .lsb = 0,
+ .msb = 7,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *ac_access_register_aarsize_values[8] = {
+ [2] = "32bit",
+ [3] = "64bit",
+ [4] = "128bit"
+};
+static const char *ac_access_register_aarpostincrement_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static const char *ac_access_register_postexec_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static const char *ac_access_register_transfer_values[2] = {
+ [0] = "disabled",
+ [1] = "enabled"
+};
+static const char *ac_access_register_write_values[2] = {
+ [0] = "arg0",
+ [1] = "register"
+};
+static riscv_debug_reg_field_list_t ac_access_register_get_cmdtype(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cmdtype",
+ .lsb = 0x18,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_aarsize(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "aarsize",
+ .lsb = 0x14,
+ .msb = 0x16,
+ .values = ac_access_register_aarsize_values
+ },
+ .get_next = ac_access_register_get_cmdtype
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_aarpostincrement(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "aarpostincrement",
+ .lsb = 0x13,
+ .msb = 0x13,
+ .values = ac_access_register_aarpostincrement_values
+ },
+ .get_next = ac_access_register_get_aarsize
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_postexec(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "postexec",
+ .lsb = 0x12,
+ .msb = 0x12,
+ .values = ac_access_register_postexec_values
+ },
+ .get_next = ac_access_register_get_aarpostincrement
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_transfer(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "transfer",
+ .lsb = 0x11,
+ .msb = 0x11,
+ .values = ac_access_register_transfer_values
+ },
+ .get_next = ac_access_register_get_postexec
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_write(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "write",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = ac_access_register_write_values
+ },
+ .get_next = ac_access_register_get_transfer
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_register_get_regno(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "regno",
+ .lsb = 0,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = ac_access_register_get_write
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_quick_access_get_cmdtype(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cmdtype",
+ .lsb = 0x18,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static const char *ac_access_memory_aamvirtual_values[2] = {
+ [0] = "physical",
+ [1] = "virtual"
+};
+static const char *ac_access_memory_aamsize_values[8] = {
+ [0] = "8bit",
+ [1] = "16bit",
+ [2] = "32bit",
+ [3] = "64bit",
+ [4] = "128bit"
+};
+static const char *ac_access_memory_write_values[2] = {
+ [0] = "arg0",
+ [1] = "memory"
+};
+static riscv_debug_reg_field_list_t ac_access_memory_get_cmdtype(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "cmdtype",
+ .lsb = 0x18,
+ .msb = 0x1f,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_memory_get_aamvirtual(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "aamvirtual",
+ .lsb = 0x17,
+ .msb = 0x17,
+ .values = ac_access_memory_aamvirtual_values
+ },
+ .get_next = ac_access_memory_get_cmdtype
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_memory_get_aamsize(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "aamsize",
+ .lsb = 0x14,
+ .msb = 0x16,
+ .values = ac_access_memory_aamsize_values
+ },
+ .get_next = ac_access_memory_get_aamvirtual
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_memory_get_aampostincrement(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "aampostincrement",
+ .lsb = 0x13,
+ .msb = 0x13,
+ .values = NULL
+ },
+ .get_next = ac_access_memory_get_aamsize
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_memory_get_write(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "write",
+ .lsb = 0x10,
+ .msb = 0x10,
+ .values = ac_access_memory_write_values
+ },
+ .get_next = ac_access_memory_get_aampostincrement
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t ac_access_memory_get_target_specific(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "target-specific",
+ .lsb = 0xe,
+ .msb = 0xf,
+ .values = NULL
+ },
+ .get_next = ac_access_memory_get_write
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t virt_priv_get_v(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "v",
+ .lsb = 2,
+ .msb = 2,
+ .values = NULL
+ },
+ .get_next = NULL
+ };
+ return result;
+}
+
+static riscv_debug_reg_field_list_t virt_priv_get_prv(riscv_debug_reg_ctx_t context)
+{
+ riscv_debug_reg_field_list_t result = {
+ .field = {
+ .name = "prv",
+ .lsb = 0,
+ .msb = 1,
+ .values = NULL
+ },
+ .get_next = virt_priv_get_v
+ };
+ return result;
+}
+
+riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal)
+{
+ static const riscv_debug_reg_info_t debug_reg_info[] = {
+ [DTM_IDCODE_ORDINAL] = {
+ .name = "idcode",
+ .get_fields_head = dtm_idcode_get_1
+ },
+ [DTM_DTMCS_ORDINAL] = {
+ .name = "dtmcs",
+ .get_fields_head = dtm_dtmcs_get_version
+ },
+ [DTM_DMI_ORDINAL] = {
+ .name = "dmi",
+ .get_fields_head = dtm_dmi_get_op
+ },
+ [DTM_BYPASS_ORDINAL] = {
+ .name = "bypass",
+ .get_fields_head = NULL
+ },
+ [CSR_DCSR_ORDINAL] = {
+ .name = "dcsr",
+ .get_fields_head = csr_dcsr_get_prv
+ },
+ [CSR_DPC_ORDINAL] = {
+ .name = "dpc",
+ .get_fields_head = csr_dpc_get_dpc
+ },
+ [CSR_DSCRATCH0_ORDINAL] = {
+ .name = "dscratch0",
+ .get_fields_head = csr_dscratch0_get_dscratch0
+ },
+ [CSR_DSCRATCH1_ORDINAL] = {
+ .name = "dscratch1",
+ .get_fields_head = csr_dscratch1_get_dscratch1
+ },
+ [CSR_TSELECT_ORDINAL] = {
+ .name = "tselect",
+ .get_fields_head = csr_tselect_get_index
+ },
+ [CSR_TDATA1_ORDINAL] = {
+ .name = "tdata1",
+ .get_fields_head = csr_tdata1_get_data
+ },
+ [CSR_TDATA2_ORDINAL] = {
+ .name = "tdata2",
+ .get_fields_head = csr_tdata2_get_data
+ },
+ [CSR_TDATA3_ORDINAL] = {
+ .name = "tdata3",
+ .get_fields_head = csr_tdata3_get_data
+ },
+ [CSR_TINFO_ORDINAL] = {
+ .name = "tinfo",
+ .get_fields_head = csr_tinfo_get_info
+ },
+ [CSR_TCONTROL_ORDINAL] = {
+ .name = "tcontrol",
+ .get_fields_head = csr_tcontrol_get_mte
+ },
+ [CSR_SCONTEXT_ORDINAL] = {
+ .name = "scontext",
+ .get_fields_head = csr_scontext_get_data
+ },
+ [CSR_MCONTEXT_ORDINAL] = {
+ .name = "mcontext",
+ .get_fields_head = csr_mcontext_get_hcontext
+ },
+ [CSR_MCONTROL_ORDINAL] = {
+ .name = "mcontrol",
+ .get_fields_head = csr_mcontrol_get_load
+ },
+ [CSR_MCONTROL6_ORDINAL] = {
+ .name = "mcontrol6",
+ .get_fields_head = csr_mcontrol6_get_load
+ },
+ [CSR_ICOUNT_ORDINAL] = {
+ .name = "icount",
+ .get_fields_head = csr_icount_get_action
+ },
+ [CSR_ITRIGGER_ORDINAL] = {
+ .name = "itrigger",
+ .get_fields_head = csr_itrigger_get_action
+ },
+ [CSR_ETRIGGER_ORDINAL] = {
+ .name = "etrigger",
+ .get_fields_head = csr_etrigger_get_action
+ },
+ [CSR_TMEXTTRIGGER_ORDINAL] = {
+ .name = "tmexttrigger",
+ .get_fields_head = csr_tmexttrigger_get_action
+ },
+ [CSR_TEXTRA32_ORDINAL] = {
+ .name = "textra32",
+ .get_fields_head = csr_textra32_get_sselect
+ },
+ [CSR_TEXTRA64_ORDINAL] = {
+ .name = "textra64",
+ .get_fields_head = csr_textra64_get_sselect
+ },
+ [DM_DMSTATUS_ORDINAL] = {
+ .name = "dmstatus",
+ .get_fields_head = dm_dmstatus_get_version
+ },
+ [DM_DMCONTROL_ORDINAL] = {
+ .name = "dmcontrol",
+ .get_fields_head = dm_dmcontrol_get_dmactive
+ },
+ [DM_HARTINFO_ORDINAL] = {
+ .name = "hartinfo",
+ .get_fields_head = dm_hartinfo_get_dataaddr
+ },
+ [DM_HAWINDOWSEL_ORDINAL] = {
+ .name = "hawindowsel",
+ .get_fields_head = dm_hawindowsel_get_hawindowsel
+ },
+ [DM_HAWINDOW_ORDINAL] = {
+ .name = "hawindow",
+ .get_fields_head = dm_hawindow_get_maskdata
+ },
+ [DM_ABSTRACTCS_ORDINAL] = {
+ .name = "abstractcs",
+ .get_fields_head = dm_abstractcs_get_datacount
+ },
+ [DM_COMMAND_ORDINAL] = {
+ .name = "command",
+ .get_fields_head = dm_command_get_control
+ },
+ [DM_ABSTRACTAUTO_ORDINAL] = {
+ .name = "abstractauto",
+ .get_fields_head = dm_abstractauto_get_autoexecdata
+ },
+ [DM_CONFSTRPTR0_ORDINAL] = {
+ .name = "confstrptr0",
+ .get_fields_head = dm_confstrptr0_get_addr
+ },
+ [DM_CONFSTRPTR1_ORDINAL] = {
+ .name = "confstrptr1",
+ .get_fields_head = dm_confstrptr1_get_addr
+ },
+ [DM_CONFSTRPTR2_ORDINAL] = {
+ .name = "confstrptr2",
+ .get_fields_head = dm_confstrptr2_get_addr
+ },
+ [DM_CONFSTRPTR3_ORDINAL] = {
+ .name = "confstrptr3",
+ .get_fields_head = dm_confstrptr3_get_addr
+ },
+ [DM_NEXTDM_ORDINAL] = {
+ .name = "nextdm",
+ .get_fields_head = dm_nextdm_get_addr
+ },
+ [DM_DATA0_ORDINAL] = {
+ .name = "data0",
+ .get_fields_head = dm_data0_get_data
+ },
+ [DM_PROGBUF0_ORDINAL] = {
+ .name = "progbuf0",
+ .get_fields_head = dm_progbuf0_get_data
+ },
+ [DM_AUTHDATA_ORDINAL] = {
+ .name = "authdata",
+ .get_fields_head = dm_authdata_get_data
+ },
+ [DM_DMCS2_ORDINAL] = {
+ .name = "dmcs2",
+ .get_fields_head = dm_dmcs2_get_hgselect
+ },
+ [DM_HALTSUM0_ORDINAL] = {
+ .name = "haltsum0",
+ .get_fields_head = dm_haltsum0_get_haltsum0
+ },
+ [DM_HALTSUM1_ORDINAL] = {
+ .name = "haltsum1",
+ .get_fields_head = dm_haltsum1_get_haltsum1
+ },
+ [DM_HALTSUM2_ORDINAL] = {
+ .name = "haltsum2",
+ .get_fields_head = dm_haltsum2_get_haltsum2
+ },
+ [DM_HALTSUM3_ORDINAL] = {
+ .name = "haltsum3",
+ .get_fields_head = dm_haltsum3_get_haltsum3
+ },
+ [DM_SBCS_ORDINAL] = {
+ .name = "sbcs",
+ .get_fields_head = dm_sbcs_get_sbaccess8
+ },
+ [DM_SBADDRESS0_ORDINAL] = {
+ .name = "sbaddress0",
+ .get_fields_head = dm_sbaddress0_get_address
+ },
+ [DM_SBADDRESS1_ORDINAL] = {
+ .name = "sbaddress1",
+ .get_fields_head = dm_sbaddress1_get_address
+ },
+ [DM_SBADDRESS2_ORDINAL] = {
+ .name = "sbaddress2",
+ .get_fields_head = dm_sbaddress2_get_address
+ },
+ [DM_SBADDRESS3_ORDINAL] = {
+ .name = "sbaddress3",
+ .get_fields_head = dm_sbaddress3_get_address
+ },
+ [DM_SBDATA0_ORDINAL] = {
+ .name = "sbdata0",
+ .get_fields_head = dm_sbdata0_get_data
+ },
+ [DM_SBDATA1_ORDINAL] = {
+ .name = "sbdata1",
+ .get_fields_head = dm_sbdata1_get_data
+ },
+ [DM_SBDATA2_ORDINAL] = {
+ .name = "sbdata2",
+ .get_fields_head = dm_sbdata2_get_data
+ },
+ [DM_SBDATA3_ORDINAL] = {
+ .name = "sbdata3",
+ .get_fields_head = dm_sbdata3_get_data
+ },
+ [SHORTNAME_ORDINAL] = {
+ .name = "shortname",
+ .get_fields_head = shortname_get_field
+ },
+ [AC_ACCESS_REGISTER_ORDINAL] = {
+ .name = "access register",
+ .get_fields_head = ac_access_register_get_regno
+ },
+ [AC_QUICK_ACCESS_ORDINAL] = {
+ .name = "quick access",
+ .get_fields_head = ac_quick_access_get_cmdtype
+ },
+ [AC_ACCESS_MEMORY_ORDINAL] = {
+ .name = "access memory",
+ .get_fields_head = ac_access_memory_get_target_specific
+ },
+ [VIRT_PRIV_ORDINAL] = {
+ .name = "priv",
+ .get_fields_head = virt_priv_get_prv
+ },
+ };
+ return debug_reg_info[reg_ordinal];
+}
diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h
index 8113d47..dbe5142 100644
--- a/src/target/riscv/debug_defines.h
+++ b/src/target/riscv/debug_defines.h
@@ -1,21 +1,23 @@
/*
- * This file is auto-generated by running 'make debug_defines.h' in
- * https://github.com/riscv/riscv-debug-spec/ (d749752)
+ * This file is auto-generated by running 'make debug_defines' in
+ * https://github.com/riscv/riscv-debug-spec/ (40b9a05)
*/
+#ifndef DEBUG_DEFINES_H
+#define DEBUG_DEFINES_H
#define DTM_IDCODE 0x01
/*
* Identifies the release version of this part.
*/
-#define DTM_IDCODE_VERSION_OFFSET 0x1c
-#define DTM_IDCODE_VERSION_LENGTH 4
-#define DTM_IDCODE_VERSION 0xf0000000U
+#define DTM_IDCODE_VERSION_OFFSET 0x1cULL
+#define DTM_IDCODE_VERSION_LENGTH 4ULL
+#define DTM_IDCODE_VERSION 0xf0000000ULL
/*
* Identifies the designer's part number of this part.
*/
-#define DTM_IDCODE_PARTNUMBER_OFFSET 0xc
-#define DTM_IDCODE_PARTNUMBER_LENGTH 0x10
-#define DTM_IDCODE_PARTNUMBER 0xffff000
+#define DTM_IDCODE_PARTNUMBER_OFFSET 0xcULL
+#define DTM_IDCODE_PARTNUMBER_LENGTH 0x10ULL
+#define DTM_IDCODE_PARTNUMBER 0xffff000ULL
/*
* Identifies the designer/manufacturer of this part. Bits 6:0 must be
* bits 6:0 of the designer/manufacturer's Identification Code as
@@ -23,14 +25,47 @@
* count of the number of continuation characters (0x7f) in that same
* Identification Code.
*/
-#define DTM_IDCODE_MANUFID_OFFSET 1
-#define DTM_IDCODE_MANUFID_LENGTH 0xb
-#define DTM_IDCODE_MANUFID 0xffe
-#define DTM_IDCODE_1_OFFSET 0
-#define DTM_IDCODE_1_LENGTH 1
-#define DTM_IDCODE_1 1
+#define DTM_IDCODE_MANUFID_OFFSET 1ULL
+#define DTM_IDCODE_MANUFID_LENGTH 0xbULL
+#define DTM_IDCODE_MANUFID 0xffeULL
+#define DTM_IDCODE_1_OFFSET 0ULL
+#define DTM_IDCODE_1_LENGTH 1ULL
+#define DTM_IDCODE_1 1ULL
#define DTM_DTMCS 0x10
/*
+ * This optional field may provide additional detail about an error
+ * that occurred when communicating with a DM. It is updated whenever
+ * \FdtmDmiOp is updated by the hardware or when 1 is written to
+ * \FdtmDtmcsDmireset.
+ */
+#define DTM_DTMCS_ERRINFO_OFFSET 0x12ULL
+#define DTM_DTMCS_ERRINFO_LENGTH 3ULL
+#define DTM_DTMCS_ERRINFO 0x1c0000ULL
+/*
+ * not implemented: This field is not implemented.
+ */
+#define DTM_DTMCS_ERRINFO_NOT_IMPLEMENTED 0
+/*
+ * dmi error: There was an error between the DTM and DMI.
+ */
+#define DTM_DTMCS_ERRINFO_DMI_ERROR 1
+/*
+ * communication error: There was an error between the DMI and a DMI subordinate.
+ */
+#define DTM_DTMCS_ERRINFO_COMMUNICATION_ERROR 2
+/*
+ * device error: The DMI subordinate reported an error.
+ */
+#define DTM_DTMCS_ERRINFO_DEVICE_ERROR 3
+/*
+ * unknown: There is no error to report, or no further information available
+ * about the error. This is the reset value if the field is implemented.
+ */
+#define DTM_DTMCS_ERRINFO_UNKNOWN 4
+/*
+ * Other values are reserved for future use by this specification.
+ */
+/*
* Writing 1 to this bit does a hard reset of the DTM,
* causing the DTM to forget about any outstanding DMI transactions, and
* returning all registers and internal state to their reset value.
@@ -39,16 +74,16 @@
* complete (e.g. a reset condition caused an inflight DMI transaction to
* be cancelled).
*/
-#define DTM_DTMCS_DMIHARDRESET_OFFSET 0x11
-#define DTM_DTMCS_DMIHARDRESET_LENGTH 1
-#define DTM_DTMCS_DMIHARDRESET 0x20000
+#define DTM_DTMCS_DTMHARDRESET_OFFSET 0x11ULL
+#define DTM_DTMCS_DTMHARDRESET_LENGTH 1ULL
+#define DTM_DTMCS_DTMHARDRESET 0x20000ULL
/*
- * Writing 1 to this bit clears the sticky error state, but does
- * not affect outstanding DMI transactions.
+ * Writing 1 to this bit clears the sticky error state and resets
+ * \FdtmDtmcsErrinfo, but does not affect outstanding DMI transactions.
*/
-#define DTM_DTMCS_DMIRESET_OFFSET 0x10
-#define DTM_DTMCS_DMIRESET_LENGTH 1
-#define DTM_DTMCS_DMIRESET 0x10000
+#define DTM_DTMCS_DMIRESET_OFFSET 0x10ULL
+#define DTM_DTMCS_DMIRESET_LENGTH 1ULL
+#define DTM_DTMCS_DMIRESET 0x10000ULL
/*
* This is a hint to the debugger of the minimum number of
* cycles a debugger should spend in
@@ -64,24 +99,24 @@
*
* And so on.
*/
-#define DTM_DTMCS_IDLE_OFFSET 0xc
-#define DTM_DTMCS_IDLE_LENGTH 3
-#define DTM_DTMCS_IDLE 0x7000
+#define DTM_DTMCS_IDLE_OFFSET 0xcULL
+#define DTM_DTMCS_IDLE_LENGTH 3ULL
+#define DTM_DTMCS_IDLE 0x7000ULL
/*
* Read-only alias of \FdtmDmiOp.
*/
-#define DTM_DTMCS_DMISTAT_OFFSET 0xa
-#define DTM_DTMCS_DMISTAT_LENGTH 2
-#define DTM_DTMCS_DMISTAT 0xc00
+#define DTM_DTMCS_DMISTAT_OFFSET 0xaULL
+#define DTM_DTMCS_DMISTAT_LENGTH 2ULL
+#define DTM_DTMCS_DMISTAT 0xc00ULL
/*
* The size of \FdmSbaddressZeroAddress in \RdtmDmi.
*/
-#define DTM_DTMCS_ABITS_OFFSET 4
-#define DTM_DTMCS_ABITS_LENGTH 6
-#define DTM_DTMCS_ABITS 0x3f0
-#define DTM_DTMCS_VERSION_OFFSET 0
-#define DTM_DTMCS_VERSION_LENGTH 4
-#define DTM_DTMCS_VERSION 0xf
+#define DTM_DTMCS_ABITS_OFFSET 4ULL
+#define DTM_DTMCS_ABITS_LENGTH 6ULL
+#define DTM_DTMCS_ABITS 0x3f0ULL
+#define DTM_DTMCS_VERSION_OFFSET 0ULL
+#define DTM_DTMCS_VERSION_LENGTH 4ULL
+#define DTM_DTMCS_VERSION 0xfULL
/*
* 0.11: Version described in spec version 0.11.
*/
@@ -98,23 +133,25 @@
/*
* Address used for DMI access. In Update-DR this value is used
* to access the DM over the DMI.
+ * \FdtmDmiOp defines what this register contains after every possible
+ * operation.
*/
-#define DTM_DMI_ADDRESS_OFFSET 0x22
-#define DTM_DMI_ADDRESS_LENGTH(abits) abits
-#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL<<abits)) + -0x400000000ULL)
+#define DTM_DMI_ADDRESS_OFFSET 0x22ULL
+#define DTM_DMI_ADDRESS_LENGTH(abits) (abits)
+#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL << (abits))) + -0x400000000ULL)
/*
* 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 0x20
+#define DTM_DMI_DATA_OFFSET 2ULL
+#define DTM_DMI_DATA_LENGTH 0x20ULL
#define DTM_DMI_DATA 0x3fffffffcULL
/*
* When the debugger writes this field, it has the following meaning:
*/
-#define DTM_DMI_OP_OFFSET 0
-#define DTM_DMI_OP_LENGTH 2
-#define DTM_DMI_OP 3
+#define DTM_DMI_OP_OFFSET 0ULL
+#define DTM_DMI_OP_LENGTH 2ULL
+#define DTM_DMI_OP 3ULL
/*
* nop: Ignore \FdmSbdataZeroData and \FdmSbaddressZeroAddress.
*
@@ -122,14 +159,24 @@
* This operation should never result in a busy or error response.
* The address and data reported in the following Capture-DR
* are undefined.
+ *
+ * This operation leaves the values in \FdtmDmiAddress and \FdtmDmiData
+ * \unspecified.
*/
#define DTM_DMI_OP_NOP 0
/*
- * read: Read from \FdmSbaddressZeroAddress.
+ * read: Read from \FdtmDmiAddress.
+ *
+ * When this operation succeeds, \FdtmDmiAddress contains the address
+ * that was read from, and \FdtmDmiData contains the value that was
+ * read.
*/
#define DTM_DMI_OP_READ 1
/*
- * write: Write \FdmSbdataZeroData to \FdmSbaddressZeroAddress.
+ * write: Write \FdtmDmiData to \FdtmDmiAddress.
+ *
+ * This operation leaves the values in \FdtmDmiAddress and \FdtmDmiData
+ * \unspecified.
*/
#define DTM_DMI_OP_WRITE 2
/*
@@ -150,10 +197,13 @@
* this access will be ignored. This status is sticky and can be
* cleared by writing \FdtmDtmcsDmireset in \RdtmDtmcs.
*
- * This indicates that the DM itself responded with an error.
+ * This indicates that the DM itself or the DMI responded with an error.
* There are no specified cases in which the DM would
* respond with an error, and DMI is not required to support
* returning errors.
+ *
+ * If a debugger sees this status, there might be additional
+ * information in \FdtmDtmcsErrinfo.
*/
#define DTM_DMI_OP_FAILED 2
/*
@@ -167,9 +217,9 @@
*/
#define DTM_DMI_OP_BUSY 3
#define CSR_DCSR 0x7b0
-#define CSR_DCSR_DEBUGVER_OFFSET 0x1c
-#define CSR_DCSR_DEBUGVER_LENGTH 4
-#define CSR_DCSR_DEBUGVER 0xf0000000U
+#define CSR_DCSR_DEBUGVER_OFFSET 0x1cULL
+#define CSR_DCSR_DEBUGVER_LENGTH 4ULL
+#define CSR_DCSR_DEBUGVER 0xf0000000ULL
/*
* none: There is no debug support.
*/
@@ -183,9 +233,9 @@
* available version of this spec.
*/
#define CSR_DCSR_DEBUGVER_CUSTOM 15
-#define CSR_DCSR_EBREAKVS_OFFSET 0x11
-#define CSR_DCSR_EBREAKVS_LENGTH 1
-#define CSR_DCSR_EBREAKVS 0x20000
+#define CSR_DCSR_EBREAKVS_OFFSET 0x11ULL
+#define CSR_DCSR_EBREAKVS_LENGTH 1ULL
+#define CSR_DCSR_EBREAKVS 0x20000ULL
/*
* exception: {\tt ebreak} instructions in VS-mode behave as described in the
* Privileged Spec.
@@ -198,9 +248,9 @@
/*
* This bit is hardwired to 0 if the hart does not support virtualization mode.
*/
-#define CSR_DCSR_EBREAKVU_OFFSET 0x10
-#define CSR_DCSR_EBREAKVU_LENGTH 1
-#define CSR_DCSR_EBREAKVU 0x10000
+#define CSR_DCSR_EBREAKVU_OFFSET 0x10ULL
+#define CSR_DCSR_EBREAKVU_LENGTH 1ULL
+#define CSR_DCSR_EBREAKVU 0x10000ULL
/*
* exception: {\tt ebreak} instructions in VU-mode behave as described in the
* Privileged Spec.
@@ -213,9 +263,9 @@
/*
* This bit is hardwired to 0 if the hart does not support virtualization mode.
*/
-#define CSR_DCSR_EBREAKM_OFFSET 0xf
-#define CSR_DCSR_EBREAKM_LENGTH 1
-#define CSR_DCSR_EBREAKM 0x8000
+#define CSR_DCSR_EBREAKM_OFFSET 0xfULL
+#define CSR_DCSR_EBREAKM_LENGTH 1ULL
+#define CSR_DCSR_EBREAKM 0x8000ULL
/*
* exception: {\tt ebreak} instructions in M-mode behave as described in the
* Privileged Spec.
@@ -225,9 +275,9 @@
* debug mode: {\tt ebreak} instructions in M-mode enter Debug Mode.
*/
#define CSR_DCSR_EBREAKM_DEBUG_MODE 1
-#define CSR_DCSR_EBREAKS_OFFSET 0xd
-#define CSR_DCSR_EBREAKS_LENGTH 1
-#define CSR_DCSR_EBREAKS 0x2000
+#define CSR_DCSR_EBREAKS_OFFSET 0xdULL
+#define CSR_DCSR_EBREAKS_LENGTH 1ULL
+#define CSR_DCSR_EBREAKS 0x2000ULL
/*
* exception: {\tt ebreak} instructions in S-mode behave as described in the
* Privileged Spec.
@@ -240,9 +290,9 @@
/*
* This bit is hardwired to 0 if the hart does not support S-mode.
*/
-#define CSR_DCSR_EBREAKU_OFFSET 0xc
-#define CSR_DCSR_EBREAKU_LENGTH 1
-#define CSR_DCSR_EBREAKU 0x1000
+#define CSR_DCSR_EBREAKU_OFFSET 0xcULL
+#define CSR_DCSR_EBREAKU_LENGTH 1ULL
+#define CSR_DCSR_EBREAKU 0x1000ULL
/*
* exception: {\tt ebreak} instructions in U-mode behave as described in the
* Privileged Spec.
@@ -255,15 +305,18 @@
/*
* This bit is hardwired to 0 if the hart does not support U-mode.
*/
-#define CSR_DCSR_STEPIE_OFFSET 0xb
-#define CSR_DCSR_STEPIE_LENGTH 1
-#define CSR_DCSR_STEPIE 0x800
+#define CSR_DCSR_STEPIE_OFFSET 0xbULL
+#define CSR_DCSR_STEPIE_LENGTH 1ULL
+#define CSR_DCSR_STEPIE 0x800ULL
/*
- * interrupts disabled: Interrupts (including NMI) are disabled during single stepping.
+ * interrupts disabled: Interrupts (including NMI) are disabled during single stepping
+ * with \FcsrDcsrStep set.
+ * This value should be supported.
*/
#define CSR_DCSR_STEPIE_INTERRUPTS_DISABLED 0
/*
- * interrupts enabled: Interrupts (including NMI) are enabled during single stepping.
+ * interrupts enabled: Interrupts (including NMI) are enabled during single stepping
+ * with \FcsrDcsrStep set.
*/
#define CSR_DCSR_STEPIE_INTERRUPTS_ENABLED 1
/*
@@ -273,9 +326,9 @@
* The debugger must not change the value of this bit while the hart
* is running.
*/
-#define CSR_DCSR_STOPCOUNT_OFFSET 0xa
-#define CSR_DCSR_STOPCOUNT_LENGTH 1
-#define CSR_DCSR_STOPCOUNT 0x400
+#define CSR_DCSR_STOPCOUNT_OFFSET 0xaULL
+#define CSR_DCSR_STOPCOUNT_LENGTH 1ULL
+#define CSR_DCSR_STOPCOUNT 0x400ULL
/*
* normal: Increment counters as usual.
*/
@@ -291,17 +344,20 @@
/*
* An implementation may hardwire this bit to 0 or 1.
*/
-#define CSR_DCSR_STOPTIME_OFFSET 9
-#define CSR_DCSR_STOPTIME_LENGTH 1
-#define CSR_DCSR_STOPTIME 0x200
+#define CSR_DCSR_STOPTIME_OFFSET 9ULL
+#define CSR_DCSR_STOPTIME_LENGTH 1ULL
+#define CSR_DCSR_STOPTIME 0x200ULL
/*
- * normal: Increment \Rtime as usual.
+ * normal: \Rtime continues to reflect \Rmtime.
*/
#define CSR_DCSR_STOPTIME_NORMAL 0
/*
- * freeze: Don't increment \Rtime while in Debug Mode. If all harts
- * have \FcsrDcsrStoptime=1 and are in Debug Mode then \Rmtime
- * is also allowed to stop incrementing.
+ * freeze: \Rtime is frozen at the time that Debug Mode was entered. When
+ * leaving Debug Mode, \Rtime will reflect the latest
+ * value of \Rmtime again.
+ *
+ * While all harts have \FcsrDcsrStoptime=1 and are in Debug Mode,
+ * \Rmtime is allowed to stop incrementing.
*/
#define CSR_DCSR_STOPTIME_FREEZE 1
/*
@@ -314,9 +370,9 @@
* cycle, hardware should set \FcsrDcsrCause to the cause with the highest
* priority. See table~\ref{tab:dcsrcausepriority} for priorities.
*/
-#define CSR_DCSR_CAUSE_OFFSET 6
-#define CSR_DCSR_CAUSE_LENGTH 3
-#define CSR_DCSR_CAUSE 0x1c0
+#define CSR_DCSR_CAUSE_OFFSET 6ULL
+#define CSR_DCSR_CAUSE_LENGTH 3ULL
+#define CSR_DCSR_CAUSE 0x1c0ULL
/*
* ebreak: An {\tt ebreak} instruction was executed.
*/
@@ -354,12 +410,12 @@
* when exiting Debug Mode.
* This bit is hardwired to 0 on harts that do not support virtualization mode.
*/
-#define CSR_DCSR_V_OFFSET 5
-#define CSR_DCSR_V_LENGTH 1
-#define CSR_DCSR_V 0x20
-#define CSR_DCSR_MPRVEN_OFFSET 4
-#define CSR_DCSR_MPRVEN_LENGTH 1
-#define CSR_DCSR_MPRVEN 0x10
+#define CSR_DCSR_V_OFFSET 5ULL
+#define CSR_DCSR_V_LENGTH 1ULL
+#define CSR_DCSR_V 0x20ULL
+#define CSR_DCSR_MPRVEN_OFFSET 4ULL
+#define CSR_DCSR_MPRVEN_LENGTH 1ULL
+#define CSR_DCSR_MPRVEN 0x10ULL
/*
* disabled: \FcsrMstatusMprv in \Rmstatus is ignored in Debug Mode.
*/
@@ -378,9 +434,9 @@
* reliable debugging may no longer be possible once this bit becomes set.
* This is implementation-dependent.
*/
-#define CSR_DCSR_NMIP_OFFSET 3
-#define CSR_DCSR_NMIP_LENGTH 1
-#define CSR_DCSR_NMIP 8
+#define CSR_DCSR_NMIP_OFFSET 3ULL
+#define CSR_DCSR_NMIP_LENGTH 1ULL
+#define CSR_DCSR_NMIP 8ULL
/*
* When set and not in Debug Mode, the hart will only execute a single
* instruction and then enter Debug Mode. See Section~\ref{stepBit}
@@ -389,9 +445,9 @@
* The debugger must not change the value of this bit while the hart
* is running.
*/
-#define CSR_DCSR_STEP_OFFSET 2
-#define CSR_DCSR_STEP_LENGTH 1
-#define CSR_DCSR_STEP 4
+#define CSR_DCSR_STEP_OFFSET 2ULL
+#define CSR_DCSR_STEP_LENGTH 1ULL
+#define CSR_DCSR_STEP 4ULL
/*
* Contains the privilege mode the hart was operating in when Debug
* Mode was entered. The encoding is described in Table
@@ -402,23 +458,29 @@
* encoding written is not supported or the debugger is not allowed to
* change to it, the hart may change to any supported privilege mode.
*/
-#define CSR_DCSR_PRV_OFFSET 0
-#define CSR_DCSR_PRV_LENGTH 2
-#define CSR_DCSR_PRV 3
+#define CSR_DCSR_PRV_OFFSET 0ULL
+#define CSR_DCSR_PRV_LENGTH 2ULL
+#define CSR_DCSR_PRV 3ULL
#define CSR_DPC 0x7b1
-#define CSR_DPC_DPC_OFFSET 0
-#define CSR_DPC_DPC_LENGTH(DXLEN) DXLEN
-#define CSR_DPC_DPC(DXLEN) ((1ULL<<DXLEN) + -1)
+#define CSR_DPC_DPC_OFFSET 0ULL
+#define CSR_DPC_DPC_LENGTH(DXLEN) (DXLEN)
+#define CSR_DPC_DPC(DXLEN) ((1ULL << (DXLEN)) + -1ULL)
#define CSR_DSCRATCH0 0x7b2
+#define CSR_DSCRATCH0_DSCRATCH0_OFFSET 0ULL
+#define CSR_DSCRATCH0_DSCRATCH0_LENGTH(DXLEN) (DXLEN)
+#define CSR_DSCRATCH0_DSCRATCH0(DXLEN) ((1ULL << (DXLEN)) + -1ULL)
#define CSR_DSCRATCH1 0x7b3
+#define CSR_DSCRATCH1_DSCRATCH1_OFFSET 0ULL
+#define CSR_DSCRATCH1_DSCRATCH1_LENGTH(DXLEN) (DXLEN)
+#define CSR_DSCRATCH1_DSCRATCH1(DXLEN) ((1ULL << (DXLEN)) + -1ULL)
#define CSR_TSELECT 0x7a0
-#define CSR_TSELECT_INDEX_OFFSET 0
-#define CSR_TSELECT_INDEX_LENGTH(XLEN) XLEN
-#define CSR_TSELECT_INDEX(XLEN) ((1ULL<<XLEN) + -1)
+#define CSR_TSELECT_INDEX_OFFSET 0ULL
+#define CSR_TSELECT_INDEX_LENGTH(XLEN) (XLEN)
+#define CSR_TSELECT_INDEX(XLEN) ((1ULL << (XLEN)) + -1ULL)
#define CSR_TDATA1 0x7a1
-#define CSR_TDATA1_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_TDATA1_TYPE_LENGTH 4
-#define CSR_TDATA1_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
+#define CSR_TDATA1_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_TDATA1_TYPE_LENGTH 4ULL
+#define CSR_TDATA1_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
/*
* none: There is no trigger at this \RcsrTselect.
*/
@@ -468,8 +530,9 @@
/*
* disabled: This trigger is disabled. In this state, \RcsrTdataTwo and
* \RcsrTdataThree can be written with any value that is supported for
- * any of the types this trigger implements. The remaining bits in this
- * register are ignored.
+ * any of the types this trigger implements.
+ * The remaining bits in this register, except for \FcsrTdataOneDmode,
+ * are ignored.
*/
#define CSR_TDATA1_TYPE_DISABLED 15
/*
@@ -478,9 +541,9 @@
/*
* If \FcsrTdataOneType is 0, then this bit is hard-wired to 0.
*/
-#define CSR_TDATA1_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_TDATA1_DMODE_LENGTH 1
-#define CSR_TDATA1_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_TDATA1_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_TDATA1_DMODE_LENGTH 1ULL
+#define CSR_TDATA1_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* both: Both Debug and M-mode can write the {\tt tdata} registers at the
* selected \RcsrTselect.
@@ -504,19 +567,44 @@
*
* Trigger-specific data.
*/
-#define CSR_TDATA1_DATA_OFFSET 0
-#define CSR_TDATA1_DATA_LENGTH(XLEN) (XLEN + -5)
-#define CSR_TDATA1_DATA(XLEN) ((1ULL<<(XLEN + -5)) + -1)
+#define CSR_TDATA1_DATA_OFFSET 0ULL
+#define CSR_TDATA1_DATA_LENGTH(XLEN) ((XLEN) + -5ULL)
+#define CSR_TDATA1_DATA(XLEN) ((1ULL << ((XLEN) + -5ULL)) + -1ULL)
#define CSR_TDATA2 0x7a2
-#define CSR_TDATA2_DATA_OFFSET 0
-#define CSR_TDATA2_DATA_LENGTH(XLEN) XLEN
-#define CSR_TDATA2_DATA(XLEN) ((1ULL<<XLEN) + -1)
+#define CSR_TDATA2_DATA_OFFSET 0ULL
+#define CSR_TDATA2_DATA_LENGTH(XLEN) (XLEN)
+#define CSR_TDATA2_DATA(XLEN) ((1ULL << (XLEN)) + -1ULL)
#define CSR_TDATA3 0x7a3
-#define CSR_TDATA3_DATA_OFFSET 0
-#define CSR_TDATA3_DATA_LENGTH(XLEN) XLEN
-#define CSR_TDATA3_DATA(XLEN) ((1ULL<<XLEN) + -1)
+#define CSR_TDATA3_DATA_OFFSET 0ULL
+#define CSR_TDATA3_DATA_LENGTH(XLEN) (XLEN)
+#define CSR_TDATA3_DATA(XLEN) ((1ULL << (XLEN)) + -1ULL)
#define CSR_TINFO 0x7a4
/*
+ * Contains the version of the Sdtrig extension implemented.
+ */
+#define CSR_TINFO_VERSION_OFFSET 0x18ULL
+#define CSR_TINFO_VERSION_LENGTH 8ULL
+#define CSR_TINFO_VERSION 0xff000000ULL
+/*
+ * 0: Supports triggers as described in this spec at commit 5a5c078,
+ * made on February 2, 2023.
+ *
+ * \begin{steps}{In these older versions:}
+ * \item \RcsrMcontrolSix has a timing bit identical to
+ * \FcsrMcontrolTiming
+ * \item \FcsrMcontrolSixHitZero behaves just as \FcsrMcontrolHit.
+ * \item \FcsrMcontrolSixHitOne is read-only 0.
+ * \item Encodings for \FcsrMcontrolSixSize for access sizes larger
+ * than 64 bits are different.
+ * \end{steps}
+ */
+#define CSR_TINFO_VERSION_0 0
+/*
+ * 1: Supports triggers as described in the ratified version 1.0 of
+ * this document.
+ */
+#define CSR_TINFO_VERSION_1 1
+/*
* One bit for each possible \FcsrTdataOneType enumerated in \RcsrTdataOne. Bit N
* corresponds to type N. If the bit is set, then that type is
* supported by the currently selected trigger.
@@ -524,9 +612,9 @@
* If the currently selected trigger doesn't exist, this field
* contains 1.
*/
-#define CSR_TINFO_INFO_OFFSET 0
-#define CSR_TINFO_INFO_LENGTH 0x10
-#define CSR_TINFO_INFO 0xffff
+#define CSR_TINFO_INFO_OFFSET 0ULL
+#define CSR_TINFO_INFO_LENGTH 0x10ULL
+#define CSR_TINFO_INFO 0xffffULL
#define CSR_TCONTROL 0x7a5
/*
* M-mode previous trigger enable field.
@@ -535,18 +623,18 @@
* regarding triggers with action=0 firing in M-mode trap handlers. See
* Section~\ref{sec:nativetrigger} for more details.
*
- * When a breakpoint trap into M-mode is taken, \FcsrTcontrolMpte is set to the value of
+ * When any trap into M-mode is taken, \FcsrTcontrolMpte is set to the value of
* \FcsrTcontrolMte.
*/
-#define CSR_TCONTROL_MPTE_OFFSET 7
-#define CSR_TCONTROL_MPTE_LENGTH 1
-#define CSR_TCONTROL_MPTE 0x80
+#define CSR_TCONTROL_MPTE_OFFSET 7ULL
+#define CSR_TCONTROL_MPTE_LENGTH 1ULL
+#define CSR_TCONTROL_MPTE 0x80ULL
/*
* M-mode trigger enable field.
*/
-#define CSR_TCONTROL_MTE_OFFSET 3
-#define CSR_TCONTROL_MTE_LENGTH 1
-#define CSR_TCONTROL_MTE 8
+#define CSR_TCONTROL_MTE_OFFSET 3ULL
+#define CSR_TCONTROL_MTE_LENGTH 1ULL
+#define CSR_TCONTROL_MTE 8ULL
/*
* disabled: Triggers with action=0 do not match/fire while the hart is in M-mode.
*/
@@ -556,25 +644,10 @@
*/
#define CSR_TCONTROL_MTE_ENABLED 1
/*
- * When a breakpoint trap into M-mode is taken, \FcsrTcontrolMte is set to 0. When {\tt
+ * When any trap into M-mode is taken, \FcsrTcontrolMte is set to 0. When {\tt
* mret} is executed, \FcsrTcontrolMte is set to the value of \FcsrTcontrolMpte.
*/
#define CSR_HCONTEXT 0x6a8
-/*
- * Hypervisor mode software can write a context number to this register,
- * which can be used to set triggers that only fire in that specific
- * context.
- *
- * An implementation may tie any number of upper bits in this field to
- * 0. If the H extension is not implemented, it's recommended to implement
- * no more than 6 bits on RV32 and 13 on RV64 (as visible through the
- * \RcsrMcontext register). If the H extension is implemented,
- * it's recommended to implement no more than 7 bits on RV32
- * and 14 on RV64.
- */
-#define CSR_HCONTEXT_HCONTEXT_OFFSET 0
-#define CSR_HCONTEXT_HCONTEXT_LENGTH(XLEN) XLEN
-#define CSR_HCONTEXT_HCONTEXT(XLEN) ((1ULL<<XLEN) + -1)
#define CSR_SCONTEXT 0x5a8
/*
* Supervisor mode software can write a context number to this
@@ -585,18 +658,33 @@
* 0. It's recommended to implement no more than 16 bits on RV32, and
* 34 on RV64.
*/
-#define CSR_SCONTEXT_DATA_OFFSET 0
-#define CSR_SCONTEXT_DATA_LENGTH(XLEN) XLEN
-#define CSR_SCONTEXT_DATA(XLEN) ((1ULL<<XLEN) + -1)
+#define CSR_SCONTEXT_DATA_OFFSET 0ULL
+#define CSR_SCONTEXT_DATA_LENGTH(XLEN) (XLEN)
+#define CSR_SCONTEXT_DATA(XLEN) ((1ULL << (XLEN)) + -1ULL)
#define CSR_MCONTEXT 0x7a8
+/*
+ * M-Mode or HS-Mode (using \RcsrHcontext) software can write a context
+ * number to this register, which can be used to set triggers that only
+ * fire in that specific context.
+ *
+ * An implementation may tie any number of upper bits in this field to
+ * 0. If the H extension is not implemented, it's recommended to implement
+ * no more than 6 bits on RV32 and 13 on RV64 (as visible through the
+ * \RcsrMcontext register). If the H extension is implemented,
+ * it's recommended to implement no more than 7 bits on RV32
+ * and 14 on RV64.
+ */
+#define CSR_MCONTEXT_HCONTEXT_OFFSET 0ULL
+#define CSR_MCONTEXT_HCONTEXT_LENGTH(XLEN) (XLEN)
+#define CSR_MCONTEXT_HCONTEXT(XLEN) ((1ULL << (XLEN)) + -1ULL)
#define CSR_MSCONTEXT 0x7aa
#define CSR_MCONTROL 0x7a1
-#define CSR_MCONTROL_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_MCONTROL_TYPE_LENGTH 4
-#define CSR_MCONTROL_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_MCONTROL_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_MCONTROL_DMODE_LENGTH 1
-#define CSR_MCONTROL_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_MCONTROL_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_MCONTROL_TYPE_LENGTH 4ULL
+#define CSR_MCONTROL_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_MCONTROL_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_MCONTROL_DMODE_LENGTH 1ULL
+#define CSR_MCONTROL_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* Specifies the largest naturally aligned powers-of-two (NAPOT) range
* supported by the hardware when \FcsrMcontrolMatch is 1. The value is the
@@ -605,18 +693,18 @@
* A value of 63 corresponds to the maximum NAPOT range, which is
* $2^{63}$ bytes in size.
*/
-#define CSR_MCONTROL_MASKMAX_OFFSET(XLEN) (XLEN + -0xb)
-#define CSR_MCONTROL_MASKMAX_LENGTH 6
-#define CSR_MCONTROL_MASKMAX(XLEN) (0x3f * (1ULL<<(XLEN + -0xb)))
+#define CSR_MCONTROL_MASKMAX_OFFSET(XLEN) ((XLEN) + -0xbULL)
+#define CSR_MCONTROL_MASKMAX_LENGTH 6ULL
+#define CSR_MCONTROL_MASKMAX(XLEN) (0x3fULL * (1ULL << ((XLEN) + -0xbULL)))
/*
* This field only exists when XLEN is at least 64.
* It contains the 2 high bits of the access size. The low bits
* come from \FcsrMcontrolSizelo. See \FcsrMcontrolSizelo for how this
* is used.
*/
-#define CSR_MCONTROL_SIZEHI_OFFSET 0x15
-#define CSR_MCONTROL_SIZEHI_LENGTH 2
-#define CSR_MCONTROL_SIZEHI 0x600000
+#define CSR_MCONTROL_SIZEHI_OFFSET 0x15ULL
+#define CSR_MCONTROL_SIZEHI_LENGTH 2ULL
+#define CSR_MCONTROL_SIZEHI 0x600000ULL
/*
* If this bit is implemented then it must become set when this
* trigger fires and may become set when this trigger matches.
@@ -625,15 +713,15 @@
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_MCONTROL_HIT_OFFSET 0x14
-#define CSR_MCONTROL_HIT_LENGTH 1
-#define CSR_MCONTROL_HIT 0x100000
+#define CSR_MCONTROL_HIT_OFFSET 0x14ULL
+#define CSR_MCONTROL_HIT_LENGTH 1ULL
+#define CSR_MCONTROL_HIT 0x100000ULL
/*
* This bit determines the contents of the XLEN-bit compare values.
*/
-#define CSR_MCONTROL_SELECT_OFFSET 0x13
-#define CSR_MCONTROL_SELECT_LENGTH 1
-#define CSR_MCONTROL_SELECT 0x80000
+#define CSR_MCONTROL_SELECT_OFFSET 0x13ULL
+#define CSR_MCONTROL_SELECT_LENGTH 1ULL
+#define CSR_MCONTROL_SELECT 0x80000ULL
/*
* address: There is at least one compare value and it contains the lowest
* virtual address of the access.
@@ -649,13 +737,13 @@
* Any bits beyond the size of the data access will contain 0.
*/
#define CSR_MCONTROL_SELECT_DATA 1
-#define CSR_MCONTROL_TIMING_OFFSET 0x12
-#define CSR_MCONTROL_TIMING_LENGTH 1
-#define CSR_MCONTROL_TIMING 0x40000
+#define CSR_MCONTROL_TIMING_OFFSET 0x12ULL
+#define CSR_MCONTROL_TIMING_LENGTH 1ULL
+#define CSR_MCONTROL_TIMING 0x40000ULL
/*
* before: The action for this trigger will be taken just before the
- * instruction that triggered it is committed, but after all preceding
- * instructions are committed. \Rxepc or \RcsrDpc (depending
+ * instruction that triggered it is retired, but after all preceding
+ * instructions are retired. \Rxepc or \RcsrDpc (depending
* on \FcsrMcontrolAction) must be set to the virtual address of the
* instruction that matched.
*
@@ -665,12 +753,16 @@
* though the load will not update its destination register. Debuggers
* should consider this when setting such breakpoints on, for example,
* memory-mapped I/O addresses.
+ *
+ * If an instruction matches this trigger and the instruction performs
+ * multiple memory accesses, it is \unspecified which memory accesses
+ * have completed before the trigger fires.
*/
#define CSR_MCONTROL_TIMING_BEFORE 0
/*
* after: The action for this trigger will be taken after the instruction
- * that triggered it is committed. It should be taken before the next
- * instruction is committed, but it is better to implement triggers imprecisely
+ * that triggered it is retired. It should be taken before the next
+ * instruction is retired, but it is better to implement triggers imprecisely
* than to not implement them at all. \Rxepc or
* \RcsrDpc (depending on \FcsrMcontrolAction) must be set to
* the virtual address of the next instruction that must be executed to
@@ -698,9 +790,9 @@
* This field contains the 2 low bits of the access size. The high bits come
* from \FcsrMcontrolSizehi. The combined value is interpreted as follows:
*/
-#define CSR_MCONTROL_SIZELO_OFFSET 0x10
-#define CSR_MCONTROL_SIZELO_LENGTH 2
-#define CSR_MCONTROL_SIZELO 0x30000
+#define CSR_MCONTROL_SIZELO_OFFSET 0x10ULL
+#define CSR_MCONTROL_SIZELO_LENGTH 2ULL
+#define CSR_MCONTROL_SIZELO 0x30000ULL
/*
* any: The trigger will attempt to match against an access of any size.
* The behavior is only well-defined if $|select|=0$, or if the access
@@ -769,9 +861,9 @@
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_MCONTROL_ACTION_OFFSET 0xc
-#define CSR_MCONTROL_ACTION_LENGTH 4
-#define CSR_MCONTROL_ACTION 0xf000
+#define CSR_MCONTROL_ACTION_OFFSET 0xcULL
+#define CSR_MCONTROL_ACTION_LENGTH 4ULL
+#define CSR_MCONTROL_ACTION 0xf000ULL
/*
* breakpoint:
*/
@@ -800,9 +892,9 @@
* external1:
*/
#define CSR_MCONTROL_ACTION_EXTERNAL1 9
-#define CSR_MCONTROL_CHAIN_OFFSET 0xb
-#define CSR_MCONTROL_CHAIN_LENGTH 1
-#define CSR_MCONTROL_CHAIN 0x800
+#define CSR_MCONTROL_CHAIN_OFFSET 0xbULL
+#define CSR_MCONTROL_CHAIN_LENGTH 1ULL
+#define CSR_MCONTROL_CHAIN 0x800ULL
/*
* disabled: When this trigger matches, the configured action is taken.
*/
@@ -836,9 +928,9 @@
* chain (eg. to meet timing requirements) may do so by zeroing
* \FcsrMcontrolChain in writes to \RcsrMcontrol that would make the chain too long.
*/
-#define CSR_MCONTROL_MATCH_OFFSET 7
-#define CSR_MCONTROL_MATCH_LENGTH 4
-#define CSR_MCONTROL_MATCH 0x780
+#define CSR_MCONTROL_MATCH_OFFSET 7ULL
+#define CSR_MCONTROL_MATCH_LENGTH 4ULL
+#define CSR_MCONTROL_MATCH 0x780ULL
/*
* equal: Matches when any compare value equals \RcsrTdataTwo.
*/
@@ -905,86 +997,142 @@
/*
* When set, enable this trigger in M-mode.
*/
-#define CSR_MCONTROL_M_OFFSET 6
-#define CSR_MCONTROL_M_LENGTH 1
-#define CSR_MCONTROL_M 0x40
+#define CSR_MCONTROL_M_OFFSET 6ULL
+#define CSR_MCONTROL_M_LENGTH 1ULL
+#define CSR_MCONTROL_M 0x40ULL
/*
* When set, enable this trigger in S/HS-mode.
* This bit is hard-wired to 0 if the hart does not support
* S-mode.
*/
-#define CSR_MCONTROL_S_OFFSET 4
-#define CSR_MCONTROL_S_LENGTH 1
-#define CSR_MCONTROL_S 0x10
+#define CSR_MCONTROL_S_OFFSET 4ULL
+#define CSR_MCONTROL_S_LENGTH 1ULL
+#define CSR_MCONTROL_S 0x10ULL
/*
* When set, enable this trigger in U-mode.
* This bit is hard-wired to 0 if the hart does not support
* U-mode.
*/
-#define CSR_MCONTROL_U_OFFSET 3
-#define CSR_MCONTROL_U_LENGTH 1
-#define CSR_MCONTROL_U 8
+#define CSR_MCONTROL_U_OFFSET 3ULL
+#define CSR_MCONTROL_U_LENGTH 1ULL
+#define CSR_MCONTROL_U 8ULL
/*
* 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 4
+#define CSR_MCONTROL_EXECUTE_OFFSET 2ULL
+#define CSR_MCONTROL_EXECUTE_LENGTH 1ULL
+#define CSR_MCONTROL_EXECUTE 4ULL
/*
* When set, the trigger fires on the virtual address or data of any
* store.
*/
-#define CSR_MCONTROL_STORE_OFFSET 1
-#define CSR_MCONTROL_STORE_LENGTH 1
-#define CSR_MCONTROL_STORE 2
+#define CSR_MCONTROL_STORE_OFFSET 1ULL
+#define CSR_MCONTROL_STORE_LENGTH 1ULL
+#define CSR_MCONTROL_STORE 2ULL
/*
* When set, the trigger fires on the virtual address or data of any
* load.
*/
-#define CSR_MCONTROL_LOAD_OFFSET 0
-#define CSR_MCONTROL_LOAD_LENGTH 1
-#define CSR_MCONTROL_LOAD 1
+#define CSR_MCONTROL_LOAD_OFFSET 0ULL
+#define CSR_MCONTROL_LOAD_LENGTH 1ULL
+#define CSR_MCONTROL_LOAD 1ULL
#define CSR_MCONTROL6 0x7a1
-#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_MCONTROL6_TYPE_LENGTH 4
-#define CSR_MCONTROL6_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_MCONTROL6_DMODE_LENGTH 1
-#define CSR_MCONTROL6_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_MCONTROL6_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_MCONTROL6_TYPE_LENGTH 4ULL
+#define CSR_MCONTROL6_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_MCONTROL6_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_MCONTROL6_DMODE_LENGTH 1ULL
+#define CSR_MCONTROL6_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
+/*
+ * If implemented, the TM updates this field every time the trigger
+ * fires.
+ */
+#define CSR_MCONTROL6_UNCERTAIN_OFFSET 0x1aULL
+#define CSR_MCONTROL6_UNCERTAIN_LENGTH 1ULL
+#define CSR_MCONTROL6_UNCERTAIN 0x4000000ULL
+/*
+ * certain: The trigger that fired satisfied the configured conditions, or
+ * this bit is not implemented.
+ */
+#define CSR_MCONTROL6_UNCERTAIN_CERTAIN 0
+/*
+ * uncertain: The trigger that fired might not have perfectly satisfied the
+ * configured conditions. Due to the implementation the hardware
+ * cannot be certain.
+ */
+#define CSR_MCONTROL6_UNCERTAIN_UNCERTAIN 1
+#define CSR_MCONTROL6_HIT1_OFFSET 0x19ULL
+#define CSR_MCONTROL6_HIT1_LENGTH 1ULL
+#define CSR_MCONTROL6_HIT1 0x2000000ULL
/*
* When set, enable this trigger in VS-mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_MCONTROL6_VS_OFFSET 0x18
-#define CSR_MCONTROL6_VS_LENGTH 1
-#define CSR_MCONTROL6_VS 0x1000000
+#define CSR_MCONTROL6_VS_OFFSET 0x18ULL
+#define CSR_MCONTROL6_VS_LENGTH 1ULL
+#define CSR_MCONTROL6_VS 0x1000000ULL
/*
* When set, enable this trigger in VU-mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_MCONTROL6_VU_OFFSET 0x17
-#define CSR_MCONTROL6_VU_LENGTH 1
-#define CSR_MCONTROL6_VU 0x800000
+#define CSR_MCONTROL6_VU_OFFSET 0x17ULL
+#define CSR_MCONTROL6_VU_LENGTH 1ULL
+#define CSR_MCONTROL6_VU 0x800000ULL
/*
- * If this bit is implemented then it must become set when this
- * trigger fires and may become set when this trigger matches.
- * The trigger's user can set or clear it at any
- * time. It is used to determine which
- * trigger(s) matched. If the bit is not implemented, it is always 0
- * and writing it has no effect.
+ * If they are implemented, \FcsrMcontrolSixHitOne (MSB) and
+ * \FcsrMcontrolSixHitZero (LSB) combine into a single 2-bit field.
+ * The TM updates this field when the trigger fires. After the debugger
+ * has seen the update, it will normally write 0 to this field to so it
+ * can see future changes.
+ *
+ * If either of the bits is not implemented, the unimplemented bits
+ * will be read-only 0.
+ */
+#define CSR_MCONTROL6_HIT0_OFFSET 0x16ULL
+#define CSR_MCONTROL6_HIT0_LENGTH 1ULL
+#define CSR_MCONTROL6_HIT0 0x400000ULL
+/*
+ * false: The trigger did not fire.
+ */
+#define CSR_MCONTROL6_HIT0_FALSE 0
+/*
+ * before: The trigger fired before the instruction that matched it was
+ * retired, but after all preceding instructions are retired. This
+ * explicitly allows for instructions to be partially executed, as
+ * described in Section \ref{sec:multistate}.
+ *
+ * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set
+ * to the virtual address of the instruction that matched.
+ */
+#define CSR_MCONTROL6_HIT0_BEFORE 1
+/*
+ * after: The trigger fired after the instruction that triggered and at least
+ * one additional instruction were retired.
+ * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set
+ * to the virtual address of the next instruction that must be executed
+ * to preserve the program flow.
+ */
+#define CSR_MCONTROL6_HIT0_AFTER 2
+/*
+ * immediately after: The trigger fired just after the instruction that triggered it was
+ * retired, but before any subsequent instructions were executed.
+ * \Rxepc or \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set
+ * to the virtual address of the next instruction that must be executed
+ * to preserve the program flow.
+ *
+ * If the instruction performed multiple memory accesses, all of them
+ * have been completed.
*/
-#define CSR_MCONTROL6_HIT_OFFSET 0x16
-#define CSR_MCONTROL6_HIT_LENGTH 1
-#define CSR_MCONTROL6_HIT 0x400000
+#define CSR_MCONTROL6_HIT0_IMMEDIATELY_AFTER 3
/*
* This bit determines the contents of the XLEN-bit compare values.
*/
-#define CSR_MCONTROL6_SELECT_OFFSET 0x15
-#define CSR_MCONTROL6_SELECT_LENGTH 1
-#define CSR_MCONTROL6_SELECT 0x200000
+#define CSR_MCONTROL6_SELECT_OFFSET 0x15ULL
+#define CSR_MCONTROL6_SELECT_LENGTH 1ULL
+#define CSR_MCONTROL6_SELECT 0x200000ULL
/*
* address: There is at least one compare value and it contains the lowest
* virtual address of the access.
@@ -1000,54 +1148,9 @@
* Any bits beyond the size of the data access will contain 0.
*/
#define CSR_MCONTROL6_SELECT_DATA 1
-#define CSR_MCONTROL6_TIMING_OFFSET 0x14
-#define CSR_MCONTROL6_TIMING_LENGTH 1
-#define CSR_MCONTROL6_TIMING 0x100000
-/*
- * before: The action for this trigger will be taken just before the
- * instruction that triggered it is committed, but after all preceding
- * instructions are committed. \Rxepc or \RcsrDpc (depending
- * on \FcsrMcontrolSixAction) must be set to the virtual address of the
- * instruction that matched.
- *
- * If this is combined with \FcsrMcontrolSixLoad and
- * \FcsrMcontrolSixSelect=1 then a memory access will be
- * performed (including any side effects of performing such an access) even
- * though the load will not update its destination register. Debuggers
- * should consider this when setting such breakpoints on, for example,
- * memory-mapped I/O addresses.
- */
-#define CSR_MCONTROL6_TIMING_BEFORE 0
-/*
- * after: The action for this trigger will be taken after the instruction
- * that triggered it is committed. It should be taken before the next
- * instruction is committed, but it is better to implement triggers imprecisely
- * than to not implement them at all. \Rxepc or
- * \RcsrDpc (depending on \FcsrMcontrolSixAction) must be set to
- * the virtual address of the next instruction that must be executed to
- * preserve the program flow.
- */
-#define CSR_MCONTROL6_TIMING_AFTER 1
-/*
- * Most hardware will only implement one timing or the other, possibly
- * dependent on \FcsrMcontrolSixSelect, \FcsrMcontrolSixExecute,
- * \FcsrMcontrolSixLoad, and \FcsrMcontrolSixStore. This bit
- * primarily exists for the hardware to communicate to the debugger
- * what will happen. Hardware may implement the bit fully writable, in
- * which case the debugger has a little more control.
- *
- * Data load triggers with \FcsrMcontrolSixTiming of 0 will result in the same load
- * happening again when the debugger lets the hart run. For data load
- * triggers, debuggers must first attempt to set the breakpoint with
- * \FcsrMcontrolSixTiming of 1.
- *
- * If a trigger with \FcsrMcontrolSixTiming of 0 matches, it is
- * implementation-dependent whether that prevents a trigger with
- * \FcsrMcontrolSixTiming of 1 matching as well.
- */
-#define CSR_MCONTROL6_SIZE_OFFSET 0x10
-#define CSR_MCONTROL6_SIZE_LENGTH 4
-#define CSR_MCONTROL6_SIZE 0xf0000
+#define CSR_MCONTROL6_SIZE_OFFSET 0x10ULL
+#define CSR_MCONTROL6_SIZE_LENGTH 3ULL
+#define CSR_MCONTROL6_SIZE 0x70000ULL
/*
* any: The trigger will attempt to match against an access of any size.
* The behavior is only well-defined if $|select|=0$, or if the access
@@ -1078,22 +1181,10 @@
*/
#define CSR_MCONTROL6_SIZE_64BIT 5
/*
- * 80bit: The trigger will only match against execution of 80-bit instructions.
- */
-#define CSR_MCONTROL6_SIZE_80BIT 6
-/*
- * 96bit: The trigger will only match against execution of 96-bit instructions.
- */
-#define CSR_MCONTROL6_SIZE_96BIT 7
-/*
- * 112bit: The trigger will only match against execution of 112-bit instructions.
- */
-#define CSR_MCONTROL6_SIZE_112BIT 8
-/*
* 128bit: The trigger will only match against 128-bit memory accesses or
* execution of 128-bit instructions.
*/
-#define CSR_MCONTROL6_SIZE_128BIT 9
+#define CSR_MCONTROL6_SIZE_128BIT 6
/*
* An implementation must support the value of 0, but all other values
* are optional. When an implementation supports address triggers
@@ -1116,9 +1207,9 @@
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_MCONTROL6_ACTION_OFFSET 0xc
-#define CSR_MCONTROL6_ACTION_LENGTH 4
-#define CSR_MCONTROL6_ACTION 0xf000
+#define CSR_MCONTROL6_ACTION_OFFSET 0xcULL
+#define CSR_MCONTROL6_ACTION_LENGTH 4ULL
+#define CSR_MCONTROL6_ACTION 0xf000ULL
/*
* breakpoint:
*/
@@ -1147,9 +1238,9 @@
* external1:
*/
#define CSR_MCONTROL6_ACTION_EXTERNAL1 9
-#define CSR_MCONTROL6_CHAIN_OFFSET 0xb
-#define CSR_MCONTROL6_CHAIN_LENGTH 1
-#define CSR_MCONTROL6_CHAIN 0x800
+#define CSR_MCONTROL6_CHAIN_OFFSET 0xbULL
+#define CSR_MCONTROL6_CHAIN_LENGTH 1ULL
+#define CSR_MCONTROL6_CHAIN 0x800ULL
/*
* disabled: When this trigger matches, the configured action is taken.
*/
@@ -1183,9 +1274,9 @@
* chain (eg. to meet timing requirements) may do so by zeroing
* \FcsrMcontrolSixChain in writes to \RcsrMcontrolSix that would make the chain too long.
*/
-#define CSR_MCONTROL6_MATCH_OFFSET 7
-#define CSR_MCONTROL6_MATCH_LENGTH 4
-#define CSR_MCONTROL6_MATCH 0x780
+#define CSR_MCONTROL6_MATCH_OFFSET 7ULL
+#define CSR_MCONTROL6_MATCH_LENGTH 4ULL
+#define CSR_MCONTROL6_MATCH 0x780ULL
/*
* equal: Matches when any compare value equals \RcsrTdataTwo.
*/
@@ -1254,69 +1345,83 @@
/*
* When set, enable this trigger in M-mode.
*/
-#define CSR_MCONTROL6_M_OFFSET 6
-#define CSR_MCONTROL6_M_LENGTH 1
-#define CSR_MCONTROL6_M 0x40
+#define CSR_MCONTROL6_M_OFFSET 6ULL
+#define CSR_MCONTROL6_M_LENGTH 1ULL
+#define CSR_MCONTROL6_M 0x40ULL
+#define CSR_MCONTROL6_UNCERTAINEN_OFFSET 5ULL
+#define CSR_MCONTROL6_UNCERTAINEN_LENGTH 1ULL
+#define CSR_MCONTROL6_UNCERTAINEN 0x20ULL
+/*
+ * disabled: This trigger will only match if the hardware can perfectly
+ * evaluate it.
+ */
+#define CSR_MCONTROL6_UNCERTAINEN_DISABLED 0
+/*
+ * enabled: This trigger will match if it's possible that it would match if
+ * the Trigger Module had perfect information about the operations
+ * being performed.
+ */
+#define CSR_MCONTROL6_UNCERTAINEN_ENABLED 1
/*
* When set, enable this trigger in S/HS-mode.
* This bit is hard-wired to 0 if the hart does not support
* S-mode.
*/
-#define CSR_MCONTROL6_S_OFFSET 4
-#define CSR_MCONTROL6_S_LENGTH 1
-#define CSR_MCONTROL6_S 0x10
+#define CSR_MCONTROL6_S_OFFSET 4ULL
+#define CSR_MCONTROL6_S_LENGTH 1ULL
+#define CSR_MCONTROL6_S 0x10ULL
/*
* When set, enable this trigger in U-mode.
* This bit is hard-wired to 0 if the hart does not support
* U-mode.
*/
-#define CSR_MCONTROL6_U_OFFSET 3
-#define CSR_MCONTROL6_U_LENGTH 1
-#define CSR_MCONTROL6_U 8
+#define CSR_MCONTROL6_U_OFFSET 3ULL
+#define CSR_MCONTROL6_U_LENGTH 1ULL
+#define CSR_MCONTROL6_U 8ULL
/*
* When set, the trigger fires on the virtual address or opcode of an
* instruction that is executed.
*/
-#define CSR_MCONTROL6_EXECUTE_OFFSET 2
-#define CSR_MCONTROL6_EXECUTE_LENGTH 1
-#define CSR_MCONTROL6_EXECUTE 4
+#define CSR_MCONTROL6_EXECUTE_OFFSET 2ULL
+#define CSR_MCONTROL6_EXECUTE_LENGTH 1ULL
+#define CSR_MCONTROL6_EXECUTE 4ULL
/*
* When set, the trigger fires on the virtual address or data of any
* store.
*/
-#define CSR_MCONTROL6_STORE_OFFSET 1
-#define CSR_MCONTROL6_STORE_LENGTH 1
-#define CSR_MCONTROL6_STORE 2
+#define CSR_MCONTROL6_STORE_OFFSET 1ULL
+#define CSR_MCONTROL6_STORE_LENGTH 1ULL
+#define CSR_MCONTROL6_STORE 2ULL
/*
* When set, the trigger fires on the virtual address or data of any
* load.
*/
-#define CSR_MCONTROL6_LOAD_OFFSET 0
-#define CSR_MCONTROL6_LOAD_LENGTH 1
-#define CSR_MCONTROL6_LOAD 1
+#define CSR_MCONTROL6_LOAD_OFFSET 0ULL
+#define CSR_MCONTROL6_LOAD_LENGTH 1ULL
+#define CSR_MCONTROL6_LOAD 1ULL
#define CSR_ICOUNT 0x7a1
-#define CSR_ICOUNT_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_ICOUNT_TYPE_LENGTH 4
-#define CSR_ICOUNT_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_ICOUNT_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_ICOUNT_DMODE_LENGTH 1
-#define CSR_ICOUNT_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_ICOUNT_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_ICOUNT_TYPE_LENGTH 4ULL
+#define CSR_ICOUNT_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_ICOUNT_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_ICOUNT_DMODE_LENGTH 1ULL
+#define CSR_ICOUNT_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* When set, enable this trigger in VS-mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ICOUNT_VS_OFFSET 0x1a
-#define CSR_ICOUNT_VS_LENGTH 1
-#define CSR_ICOUNT_VS 0x4000000
+#define CSR_ICOUNT_VS_OFFSET 0x1aULL
+#define CSR_ICOUNT_VS_LENGTH 1ULL
+#define CSR_ICOUNT_VS 0x4000000ULL
/*
* When set, enable this trigger in VU-mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ICOUNT_VU_OFFSET 0x19
-#define CSR_ICOUNT_VU_LENGTH 1
-#define CSR_ICOUNT_VU 0x2000000
+#define CSR_ICOUNT_VU_OFFSET 0x19ULL
+#define CSR_ICOUNT_VU_LENGTH 1ULL
+#define CSR_ICOUNT_VU 0x2000000ULL
/*
* If this bit is implemented, the hardware sets it when this
* trigger fires. The trigger's user can set or clear it at any
@@ -1324,53 +1429,53 @@
* trigger(s) fires. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_ICOUNT_HIT_OFFSET 0x18
-#define CSR_ICOUNT_HIT_LENGTH 1
-#define CSR_ICOUNT_HIT 0x1000000
+#define CSR_ICOUNT_HIT_OFFSET 0x18ULL
+#define CSR_ICOUNT_HIT_LENGTH 1ULL
+#define CSR_ICOUNT_HIT 0x1000000ULL
/*
* The trigger will generally fire after \FcsrIcountCount instructions
* in enabled modes have been executed. See above for the precise behavior.
*/
-#define CSR_ICOUNT_COUNT_OFFSET 0xa
-#define CSR_ICOUNT_COUNT_LENGTH 0xe
-#define CSR_ICOUNT_COUNT 0xfffc00
+#define CSR_ICOUNT_COUNT_OFFSET 0xaULL
+#define CSR_ICOUNT_COUNT_LENGTH 0xeULL
+#define CSR_ICOUNT_COUNT 0xfffc00ULL
/*
* When set, enable this trigger in M-mode.
*/
-#define CSR_ICOUNT_M_OFFSET 9
-#define CSR_ICOUNT_M_LENGTH 1
-#define CSR_ICOUNT_M 0x200
+#define CSR_ICOUNT_M_OFFSET 9ULL
+#define CSR_ICOUNT_M_LENGTH 1ULL
+#define CSR_ICOUNT_M 0x200ULL
/*
* This bit becomes set when \FcsrIcountCount is decremented from 1
* to 0. It is cleared when the trigger fires, which will happen just
* before executing the next instruction in one of the enabled modes.
*/
-#define CSR_ICOUNT_PENDING_OFFSET 8
-#define CSR_ICOUNT_PENDING_LENGTH 1
-#define CSR_ICOUNT_PENDING 0x100
+#define CSR_ICOUNT_PENDING_OFFSET 8ULL
+#define CSR_ICOUNT_PENDING_LENGTH 1ULL
+#define CSR_ICOUNT_PENDING 0x100ULL
/*
* When set, enable this trigger in S/HS-mode.
* This bit is hard-wired to 0 if the hart does not support
* S-mode.
*/
-#define CSR_ICOUNT_S_OFFSET 7
-#define CSR_ICOUNT_S_LENGTH 1
-#define CSR_ICOUNT_S 0x80
+#define CSR_ICOUNT_S_OFFSET 7ULL
+#define CSR_ICOUNT_S_LENGTH 1ULL
+#define CSR_ICOUNT_S 0x80ULL
/*
* When set, enable this trigger in U-mode.
* This bit is hard-wired to 0 if the hart does not support
* U-mode.
*/
-#define CSR_ICOUNT_U_OFFSET 6
-#define CSR_ICOUNT_U_LENGTH 1
-#define CSR_ICOUNT_U 0x40
+#define CSR_ICOUNT_U_OFFSET 6ULL
+#define CSR_ICOUNT_U_LENGTH 1ULL
+#define CSR_ICOUNT_U 0x40ULL
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_ICOUNT_ACTION_OFFSET 0
-#define CSR_ICOUNT_ACTION_LENGTH 6
-#define CSR_ICOUNT_ACTION 0x3f
+#define CSR_ICOUNT_ACTION_OFFSET 0ULL
+#define CSR_ICOUNT_ACTION_LENGTH 6ULL
+#define CSR_ICOUNT_ACTION 0x3fULL
/*
* breakpoint:
*/
@@ -1400,12 +1505,12 @@
*/
#define CSR_ICOUNT_ACTION_EXTERNAL1 9
#define CSR_ITRIGGER 0x7a1
-#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_ITRIGGER_TYPE_LENGTH 4
-#define CSR_ITRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_ITRIGGER_DMODE_LENGTH 1
-#define CSR_ITRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_ITRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_ITRIGGER_TYPE_LENGTH 4ULL
+#define CSR_ITRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_ITRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_ITRIGGER_DMODE_LENGTH 1ULL
+#define CSR_ITRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* If this bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
@@ -1413,66 +1518,66 @@
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_ITRIGGER_HIT_OFFSET(XLEN) (XLEN + -6)
-#define CSR_ITRIGGER_HIT_LENGTH 1
-#define CSR_ITRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6))
+#define CSR_ITRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL)
+#define CSR_ITRIGGER_HIT_LENGTH 1ULL
+#define CSR_ITRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL))
/*
* When set, enable this trigger for interrupts that are taken from VS
* mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ITRIGGER_VS_OFFSET 0xc
-#define CSR_ITRIGGER_VS_LENGTH 1
-#define CSR_ITRIGGER_VS 0x1000
+#define CSR_ITRIGGER_VS_OFFSET 0xcULL
+#define CSR_ITRIGGER_VS_LENGTH 1ULL
+#define CSR_ITRIGGER_VS 0x1000ULL
/*
* When set, enable this trigger for interrupts that are taken from VU
* mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ITRIGGER_VU_OFFSET 0xb
-#define CSR_ITRIGGER_VU_LENGTH 1
-#define CSR_ITRIGGER_VU 0x800
+#define CSR_ITRIGGER_VU_OFFSET 0xbULL
+#define CSR_ITRIGGER_VU_LENGTH 1ULL
+#define CSR_ITRIGGER_VU 0x800ULL
/*
* When set, non-maskable interrupts cause this
* trigger to fire if the trigger is enabled for the current mode.
*/
-#define CSR_ITRIGGER_NMI_OFFSET 0xa
-#define CSR_ITRIGGER_NMI_LENGTH 1
-#define CSR_ITRIGGER_NMI 0x400
+#define CSR_ITRIGGER_NMI_OFFSET 0xaULL
+#define CSR_ITRIGGER_NMI_LENGTH 1ULL
+#define CSR_ITRIGGER_NMI 0x400ULL
/*
* When set, enable this trigger for interrupts that are taken from M
* mode.
*/
-#define CSR_ITRIGGER_M_OFFSET 9
-#define CSR_ITRIGGER_M_LENGTH 1
-#define CSR_ITRIGGER_M 0x200
+#define CSR_ITRIGGER_M_OFFSET 9ULL
+#define CSR_ITRIGGER_M_LENGTH 1ULL
+#define CSR_ITRIGGER_M 0x200ULL
/*
* When set, enable this trigger for interrupts that are taken from S/HS
* mode.
* This bit is hard-wired to 0 if the hart does not support
* S-mode.
*/
-#define CSR_ITRIGGER_S_OFFSET 7
-#define CSR_ITRIGGER_S_LENGTH 1
-#define CSR_ITRIGGER_S 0x80
+#define CSR_ITRIGGER_S_OFFSET 7ULL
+#define CSR_ITRIGGER_S_LENGTH 1ULL
+#define CSR_ITRIGGER_S 0x80ULL
/*
* When set, enable this trigger for interrupts that are taken from U
* mode.
* This bit is hard-wired to 0 if the hart does not support
* U-mode.
*/
-#define CSR_ITRIGGER_U_OFFSET 6
-#define CSR_ITRIGGER_U_LENGTH 1
-#define CSR_ITRIGGER_U 0x40
+#define CSR_ITRIGGER_U_OFFSET 6ULL
+#define CSR_ITRIGGER_U_LENGTH 1ULL
+#define CSR_ITRIGGER_U 0x40ULL
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_ITRIGGER_ACTION_OFFSET 0
-#define CSR_ITRIGGER_ACTION_LENGTH 6
-#define CSR_ITRIGGER_ACTION 0x3f
+#define CSR_ITRIGGER_ACTION_OFFSET 0ULL
+#define CSR_ITRIGGER_ACTION_LENGTH 6ULL
+#define CSR_ITRIGGER_ACTION 0x3fULL
/*
* breakpoint:
*/
@@ -1502,12 +1607,12 @@
*/
#define CSR_ITRIGGER_ACTION_EXTERNAL1 9
#define CSR_ETRIGGER 0x7a1
-#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_ETRIGGER_TYPE_LENGTH 4
-#define CSR_ETRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_ETRIGGER_DMODE_LENGTH 1
-#define CSR_ETRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_ETRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_ETRIGGER_TYPE_LENGTH 4ULL
+#define CSR_ETRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_ETRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_ETRIGGER_DMODE_LENGTH 1ULL
+#define CSR_ETRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* If this bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
@@ -1515,59 +1620,59 @@
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_ETRIGGER_HIT_OFFSET(XLEN) (XLEN + -6)
-#define CSR_ETRIGGER_HIT_LENGTH 1
-#define CSR_ETRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6))
+#define CSR_ETRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL)
+#define CSR_ETRIGGER_HIT_LENGTH 1ULL
+#define CSR_ETRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL))
/*
* When set, enable this trigger for exceptions that are taken from VS
* mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ETRIGGER_VS_OFFSET 0xc
-#define CSR_ETRIGGER_VS_LENGTH 1
-#define CSR_ETRIGGER_VS 0x1000
+#define CSR_ETRIGGER_VS_OFFSET 0xcULL
+#define CSR_ETRIGGER_VS_LENGTH 1ULL
+#define CSR_ETRIGGER_VS 0x1000ULL
/*
* When set, enable this trigger for exceptions that are taken from VU
* mode.
* This bit is hard-wired to 0 if the hart does not support
* virtualization mode.
*/
-#define CSR_ETRIGGER_VU_OFFSET 0xb
-#define CSR_ETRIGGER_VU_LENGTH 1
-#define CSR_ETRIGGER_VU 0x800
+#define CSR_ETRIGGER_VU_OFFSET 0xbULL
+#define CSR_ETRIGGER_VU_LENGTH 1ULL
+#define CSR_ETRIGGER_VU 0x800ULL
/*
* When set, enable this trigger for exceptions that are taken from M
* mode.
*/
-#define CSR_ETRIGGER_M_OFFSET 9
-#define CSR_ETRIGGER_M_LENGTH 1
-#define CSR_ETRIGGER_M 0x200
+#define CSR_ETRIGGER_M_OFFSET 9ULL
+#define CSR_ETRIGGER_M_LENGTH 1ULL
+#define CSR_ETRIGGER_M 0x200ULL
/*
* When set, enable this trigger for exceptions that are taken from S/HS
* mode.
* This bit is hard-wired to 0 if the hart does not support
* S-mode.
*/
-#define CSR_ETRIGGER_S_OFFSET 7
-#define CSR_ETRIGGER_S_LENGTH 1
-#define CSR_ETRIGGER_S 0x80
+#define CSR_ETRIGGER_S_OFFSET 7ULL
+#define CSR_ETRIGGER_S_LENGTH 1ULL
+#define CSR_ETRIGGER_S 0x80ULL
/*
* When set, enable this trigger for exceptions that are taken from U
* mode.
* This bit is hard-wired to 0 if the hart does not support
* U-mode.
*/
-#define CSR_ETRIGGER_U_OFFSET 6
-#define CSR_ETRIGGER_U_LENGTH 1
-#define CSR_ETRIGGER_U 0x40
+#define CSR_ETRIGGER_U_OFFSET 6ULL
+#define CSR_ETRIGGER_U_LENGTH 1ULL
+#define CSR_ETRIGGER_U 0x40ULL
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_ETRIGGER_ACTION_OFFSET 0
-#define CSR_ETRIGGER_ACTION_LENGTH 6
-#define CSR_ETRIGGER_ACTION 0x3f
+#define CSR_ETRIGGER_ACTION_OFFSET 0ULL
+#define CSR_ETRIGGER_ACTION_LENGTH 6ULL
+#define CSR_ETRIGGER_ACTION 0x3fULL
/*
* breakpoint:
*/
@@ -1597,12 +1702,12 @@
*/
#define CSR_ETRIGGER_ACTION_EXTERNAL1 9
#define CSR_TMEXTTRIGGER 0x7a1
-#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) (XLEN + -4)
-#define CSR_TMEXTTRIGGER_TYPE_LENGTH 4
-#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xf * (1ULL<<(XLEN + -4)))
-#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) (XLEN + -5)
-#define CSR_TMEXTTRIGGER_DMODE_LENGTH 1
-#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL<<(XLEN + -5))
+#define CSR_TMEXTTRIGGER_TYPE_OFFSET(XLEN) ((XLEN) + -4ULL)
+#define CSR_TMEXTTRIGGER_TYPE_LENGTH 4ULL
+#define CSR_TMEXTTRIGGER_TYPE(XLEN) (0xfULL * (1ULL << ((XLEN) + -4ULL)))
+#define CSR_TMEXTTRIGGER_DMODE_OFFSET(XLEN) ((XLEN) + -5ULL)
+#define CSR_TMEXTTRIGGER_DMODE_LENGTH 1ULL
+#define CSR_TMEXTTRIGGER_DMODE(XLEN) (1ULL << ((XLEN) + -5ULL))
/*
* If this bit is implemented, the hardware sets it when this
* trigger matches. The trigger's user can set or clear it at any
@@ -1610,30 +1715,30 @@
* trigger(s) matched. If the bit is not implemented, it is always 0
* and writing it has no effect.
*/
-#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) (XLEN + -6)
-#define CSR_TMEXTTRIGGER_HIT_LENGTH 1
-#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL<<(XLEN + -6))
+#define CSR_TMEXTTRIGGER_HIT_OFFSET(XLEN) ((XLEN) + -6ULL)
+#define CSR_TMEXTTRIGGER_HIT_LENGTH 1ULL
+#define CSR_TMEXTTRIGGER_HIT(XLEN) (1ULL << ((XLEN) + -6ULL))
/*
* This optional bit, when set, causes this trigger to fire whenever an attached
* interrupt controller signals a trigger.
*/
-#define CSR_TMEXTTRIGGER_INTCTL_OFFSET 0x16
-#define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1
-#define CSR_TMEXTTRIGGER_INTCTL 0x400000
+#define CSR_TMEXTTRIGGER_INTCTL_OFFSET 0x16ULL
+#define CSR_TMEXTTRIGGER_INTCTL_LENGTH 1ULL
+#define CSR_TMEXTTRIGGER_INTCTL 0x400000ULL
/*
- * Selects any combination of up to 16 external debug trigger inputs
+ * Selects any combination of up to 16 TM external trigger inputs
* that cause this trigger to fire.
*/
-#define CSR_TMEXTTRIGGER_SELECT_OFFSET 6
-#define CSR_TMEXTTRIGGER_SELECT_LENGTH 0x10
-#define CSR_TMEXTTRIGGER_SELECT 0x3fffc0
+#define CSR_TMEXTTRIGGER_SELECT_OFFSET 6ULL
+#define CSR_TMEXTTRIGGER_SELECT_LENGTH 0x10ULL
+#define CSR_TMEXTTRIGGER_SELECT 0x3fffc0ULL
/*
* The action to take when the trigger fires. The values are explained
* in Table~\ref{tab:action}.
*/
-#define CSR_TMEXTTRIGGER_ACTION_OFFSET 0
-#define CSR_TMEXTTRIGGER_ACTION_LENGTH 6
-#define CSR_TMEXTTRIGGER_ACTION 0x3f
+#define CSR_TMEXTTRIGGER_ACTION_OFFSET 0ULL
+#define CSR_TMEXTTRIGGER_ACTION_LENGTH 6ULL
+#define CSR_TMEXTTRIGGER_ACTION 0x3fULL
/*
* breakpoint:
*/
@@ -1666,26 +1771,28 @@
/*
* Data used together with \FcsrTextraThirtytwoMhselect.
*/
-#define CSR_TEXTRA32_MHVALUE_OFFSET 0x1a
-#define CSR_TEXTRA32_MHVALUE_LENGTH 6
-#define CSR_TEXTRA32_MHVALUE 0xfc000000U
-#define CSR_TEXTRA32_MHSELECT_OFFSET 0x17
-#define CSR_TEXTRA32_MHSELECT_LENGTH 3
-#define CSR_TEXTRA32_MHSELECT 0x3800000
+#define CSR_TEXTRA32_MHVALUE_OFFSET 0x1aULL
+#define CSR_TEXTRA32_MHVALUE_LENGTH 6ULL
+#define CSR_TEXTRA32_MHVALUE 0xfc000000ULL
+#define CSR_TEXTRA32_MHSELECT_OFFSET 0x17ULL
+#define CSR_TEXTRA32_MHSELECT_LENGTH 3ULL
+#define CSR_TEXTRA32_MHSELECT 0x3800000ULL
/*
* ignore: Ignore \FcsrTextraThirtytwoMhvalue.
*/
#define CSR_TEXTRA32_MHSELECT_IGNORE 0
/*
- * mcontext: This trigger will only match if the low bits of
+ * mcontext: This trigger will only match or fire if the low bits of
* \RcsrMcontext/\RcsrHcontext equal \FcsrTextraThirtytwoMhvalue.
*/
#define CSR_TEXTRA32_MHSELECT_MCONTEXT 4
/*
- * 1, 5 (mcontext\_select): This trigger will only match if the low bits of
+ * 1, 5 (mcontext\_select): This trigger will only match or fire if the
+ * low bits of
* \RcsrMcontext/\RcsrHcontext equal \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}.
*
- * 2, 6 (vmid\_select): This trigger will only match if VMID in hgatp equals the lower VMIDMAX
+ * 2, 6 (vmid\_select): This trigger will only match or fire if VMID in
+ * hgatp equals the lower VMIDMAX
* (defined in the Privileged Spec) bits of \{\FcsrTextraThirtytwoMhvalue, mhselect[2]\}.
*
* 3, 7 (reserved): Reserved.
@@ -1698,31 +1805,31 @@
* When the next most significant bit of this field is 1, it causes bits 15:8
* to be ignored in the comparison, when \FcsrTextraThirtytwoSselect=1.
*/
-#define CSR_TEXTRA32_SBYTEMASK_OFFSET 0x12
-#define CSR_TEXTRA32_SBYTEMASK_LENGTH 2
-#define CSR_TEXTRA32_SBYTEMASK 0xc0000
+#define CSR_TEXTRA32_SBYTEMASK_OFFSET 0x12ULL
+#define CSR_TEXTRA32_SBYTEMASK_LENGTH 2ULL
+#define CSR_TEXTRA32_SBYTEMASK 0xc0000ULL
/*
* Data used together with \FcsrTextraThirtytwoSselect.
*
* This field should be tied to 0 when S-mode is not supported.
*/
-#define CSR_TEXTRA32_SVALUE_OFFSET 2
-#define CSR_TEXTRA32_SVALUE_LENGTH 0x10
-#define CSR_TEXTRA32_SVALUE 0x3fffc
-#define CSR_TEXTRA32_SSELECT_OFFSET 0
-#define CSR_TEXTRA32_SSELECT_LENGTH 2
-#define CSR_TEXTRA32_SSELECT 3
+#define CSR_TEXTRA32_SVALUE_OFFSET 2ULL
+#define CSR_TEXTRA32_SVALUE_LENGTH 0x10ULL
+#define CSR_TEXTRA32_SVALUE 0x3fffcULL
+#define CSR_TEXTRA32_SSELECT_OFFSET 0ULL
+#define CSR_TEXTRA32_SSELECT_LENGTH 2ULL
+#define CSR_TEXTRA32_SSELECT 3ULL
/*
* ignore: Ignore \FcsrTextraThirtytwoSvalue.
*/
#define CSR_TEXTRA32_SSELECT_IGNORE 0
/*
- * scontext: This trigger will only match if the low bits of
+ * scontext: This trigger will only match or fire if the low bits of
* \RcsrScontext equal \FcsrTextraThirtytwoSvalue.
*/
#define CSR_TEXTRA32_SSELECT_SCONTEXT 1
/*
- * asid: This trigger will only match if:
+ * asid: This trigger will only match or fire if:
* \begin{itemize}[noitemsep,nolistsep]
* \item the mode is VS-mode or VU-mode and ASID in \Rvsatp
* equals the lower ASIDMAX (defined in the Privileged Spec) bits
@@ -1737,11 +1844,11 @@
* This field should be tied to 0 when S-mode is not supported.
*/
#define CSR_TEXTRA64 0x7a3
-#define CSR_TEXTRA64_MHVALUE_OFFSET 0x33
-#define CSR_TEXTRA64_MHVALUE_LENGTH 0xd
+#define CSR_TEXTRA64_MHVALUE_OFFSET 0x33ULL
+#define CSR_TEXTRA64_MHVALUE_LENGTH 0xdULL
#define CSR_TEXTRA64_MHVALUE 0xfff8000000000000ULL
-#define CSR_TEXTRA64_MHSELECT_OFFSET 0x30
-#define CSR_TEXTRA64_MHSELECT_LENGTH 3
+#define CSR_TEXTRA64_MHSELECT_OFFSET 0x30ULL
+#define CSR_TEXTRA64_MHSELECT_LENGTH 3ULL
#define CSR_TEXTRA64_MHSELECT 0x7000000000000ULL
/*
* When the least significant bit of this field is 1, it causes bits 7:0
@@ -1751,19 +1858,19 @@
* fourth bit controls the comparison of bits 31:24, and
* fifth bit controls the comparison of bits 33:32.
*/
-#define CSR_TEXTRA64_SBYTEMASK_OFFSET 0x24
-#define CSR_TEXTRA64_SBYTEMASK_LENGTH 5
+#define CSR_TEXTRA64_SBYTEMASK_OFFSET 0x24ULL
+#define CSR_TEXTRA64_SBYTEMASK_LENGTH 5ULL
#define CSR_TEXTRA64_SBYTEMASK 0x1f000000000ULL
-#define CSR_TEXTRA64_SVALUE_OFFSET 2
-#define CSR_TEXTRA64_SVALUE_LENGTH 0x22
+#define CSR_TEXTRA64_SVALUE_OFFSET 2ULL
+#define CSR_TEXTRA64_SVALUE_LENGTH 0x22ULL
#define CSR_TEXTRA64_SVALUE 0xffffffffcULL
-#define CSR_TEXTRA64_SSELECT_OFFSET 0
-#define CSR_TEXTRA64_SSELECT_LENGTH 2
-#define CSR_TEXTRA64_SSELECT 3
+#define CSR_TEXTRA64_SSELECT_OFFSET 0ULL
+#define CSR_TEXTRA64_SSELECT_LENGTH 2ULL
+#define CSR_TEXTRA64_SSELECT 3ULL
#define DM_DMSTATUS 0x11
-#define DM_DMSTATUS_NDMRESETPENDING_OFFSET 0x18
-#define DM_DMSTATUS_NDMRESETPENDING_LENGTH 1
-#define DM_DMSTATUS_NDMRESETPENDING 0x1000000
+#define DM_DMSTATUS_NDMRESETPENDING_OFFSET 0x18ULL
+#define DM_DMSTATUS_NDMRESETPENDING_LENGTH 1ULL
+#define DM_DMSTATUS_NDMRESETPENDING 0x1000000ULL
/*
* false: Unimplemented, or \FdmDmcontrolNdmreset is zero and no ndmreset is currently
* in progress.
@@ -1773,9 +1880,9 @@
* true: \FdmDmcontrolNdmreset is currently nonzero, or there is an ndmreset in progress.
*/
#define DM_DMSTATUS_NDMRESETPENDING_TRUE 1
-#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET 0x17
-#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH 1
-#define DM_DMSTATUS_STICKYUNAVAIL 0x800000
+#define DM_DMSTATUS_STICKYUNAVAIL_OFFSET 0x17ULL
+#define DM_DMSTATUS_STICKYUNAVAIL_LENGTH 1ULL
+#define DM_DMSTATUS_STICKYUNAVAIL 0x800000ULL
/*
* current: The per-hart {\tt unavail} bits reflect the current state of the hart.
*/
@@ -1793,94 +1900,94 @@
*
* This must be 1 when \FdmAbstractcsProgbufsize is 1.
*/
-#define DM_DMSTATUS_IMPEBREAK_OFFSET 0x16
-#define DM_DMSTATUS_IMPEBREAK_LENGTH 1
-#define DM_DMSTATUS_IMPEBREAK 0x400000
+#define DM_DMSTATUS_IMPEBREAK_OFFSET 0x16ULL
+#define DM_DMSTATUS_IMPEBREAK_LENGTH 1ULL
+#define DM_DMSTATUS_IMPEBREAK 0x400000ULL
/*
* This field is 1 when all currently selected harts have been reset
* and reset has not been acknowledged for any of them.
*/
-#define DM_DMSTATUS_ALLHAVERESET_OFFSET 0x13
-#define DM_DMSTATUS_ALLHAVERESET_LENGTH 1
-#define DM_DMSTATUS_ALLHAVERESET 0x80000
+#define DM_DMSTATUS_ALLHAVERESET_OFFSET 0x13ULL
+#define DM_DMSTATUS_ALLHAVERESET_LENGTH 1ULL
+#define DM_DMSTATUS_ALLHAVERESET 0x80000ULL
/*
* This field is 1 when at least one currently selected hart has been
* reset and reset has not been acknowledged for that hart.
*/
-#define DM_DMSTATUS_ANYHAVERESET_OFFSET 0x12
-#define DM_DMSTATUS_ANYHAVERESET_LENGTH 1
-#define DM_DMSTATUS_ANYHAVERESET 0x40000
+#define DM_DMSTATUS_ANYHAVERESET_OFFSET 0x12ULL
+#define DM_DMSTATUS_ANYHAVERESET_LENGTH 1ULL
+#define DM_DMSTATUS_ANYHAVERESET 0x40000ULL
/*
* This field is 1 when all currently selected harts have their
* resume ack bit\index{resume ack bit} set.
*/
-#define DM_DMSTATUS_ALLRESUMEACK_OFFSET 0x11
-#define DM_DMSTATUS_ALLRESUMEACK_LENGTH 1
-#define DM_DMSTATUS_ALLRESUMEACK 0x20000
+#define DM_DMSTATUS_ALLRESUMEACK_OFFSET 0x11ULL
+#define DM_DMSTATUS_ALLRESUMEACK_LENGTH 1ULL
+#define DM_DMSTATUS_ALLRESUMEACK 0x20000ULL
/*
* This field is 1 when any currently selected hart has its
* resume ack bit\index{resume ack bit} set.
*/
-#define DM_DMSTATUS_ANYRESUMEACK_OFFSET 0x10
-#define DM_DMSTATUS_ANYRESUMEACK_LENGTH 1
-#define DM_DMSTATUS_ANYRESUMEACK 0x10000
+#define DM_DMSTATUS_ANYRESUMEACK_OFFSET 0x10ULL
+#define DM_DMSTATUS_ANYRESUMEACK_LENGTH 1ULL
+#define DM_DMSTATUS_ANYRESUMEACK 0x10000ULL
/*
* This field is 1 when all currently selected harts do not exist in
* this hardware platform.
*/
-#define DM_DMSTATUS_ALLNONEXISTENT_OFFSET 0xf
-#define DM_DMSTATUS_ALLNONEXISTENT_LENGTH 1
-#define DM_DMSTATUS_ALLNONEXISTENT 0x8000
+#define DM_DMSTATUS_ALLNONEXISTENT_OFFSET 0xfULL
+#define DM_DMSTATUS_ALLNONEXISTENT_LENGTH 1ULL
+#define DM_DMSTATUS_ALLNONEXISTENT 0x8000ULL
/*
* This field is 1 when any currently selected hart does not exist in
* this hardware platform.
*/
-#define DM_DMSTATUS_ANYNONEXISTENT_OFFSET 0xe
-#define DM_DMSTATUS_ANYNONEXISTENT_LENGTH 1
-#define DM_DMSTATUS_ANYNONEXISTENT 0x4000
+#define DM_DMSTATUS_ANYNONEXISTENT_OFFSET 0xeULL
+#define DM_DMSTATUS_ANYNONEXISTENT_LENGTH 1ULL
+#define DM_DMSTATUS_ANYNONEXISTENT 0x4000ULL
/*
* This field is 1 when all currently selected harts are
* unavailable, or (if \FdmDmstatusStickyunavail is 1) were
* unavailable without that being acknowledged.
*/
-#define DM_DMSTATUS_ALLUNAVAIL_OFFSET 0xd
-#define DM_DMSTATUS_ALLUNAVAIL_LENGTH 1
-#define DM_DMSTATUS_ALLUNAVAIL 0x2000
+#define DM_DMSTATUS_ALLUNAVAIL_OFFSET 0xdULL
+#define DM_DMSTATUS_ALLUNAVAIL_LENGTH 1ULL
+#define DM_DMSTATUS_ALLUNAVAIL 0x2000ULL
/*
* This field is 1 when any currently selected hart is unavailable,
* or (if \FdmDmstatusStickyunavail is 1) was unavailable without
* that being acknowledged.
*/
-#define DM_DMSTATUS_ANYUNAVAIL_OFFSET 0xc
-#define DM_DMSTATUS_ANYUNAVAIL_LENGTH 1
-#define DM_DMSTATUS_ANYUNAVAIL 0x1000
+#define DM_DMSTATUS_ANYUNAVAIL_OFFSET 0xcULL
+#define DM_DMSTATUS_ANYUNAVAIL_LENGTH 1ULL
+#define DM_DMSTATUS_ANYUNAVAIL 0x1000ULL
/*
* This field is 1 when all currently selected harts are running.
*/
-#define DM_DMSTATUS_ALLRUNNING_OFFSET 0xb
-#define DM_DMSTATUS_ALLRUNNING_LENGTH 1
-#define DM_DMSTATUS_ALLRUNNING 0x800
+#define DM_DMSTATUS_ALLRUNNING_OFFSET 0xbULL
+#define DM_DMSTATUS_ALLRUNNING_LENGTH 1ULL
+#define DM_DMSTATUS_ALLRUNNING 0x800ULL
/*
* This field is 1 when any currently selected hart is running.
*/
-#define DM_DMSTATUS_ANYRUNNING_OFFSET 0xa
-#define DM_DMSTATUS_ANYRUNNING_LENGTH 1
-#define DM_DMSTATUS_ANYRUNNING 0x400
+#define DM_DMSTATUS_ANYRUNNING_OFFSET 0xaULL
+#define DM_DMSTATUS_ANYRUNNING_LENGTH 1ULL
+#define DM_DMSTATUS_ANYRUNNING 0x400ULL
/*
* This field is 1 when all currently selected harts are halted.
*/
-#define DM_DMSTATUS_ALLHALTED_OFFSET 9
-#define DM_DMSTATUS_ALLHALTED_LENGTH 1
-#define DM_DMSTATUS_ALLHALTED 0x200
+#define DM_DMSTATUS_ALLHALTED_OFFSET 9ULL
+#define DM_DMSTATUS_ALLHALTED_LENGTH 1ULL
+#define DM_DMSTATUS_ALLHALTED 0x200ULL
/*
* This field is 1 when any currently selected hart is halted.
*/
-#define DM_DMSTATUS_ANYHALTED_OFFSET 8
-#define DM_DMSTATUS_ANYHALTED_LENGTH 1
-#define DM_DMSTATUS_ANYHALTED 0x100
-#define DM_DMSTATUS_AUTHENTICATED_OFFSET 7
-#define DM_DMSTATUS_AUTHENTICATED_LENGTH 1
-#define DM_DMSTATUS_AUTHENTICATED 0x80
+#define DM_DMSTATUS_ANYHALTED_OFFSET 8ULL
+#define DM_DMSTATUS_ANYHALTED_LENGTH 1ULL
+#define DM_DMSTATUS_ANYHALTED 0x100ULL
+#define DM_DMSTATUS_AUTHENTICATED_OFFSET 7ULL
+#define DM_DMSTATUS_AUTHENTICATED_LENGTH 1ULL
+#define DM_DMSTATUS_AUTHENTICATED 0x80ULL
/*
* false: Authentication is required before using the DM.
*/
@@ -1893,9 +2000,9 @@
* On components that don't implement authentication, this bit must be
* preset as 1.
*/
-#define DM_DMSTATUS_AUTHBUSY_OFFSET 6
-#define DM_DMSTATUS_AUTHBUSY_LENGTH 1
-#define DM_DMSTATUS_AUTHBUSY 0x40
+#define DM_DMSTATUS_AUTHBUSY_OFFSET 6ULL
+#define DM_DMSTATUS_AUTHBUSY_LENGTH 1ULL
+#define DM_DMSTATUS_AUTHBUSY 0x40ULL
/*
* ready: The authentication module is ready to process the next
* read/write to \RdmAuthdata.
@@ -1915,12 +2022,12 @@
* controllable by the \FdmDmcontrolSetresethaltreq and \FdmDmcontrolClrresethaltreq bits.
* 0 otherwise.
*/
-#define DM_DMSTATUS_HASRESETHALTREQ_OFFSET 5
-#define DM_DMSTATUS_HASRESETHALTREQ_LENGTH 1
-#define DM_DMSTATUS_HASRESETHALTREQ 0x20
-#define DM_DMSTATUS_CONFSTRPTRVALID_OFFSET 4
-#define DM_DMSTATUS_CONFSTRPTRVALID_LENGTH 1
-#define DM_DMSTATUS_CONFSTRPTRVALID 0x10
+#define DM_DMSTATUS_HASRESETHALTREQ_OFFSET 5ULL
+#define DM_DMSTATUS_HASRESETHALTREQ_LENGTH 1ULL
+#define DM_DMSTATUS_HASRESETHALTREQ 0x20ULL
+#define DM_DMSTATUS_CONFSTRPTRVALID_OFFSET 4ULL
+#define DM_DMSTATUS_CONFSTRPTRVALID_LENGTH 1ULL
+#define DM_DMSTATUS_CONFSTRPTRVALID 0x10ULL
/*
* invalid: \RdmConfstrptrZero--\RdmConfstrptrThree hold information which
* is not relevant to the configuration structure.
@@ -1931,9 +2038,9 @@
* configuration structure.
*/
#define DM_DMSTATUS_CONFSTRPTRVALID_VALID 1
-#define DM_DMSTATUS_VERSION_OFFSET 0
-#define DM_DMSTATUS_VERSION_LENGTH 4
-#define DM_DMSTATUS_VERSION 0xf
+#define DM_DMSTATUS_VERSION_OFFSET 0ULL
+#define DM_DMSTATUS_VERSION_LENGTH 4ULL
+#define DM_DMSTATUS_VERSION 0xfULL
/*
* none: There is no Debug Module present.
*/
@@ -1969,9 +2076,9 @@
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_HALTREQ_OFFSET 0x1f
-#define DM_DMCONTROL_HALTREQ_LENGTH 1
-#define DM_DMCONTROL_HALTREQ 0x80000000U
+#define DM_DMCONTROL_HALTREQ_OFFSET 0x1fULL
+#define DM_DMCONTROL_HALTREQ_LENGTH 1ULL
+#define DM_DMCONTROL_HALTREQ 0x80000000ULL
/*
* Writing 1 causes the currently selected harts to resume once, if
* they are halted when the write occurs. It also clears the resume
@@ -1981,9 +2088,9 @@
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_RESUMEREQ_OFFSET 0x1e
-#define DM_DMCONTROL_RESUMEREQ_LENGTH 1
-#define DM_DMCONTROL_RESUMEREQ 0x40000000
+#define DM_DMCONTROL_RESUMEREQ_OFFSET 0x1eULL
+#define DM_DMCONTROL_RESUMEREQ_LENGTH 1ULL
+#define DM_DMCONTROL_RESUMEREQ 0x40000000ULL
/*
* This optional field writes the reset bit for all the currently
* selected harts. To perform a reset the debugger writes 1, and then
@@ -1998,12 +2105,12 @@
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_HARTRESET_OFFSET 0x1d
-#define DM_DMCONTROL_HARTRESET_LENGTH 1
-#define DM_DMCONTROL_HARTRESET 0x20000000
-#define DM_DMCONTROL_ACKHAVERESET_OFFSET 0x1c
-#define DM_DMCONTROL_ACKHAVERESET_LENGTH 1
-#define DM_DMCONTROL_ACKHAVERESET 0x10000000
+#define DM_DMCONTROL_HARTRESET_OFFSET 0x1dULL
+#define DM_DMCONTROL_HARTRESET_LENGTH 1ULL
+#define DM_DMCONTROL_HARTRESET 0x20000000ULL
+#define DM_DMCONTROL_ACKHAVERESET_OFFSET 0x1cULL
+#define DM_DMCONTROL_ACKHAVERESET_LENGTH 1ULL
+#define DM_DMCONTROL_ACKHAVERESET 0x10000000ULL
/*
* nop: No effect.
*/
@@ -2015,9 +2122,9 @@
/*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_ACKUNAVAIL_OFFSET 0x1b
-#define DM_DMCONTROL_ACKUNAVAIL_LENGTH 1
-#define DM_DMCONTROL_ACKUNAVAIL 0x8000000
+#define DM_DMCONTROL_ACKUNAVAIL_OFFSET 0x1bULL
+#define DM_DMCONTROL_ACKUNAVAIL_LENGTH 1ULL
+#define DM_DMCONTROL_ACKUNAVAIL 0x8000000ULL
/*
* nop: No effect.
*/
@@ -2032,9 +2139,9 @@
/*
* Selects the definition of currently selected harts.
*/
-#define DM_DMCONTROL_HASEL_OFFSET 0x1a
-#define DM_DMCONTROL_HASEL_LENGTH 1
-#define DM_DMCONTROL_HASEL 0x4000000
+#define DM_DMCONTROL_HASEL_OFFSET 0x1aULL
+#define DM_DMCONTROL_HASEL_LENGTH 1ULL
+#define DM_DMCONTROL_HASEL 0x4000000ULL
/*
* single: There is a single currently selected hart, that is selected by \Fhartsel.
*/
@@ -2055,16 +2162,16 @@
* The low 10 bits of \Fhartsel: the DM-specific index of the hart to
* select. This hart is always part of the currently selected harts.
*/
-#define DM_DMCONTROL_HARTSELLO_OFFSET 0x10
-#define DM_DMCONTROL_HARTSELLO_LENGTH 0xa
-#define DM_DMCONTROL_HARTSELLO 0x3ff0000
+#define DM_DMCONTROL_HARTSELLO_OFFSET 0x10ULL
+#define DM_DMCONTROL_HARTSELLO_LENGTH 0xaULL
+#define DM_DMCONTROL_HARTSELLO 0x3ff0000ULL
/*
* The high 10 bits of \Fhartsel: the DM-specific index of the hart to
* select. This hart is always part of the currently selected harts.
*/
-#define DM_DMCONTROL_HARTSELHI_OFFSET 6
-#define DM_DMCONTROL_HARTSELHI_LENGTH 0xa
-#define DM_DMCONTROL_HARTSELHI 0xffc0
+#define DM_DMCONTROL_HARTSELHI_OFFSET 6ULL
+#define DM_DMCONTROL_HARTSELHI_LENGTH 0xaULL
+#define DM_DMCONTROL_HARTSELHI 0xffc0ULL
/*
* This optional field sets \Fkeepalive for all currently selected
* harts, unless \FdmDmcontrolClrkeepalive is simultaneously set to
@@ -2072,18 +2179,18 @@
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_SETKEEPALIVE_OFFSET 5
-#define DM_DMCONTROL_SETKEEPALIVE_LENGTH 1
-#define DM_DMCONTROL_SETKEEPALIVE 0x20
+#define DM_DMCONTROL_SETKEEPALIVE_OFFSET 5ULL
+#define DM_DMCONTROL_SETKEEPALIVE_LENGTH 1ULL
+#define DM_DMCONTROL_SETKEEPALIVE 0x20ULL
/*
* This optional field clears \Fkeepalive for all currently selected
* harts.
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET 4
-#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH 1
-#define DM_DMCONTROL_CLRKEEPALIVE 0x10
+#define DM_DMCONTROL_CLRKEEPALIVE_OFFSET 4ULL
+#define DM_DMCONTROL_CLRKEEPALIVE_LENGTH 1ULL
+#define DM_DMCONTROL_CLRKEEPALIVE 0x10ULL
/*
* This optional field writes the halt-on-reset request bit for all
* currently selected harts, unless \FdmDmcontrolClrresethaltreq is
@@ -2096,18 +2203,18 @@
*
* If \FdmDmstatusHasresethaltreq is 0, this field is not implemented.
*/
-#define DM_DMCONTROL_SETRESETHALTREQ_OFFSET 3
-#define DM_DMCONTROL_SETRESETHALTREQ_LENGTH 1
-#define DM_DMCONTROL_SETRESETHALTREQ 8
+#define DM_DMCONTROL_SETRESETHALTREQ_OFFSET 3ULL
+#define DM_DMCONTROL_SETRESETHALTREQ_LENGTH 1ULL
+#define DM_DMCONTROL_SETRESETHALTREQ 8ULL
/*
* This optional field clears the halt-on-reset request bit for all
* currently selected harts.
*
* Writes apply to the new value of \Fhartsel and \FdmDmcontrolHasel.
*/
-#define DM_DMCONTROL_CLRRESETHALTREQ_OFFSET 2
-#define DM_DMCONTROL_CLRRESETHALTREQ_LENGTH 1
-#define DM_DMCONTROL_CLRRESETHALTREQ 4
+#define DM_DMCONTROL_CLRRESETHALTREQ_OFFSET 2ULL
+#define DM_DMCONTROL_CLRRESETHALTREQ_LENGTH 1ULL
+#define DM_DMCONTROL_CLRRESETHALTREQ 4ULL
/*
* This bit controls the reset signal from the DM to the rest of the
* hardware platform. The signal should reset every part of the hardware platform, including
@@ -2117,9 +2224,9 @@
* and then writes 0
* to deassert the reset.
*/
-#define DM_DMCONTROL_NDMRESET_OFFSET 1
-#define DM_DMCONTROL_NDMRESET_LENGTH 1
-#define DM_DMCONTROL_NDMRESET 2
+#define DM_DMCONTROL_NDMRESET_OFFSET 1ULL
+#define DM_DMCONTROL_NDMRESET_LENGTH 1ULL
+#define DM_DMCONTROL_NDMRESET 2ULL
/*
* This bit serves as a reset signal for the Debug Module itself.
* After changing the value of this bit, the debugger must poll
@@ -2129,9 +2236,9 @@
* take an arbitrarily long time to complete activation or deactivation and will
* indicate completion by setting \FdmDmcontrolDmactive to the requested value.
*/
-#define DM_DMCONTROL_DMACTIVE_OFFSET 0
-#define DM_DMCONTROL_DMACTIVE_LENGTH 1
-#define DM_DMCONTROL_DMACTIVE 1
+#define DM_DMCONTROL_DMACTIVE_OFFSET 0ULL
+#define DM_DMCONTROL_DMACTIVE_LENGTH 1ULL
+#define DM_DMCONTROL_DMACTIVE 1ULL
/*
* inactive: The module's state, including authentication mechanism,
* takes its reset values (the \FdmDmcontrolDmactive bit is the only bit which can
@@ -2163,12 +2270,12 @@
* The debugger can make no assumptions about the contents of these
* registers between commands.
*/
-#define DM_HARTINFO_NSCRATCH_OFFSET 0x14
-#define DM_HARTINFO_NSCRATCH_LENGTH 4
-#define DM_HARTINFO_NSCRATCH 0xf00000
-#define DM_HARTINFO_DATAACCESS_OFFSET 0x10
-#define DM_HARTINFO_DATAACCESS_LENGTH 1
-#define DM_HARTINFO_DATAACCESS 0x10000
+#define DM_HARTINFO_NSCRATCH_OFFSET 0x14ULL
+#define DM_HARTINFO_NSCRATCH_LENGTH 4ULL
+#define DM_HARTINFO_NSCRATCH 0xf00000ULL
+#define DM_HARTINFO_DATAACCESS_OFFSET 0x10ULL
+#define DM_HARTINFO_DATAACCESS_LENGTH 1ULL
+#define DM_HARTINFO_DATAACCESS 0x10000ULL
/*
* csr: The {\tt data} registers are shadowed in the hart by CSRs.
* Each CSR is DXLEN bits in size, and corresponds
@@ -2187,16 +2294,12 @@
* If \FdmHartinfoDataaccess is 1: Number of 32-bit words in the memory map
* dedicated to shadowing the {\tt data} registers.
*
- * If this value is non-zero, then the {tt data} registers must go
- * beyond being MRs and guarantee they each store a single value, that is
- * readable/writable by either side.
- *
* Since there are at most 12 {\tt data} registers, the value in this
* register must be 12 or smaller.
*/
-#define DM_HARTINFO_DATASIZE_OFFSET 0xc
-#define DM_HARTINFO_DATASIZE_LENGTH 4
-#define DM_HARTINFO_DATASIZE 0xf000
+#define DM_HARTINFO_DATASIZE_OFFSET 0xcULL
+#define DM_HARTINFO_DATASIZE_LENGTH 4ULL
+#define DM_HARTINFO_DATASIZE 0xf000ULL
/*
* If \FdmHartinfoDataaccess is 0: The number of the first CSR dedicated to
* shadowing the {\tt data} registers.
@@ -2206,32 +2309,32 @@
* range of -2048 to 2047, easily addressed with a load or store using
* \Xzero as the address register.
*/
-#define DM_HARTINFO_DATAADDR_OFFSET 0
-#define DM_HARTINFO_DATAADDR_LENGTH 0xc
-#define DM_HARTINFO_DATAADDR 0xfff
+#define DM_HARTINFO_DATAADDR_OFFSET 0ULL
+#define DM_HARTINFO_DATAADDR_LENGTH 0xcULL
+#define DM_HARTINFO_DATAADDR 0xfffULL
#define DM_HAWINDOWSEL 0x14
/*
* The high bits of this field may be tied to 0, depending on how large
* the array mask register is. E.g.\ on a hardware platform with 48 harts only bit 0
* of this field may actually be writable.
*/
-#define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0
-#define DM_HAWINDOWSEL_HAWINDOWSEL_LENGTH 0xf
-#define DM_HAWINDOWSEL_HAWINDOWSEL 0x7fff
+#define DM_HAWINDOWSEL_HAWINDOWSEL_OFFSET 0ULL
+#define DM_HAWINDOWSEL_HAWINDOWSEL_LENGTH 0xfULL
+#define DM_HAWINDOWSEL_HAWINDOWSEL 0x7fffULL
#define DM_HAWINDOW 0x15
-#define DM_HAWINDOW_MASKDATA_OFFSET 0
-#define DM_HAWINDOW_MASKDATA_LENGTH 0x20
-#define DM_HAWINDOW_MASKDATA 0xffffffffU
+#define DM_HAWINDOW_MASKDATA_OFFSET 0ULL
+#define DM_HAWINDOW_MASKDATA_LENGTH 0x20ULL
+#define DM_HAWINDOW_MASKDATA 0xffffffffULL
#define DM_ABSTRACTCS 0x16
/*
* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16.
*/
-#define DM_ABSTRACTCS_PROGBUFSIZE_OFFSET 0x18
-#define DM_ABSTRACTCS_PROGBUFSIZE_LENGTH 5
-#define DM_ABSTRACTCS_PROGBUFSIZE 0x1f000000
-#define DM_ABSTRACTCS_BUSY_OFFSET 0xc
-#define DM_ABSTRACTCS_BUSY_LENGTH 1
-#define DM_ABSTRACTCS_BUSY 0x1000
+#define DM_ABSTRACTCS_PROGBUFSIZE_OFFSET 0x18ULL
+#define DM_ABSTRACTCS_PROGBUFSIZE_LENGTH 5ULL
+#define DM_ABSTRACTCS_PROGBUFSIZE 0x1f000000ULL
+#define DM_ABSTRACTCS_BUSY_OFFSET 0xcULL
+#define DM_ABSTRACTCS_BUSY_LENGTH 1ULL
+#define DM_ABSTRACTCS_BUSY 0x1000ULL
/*
* ready: There is no abstract command currently being executed.
*/
@@ -2250,12 +2353,19 @@
* permission checks that apply based on the current architectural
* state of the hart performing the access, or with a relaxed set of
* permission checks (e.g. PMP restrictions are ignored). The
- * details of the latter are implementation-specific. When set to 0,
- * full permissions apply; when set to 1, relaxed permissions apply.
+ * details of the latter are implementation-specific.
+ */
+#define DM_ABSTRACTCS_RELAXEDPRIV_OFFSET 0xbULL
+#define DM_ABSTRACTCS_RELAXEDPRIV_LENGTH 1ULL
+#define DM_ABSTRACTCS_RELAXEDPRIV 0x800ULL
+/*
+ * full checks: Full permission checks apply.
*/
-#define DM_ABSTRACTCS_RELAXEDPRIV_OFFSET 0xb
-#define DM_ABSTRACTCS_RELAXEDPRIV_LENGTH 1
-#define DM_ABSTRACTCS_RELAXEDPRIV 0x800
+#define DM_ABSTRACTCS_RELAXEDPRIV_FULL_CHECKS 0
+/*
+ * relaxed checks: Relaxed permission checks apply.
+ */
+#define DM_ABSTRACTCS_RELAXEDPRIV_RELAXED_CHECKS 1
/*
* 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
@@ -2263,9 +2373,9 @@
*
* This field only contains a valid value if \FdmAbstractcsBusy is 0.
*/
-#define DM_ABSTRACTCS_CMDERR_OFFSET 8
-#define DM_ABSTRACTCS_CMDERR_LENGTH 3
-#define DM_ABSTRACTCS_CMDERR 0x700
+#define DM_ABSTRACTCS_CMDERR_OFFSET 8ULL
+#define DM_ABSTRACTCS_CMDERR_LENGTH 3ULL
+#define DM_ABSTRACTCS_CMDERR 0x700ULL
/*
* none: No error.
*/
@@ -2311,24 +2421,24 @@
* Number of {\tt data} registers that are implemented as part of the
* abstract command interface. Valid sizes are 1 -- 12.
*/
-#define DM_ABSTRACTCS_DATACOUNT_OFFSET 0
-#define DM_ABSTRACTCS_DATACOUNT_LENGTH 4
-#define DM_ABSTRACTCS_DATACOUNT 0xf
+#define DM_ABSTRACTCS_DATACOUNT_OFFSET 0ULL
+#define DM_ABSTRACTCS_DATACOUNT_LENGTH 4ULL
+#define DM_ABSTRACTCS_DATACOUNT 0xfULL
#define DM_COMMAND 0x17
/*
* The type determines the overall functionality of this
* abstract command.
*/
-#define DM_COMMAND_CMDTYPE_OFFSET 0x18
-#define DM_COMMAND_CMDTYPE_LENGTH 8
-#define DM_COMMAND_CMDTYPE 0xff000000U
+#define DM_COMMAND_CMDTYPE_OFFSET 0x18ULL
+#define DM_COMMAND_CMDTYPE_LENGTH 8ULL
+#define DM_COMMAND_CMDTYPE 0xff000000ULL
/*
* This field is interpreted in a command-specific manner,
* described for each abstract command.
*/
-#define DM_COMMAND_CONTROL_OFFSET 0
-#define DM_COMMAND_CONTROL_LENGTH 0x18
-#define DM_COMMAND_CONTROL 0xffffff
+#define DM_COMMAND_CONTROL_OFFSET 0ULL
+#define DM_COMMAND_CONTROL_LENGTH 0x18ULL
+#define DM_COMMAND_CONTROL 0xffffffULL
#define DM_ABSTRACTAUTO 0x18
/*
* When a bit in this field is 1, read or write accesses to the
@@ -2336,42 +2446,42 @@
* current value in \RdmCommand was written there again after the
* access to {\tt progbuf} completes.
*/
-#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 0x10
-#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 0x10
-#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF 0xffff0000U
+#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_OFFSET 0x10ULL
+#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF_LENGTH 0x10ULL
+#define DM_ABSTRACTAUTO_AUTOEXECPROGBUF 0xffff0000ULL
/*
* When a bit in this field is 1, read or write accesses to the
* corresponding {\tt data} word cause the DM to act as if the current
* value in \RdmCommand was written there again after the
* access to {\tt data} completes.
*/
-#define DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0
-#define DM_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 0xc
-#define DM_ABSTRACTAUTO_AUTOEXECDATA 0xfff
+#define DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET 0ULL
+#define DM_ABSTRACTAUTO_AUTOEXECDATA_LENGTH 0xcULL
+#define DM_ABSTRACTAUTO_AUTOEXECDATA 0xfffULL
#define DM_CONFSTRPTR0 0x19
-#define DM_CONFSTRPTR0_ADDR_OFFSET 0
-#define DM_CONFSTRPTR0_ADDR_LENGTH 0x20
-#define DM_CONFSTRPTR0_ADDR 0xffffffffU
+#define DM_CONFSTRPTR0_ADDR_OFFSET 0ULL
+#define DM_CONFSTRPTR0_ADDR_LENGTH 0x20ULL
+#define DM_CONFSTRPTR0_ADDR 0xffffffffULL
#define DM_CONFSTRPTR1 0x1a
-#define DM_CONFSTRPTR1_ADDR_OFFSET 0
-#define DM_CONFSTRPTR1_ADDR_LENGTH 0x20
-#define DM_CONFSTRPTR1_ADDR 0xffffffffU
+#define DM_CONFSTRPTR1_ADDR_OFFSET 0ULL
+#define DM_CONFSTRPTR1_ADDR_LENGTH 0x20ULL
+#define DM_CONFSTRPTR1_ADDR 0xffffffffULL
#define DM_CONFSTRPTR2 0x1b
-#define DM_CONFSTRPTR2_ADDR_OFFSET 0
-#define DM_CONFSTRPTR2_ADDR_LENGTH 0x20
-#define DM_CONFSTRPTR2_ADDR 0xffffffffU
+#define DM_CONFSTRPTR2_ADDR_OFFSET 0ULL
+#define DM_CONFSTRPTR2_ADDR_LENGTH 0x20ULL
+#define DM_CONFSTRPTR2_ADDR 0xffffffffULL
#define DM_CONFSTRPTR3 0x1c
-#define DM_CONFSTRPTR3_ADDR_OFFSET 0
-#define DM_CONFSTRPTR3_ADDR_LENGTH 0x20
-#define DM_CONFSTRPTR3_ADDR 0xffffffffU
+#define DM_CONFSTRPTR3_ADDR_OFFSET 0ULL
+#define DM_CONFSTRPTR3_ADDR_LENGTH 0x20ULL
+#define DM_CONFSTRPTR3_ADDR 0xffffffffULL
#define DM_NEXTDM 0x1d
-#define DM_NEXTDM_ADDR_OFFSET 0
-#define DM_NEXTDM_ADDR_LENGTH 0x20
-#define DM_NEXTDM_ADDR 0xffffffffU
+#define DM_NEXTDM_ADDR_OFFSET 0ULL
+#define DM_NEXTDM_ADDR_LENGTH 0x20ULL
+#define DM_NEXTDM_ADDR 0xffffffffULL
#define DM_DATA0 0x04
-#define DM_DATA0_DATA_OFFSET 0
-#define DM_DATA0_DATA_LENGTH 0x20
-#define DM_DATA0_DATA 0xffffffffU
+#define DM_DATA0_DATA_OFFSET 0ULL
+#define DM_DATA0_DATA_LENGTH 0x20ULL
+#define DM_DATA0_DATA 0xffffffffULL
#define DM_DATA1 0x05
#define DM_DATA2 0x06
#define DM_DATA3 0x07
@@ -2384,9 +2494,9 @@
#define DM_DATA10 0x0e
#define DM_DATA11 0x0f
#define DM_PROGBUF0 0x20
-#define DM_PROGBUF0_DATA_OFFSET 0
-#define DM_PROGBUF0_DATA_LENGTH 0x20
-#define DM_PROGBUF0_DATA 0xffffffffU
+#define DM_PROGBUF0_DATA_OFFSET 0ULL
+#define DM_PROGBUF0_DATA_LENGTH 0x20ULL
+#define DM_PROGBUF0_DATA 0xffffffffULL
#define DM_PROGBUF1 0x21
#define DM_PROGBUF2 0x22
#define DM_PROGBUF3 0x23
@@ -2403,13 +2513,13 @@
#define DM_PROGBUF14 0x2e
#define DM_PROGBUF15 0x2f
#define DM_AUTHDATA 0x30
-#define DM_AUTHDATA_DATA_OFFSET 0
-#define DM_AUTHDATA_DATA_LENGTH 0x20
-#define DM_AUTHDATA_DATA 0xffffffffU
+#define DM_AUTHDATA_DATA_OFFSET 0ULL
+#define DM_AUTHDATA_DATA_LENGTH 0x20ULL
+#define DM_AUTHDATA_DATA 0xffffffffULL
#define DM_DMCS2 0x32
-#define DM_DMCS2_GROUPTYPE_OFFSET 0xb
-#define DM_DMCS2_GROUPTYPE_LENGTH 1
-#define DM_DMCS2_GROUPTYPE 0x800
+#define DM_DMCS2_GROUPTYPE_OFFSET 0xbULL
+#define DM_DMCS2_GROUPTYPE_LENGTH 1ULL
+#define DM_DMCS2_GROUPTYPE 0x800ULL
/*
* halt: The remaining fields in this register configure halt groups.
*/
@@ -2424,9 +2534,9 @@
* If a non-existent trigger value is written here, the hardware will
* change it to a valid one or 0 if no DM external triggers exist.
*/
-#define DM_DMCS2_DMEXTTRIGGER_OFFSET 7
-#define DM_DMCS2_DMEXTTRIGGER_LENGTH 4
-#define DM_DMCS2_DMEXTTRIGGER 0x780
+#define DM_DMCS2_DMEXTTRIGGER_OFFSET 7ULL
+#define DM_DMCS2_DMEXTTRIGGER_LENGTH 4ULL
+#define DM_DMCS2_DMEXTTRIGGER 0x780ULL
/*
* When \FdmDmcsTwoHgselect is 0, contains the group of the hart
* specified by \Fhartsel.
@@ -2444,9 +2554,9 @@
*
* If groups aren't implemented, then this entire field is 0.
*/
-#define DM_DMCS2_GROUP_OFFSET 2
-#define DM_DMCS2_GROUP_LENGTH 5
-#define DM_DMCS2_GROUP 0x7c
+#define DM_DMCS2_GROUP_OFFSET 2ULL
+#define DM_DMCS2_GROUP_LENGTH 5ULL
+#define DM_DMCS2_GROUP 0x7cULL
/*
* When 1 is written and \FdmDmcsTwoHgselect is 0, for every selected
* hart the DM will change its group to the value written to \FdmDmcsTwoGroup,
@@ -2462,12 +2572,12 @@
*
* Writing 0 has no effect.
*/
-#define DM_DMCS2_HGWRITE_OFFSET 1
-#define DM_DMCS2_HGWRITE_LENGTH 1
-#define DM_DMCS2_HGWRITE 2
-#define DM_DMCS2_HGSELECT_OFFSET 0
-#define DM_DMCS2_HGSELECT_LENGTH 1
-#define DM_DMCS2_HGSELECT 1
+#define DM_DMCS2_HGWRITE_OFFSET 1ULL
+#define DM_DMCS2_HGWRITE_LENGTH 1ULL
+#define DM_DMCS2_HGWRITE 2ULL
+#define DM_DMCS2_HGSELECT_OFFSET 0ULL
+#define DM_DMCS2_HGSELECT_LENGTH 1ULL
+#define DM_DMCS2_HGSELECT 1ULL
/*
* harts: Operate on harts.
*/
@@ -2480,25 +2590,25 @@
* If there are no DM external triggers, this field must be tied to 0.
*/
#define DM_HALTSUM0 0x40
-#define DM_HALTSUM0_HALTSUM0_OFFSET 0
-#define DM_HALTSUM0_HALTSUM0_LENGTH 0x20
-#define DM_HALTSUM0_HALTSUM0 0xffffffffU
+#define DM_HALTSUM0_HALTSUM0_OFFSET 0ULL
+#define DM_HALTSUM0_HALTSUM0_LENGTH 0x20ULL
+#define DM_HALTSUM0_HALTSUM0 0xffffffffULL
#define DM_HALTSUM1 0x13
-#define DM_HALTSUM1_HALTSUM1_OFFSET 0
-#define DM_HALTSUM1_HALTSUM1_LENGTH 0x20
-#define DM_HALTSUM1_HALTSUM1 0xffffffffU
+#define DM_HALTSUM1_HALTSUM1_OFFSET 0ULL
+#define DM_HALTSUM1_HALTSUM1_LENGTH 0x20ULL
+#define DM_HALTSUM1_HALTSUM1 0xffffffffULL
#define DM_HALTSUM2 0x34
-#define DM_HALTSUM2_HALTSUM2_OFFSET 0
-#define DM_HALTSUM2_HALTSUM2_LENGTH 0x20
-#define DM_HALTSUM2_HALTSUM2 0xffffffffU
+#define DM_HALTSUM2_HALTSUM2_OFFSET 0ULL
+#define DM_HALTSUM2_HALTSUM2_LENGTH 0x20ULL
+#define DM_HALTSUM2_HALTSUM2 0xffffffffULL
#define DM_HALTSUM3 0x35
-#define DM_HALTSUM3_HALTSUM3_OFFSET 0
-#define DM_HALTSUM3_HALTSUM3_LENGTH 0x20
-#define DM_HALTSUM3_HALTSUM3 0xffffffffU
+#define DM_HALTSUM3_HALTSUM3_OFFSET 0ULL
+#define DM_HALTSUM3_HALTSUM3_LENGTH 0x20ULL
+#define DM_HALTSUM3_HALTSUM3 0xffffffffULL
#define DM_SBCS 0x38
-#define DM_SBCS_SBVERSION_OFFSET 0x1d
-#define DM_SBCS_SBVERSION_LENGTH 3
-#define DM_SBCS_SBVERSION 0xe0000000U
+#define DM_SBCS_SBVERSION_OFFSET 0x1dULL
+#define DM_SBCS_SBVERSION_LENGTH 3ULL
+#define DM_SBCS_SBVERSION 0xe0000000ULL
/*
* legacy: The System Bus interface conforms to mainline drafts of this
* spec older than 1 January, 2018.
@@ -2520,11 +2630,11 @@
* While this field is set, no more system bus accesses can be
* initiated by the Debug Module.
*/
-#define DM_SBCS_SBBUSYERROR_OFFSET 0x16
-#define DM_SBCS_SBBUSYERROR_LENGTH 1
-#define DM_SBCS_SBBUSYERROR 0x400000
+#define DM_SBCS_SBBUSYERROR_OFFSET 0x16ULL
+#define DM_SBCS_SBBUSYERROR_LENGTH 1ULL
+#define DM_SBCS_SBBUSYERROR 0x400000ULL
/*
- * When 1, indicates the system bus master is busy. (Whether the
+ * When 1, indicates the system bus manager is busy. (Whether the
* system bus itself is busy is related, but not the same thing.) This
* bit goes high immediately when a read or write is requested for any
* reason, and does not go low until the access is fully completed.
@@ -2533,22 +2643,22 @@
* behavior. A debugger must not write to \RdmSbcs until it reads
* \FdmSbcsSbbusy as 0.
*/
-#define DM_SBCS_SBBUSY_OFFSET 0x15
-#define DM_SBCS_SBBUSY_LENGTH 1
-#define DM_SBCS_SBBUSY 0x200000
+#define DM_SBCS_SBBUSY_OFFSET 0x15ULL
+#define DM_SBCS_SBBUSY_LENGTH 1ULL
+#define DM_SBCS_SBBUSY 0x200000ULL
/*
* When 1, every write to \RdmSbaddressZero automatically triggers a
* system bus read at the new address.
*/
-#define DM_SBCS_SBREADONADDR_OFFSET 0x14
-#define DM_SBCS_SBREADONADDR_LENGTH 1
-#define DM_SBCS_SBREADONADDR 0x100000
+#define DM_SBCS_SBREADONADDR_OFFSET 0x14ULL
+#define DM_SBCS_SBREADONADDR_LENGTH 1ULL
+#define DM_SBCS_SBREADONADDR 0x100000ULL
/*
* Select the access size to use for system bus accesses.
*/
-#define DM_SBCS_SBACCESS_OFFSET 0x11
-#define DM_SBCS_SBACCESS_LENGTH 3
-#define DM_SBCS_SBACCESS 0xe0000
+#define DM_SBCS_SBACCESS_OFFSET 0x11ULL
+#define DM_SBCS_SBACCESS_LENGTH 3ULL
+#define DM_SBCS_SBACCESS 0xe0000ULL
/*
* 8bit: 8-bit
*/
@@ -2577,28 +2687,28 @@
* When 1, {\tt sbaddress} is incremented by the access size (in
* bytes) selected in \FdmSbcsSbaccess after every system bus access.
*/
-#define DM_SBCS_SBAUTOINCREMENT_OFFSET 0x10
-#define DM_SBCS_SBAUTOINCREMENT_LENGTH 1
-#define DM_SBCS_SBAUTOINCREMENT 0x10000
+#define DM_SBCS_SBAUTOINCREMENT_OFFSET 0x10ULL
+#define DM_SBCS_SBAUTOINCREMENT_LENGTH 1ULL
+#define DM_SBCS_SBAUTOINCREMENT 0x10000ULL
/*
* When 1, every read from \RdmSbdataZero automatically triggers a
* system bus read at the (possibly auto-incremented) address.
*/
-#define DM_SBCS_SBREADONDATA_OFFSET 0xf
-#define DM_SBCS_SBREADONDATA_LENGTH 1
-#define DM_SBCS_SBREADONDATA 0x8000
+#define DM_SBCS_SBREADONDATA_OFFSET 0xfULL
+#define DM_SBCS_SBREADONDATA_LENGTH 1ULL
+#define DM_SBCS_SBREADONDATA 0x8000ULL
/*
* When the Debug Module's system bus
- * master encounters an error, this field gets set. The bits in this
+ * manager encounters an 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.
*
* An implementation may report ``Other'' (7) for any error condition.
*/
-#define DM_SBCS_SBERROR_OFFSET 0xc
-#define DM_SBCS_SBERROR_LENGTH 3
-#define DM_SBCS_SBERROR 0x7000
+#define DM_SBCS_SBERROR_OFFSET 0xcULL
+#define DM_SBCS_SBERROR_LENGTH 3ULL
+#define DM_SBCS_SBERROR 0x7000ULL
/*
* none: There was no bus error.
*/
@@ -2627,101 +2737,101 @@
* Width of system bus addresses in bits. (0 indicates there is no bus
* access support.)
*/
-#define DM_SBCS_SBASIZE_OFFSET 5
-#define DM_SBCS_SBASIZE_LENGTH 7
-#define DM_SBCS_SBASIZE 0xfe0
+#define DM_SBCS_SBASIZE_OFFSET 5ULL
+#define DM_SBCS_SBASIZE_LENGTH 7ULL
+#define DM_SBCS_SBASIZE 0xfe0ULL
/*
* 1 when 128-bit system bus accesses are supported.
*/
-#define DM_SBCS_SBACCESS128_OFFSET 4
-#define DM_SBCS_SBACCESS128_LENGTH 1
-#define DM_SBCS_SBACCESS128 0x10
+#define DM_SBCS_SBACCESS128_OFFSET 4ULL
+#define DM_SBCS_SBACCESS128_LENGTH 1ULL
+#define DM_SBCS_SBACCESS128 0x10ULL
/*
* 1 when 64-bit system bus accesses are supported.
*/
-#define DM_SBCS_SBACCESS64_OFFSET 3
-#define DM_SBCS_SBACCESS64_LENGTH 1
-#define DM_SBCS_SBACCESS64 8
+#define DM_SBCS_SBACCESS64_OFFSET 3ULL
+#define DM_SBCS_SBACCESS64_LENGTH 1ULL
+#define DM_SBCS_SBACCESS64 8ULL
/*
* 1 when 32-bit system bus accesses are supported.
*/
-#define DM_SBCS_SBACCESS32_OFFSET 2
-#define DM_SBCS_SBACCESS32_LENGTH 1
-#define DM_SBCS_SBACCESS32 4
+#define DM_SBCS_SBACCESS32_OFFSET 2ULL
+#define DM_SBCS_SBACCESS32_LENGTH 1ULL
+#define DM_SBCS_SBACCESS32 4ULL
/*
* 1 when 16-bit system bus accesses are supported.
*/
-#define DM_SBCS_SBACCESS16_OFFSET 1
-#define DM_SBCS_SBACCESS16_LENGTH 1
-#define DM_SBCS_SBACCESS16 2
+#define DM_SBCS_SBACCESS16_OFFSET 1ULL
+#define DM_SBCS_SBACCESS16_LENGTH 1ULL
+#define DM_SBCS_SBACCESS16 2ULL
/*
* 1 when 8-bit system bus accesses are supported.
*/
-#define DM_SBCS_SBACCESS8_OFFSET 0
-#define DM_SBCS_SBACCESS8_LENGTH 1
-#define DM_SBCS_SBACCESS8 1
+#define DM_SBCS_SBACCESS8_OFFSET 0ULL
+#define DM_SBCS_SBACCESS8_LENGTH 1ULL
+#define DM_SBCS_SBACCESS8 1ULL
#define DM_SBADDRESS0 0x39
/*
* Accesses bits 31:0 of the physical address in {\tt sbaddress}.
*/
-#define DM_SBADDRESS0_ADDRESS_OFFSET 0
-#define DM_SBADDRESS0_ADDRESS_LENGTH 0x20
-#define DM_SBADDRESS0_ADDRESS 0xffffffffU
+#define DM_SBADDRESS0_ADDRESS_OFFSET 0ULL
+#define DM_SBADDRESS0_ADDRESS_LENGTH 0x20ULL
+#define DM_SBADDRESS0_ADDRESS 0xffffffffULL
#define DM_SBADDRESS1 0x3a
/*
* Accesses bits 63:32 of the physical address in {\tt sbaddress} (if
* the system address bus is that wide).
*/
-#define DM_SBADDRESS1_ADDRESS_OFFSET 0
-#define DM_SBADDRESS1_ADDRESS_LENGTH 0x20
-#define DM_SBADDRESS1_ADDRESS 0xffffffffU
+#define DM_SBADDRESS1_ADDRESS_OFFSET 0ULL
+#define DM_SBADDRESS1_ADDRESS_LENGTH 0x20ULL
+#define DM_SBADDRESS1_ADDRESS 0xffffffffULL
#define DM_SBADDRESS2 0x3b
/*
* Accesses bits 95:64 of the physical address in {\tt sbaddress} (if
* the system address bus is that wide).
*/
-#define DM_SBADDRESS2_ADDRESS_OFFSET 0
-#define DM_SBADDRESS2_ADDRESS_LENGTH 0x20
-#define DM_SBADDRESS2_ADDRESS 0xffffffffU
+#define DM_SBADDRESS2_ADDRESS_OFFSET 0ULL
+#define DM_SBADDRESS2_ADDRESS_LENGTH 0x20ULL
+#define DM_SBADDRESS2_ADDRESS 0xffffffffULL
#define DM_SBADDRESS3 0x37
/*
* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
* the system address bus is that wide).
*/
-#define DM_SBADDRESS3_ADDRESS_OFFSET 0
-#define DM_SBADDRESS3_ADDRESS_LENGTH 0x20
-#define DM_SBADDRESS3_ADDRESS 0xffffffffU
+#define DM_SBADDRESS3_ADDRESS_OFFSET 0ULL
+#define DM_SBADDRESS3_ADDRESS_LENGTH 0x20ULL
+#define DM_SBADDRESS3_ADDRESS 0xffffffffULL
#define DM_SBDATA0 0x3c
/*
* Accesses bits 31:0 of {\tt sbdata}.
*/
-#define DM_SBDATA0_DATA_OFFSET 0
-#define DM_SBDATA0_DATA_LENGTH 0x20
-#define DM_SBDATA0_DATA 0xffffffffU
+#define DM_SBDATA0_DATA_OFFSET 0ULL
+#define DM_SBDATA0_DATA_LENGTH 0x20ULL
+#define DM_SBDATA0_DATA 0xffffffffULL
#define DM_SBDATA1 0x3d
/*
* Accesses bits 63:32 of {\tt sbdata} (if the system bus is that
* wide).
*/
-#define DM_SBDATA1_DATA_OFFSET 0
-#define DM_SBDATA1_DATA_LENGTH 0x20
-#define DM_SBDATA1_DATA 0xffffffffU
+#define DM_SBDATA1_DATA_OFFSET 0ULL
+#define DM_SBDATA1_DATA_LENGTH 0x20ULL
+#define DM_SBDATA1_DATA 0xffffffffULL
#define DM_SBDATA2 0x3e
/*
* Accesses bits 95:64 of {\tt sbdata} (if the system bus is that
* wide).
*/
-#define DM_SBDATA2_DATA_OFFSET 0
-#define DM_SBDATA2_DATA_LENGTH 0x20
-#define DM_SBDATA2_DATA 0xffffffffU
+#define DM_SBDATA2_DATA_OFFSET 0ULL
+#define DM_SBDATA2_DATA_LENGTH 0x20ULL
+#define DM_SBDATA2_DATA 0xffffffffULL
#define DM_SBDATA3 0x3f
/*
* Accesses bits 127:96 of {\tt sbdata} (if the system bus is that
* wide).
*/
-#define DM_SBDATA3_DATA_OFFSET 0
-#define DM_SBDATA3_DATA_LENGTH 0x20
-#define DM_SBDATA3_DATA 0xffffffffU
+#define DM_SBDATA3_DATA_OFFSET 0ULL
+#define DM_SBDATA3_DATA_LENGTH 0x20ULL
+#define DM_SBDATA3_DATA 0xffffffffULL
#define DM_CUSTOM 0x1f
#define DM_CUSTOM0 0x70
#define DM_CUSTOM1 0x71
@@ -2743,18 +2853,18 @@
/*
* Description of what this field is used for.
*/
-#define SHORTNAME_FIELD_OFFSET 0
-#define SHORTNAME_FIELD_LENGTH 8
-#define SHORTNAME_FIELD 0xff
+#define SHORTNAME_FIELD_OFFSET 0ULL
+#define SHORTNAME_FIELD_LENGTH 8ULL
+#define SHORTNAME_FIELD 0xffULL
/*
* This is 0 to indicate Access Register Command.
*/
-#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 0x18
-#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8
-#define AC_ACCESS_REGISTER_CMDTYPE 0xff000000U
-#define AC_ACCESS_REGISTER_AARSIZE_OFFSET 0x14
-#define AC_ACCESS_REGISTER_AARSIZE_LENGTH 3
-#define AC_ACCESS_REGISTER_AARSIZE 0x700000
+#define AC_ACCESS_REGISTER_CMDTYPE_OFFSET 0x18ULL
+#define AC_ACCESS_REGISTER_CMDTYPE_LENGTH 8ULL
+#define AC_ACCESS_REGISTER_CMDTYPE 0xff000000ULL
+#define AC_ACCESS_REGISTER_AARSIZE_OFFSET 0x14ULL
+#define AC_ACCESS_REGISTER_AARSIZE_LENGTH 3ULL
+#define AC_ACCESS_REGISTER_AARSIZE 0x700000ULL
/*
* 32bit: Access the lowest 32 bits of the register.
*/
@@ -2777,9 +2887,9 @@
* This field controls the Argument Width as referenced in
* Table~\ref{tab:datareg}.
*/
-#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 0x13
-#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1
-#define AC_ACCESS_REGISTER_AARPOSTINCREMENT 0x80000
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_OFFSET 0x13ULL
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_LENGTH 1ULL
+#define AC_ACCESS_REGISTER_AARPOSTINCREMENT 0x80000ULL
/*
* disabled: No effect. This variant must be supported.
*/
@@ -2792,9 +2902,9 @@
* happens when \FacAccessregisterTransfer is 0.
*/
#define AC_ACCESS_REGISTER_AARPOSTINCREMENT_ENABLED 1
-#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 0x12
-#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1
-#define AC_ACCESS_REGISTER_POSTEXEC 0x40000
+#define AC_ACCESS_REGISTER_POSTEXEC_OFFSET 0x12ULL
+#define AC_ACCESS_REGISTER_POSTEXEC_LENGTH 1ULL
+#define AC_ACCESS_REGISTER_POSTEXEC 0x40000ULL
/*
* disabled: No effect. This variant must be supported, and is the only
* supported one if \FdmAbstractcsProgbufsize is 0.
@@ -2806,9 +2916,9 @@
* optional.
*/
#define AC_ACCESS_REGISTER_POSTEXEC_ENABLED 1
-#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 0x11
-#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1
-#define AC_ACCESS_REGISTER_TRANSFER 0x20000
+#define AC_ACCESS_REGISTER_TRANSFER_OFFSET 0x11ULL
+#define AC_ACCESS_REGISTER_TRANSFER_LENGTH 1ULL
+#define AC_ACCESS_REGISTER_TRANSFER 0x20000ULL
/*
* disabled: Don't do the operation specified by \FacAccessregisterWrite.
*/
@@ -2824,9 +2934,9 @@
/*
* When \FacAccessregisterTransfer is set:
*/
-#define AC_ACCESS_REGISTER_WRITE_OFFSET 0x10
-#define AC_ACCESS_REGISTER_WRITE_LENGTH 1
-#define AC_ACCESS_REGISTER_WRITE 0x10000
+#define AC_ACCESS_REGISTER_WRITE_OFFSET 0x10ULL
+#define AC_ACCESS_REGISTER_WRITE_LENGTH 1ULL
+#define AC_ACCESS_REGISTER_WRITE 0x10000ULL
/*
* arg0: Copy data from the specified register into {\tt arg0} portion
* of {\tt data}.
@@ -2843,29 +2953,29 @@
* \RcsrDpc may be used as an alias for PC if this command is
* supported on a non-halted hart.
*/
-#define AC_ACCESS_REGISTER_REGNO_OFFSET 0
-#define AC_ACCESS_REGISTER_REGNO_LENGTH 0x10
-#define AC_ACCESS_REGISTER_REGNO 0xffff
+#define AC_ACCESS_REGISTER_REGNO_OFFSET 0ULL
+#define AC_ACCESS_REGISTER_REGNO_LENGTH 0x10ULL
+#define AC_ACCESS_REGISTER_REGNO 0xffffULL
/*
* This is 1 to indicate Quick Access command.
*/
-#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 0x18
-#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8
-#define AC_QUICK_ACCESS_CMDTYPE 0xff000000U
+#define AC_QUICK_ACCESS_CMDTYPE_OFFSET 0x18ULL
+#define AC_QUICK_ACCESS_CMDTYPE_LENGTH 8ULL
+#define AC_QUICK_ACCESS_CMDTYPE 0xff000000ULL
/*
* This is 2 to indicate Access Memory Command.
*/
-#define AC_ACCESS_MEMORY_CMDTYPE_OFFSET 0x18
-#define AC_ACCESS_MEMORY_CMDTYPE_LENGTH 8
-#define AC_ACCESS_MEMORY_CMDTYPE 0xff000000U
+#define AC_ACCESS_MEMORY_CMDTYPE_OFFSET 0x18ULL
+#define AC_ACCESS_MEMORY_CMDTYPE_LENGTH 8ULL
+#define AC_ACCESS_MEMORY_CMDTYPE 0xff000000ULL
/*
* An implementation does not have to implement both virtual and
* physical accesses, but it must fail accesses that it doesn't
* support.
*/
-#define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 0x17
-#define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1
-#define AC_ACCESS_MEMORY_AAMVIRTUAL 0x800000
+#define AC_ACCESS_MEMORY_AAMVIRTUAL_OFFSET 0x17ULL
+#define AC_ACCESS_MEMORY_AAMVIRTUAL_LENGTH 1ULL
+#define AC_ACCESS_MEMORY_AAMVIRTUAL 0x800000ULL
/*
* physical: Addresses are physical (to the hart they are performed on).
*/
@@ -2880,9 +2990,9 @@
* may optionally allow \FacAccessmemoryAamvirtual set to 1, which would produce the same result as
* that same abstract command with \FacAccessmemoryAamvirtual cleared.
*/
-#define AC_ACCESS_MEMORY_AAMSIZE_OFFSET 0x14
-#define AC_ACCESS_MEMORY_AAMSIZE_LENGTH 3
-#define AC_ACCESS_MEMORY_AAMSIZE 0x700000
+#define AC_ACCESS_MEMORY_AAMSIZE_OFFSET 0x14ULL
+#define AC_ACCESS_MEMORY_AAMSIZE_LENGTH 3ULL
+#define AC_ACCESS_MEMORY_AAMSIZE 0x700000ULL
/*
* 8bit: Access the lowest 8 bits of the memory location.
*/
@@ -2911,12 +3021,12 @@
* Supporting this variant is optional, but highly recommended for
* performance reasons.
*/
-#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET 0x13
-#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_LENGTH 1
-#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT 0x80000
-#define AC_ACCESS_MEMORY_WRITE_OFFSET 0x10
-#define AC_ACCESS_MEMORY_WRITE_LENGTH 1
-#define AC_ACCESS_MEMORY_WRITE 0x10000
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_OFFSET 0x13ULL
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT_LENGTH 1ULL
+#define AC_ACCESS_MEMORY_AAMPOSTINCREMENT 0x80000ULL
+#define AC_ACCESS_MEMORY_WRITE_OFFSET 0x10ULL
+#define AC_ACCESS_MEMORY_WRITE_LENGTH 1ULL
+#define AC_ACCESS_MEMORY_WRITE 0x10000ULL
/*
* arg0: Copy data from the memory location specified in {\tt arg1} into
* the low bits of {\tt arg0}. The value of the remaining bits of
@@ -2931,9 +3041,9 @@
/*
* These bits are reserved for target-specific uses.
*/
-#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET 0xe
-#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2
-#define AC_ACCESS_MEMORY_TARGET_SPECIFIC 0xc000
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_OFFSET 0xeULL
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC_LENGTH 2ULL
+#define AC_ACCESS_MEMORY_TARGET_SPECIFIC 0xc000ULL
#define VIRT_PRIV virtual
/*
* Contains the virtualization mode the hart was operating in when Debug
@@ -2942,9 +3052,9 @@
* A user can write this value to change the hart's virtualization mode
* when exiting Debug Mode.
*/
-#define VIRT_PRIV_V_OFFSET 2
-#define VIRT_PRIV_V_LENGTH 1
-#define VIRT_PRIV_V 4
+#define VIRT_PRIV_V_OFFSET 2ULL
+#define VIRT_PRIV_V_LENGTH 1ULL
+#define VIRT_PRIV_V 4ULL
/*
* Contains the privilege mode the hart was operating in when Debug
* Mode was entered. The encoding is described in Table
@@ -2952,110 +3062,95 @@
* the Privileged Spec. A user can write this
* value to change the hart's privilege mode when exiting Debug Mode.
*/
-#define VIRT_PRIV_PRV_OFFSET 0
-#define VIRT_PRIV_PRV_LENGTH 2
-#define VIRT_PRIV_PRV 3
-#define DMI_SERCS 0x34
-/*
- * Number of supported serial ports.
- */
-#define DMI_SERCS_SERIALCOUNT_OFFSET 0x1c
-#define DMI_SERCS_SERIALCOUNT_LENGTH 4
-#define DMI_SERCS_SERIALCOUNT 0xf0000000U
-/*
- * Select which serial port is accessed by \RdmiSerrx and \RdmiSertx.
- */
-#define DMI_SERCS_SERIAL_OFFSET 0x18
-#define DMI_SERCS_SERIAL_LENGTH 3
-#define DMI_SERCS_SERIAL 0x7000000
-#define DMI_SERCS_ERROR7_OFFSET 0x17
-#define DMI_SERCS_ERROR7_LENGTH 1
-#define DMI_SERCS_ERROR7 0x800000
-#define DMI_SERCS_VALID7_OFFSET 0x16
-#define DMI_SERCS_VALID7_LENGTH 1
-#define DMI_SERCS_VALID7 0x400000
-#define DMI_SERCS_FULL7_OFFSET 0x15
-#define DMI_SERCS_FULL7_LENGTH 1
-#define DMI_SERCS_FULL7 0x200000
-#define DMI_SERCS_ERROR6_OFFSET 0x14
-#define DMI_SERCS_ERROR6_LENGTH 1
-#define DMI_SERCS_ERROR6 0x100000
-#define DMI_SERCS_VALID6_OFFSET 0x13
-#define DMI_SERCS_VALID6_LENGTH 1
-#define DMI_SERCS_VALID6 0x80000
-#define DMI_SERCS_FULL6_OFFSET 0x12
-#define DMI_SERCS_FULL6_LENGTH 1
-#define DMI_SERCS_FULL6 0x40000
-#define DMI_SERCS_ERROR5_OFFSET 0x11
-#define DMI_SERCS_ERROR5_LENGTH 1
-#define DMI_SERCS_ERROR5 0x20000
-#define DMI_SERCS_VALID5_OFFSET 0x10
-#define DMI_SERCS_VALID5_LENGTH 1
-#define DMI_SERCS_VALID5 0x10000
-#define DMI_SERCS_FULL5_OFFSET 0xf
-#define DMI_SERCS_FULL5_LENGTH 1
-#define DMI_SERCS_FULL5 0x8000
-#define DMI_SERCS_ERROR4_OFFSET 0xe
-#define DMI_SERCS_ERROR4_LENGTH 1
-#define DMI_SERCS_ERROR4 0x4000
-#define DMI_SERCS_VALID4_OFFSET 0xd
-#define DMI_SERCS_VALID4_LENGTH 1
-#define DMI_SERCS_VALID4 0x2000
-#define DMI_SERCS_FULL4_OFFSET 0xc
-#define DMI_SERCS_FULL4_LENGTH 1
-#define DMI_SERCS_FULL4 0x1000
-#define DMI_SERCS_ERROR3_OFFSET 0xb
-#define DMI_SERCS_ERROR3_LENGTH 1
-#define DMI_SERCS_ERROR3 0x800
-#define DMI_SERCS_VALID3_OFFSET 0xa
-#define DMI_SERCS_VALID3_LENGTH 1
-#define DMI_SERCS_VALID3 0x400
-#define DMI_SERCS_FULL3_OFFSET 9
-#define DMI_SERCS_FULL3_LENGTH 1
-#define DMI_SERCS_FULL3 0x200
-#define DMI_SERCS_ERROR2_OFFSET 8
-#define DMI_SERCS_ERROR2_LENGTH 1
-#define DMI_SERCS_ERROR2 0x100
-#define DMI_SERCS_VALID2_OFFSET 7
-#define DMI_SERCS_VALID2_LENGTH 1
-#define DMI_SERCS_VALID2 0x80
-#define DMI_SERCS_FULL2_OFFSET 6
-#define DMI_SERCS_FULL2_LENGTH 1
-#define DMI_SERCS_FULL2 0x40
-#define DMI_SERCS_ERROR1_OFFSET 5
-#define DMI_SERCS_ERROR1_LENGTH 1
-#define DMI_SERCS_ERROR1 0x20
-#define DMI_SERCS_VALID1_OFFSET 4
-#define DMI_SERCS_VALID1_LENGTH 1
-#define DMI_SERCS_VALID1 0x10
-#define DMI_SERCS_FULL1_OFFSET 3
-#define DMI_SERCS_FULL1_LENGTH 1
-#define DMI_SERCS_FULL1 8
-/*
- * 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 4
-/*
- * 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 2
-/*
- * 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 1
-#define DMI_SERTX 0x35
-#define DMI_SERTX_DATA_OFFSET 0
-#define DMI_SERTX_DATA_LENGTH 0x20
-#define DMI_SERTX_DATA 0xffffffffU
-#define DMI_SERRX 0x36
-#define DMI_SERRX_DATA_OFFSET 0
-#define DMI_SERRX_DATA_LENGTH 0x20
-#define DMI_SERRX_DATA 0xffffffffU
+#define VIRT_PRIV_PRV_OFFSET 0ULL
+#define VIRT_PRIV_PRV_LENGTH 2ULL
+#define VIRT_PRIV_PRV 3ULL
+enum riscv_debug_reg_ordinal {
+ DTM_IDCODE_ORDINAL,
+ DTM_DTMCS_ORDINAL,
+ DTM_DMI_ORDINAL,
+ DTM_BYPASS_ORDINAL,
+ CSR_DCSR_ORDINAL,
+ CSR_DPC_ORDINAL,
+ CSR_DSCRATCH0_ORDINAL,
+ CSR_DSCRATCH1_ORDINAL,
+ CSR_TSELECT_ORDINAL,
+ CSR_TDATA1_ORDINAL,
+ CSR_TDATA2_ORDINAL,
+ CSR_TDATA3_ORDINAL,
+ CSR_TINFO_ORDINAL,
+ CSR_TCONTROL_ORDINAL,
+ CSR_SCONTEXT_ORDINAL,
+ CSR_MCONTEXT_ORDINAL,
+ CSR_MCONTROL_ORDINAL,
+ CSR_MCONTROL6_ORDINAL,
+ CSR_ICOUNT_ORDINAL,
+ CSR_ITRIGGER_ORDINAL,
+ CSR_ETRIGGER_ORDINAL,
+ CSR_TMEXTTRIGGER_ORDINAL,
+ CSR_TEXTRA32_ORDINAL,
+ CSR_TEXTRA64_ORDINAL,
+ DM_DMSTATUS_ORDINAL,
+ DM_DMCONTROL_ORDINAL,
+ DM_HARTINFO_ORDINAL,
+ DM_HAWINDOWSEL_ORDINAL,
+ DM_HAWINDOW_ORDINAL,
+ DM_ABSTRACTCS_ORDINAL,
+ DM_COMMAND_ORDINAL,
+ DM_ABSTRACTAUTO_ORDINAL,
+ DM_CONFSTRPTR0_ORDINAL,
+ DM_CONFSTRPTR1_ORDINAL,
+ DM_CONFSTRPTR2_ORDINAL,
+ DM_CONFSTRPTR3_ORDINAL,
+ DM_NEXTDM_ORDINAL,
+ DM_DATA0_ORDINAL,
+ DM_PROGBUF0_ORDINAL,
+ DM_AUTHDATA_ORDINAL,
+ DM_DMCS2_ORDINAL,
+ DM_HALTSUM0_ORDINAL,
+ DM_HALTSUM1_ORDINAL,
+ DM_HALTSUM2_ORDINAL,
+ DM_HALTSUM3_ORDINAL,
+ DM_SBCS_ORDINAL,
+ DM_SBADDRESS0_ORDINAL,
+ DM_SBADDRESS1_ORDINAL,
+ DM_SBADDRESS2_ORDINAL,
+ DM_SBADDRESS3_ORDINAL,
+ DM_SBDATA0_ORDINAL,
+ DM_SBDATA1_ORDINAL,
+ DM_SBDATA2_ORDINAL,
+ DM_SBDATA3_ORDINAL,
+ SHORTNAME_ORDINAL,
+ AC_ACCESS_REGISTER_ORDINAL,
+ AC_QUICK_ACCESS_ORDINAL,
+ AC_ACCESS_MEMORY_ORDINAL,
+ VIRT_PRIV_ORDINAL
+};
+typedef struct {
+ struct {
+ unsigned int value; int is_set;
+ } DXLEN;
+ struct {
+ unsigned int value; int is_set;
+ } XLEN;
+ struct {
+ unsigned int value; int is_set;
+ } abits;
+} riscv_debug_reg_ctx_t;
+
+typedef struct {
+ const char *name;
+ unsigned int lsb; // inclusive
+ unsigned int msb; // inclusive
+ const char **values; // If non-NULL, array of human-readable string for each possible value
+} riscv_debug_reg_field_info_t;
+typedef struct riscv_debug_reg_field_list_t {
+ riscv_debug_reg_field_info_t field;
+ struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t context);
+} riscv_debug_reg_field_list_t;
+typedef struct {
+ const char *name;
+ struct riscv_debug_reg_field_list_t (* const get_fields_head)(riscv_debug_reg_ctx_t context);
+} riscv_debug_reg_info_t;
+riscv_debug_reg_info_t get_riscv_debug_reg_info(enum riscv_debug_reg_ordinal reg_ordinal);
+#endif
diff --git a/src/target/riscv/debug_reg_printer.c b/src/target/riscv/debug_reg_printer.c
new file mode 100644
index 0000000..fc7dabd
--- /dev/null
+++ b/src/target/riscv/debug_reg_printer.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+
+#include "debug_reg_printer.h"
+
+static unsigned int get_len_or_sprintf(char *buf, unsigned int curr, const char *format, ...)
+{
+ assert(format);
+ va_list args;
+ int length;
+
+ va_start(args, format);
+ if (buf)
+ length = vsprintf(buf + curr, format, args);
+ else
+ length = vsnprintf(NULL, 0, format, args);
+ va_end(args);
+ assert(length >= 0);
+ return (unsigned int)length;
+}
+
+static unsigned int print_number(char *buf, unsigned int offset, uint64_t value)
+{
+ const char * const format = value > 9 ? "0x%" PRIx64 : "%" PRIx64;
+
+ return get_len_or_sprintf(buf, offset, format, value);
+}
+
+static unsigned int riscv_debug_reg_field_value_to_s(char *buf, unsigned int offset,
+ const char * const *field_value_names, uint64_t field_value)
+{
+ const char * const field_value_name = field_value_names ?
+ field_value_names[field_value] :
+ NULL;
+
+ if (!field_value_name)
+ return print_number(buf, offset, field_value);
+ return get_len_or_sprintf(buf, offset, "%s", field_value_name);
+}
+
+static unsigned int riscv_debug_reg_field_to_s(char *buf, unsigned int offset,
+ riscv_debug_reg_field_info_t field, riscv_debug_reg_ctx_t context,
+ uint64_t field_value)
+{
+ const unsigned int name_len = get_len_or_sprintf(buf, offset, "%s=", field.name);
+
+ return name_len + riscv_debug_reg_field_value_to_s(buf, offset + name_len,
+ field.values, field_value);
+}
+
+static uint64_t riscv_debug_reg_field_value(riscv_debug_reg_field_info_t field, uint64_t value)
+{
+ assert(field.msb < 64);
+ assert(field.msb >= field.lsb);
+ const uint64_t trailing_ones_mask = (uint64_t)(-1) >> (63 - field.msb);
+ return (value & trailing_ones_mask) >> field.lsb;
+}
+
+static unsigned int riscv_debug_reg_fields_to_s(char *buf, unsigned int offset,
+ struct riscv_debug_reg_field_list_t (*get_next)(riscv_debug_reg_ctx_t contex),
+ riscv_debug_reg_ctx_t context, uint64_t value,
+ enum riscv_debug_reg_show show)
+{
+ unsigned int curr = offset;
+ curr += get_len_or_sprintf(buf, curr, " {");
+ char *separator = "";
+ for (struct riscv_debug_reg_field_list_t list; get_next; get_next = list.get_next) {
+ list = get_next(context);
+
+ uint64_t field_value = riscv_debug_reg_field_value(list.field, value);
+
+ if (show == RISCV_DEBUG_REG_SHOW_ALL ||
+ (show == RISCV_DEBUG_REG_HIDE_UNNAMED_0 &&
+ (field_value != 0 ||
+ (list.field.values && list.field.values[0]))) ||
+ (show == RISCV_DEBUG_REG_HIDE_ALL_0 && field_value != 0)) {
+ curr += get_len_or_sprintf(buf, curr, separator);
+ curr += riscv_debug_reg_field_to_s(buf, curr, list.field, context,
+ field_value);
+ separator = " ";
+ }
+ }
+ curr += get_len_or_sprintf(buf, curr, "}");
+ return curr - offset;
+}
+
+unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal,
+ riscv_debug_reg_ctx_t context, uint64_t value,
+ enum riscv_debug_reg_show show)
+{
+ unsigned int length = 0;
+
+ riscv_debug_reg_info_t reg = get_riscv_debug_reg_info(reg_ordinal);
+
+ length += get_len_or_sprintf(buf, length, "%s=", reg.name);
+ length += print_number(buf, length, value);
+
+ if (reg.get_fields_head)
+ length += riscv_debug_reg_fields_to_s(buf, length,
+ reg.get_fields_head, context, value, show);
+
+ if (buf)
+ buf[length] = '\0';
+ return length;
+}
diff --git a/src/target/riscv/debug_reg_printer.h b/src/target/riscv/debug_reg_printer.h
new file mode 100644
index 0000000..98226b7
--- /dev/null
+++ b/src/target/riscv/debug_reg_printer.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "debug_defines.h"
+
+enum riscv_debug_reg_show {
+ RISCV_DEBUG_REG_SHOW_ALL,
+ RISCV_DEBUG_REG_HIDE_ALL_0,
+ RISCV_DEBUG_REG_HIDE_UNNAMED_0,
+};
+
+/**
+ * This function is used to fill a buffer with a decoded string representation
+ * of register's value.
+ * @param buf If non-NULL, the buffer to write the string into.
+ * Otherwise, the function does not perform any writes,
+ * it just calculates the number of characters used.
+ * @param reg_ordinal
+ * The ordinal of the register.
+ * @param context The structure, containing the information about the target,
+ * necessary to decode the register.
+ * @param value The value to be decoded.
+ *
+ * Returns the number of characters used by the string representation
+ * (excluding '\0').
+ *
+ * Example:
+ * const struct riscv_debug_reg_ctx_t context = {
+ * .abits = { .value = <abits value>, .is_set = true }
+ * };
+ * char buf[riscv_debug_reg_to_s(NULL, DTM_DMI_ORDINAL, context, <dmi value>) + 1]
+ * riscv_debug_reg_to_s(buf, DTM_DMI_ORDINAL, context, <dmi value>);
+ */
+unsigned int riscv_debug_reg_to_s(char *buf, enum riscv_debug_reg_ordinal reg_ordinal,
+ riscv_debug_reg_ctx_t context, uint64_t value,
+ enum riscv_debug_reg_show show);
diff --git a/src/target/riscv/encoding.h b/src/target/riscv/encoding.h
index c2da4e6..3ac537c 100644
--- a/src/target/riscv/encoding.h
+++ b/src/target/riscv/encoding.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: BSD-3-Clause */
-/* Copyright (c) 2022 RISC-V International */
+/* Copyright (c) 2023 RISC-V International */
/*
* This file is auto-generated by running 'make' in
- * https://github.com/riscv/riscv-opcodes (dcdf8d3)
+ * https://github.com/riscv/riscv-opcodes (ed68c21)
*/
#ifndef RISCV_CSR_ENCODING_H
@@ -156,14 +156,17 @@
#define MENVCFG_CBIE 0x00000030
#define MENVCFG_CBCFE 0x00000040
#define MENVCFG_CBZE 0x00000080
+#define MENVCFG_HADE 0x2000000000000000
#define MENVCFG_PBMTE 0x4000000000000000
#define MENVCFG_STCE 0x8000000000000000
+#define MENVCFGH_HADE 0x20000000
#define MENVCFGH_PBMTE 0x40000000
#define MENVCFGH_STCE 0x80000000
#define MSTATEEN0_CS 0x00000001
#define MSTATEEN0_FCSR 0x00000002
+#define MSTATEEN0_JVT 0x00000004
#define MSTATEEN0_HCONTEXT 0x0200000000000000
#define MSTATEEN0_HENVCFG 0x4000000000000000
#define MSTATEEN_HSTATEEN 0x8000000000000000
@@ -190,14 +193,17 @@
#define HENVCFG_CBIE 0x00000030
#define HENVCFG_CBCFE 0x00000040
#define HENVCFG_CBZE 0x00000080
+#define HENVCFG_HADE 0x2000000000000000
#define HENVCFG_PBMTE 0x4000000000000000
#define HENVCFG_STCE 0x8000000000000000
+#define HENVCFGH_HADE 0x20000000
#define HENVCFGH_PBMTE 0x40000000
#define HENVCFGH_STCE 0x80000000
#define HSTATEEN0_CS 0x00000001
#define HSTATEEN0_FCSR 0x00000002
+#define HSTATEEN0_JVT 0x00000004
#define HSTATEEN0_SCONTEXT 0x0200000000000000
#define HSTATEEN0_SENVCFG 0x4000000000000000
#define HSTATEEN_SSTATEEN 0x8000000000000000
@@ -213,6 +219,7 @@
#define SSTATEEN0_CS 0x00000001
#define SSTATEEN0_FCSR 0x00000002
+#define SSTATEEN0_JVT 0x00000004
#define MSECCFG_MML 0x00000001
#define MSECCFG_MMWP 0x00000002
@@ -220,6 +227,10 @@
#define MSECCFG_USEED 0x00000100
#define MSECCFG_SSEED 0x00000200
+/* jvt fields */
+#define JVT_MODE 0x3F
+#define JVT_BASE (~0x3F)
+
#define PRV_U 0
#define PRV_S 1
#define PRV_M 3
@@ -366,12 +377,8 @@
#define MASK_ADD8 0xfe00707f
#define MATCH_ADD_UW 0x800003b
#define MASK_ADD_UW 0xfe00707f
-#define MATCH_ADDD 0x7b
-#define MASK_ADDD 0xfe00707f
#define MATCH_ADDI 0x13
#define MASK_ADDI 0x707f
-#define MATCH_ADDID 0x5b
-#define MASK_ADDID 0x707f
#define MATCH_ADDIW 0x1b
#define MASK_ADDIW 0x707f
#define MATCH_ADDW 0x3b
@@ -474,10 +481,6 @@
#define MASK_BINV 0xfe00707f
#define MATCH_BINVI 0x68001013
#define MASK_BINVI 0xfc00707f
-#define MATCH_BITREV 0xe6000077
-#define MASK_BITREV 0xfe00707f
-#define MATCH_BITREVI 0xe8000077
-#define MASK_BITREVI 0xfc00707f
#define MATCH_BLT 0x4063
#define MASK_BLT 0x707f
#define MATCH_BLTU 0x6063
@@ -490,8 +493,6 @@
#define MASK_BMATXOR 0xfe00707f
#define MATCH_BNE 0x1063
#define MASK_BNE 0x707f
-#define MATCH_BPICK 0x3077
-#define MASK_BPICK 0x600707f
#define MATCH_BSET 0x28001033
#define MASK_BSET 0xfe00707f
#define MATCH_BSETI 0x28001013
@@ -542,38 +543,48 @@
#define MASK_C_JALR 0xf07f
#define MATCH_C_JR 0x8002
#define MASK_C_JR 0xf07f
+#define MATCH_C_LBU 0x8000
+#define MASK_C_LBU 0xfc03
#define MATCH_C_LD 0x6000
#define MASK_C_LD 0xe003
#define MATCH_C_LDSP 0x6002
#define MASK_C_LDSP 0xe003
+#define MATCH_C_LH 0x8440
+#define MASK_C_LH 0xfc43
+#define MATCH_C_LHU 0x8400
+#define MASK_C_LHU 0xfc43
#define MATCH_C_LI 0x4001
#define MASK_C_LI 0xe003
-#define MATCH_C_LQ 0x2000
-#define MASK_C_LQ 0xe003
-#define MATCH_C_LQSP 0x2002
-#define MASK_C_LQSP 0xe003
#define MATCH_C_LUI 0x6001
#define MASK_C_LUI 0xe003
#define MATCH_C_LW 0x4000
#define MASK_C_LW 0xe003
#define MATCH_C_LWSP 0x4002
#define MASK_C_LWSP 0xe003
+#define MATCH_C_MUL 0x9c41
+#define MASK_C_MUL 0xfc63
#define MATCH_C_MV 0x8002
#define MASK_C_MV 0xf003
#define MATCH_C_NOP 0x1
#define MASK_C_NOP 0xef83
+#define MATCH_C_NOT 0x9c75
+#define MASK_C_NOT 0xfc7f
#define MATCH_C_OR 0x8c41
#define MASK_C_OR 0xfc63
+#define MATCH_C_SB 0x8800
+#define MASK_C_SB 0xfc03
#define MATCH_C_SD 0xe000
#define MASK_C_SD 0xe003
#define MATCH_C_SDSP 0xe002
#define MASK_C_SDSP 0xe003
+#define MATCH_C_SEXT_B 0x9c65
+#define MASK_C_SEXT_B 0xfc7f
+#define MATCH_C_SEXT_H 0x9c6d
+#define MASK_C_SEXT_H 0xfc7f
+#define MATCH_C_SH 0x8c00
+#define MASK_C_SH 0xfc43
#define MATCH_C_SLLI 0x2
#define MASK_C_SLLI 0xe003
-#define MATCH_C_SQ 0xa000
-#define MASK_C_SQ 0xe003
-#define MATCH_C_SQSP 0xa002
-#define MASK_C_SQSP 0xe003
#define MATCH_C_SRAI 0x8401
#define MASK_C_SRAI 0xec03
#define MATCH_C_SRLI 0x8001
@@ -588,6 +599,12 @@
#define MASK_C_SWSP 0xe003
#define MATCH_C_XOR 0x8c21
#define MASK_C_XOR 0xfc63
+#define MATCH_C_ZEXT_B 0x9c61
+#define MASK_C_ZEXT_B 0xfc7f
+#define MATCH_C_ZEXT_H 0x9c69
+#define MASK_C_ZEXT_H 0xfc7f
+#define MATCH_C_ZEXT_W 0x9c71
+#define MASK_C_ZEXT_W 0xfc7f
#define MATCH_CBO_CLEAN 0x10200f
#define MASK_CBO_CLEAN 0xfff07fff
#define MATCH_CBO_FLUSH 0x20200f
@@ -602,12 +619,6 @@
#define MASK_CLMULH 0xfe00707f
#define MATCH_CLMULR 0xa002033
#define MASK_CLMULR 0xfe00707f
-#define MATCH_CLO16 0xaeb00077
-#define MASK_CLO16 0xfff0707f
-#define MATCH_CLO32 0xafb00077
-#define MASK_CLO32 0xfff0707f
-#define MATCH_CLO8 0xae300077
-#define MASK_CLO8 0xfff0707f
#define MATCH_CLRS16 0xae800077
#define MASK_CLRS16 0xfff0707f
#define MATCH_CLRS32 0xaf800077
@@ -624,6 +635,20 @@
#define MASK_CLZ8 0xfff0707f
#define MATCH_CLZW 0x6000101b
#define MASK_CLZW 0xfff0707f
+#define MATCH_CM_JALT 0xa002
+#define MASK_CM_JALT 0xfc03
+#define MATCH_CM_MVA01S 0xac62
+#define MASK_CM_MVA01S 0xfc63
+#define MATCH_CM_MVSA01 0xac22
+#define MASK_CM_MVSA01 0xfc63
+#define MATCH_CM_POP 0xba02
+#define MASK_CM_POP 0xff03
+#define MATCH_CM_POPRET 0xbe02
+#define MASK_CM_POPRET 0xff03
+#define MATCH_CM_POPRETZ 0xbc02
+#define MASK_CM_POPRETZ 0xff03
+#define MATCH_CM_PUSH 0xb802
+#define MASK_CM_PUSH 0xff03
#define MATCH_CMIX 0x6001033
#define MASK_CMIX 0x600707f
#define MATCH_CMOV 0x6005033
@@ -676,6 +701,10 @@
#define MASK_CTZ 0xfff0707f
#define MATCH_CTZW 0x6010101b
#define MASK_CTZW 0xfff0707f
+#define MATCH_CZERO_EQZ 0xe005033
+#define MASK_CZERO_EQZ 0xfe00707f
+#define MATCH_CZERO_NEZ 0xe007033
+#define MASK_CZERO_NEZ 0xfe00707f
#define MATCH_DIV 0x2004033
#define MASK_DIV 0xfe00707f
#define MATCH_DIVU 0x2005033
@@ -1238,14 +1267,10 @@
#define MASK_LBU 0x707f
#define MATCH_LD 0x3003
#define MASK_LD 0x707f
-#define MATCH_LDU 0x7003
-#define MASK_LDU 0x707f
#define MATCH_LH 0x1003
#define MASK_LH 0x707f
#define MATCH_LHU 0x5003
#define MASK_LHU 0x707f
-#define MATCH_LQ 0x300f
-#define MASK_LQ 0x707f
#define MATCH_LR_D 0x1000302f
#define MASK_LR_D 0xf9f0707f
#define MATCH_LR_W 0x1000202f
@@ -1262,14 +1287,10 @@
#define MASK_MAX 0xfe00707f
#define MATCH_MAXU 0xa007033
#define MASK_MAXU 0xfe00707f
-#define MATCH_MAXW 0xf2000077
-#define MASK_MAXW 0xfe00707f
#define MATCH_MIN 0xa004033
#define MASK_MIN 0xfe00707f
#define MATCH_MINU 0xa005033
#define MASK_MINU 0xfe00707f
-#define MATCH_MINW 0xf0000077
-#define MASK_MINW 0xfe00707f
#define MATCH_MRET 0x30200073
#define MASK_MRET 0xffffffff
#define MATCH_MSUBR32 0xc6001077
@@ -1312,8 +1333,6 @@
#define MASK_PBSADA 0xfe00707f
#define MATCH_PKBB16 0xe001077
#define MASK_PKBB16 0xfe00707f
-#define MATCH_PKBB32 0xe002077
-#define MASK_PKBB32 0xfe00707f
#define MATCH_PKBT16 0x1e001077
#define MASK_PKBT16 0xfe00707f
#define MATCH_PKBT32 0x1e002077
@@ -1324,8 +1343,6 @@
#define MASK_PKTB32 0xfe00707f
#define MATCH_PKTT16 0x2e001077
#define MASK_PKTT16 0xfe00707f
-#define MATCH_PKTT32 0x2e002077
-#define MASK_PKTT32 0xfe00707f
#define MATCH_PREFETCH_I 0x6013
#define MASK_PREFETCH_I 0x1f07fff
#define MATCH_PREFETCH_R 0x106013
@@ -1478,20 +1495,18 @@
#define MASK_SLL32 0xfe00707f
#define MATCH_SLL8 0x5c000077
#define MASK_SLL8 0xfe00707f
-#define MATCH_SLLD 0x107b
-#define MASK_SLLD 0xfe00707f
#define MATCH_SLLI 0x1013
-#define MASK_SLLI 0xf800707f
+#define MASK_SLLI 0xfc00707f
#define MATCH_SLLI16 0x74000077
#define MASK_SLLI16 0xff00707f
#define MATCH_SLLI32 0x74002077
#define MASK_SLLI32 0xfe00707f
#define MATCH_SLLI8 0x7c000077
#define MASK_SLLI8 0xff80707f
+#define MATCH_SLLI_RV32 0x1013
+#define MASK_SLLI_RV32 0xfe00707f
#define MATCH_SLLI_UW 0x800101b
#define MASK_SLLI_UW 0xfc00707f
-#define MATCH_SLLID 0x105b
-#define MASK_SLLID 0xfc00707f
#define MATCH_SLLIW 0x101b
#define MASK_SLLIW 0xfe00707f
#define MATCH_SLLW 0x103b
@@ -1604,8 +1619,6 @@
#define MASK_SMXDS 0xfe00707f
#define MATCH_SMXDS32 0x78002077
#define MASK_SMXDS32 0xfe00707f
-#define MATCH_SQ 0x4023
-#define MASK_SQ 0x707f
#define MATCH_SRA 0x40005033
#define MASK_SRA 0xfe00707f
#define MATCH_SRA16 0x50000077
@@ -1622,10 +1635,8 @@
#define MASK_SRA8_U 0xfe00707f
#define MATCH_SRA_U 0x24001077
#define MASK_SRA_U 0xfe00707f
-#define MATCH_SRAD 0x4000507b
-#define MASK_SRAD 0xfe00707f
#define MATCH_SRAI 0x40005013
-#define MASK_SRAI 0xf800707f
+#define MASK_SRAI 0xfc00707f
#define MATCH_SRAI16 0x70000077
#define MASK_SRAI16 0xff00707f
#define MATCH_SRAI16_U 0x71000077
@@ -1638,10 +1649,10 @@
#define MASK_SRAI8 0xff80707f
#define MATCH_SRAI8_U 0x78800077
#define MASK_SRAI8_U 0xff80707f
+#define MATCH_SRAI_RV32 0x40005013
+#define MASK_SRAI_RV32 0xfe00707f
#define MATCH_SRAI_U 0xd4001077
#define MASK_SRAI_U 0xfc00707f
-#define MATCH_SRAID 0x4000505b
-#define MASK_SRAID 0xfc00707f
#define MATCH_SRAIW 0x4000501b
#define MASK_SRAIW 0xfe00707f
#define MATCH_SRAIW_U 0x34001077
@@ -1664,10 +1675,8 @@
#define MASK_SRL8 0xfe00707f
#define MATCH_SRL8_U 0x6a000077
#define MASK_SRL8_U 0xfe00707f
-#define MATCH_SRLD 0x507b
-#define MASK_SRLD 0xfe00707f
#define MATCH_SRLI 0x5013
-#define MASK_SRLI 0xf800707f
+#define MASK_SRLI 0xfc00707f
#define MATCH_SRLI16 0x72000077
#define MASK_SRLI16 0xff00707f
#define MATCH_SRLI16_U 0x73000077
@@ -1680,8 +1689,8 @@
#define MASK_SRLI8 0xff80707f
#define MATCH_SRLI8_U 0x7a800077
#define MASK_SRLI8_U 0xff80707f
-#define MATCH_SRLID 0x505b
-#define MASK_SRLID 0xfc00707f
+#define MATCH_SRLI_RV32 0x5013
+#define MASK_SRLI_RV32 0xfe00707f
#define MATCH_SRLIW 0x501b
#define MASK_SRLIW 0xfe00707f
#define MATCH_SRLW 0x503b
@@ -1712,8 +1721,6 @@
#define MASK_SUB64 0xfe00707f
#define MATCH_SUB8 0x4a000077
#define MASK_SUB8 0xfe00707f
-#define MATCH_SUBD 0x4000007b
-#define MASK_SUBD 0xfe00707f
#define MATCH_SUBW 0x4000003b
#define MASK_SUBW 0xfe00707f
#define MATCH_SUNPKD810 0xac800077
@@ -1728,8 +1735,6 @@
#define MASK_SUNPKD832 0xfff0707f
#define MATCH_SW 0x2023
#define MASK_SW 0x707f
-#define MATCH_SWAP8 0xad800077
-#define MASK_SWAP8 0xfff0707f
#define MATCH_UCLIP16 0x85000077
#define MASK_UCLIP16 0xff00707f
#define MATCH_UCLIP32 0xf4000077
@@ -2750,10 +2755,6 @@
#define MASK_VZEXT_VF4 0xfc0ff07f
#define MATCH_VZEXT_VF8 0x48012057
#define MASK_VZEXT_VF8 0xfc0ff07f
-#define MATCH_WEXT 0xce000077
-#define MASK_WEXT 0xfe00707f
-#define MATCH_WEXTI 0xde000077
-#define MASK_WEXTI 0xfe00707f
#define MATCH_WFI 0x10500073
#define MASK_WFI 0xffffffff
#define MATCH_WRS_NTO 0xd00073
@@ -2793,6 +2794,7 @@
#define CSR_VXRM 0xa
#define CSR_VCSR 0xf
#define CSR_SEED 0x15
+#define CSR_JVT 0x17
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
#define CSR_INSTRET 0xc02
@@ -2845,6 +2847,9 @@
#define CSR_STVAL 0x143
#define CSR_SIP 0x144
#define CSR_STIMECMP 0x14d
+#define CSR_SISELECT 0x150
+#define CSR_SIREG 0x151
+#define CSR_STOPEI 0x15c
#define CSR_SATP 0x180
#define CSR_SCONTEXT 0x5a8
#define CSR_VSSTATUS 0x200
@@ -2856,6 +2861,9 @@
#define CSR_VSTVAL 0x243
#define CSR_VSIP 0x244
#define CSR_VSTIMECMP 0x24d
+#define CSR_VSISELECT 0x250
+#define CSR_VSIREG 0x251
+#define CSR_VSTOPEI 0x25c
#define CSR_VSATP 0x280
#define CSR_HSTATUS 0x600
#define CSR_HEDELEG 0x602
@@ -2864,6 +2872,8 @@
#define CSR_HTIMEDELTA 0x605
#define CSR_HCOUNTEREN 0x606
#define CSR_HGEIE 0x607
+#define CSR_HVIEN 0x608
+#define CSR_HVICTL 0x609
#define CSR_HENVCFG 0x60a
#define CSR_HSTATEEN0 0x60c
#define CSR_HSTATEEN1 0x60d
@@ -2872,11 +2882,15 @@
#define CSR_HTVAL 0x643
#define CSR_HIP 0x644
#define CSR_HVIP 0x645
+#define CSR_HVIPRIO1 0x646
+#define CSR_HVIPRIO2 0x647
#define CSR_HTINST 0x64a
#define CSR_HGATP 0x680
#define CSR_HCONTEXT 0x6a8
#define CSR_HGEIP 0xe12
+#define CSR_VSTOPI 0xeb0
#define CSR_SCOUNTOVF 0xda0
+#define CSR_STOPI 0xdb0
#define CSR_UTVT 0x7
#define CSR_UNXTI 0x45
#define CSR_UINTSTATUS 0x46
@@ -2899,6 +2913,8 @@
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MCOUNTEREN 0x306
+#define CSR_MVIEN 0x308
+#define CSR_MVIP 0x309
#define CSR_MENVCFG 0x30a
#define CSR_MSTATEEN0 0x30c
#define CSR_MSTATEEN1 0x30d
@@ -2912,6 +2928,9 @@
#define CSR_MIP 0x344
#define CSR_MTINST 0x34a
#define CSR_MTVAL2 0x34b
+#define CSR_MISELECT 0x350
+#define CSR_MIREG 0x351
+#define CSR_MTOPEI 0x35c
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPCFG1 0x3a1
#define CSR_PMPCFG2 0x3a2
@@ -3070,10 +3089,20 @@
#define CSR_MIMPID 0xf13
#define CSR_MHARTID 0xf14
#define CSR_MCONFIGPTR 0xf15
+#define CSR_MTOPI 0xfb0
+#define CSR_SIEH 0x114
+#define CSR_SIPH 0x154
#define CSR_STIMECMPH 0x15d
+#define CSR_VSIEH 0x214
+#define CSR_VSIPH 0x254
#define CSR_VSTIMECMPH 0x25d
#define CSR_HTIMEDELTAH 0x615
+#define CSR_HIDELEGH 0x613
+#define CSR_HVIENH 0x618
#define CSR_HENVCFGH 0x61a
+#define CSR_HVIPH 0x655
+#define CSR_HVIPRIO1H 0x656
+#define CSR_HVIPRIO2H 0x657
#define CSR_HSTATEEN0H 0x61c
#define CSR_HSTATEEN1H 0x61d
#define CSR_HSTATEEN2H 0x61e
@@ -3111,11 +3140,16 @@
#define CSR_HPMCOUNTER30H 0xc9e
#define CSR_HPMCOUNTER31H 0xc9f
#define CSR_MSTATUSH 0x310
+#define CSR_MIDELEGH 0x313
+#define CSR_MIEH 0x314
+#define CSR_MVIENH 0x318
+#define CSR_MVIPH 0x319
#define CSR_MENVCFGH 0x31a
#define CSR_MSTATEEN0H 0x31c
#define CSR_MSTATEEN1H 0x31d
#define CSR_MSTATEEN2H 0x31e
#define CSR_MSTATEEN3H 0x31f
+#define CSR_MIPH 0x354
#define CSR_MHPMEVENT3H 0x723
#define CSR_MHPMEVENT4H 0x724
#define CSR_MHPMEVENT5H 0x725
@@ -3221,7 +3255,7 @@
#define INSN_FIELD_IMM12LO 0xf80
#define INSN_FIELD_BIMM12LO 0xf80
#define INSN_FIELD_ZIMM 0xf8000
-#define INSN_FIELD_SHAMT 0x7f00000
+#define INSN_FIELD_SHAMTQ 0x7f00000
#define INSN_FIELD_SHAMTW 0x1f00000
#define INSN_FIELD_SHAMTW4 0xf00000
#define INSN_FIELD_SHAMTD 0x3f00000
@@ -3276,6 +3310,11 @@
#define INSN_FIELD_C_UIMM9SPHI 0x1000
#define INSN_FIELD_C_UIMM10SP_S 0x1f80
#define INSN_FIELD_C_UIMM9SP_S 0x1f80
+#define INSN_FIELD_C_UIMM2 0x60
+#define INSN_FIELD_C_UIMM1 0x20
+#define INSN_FIELD_C_RLIST 0xf0
+#define INSN_FIELD_C_SPIMM 0xc
+#define INSN_FIELD_C_INDEX 0x3fc
#define INSN_FIELD_RS1_P 0x380
#define INSN_FIELD_RS2_P 0x1c
#define INSN_FIELD_RD_P 0x1c
@@ -3288,6 +3327,8 @@
#define INSN_FIELD_C_RS2_N0 0x7c
#define INSN_FIELD_C_RS1_N0 0xf80
#define INSN_FIELD_C_RS2 0x7c
+#define INSN_FIELD_C_SREG1 0x380
+#define INSN_FIELD_C_SREG2 0x1c
#endif
#ifdef DECLARE_INSN
DECLARE_INSN(add, MATCH_ADD, MASK_ADD)
@@ -3296,9 +3337,7 @@ DECLARE_INSN(add32, MATCH_ADD32, MASK_ADD32)
DECLARE_INSN(add64, MATCH_ADD64, MASK_ADD64)
DECLARE_INSN(add8, MATCH_ADD8, MASK_ADD8)
DECLARE_INSN(add_uw, MATCH_ADD_UW, MASK_ADD_UW)
-DECLARE_INSN(addd, MATCH_ADDD, MASK_ADDD)
DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI)
-DECLARE_INSN(addid, MATCH_ADDID, MASK_ADDID)
DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW)
DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW)
DECLARE_INSN(aes32dsi, MATCH_AES32DSI, MASK_AES32DSI)
@@ -3350,15 +3389,12 @@ DECLARE_INSN(bge, MATCH_BGE, MASK_BGE)
DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU)
DECLARE_INSN(binv, MATCH_BINV, MASK_BINV)
DECLARE_INSN(binvi, MATCH_BINVI, MASK_BINVI)
-DECLARE_INSN(bitrev, MATCH_BITREV, MASK_BITREV)
-DECLARE_INSN(bitrevi, MATCH_BITREVI, MASK_BITREVI)
DECLARE_INSN(blt, MATCH_BLT, MASK_BLT)
DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU)
DECLARE_INSN(bmatflip, MATCH_BMATFLIP, MASK_BMATFLIP)
DECLARE_INSN(bmator, MATCH_BMATOR, MASK_BMATOR)
DECLARE_INSN(bmatxor, MATCH_BMATXOR, MASK_BMATXOR)
DECLARE_INSN(bne, MATCH_BNE, MASK_BNE)
-DECLARE_INSN(bpick, MATCH_BPICK, MASK_BPICK)
DECLARE_INSN(bset, MATCH_BSET, MASK_BSET)
DECLARE_INSN(bseti, MATCH_BSETI, MASK_BSETI)
DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD)
@@ -3384,22 +3420,27 @@ DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J)
DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL)
DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR)
DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR)
+DECLARE_INSN(c_lbu, MATCH_C_LBU, MASK_C_LBU)
DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD)
DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP)
+DECLARE_INSN(c_lh, MATCH_C_LH, MASK_C_LH)
+DECLARE_INSN(c_lhu, MATCH_C_LHU, MASK_C_LHU)
DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI)
-DECLARE_INSN(c_lq, MATCH_C_LQ, MASK_C_LQ)
-DECLARE_INSN(c_lqsp, MATCH_C_LQSP, MASK_C_LQSP)
DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI)
DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW)
DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP)
+DECLARE_INSN(c_mul, MATCH_C_MUL, MASK_C_MUL)
DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV)
DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP)
+DECLARE_INSN(c_not, MATCH_C_NOT, MASK_C_NOT)
DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR)
+DECLARE_INSN(c_sb, MATCH_C_SB, MASK_C_SB)
DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD)
DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP)
+DECLARE_INSN(c_sext_b, MATCH_C_SEXT_B, MASK_C_SEXT_B)
+DECLARE_INSN(c_sext_h, MATCH_C_SEXT_H, MASK_C_SEXT_H)
+DECLARE_INSN(c_sh, MATCH_C_SH, MASK_C_SH)
DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI)
-DECLARE_INSN(c_sq, MATCH_C_SQ, MASK_C_SQ)
-DECLARE_INSN(c_sqsp, MATCH_C_SQSP, MASK_C_SQSP)
DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI)
DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI)
DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB)
@@ -3407,6 +3448,9 @@ DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW)
DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW)
DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP)
DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR)
+DECLARE_INSN(c_zext_b, MATCH_C_ZEXT_B, MASK_C_ZEXT_B)
+DECLARE_INSN(c_zext_h, MATCH_C_ZEXT_H, MASK_C_ZEXT_H)
+DECLARE_INSN(c_zext_w, MATCH_C_ZEXT_W, MASK_C_ZEXT_W)
DECLARE_INSN(cbo_clean, MATCH_CBO_CLEAN, MASK_CBO_CLEAN)
DECLARE_INSN(cbo_flush, MATCH_CBO_FLUSH, MASK_CBO_FLUSH)
DECLARE_INSN(cbo_inval, MATCH_CBO_INVAL, MASK_CBO_INVAL)
@@ -3414,9 +3458,6 @@ DECLARE_INSN(cbo_zero, MATCH_CBO_ZERO, MASK_CBO_ZERO)
DECLARE_INSN(clmul, MATCH_CLMUL, MASK_CLMUL)
DECLARE_INSN(clmulh, MATCH_CLMULH, MASK_CLMULH)
DECLARE_INSN(clmulr, MATCH_CLMULR, MASK_CLMULR)
-DECLARE_INSN(clo16, MATCH_CLO16, MASK_CLO16)
-DECLARE_INSN(clo32, MATCH_CLO32, MASK_CLO32)
-DECLARE_INSN(clo8, MATCH_CLO8, MASK_CLO8)
DECLARE_INSN(clrs16, MATCH_CLRS16, MASK_CLRS16)
DECLARE_INSN(clrs32, MATCH_CLRS32, MASK_CLRS32)
DECLARE_INSN(clrs8, MATCH_CLRS8, MASK_CLRS8)
@@ -3425,6 +3466,13 @@ DECLARE_INSN(clz16, MATCH_CLZ16, MASK_CLZ16)
DECLARE_INSN(clz32, MATCH_CLZ32, MASK_CLZ32)
DECLARE_INSN(clz8, MATCH_CLZ8, MASK_CLZ8)
DECLARE_INSN(clzw, MATCH_CLZW, MASK_CLZW)
+DECLARE_INSN(cm_jalt, MATCH_CM_JALT, MASK_CM_JALT)
+DECLARE_INSN(cm_mva01s, MATCH_CM_MVA01S, MASK_CM_MVA01S)
+DECLARE_INSN(cm_mvsa01, MATCH_CM_MVSA01, MASK_CM_MVSA01)
+DECLARE_INSN(cm_pop, MATCH_CM_POP, MASK_CM_POP)
+DECLARE_INSN(cm_popret, MATCH_CM_POPRET, MASK_CM_POPRET)
+DECLARE_INSN(cm_popretz, MATCH_CM_POPRETZ, MASK_CM_POPRETZ)
+DECLARE_INSN(cm_push, MATCH_CM_PUSH, MASK_CM_PUSH)
DECLARE_INSN(cmix, MATCH_CMIX, MASK_CMIX)
DECLARE_INSN(cmov, MATCH_CMOV, MASK_CMOV)
DECLARE_INSN(cmpeq16, MATCH_CMPEQ16, MASK_CMPEQ16)
@@ -3451,6 +3499,8 @@ DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW)
DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI)
DECLARE_INSN(ctz, MATCH_CTZ, MASK_CTZ)
DECLARE_INSN(ctzw, MATCH_CTZW, MASK_CTZW)
+DECLARE_INSN(czero_eqz, MATCH_CZERO_EQZ, MASK_CZERO_EQZ)
+DECLARE_INSN(czero_nez, MATCH_CZERO_NEZ, MASK_CZERO_NEZ)
DECLARE_INSN(div, MATCH_DIV, MASK_DIV)
DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU)
DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW)
@@ -3732,10 +3782,8 @@ DECLARE_INSN(kwmmul_u, MATCH_KWMMUL_U, MASK_KWMMUL_U)
DECLARE_INSN(lb, MATCH_LB, MASK_LB)
DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU)
DECLARE_INSN(ld, MATCH_LD, MASK_LD)
-DECLARE_INSN(ldu, MATCH_LDU, MASK_LDU)
DECLARE_INSN(lh, MATCH_LH, MASK_LH)
DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU)
-DECLARE_INSN(lq, MATCH_LQ, MASK_LQ)
DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D)
DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W)
DECLARE_INSN(lui, MATCH_LUI, MASK_LUI)
@@ -3744,10 +3792,8 @@ DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU)
DECLARE_INSN(maddr32, MATCH_MADDR32, MASK_MADDR32)
DECLARE_INSN(max, MATCH_MAX, MASK_MAX)
DECLARE_INSN(maxu, MATCH_MAXU, MASK_MAXU)
-DECLARE_INSN(maxw, MATCH_MAXW, MASK_MAXW)
DECLARE_INSN(min, MATCH_MIN, MASK_MIN)
DECLARE_INSN(minu, MATCH_MINU, MASK_MINU)
-DECLARE_INSN(minw, MATCH_MINW, MASK_MINW)
DECLARE_INSN(mret, MATCH_MRET, MASK_MRET)
DECLARE_INSN(msubr32, MATCH_MSUBR32, MASK_MSUBR32)
DECLARE_INSN(mul, MATCH_MUL, MASK_MUL)
@@ -3769,13 +3815,11 @@ DECLARE_INSN(pause, MATCH_PAUSE, MASK_PAUSE)
DECLARE_INSN(pbsad, MATCH_PBSAD, MASK_PBSAD)
DECLARE_INSN(pbsada, MATCH_PBSADA, MASK_PBSADA)
DECLARE_INSN(pkbb16, MATCH_PKBB16, MASK_PKBB16)
-DECLARE_INSN(pkbb32, MATCH_PKBB32, MASK_PKBB32)
DECLARE_INSN(pkbt16, MATCH_PKBT16, MASK_PKBT16)
DECLARE_INSN(pkbt32, MATCH_PKBT32, MASK_PKBT32)
DECLARE_INSN(pktb16, MATCH_PKTB16, MASK_PKTB16)
DECLARE_INSN(pktb32, MATCH_PKTB32, MASK_PKTB32)
DECLARE_INSN(pktt16, MATCH_PKTT16, MASK_PKTT16)
-DECLARE_INSN(pktt32, MATCH_PKTT32, MASK_PKTT32)
DECLARE_INSN(prefetch_i, MATCH_PREFETCH_I, MASK_PREFETCH_I)
DECLARE_INSN(prefetch_r, MATCH_PREFETCH_R, MASK_PREFETCH_R)
DECLARE_INSN(prefetch_w, MATCH_PREFETCH_W, MASK_PREFETCH_W)
@@ -3852,13 +3896,12 @@ DECLARE_INSN(sll, MATCH_SLL, MASK_SLL)
DECLARE_INSN(sll16, MATCH_SLL16, MASK_SLL16)
DECLARE_INSN(sll32, MATCH_SLL32, MASK_SLL32)
DECLARE_INSN(sll8, MATCH_SLL8, MASK_SLL8)
-DECLARE_INSN(slld, MATCH_SLLD, MASK_SLLD)
DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI)
DECLARE_INSN(slli16, MATCH_SLLI16, MASK_SLLI16)
DECLARE_INSN(slli32, MATCH_SLLI32, MASK_SLLI32)
DECLARE_INSN(slli8, MATCH_SLLI8, MASK_SLLI8)
+DECLARE_INSN(slli_rv32, MATCH_SLLI_RV32, MASK_SLLI_RV32)
DECLARE_INSN(slli_uw, MATCH_SLLI_UW, MASK_SLLI_UW)
-DECLARE_INSN(sllid, MATCH_SLLID, MASK_SLLID)
DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW)
DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW)
DECLARE_INSN(slo, MATCH_SLO, MASK_SLO)
@@ -3915,7 +3958,6 @@ DECLARE_INSN(smulx16, MATCH_SMULX16, MASK_SMULX16)
DECLARE_INSN(smulx8, MATCH_SMULX8, MASK_SMULX8)
DECLARE_INSN(smxds, MATCH_SMXDS, MASK_SMXDS)
DECLARE_INSN(smxds32, MATCH_SMXDS32, MASK_SMXDS32)
-DECLARE_INSN(sq, MATCH_SQ, MASK_SQ)
DECLARE_INSN(sra, MATCH_SRA, MASK_SRA)
DECLARE_INSN(sra16, MATCH_SRA16, MASK_SRA16)
DECLARE_INSN(sra16_u, MATCH_SRA16_U, MASK_SRA16_U)
@@ -3924,7 +3966,6 @@ DECLARE_INSN(sra32_u, MATCH_SRA32_U, MASK_SRA32_U)
DECLARE_INSN(sra8, MATCH_SRA8, MASK_SRA8)
DECLARE_INSN(sra8_u, MATCH_SRA8_U, MASK_SRA8_U)
DECLARE_INSN(sra_u, MATCH_SRA_U, MASK_SRA_U)
-DECLARE_INSN(srad, MATCH_SRAD, MASK_SRAD)
DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI)
DECLARE_INSN(srai16, MATCH_SRAI16, MASK_SRAI16)
DECLARE_INSN(srai16_u, MATCH_SRAI16_U, MASK_SRAI16_U)
@@ -3932,8 +3973,8 @@ DECLARE_INSN(srai32, MATCH_SRAI32, MASK_SRAI32)
DECLARE_INSN(srai32_u, MATCH_SRAI32_U, MASK_SRAI32_U)
DECLARE_INSN(srai8, MATCH_SRAI8, MASK_SRAI8)
DECLARE_INSN(srai8_u, MATCH_SRAI8_U, MASK_SRAI8_U)
+DECLARE_INSN(srai_rv32, MATCH_SRAI_RV32, MASK_SRAI_RV32)
DECLARE_INSN(srai_u, MATCH_SRAI_U, MASK_SRAI_U)
-DECLARE_INSN(sraid, MATCH_SRAID, MASK_SRAID)
DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW)
DECLARE_INSN(sraiw_u, MATCH_SRAIW_U, MASK_SRAIW_U)
DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW)
@@ -3945,7 +3986,6 @@ DECLARE_INSN(srl32, MATCH_SRL32, MASK_SRL32)
DECLARE_INSN(srl32_u, MATCH_SRL32_U, MASK_SRL32_U)
DECLARE_INSN(srl8, MATCH_SRL8, MASK_SRL8)
DECLARE_INSN(srl8_u, MATCH_SRL8_U, MASK_SRL8_U)
-DECLARE_INSN(srld, MATCH_SRLD, MASK_SRLD)
DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI)
DECLARE_INSN(srli16, MATCH_SRLI16, MASK_SRLI16)
DECLARE_INSN(srli16_u, MATCH_SRLI16_U, MASK_SRLI16_U)
@@ -3953,7 +3993,7 @@ DECLARE_INSN(srli32, MATCH_SRLI32, MASK_SRLI32)
DECLARE_INSN(srli32_u, MATCH_SRLI32_U, MASK_SRLI32_U)
DECLARE_INSN(srli8, MATCH_SRLI8, MASK_SRLI8)
DECLARE_INSN(srli8_u, MATCH_SRLI8_U, MASK_SRLI8_U)
-DECLARE_INSN(srlid, MATCH_SRLID, MASK_SRLID)
+DECLARE_INSN(srli_rv32, MATCH_SRLI_RV32, MASK_SRLI_RV32)
DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW)
DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW)
DECLARE_INSN(sro, MATCH_SRO, MASK_SRO)
@@ -3969,7 +4009,6 @@ DECLARE_INSN(sub16, MATCH_SUB16, MASK_SUB16)
DECLARE_INSN(sub32, MATCH_SUB32, MASK_SUB32)
DECLARE_INSN(sub64, MATCH_SUB64, MASK_SUB64)
DECLARE_INSN(sub8, MATCH_SUB8, MASK_SUB8)
-DECLARE_INSN(subd, MATCH_SUBD, MASK_SUBD)
DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW)
DECLARE_INSN(sunpkd810, MATCH_SUNPKD810, MASK_SUNPKD810)
DECLARE_INSN(sunpkd820, MATCH_SUNPKD820, MASK_SUNPKD820)
@@ -3977,7 +4016,6 @@ DECLARE_INSN(sunpkd830, MATCH_SUNPKD830, MASK_SUNPKD830)
DECLARE_INSN(sunpkd831, MATCH_SUNPKD831, MASK_SUNPKD831)
DECLARE_INSN(sunpkd832, MATCH_SUNPKD832, MASK_SUNPKD832)
DECLARE_INSN(sw, MATCH_SW, MASK_SW)
-DECLARE_INSN(swap8, MATCH_SWAP8, MASK_SWAP8)
DECLARE_INSN(uclip16, MATCH_UCLIP16, MASK_UCLIP16)
DECLARE_INSN(uclip32, MATCH_UCLIP32, MASK_UCLIP32)
DECLARE_INSN(uclip8, MATCH_UCLIP8, MASK_UCLIP8)
@@ -4488,8 +4526,6 @@ DECLARE_INSN(vxor_vx, MATCH_VXOR_VX, MASK_VXOR_VX)
DECLARE_INSN(vzext_vf2, MATCH_VZEXT_VF2, MASK_VZEXT_VF2)
DECLARE_INSN(vzext_vf4, MATCH_VZEXT_VF4, MASK_VZEXT_VF4)
DECLARE_INSN(vzext_vf8, MATCH_VZEXT_VF8, MASK_VZEXT_VF8)
-DECLARE_INSN(wext, MATCH_WEXT, MASK_WEXT)
-DECLARE_INSN(wexti, MATCH_WEXTI, MASK_WEXTI)
DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI)
DECLARE_INSN(wrs_nto, MATCH_WRS_NTO, MASK_WRS_NTO)
DECLARE_INSN(wrs_sto, MATCH_WRS_STO, MASK_WRS_STO)
@@ -4515,6 +4551,7 @@ DECLARE_CSR(vxsat, CSR_VXSAT)
DECLARE_CSR(vxrm, CSR_VXRM)
DECLARE_CSR(vcsr, CSR_VCSR)
DECLARE_CSR(seed, CSR_SEED)
+DECLARE_CSR(jvt, CSR_JVT)
DECLARE_CSR(cycle, CSR_CYCLE)
DECLARE_CSR(time, CSR_TIME)
DECLARE_CSR(instret, CSR_INSTRET)
@@ -4567,6 +4604,9 @@ DECLARE_CSR(scause, CSR_SCAUSE)
DECLARE_CSR(stval, CSR_STVAL)
DECLARE_CSR(sip, CSR_SIP)
DECLARE_CSR(stimecmp, CSR_STIMECMP)
+DECLARE_CSR(siselect, CSR_SISELECT)
+DECLARE_CSR(sireg, CSR_SIREG)
+DECLARE_CSR(stopei, CSR_STOPEI)
DECLARE_CSR(satp, CSR_SATP)
DECLARE_CSR(scontext, CSR_SCONTEXT)
DECLARE_CSR(vsstatus, CSR_VSSTATUS)
@@ -4578,6 +4618,9 @@ DECLARE_CSR(vscause, CSR_VSCAUSE)
DECLARE_CSR(vstval, CSR_VSTVAL)
DECLARE_CSR(vsip, CSR_VSIP)
DECLARE_CSR(vstimecmp, CSR_VSTIMECMP)
+DECLARE_CSR(vsiselect, CSR_VSISELECT)
+DECLARE_CSR(vsireg, CSR_VSIREG)
+DECLARE_CSR(vstopei, CSR_VSTOPEI)
DECLARE_CSR(vsatp, CSR_VSATP)
DECLARE_CSR(hstatus, CSR_HSTATUS)
DECLARE_CSR(hedeleg, CSR_HEDELEG)
@@ -4586,6 +4629,8 @@ DECLARE_CSR(hie, CSR_HIE)
DECLARE_CSR(htimedelta, CSR_HTIMEDELTA)
DECLARE_CSR(hcounteren, CSR_HCOUNTEREN)
DECLARE_CSR(hgeie, CSR_HGEIE)
+DECLARE_CSR(hvien, CSR_HVIEN)
+DECLARE_CSR(hvictl, CSR_HVICTL)
DECLARE_CSR(henvcfg, CSR_HENVCFG)
DECLARE_CSR(hstateen0, CSR_HSTATEEN0)
DECLARE_CSR(hstateen1, CSR_HSTATEEN1)
@@ -4594,11 +4639,15 @@ DECLARE_CSR(hstateen3, CSR_HSTATEEN3)
DECLARE_CSR(htval, CSR_HTVAL)
DECLARE_CSR(hip, CSR_HIP)
DECLARE_CSR(hvip, CSR_HVIP)
+DECLARE_CSR(hviprio1, CSR_HVIPRIO1)
+DECLARE_CSR(hviprio2, CSR_HVIPRIO2)
DECLARE_CSR(htinst, CSR_HTINST)
DECLARE_CSR(hgatp, CSR_HGATP)
DECLARE_CSR(hcontext, CSR_HCONTEXT)
DECLARE_CSR(hgeip, CSR_HGEIP)
+DECLARE_CSR(vstopi, CSR_VSTOPI)
DECLARE_CSR(scountovf, CSR_SCOUNTOVF)
+DECLARE_CSR(stopi, CSR_STOPI)
DECLARE_CSR(utvt, CSR_UTVT)
DECLARE_CSR(unxti, CSR_UNXTI)
DECLARE_CSR(uintstatus, CSR_UINTSTATUS)
@@ -4621,6 +4670,8 @@ DECLARE_CSR(mideleg, CSR_MIDELEG)
DECLARE_CSR(mie, CSR_MIE)
DECLARE_CSR(mtvec, CSR_MTVEC)
DECLARE_CSR(mcounteren, CSR_MCOUNTEREN)
+DECLARE_CSR(mvien, CSR_MVIEN)
+DECLARE_CSR(mvip, CSR_MVIP)
DECLARE_CSR(menvcfg, CSR_MENVCFG)
DECLARE_CSR(mstateen0, CSR_MSTATEEN0)
DECLARE_CSR(mstateen1, CSR_MSTATEEN1)
@@ -4634,6 +4685,9 @@ DECLARE_CSR(mtval, CSR_MTVAL)
DECLARE_CSR(mip, CSR_MIP)
DECLARE_CSR(mtinst, CSR_MTINST)
DECLARE_CSR(mtval2, CSR_MTVAL2)
+DECLARE_CSR(miselect, CSR_MISELECT)
+DECLARE_CSR(mireg, CSR_MIREG)
+DECLARE_CSR(mtopei, CSR_MTOPEI)
DECLARE_CSR(pmpcfg0, CSR_PMPCFG0)
DECLARE_CSR(pmpcfg1, CSR_PMPCFG1)
DECLARE_CSR(pmpcfg2, CSR_PMPCFG2)
@@ -4792,10 +4846,20 @@ DECLARE_CSR(marchid, CSR_MARCHID)
DECLARE_CSR(mimpid, CSR_MIMPID)
DECLARE_CSR(mhartid, CSR_MHARTID)
DECLARE_CSR(mconfigptr, CSR_MCONFIGPTR)
+DECLARE_CSR(mtopi, CSR_MTOPI)
+DECLARE_CSR(sieh, CSR_SIEH)
+DECLARE_CSR(siph, CSR_SIPH)
DECLARE_CSR(stimecmph, CSR_STIMECMPH)
+DECLARE_CSR(vsieh, CSR_VSIEH)
+DECLARE_CSR(vsiph, CSR_VSIPH)
DECLARE_CSR(vstimecmph, CSR_VSTIMECMPH)
DECLARE_CSR(htimedeltah, CSR_HTIMEDELTAH)
+DECLARE_CSR(hidelegh, CSR_HIDELEGH)
+DECLARE_CSR(hvienh, CSR_HVIENH)
DECLARE_CSR(henvcfgh, CSR_HENVCFGH)
+DECLARE_CSR(hviph, CSR_HVIPH)
+DECLARE_CSR(hviprio1h, CSR_HVIPRIO1H)
+DECLARE_CSR(hviprio2h, CSR_HVIPRIO2H)
DECLARE_CSR(hstateen0h, CSR_HSTATEEN0H)
DECLARE_CSR(hstateen1h, CSR_HSTATEEN1H)
DECLARE_CSR(hstateen2h, CSR_HSTATEEN2H)
@@ -4833,11 +4897,16 @@ DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H)
DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H)
DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H)
DECLARE_CSR(mstatush, CSR_MSTATUSH)
+DECLARE_CSR(midelegh, CSR_MIDELEGH)
+DECLARE_CSR(mieh, CSR_MIEH)
+DECLARE_CSR(mvienh, CSR_MVIENH)
+DECLARE_CSR(mviph, CSR_MVIPH)
DECLARE_CSR(menvcfgh, CSR_MENVCFGH)
DECLARE_CSR(mstateen0h, CSR_MSTATEEN0H)
DECLARE_CSR(mstateen1h, CSR_MSTATEEN1H)
DECLARE_CSR(mstateen2h, CSR_MSTATEEN2H)
DECLARE_CSR(mstateen3h, CSR_MSTATEEN3H)
+DECLARE_CSR(miph, CSR_MIPH)
DECLARE_CSR(mhpmevent3h, CSR_MHPMEVENT3H)
DECLARE_CSR(mhpmevent4h, CSR_MHPMEVENT4H)
DECLARE_CSR(mhpmevent5h, CSR_MHPMEVENT5H)
diff --git a/src/target/riscv/field_helpers.h b/src/target/riscv/field_helpers.h
new file mode 100644
index 0000000..16578f1
--- /dev/null
+++ b/src/target/riscv/field_helpers.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef FIELD_HELPERS_H
+#define FIELD_HELPERS_H
+
+#include <stdint.h>
+#include <assert.h>
+
+static inline uint64_t get_field(uint64_t reg, uint64_t mask)
+{
+ return (reg & mask) / (mask & ~(mask << 1));
+}
+
+static inline uint32_t get_field32(uint64_t reg, uint64_t mask)
+{
+ uint64_t value = get_field(reg, mask);
+ assert(value <= UINT32_MAX);
+ return value;
+}
+
+static inline uint64_t set_field(uint64_t reg, uint64_t mask, uint64_t val)
+{
+ /* Clear current value from field. */
+ reg &= ~mask;
+ uint64_t low_field_bit = mask & ~(mask << 1);
+ /* Assert if the value doesn't fit in the field. */
+ assert(((val * low_field_bit) & ~mask) == 0);
+ reg |= (val * low_field_bit) & mask;
+ return reg;
+}
+
+static inline uint32_t set_field32(uint32_t reg, uint32_t mask, uint32_t val)
+{
+ return (uint32_t)set_field(reg, mask, val);
+}
+
+static inline uint64_t field_value(uint64_t mask, uint64_t val)
+{
+ return set_field(0, mask, val);
+}
+
+static inline uint32_t field_value32(uint32_t mask, uint32_t val)
+{
+ return set_field32(0, mask, val);
+}
+
+#endif
diff --git a/src/target/riscv/gdb_regs.h b/src/target/riscv/gdb_regs.h
index 32bc1d5..92c8cc5 100644
--- a/src/target/riscv/gdb_regs.h
+++ b/src/target/riscv/gdb_regs.h
@@ -78,15 +78,21 @@ enum gdb_regno {
GDB_REGNO_FT11,
GDB_REGNO_FPR31 = GDB_REGNO_FT11,
GDB_REGNO_CSR0 = 65,
+ GDB_REGNO_FCSR = CSR_FCSR + GDB_REGNO_CSR0,
+ GDB_REGNO_FFLAGS = CSR_FFLAGS + GDB_REGNO_CSR0,
+ GDB_REGNO_FRM = CSR_FRM + GDB_REGNO_CSR0,
GDB_REGNO_VSTART = CSR_VSTART + GDB_REGNO_CSR0,
GDB_REGNO_VXSAT = CSR_VXSAT + GDB_REGNO_CSR0,
GDB_REGNO_VXRM = CSR_VXRM + GDB_REGNO_CSR0,
+ GDB_REGNO_VCSR = CSR_VCSR + GDB_REGNO_CSR0,
GDB_REGNO_VLENB = CSR_VLENB + GDB_REGNO_CSR0,
GDB_REGNO_VL = CSR_VL + GDB_REGNO_CSR0,
GDB_REGNO_VTYPE = CSR_VTYPE + GDB_REGNO_CSR0,
GDB_REGNO_TSELECT = CSR_TSELECT + GDB_REGNO_CSR0,
GDB_REGNO_TDATA1 = CSR_TDATA1 + GDB_REGNO_CSR0,
GDB_REGNO_TDATA2 = CSR_TDATA2 + GDB_REGNO_CSR0,
+ GDB_REGNO_TDATA3 = CSR_TDATA3 + GDB_REGNO_CSR0,
+ GDB_REGNO_TINFO = CSR_TINFO + GDB_REGNO_CSR0,
GDB_REGNO_MISA = CSR_MISA + GDB_REGNO_CSR0,
GDB_REGNO_DPC = CSR_DPC + GDB_REGNO_CSR0,
GDB_REGNO_DCSR = CSR_DCSR + GDB_REGNO_CSR0,
@@ -95,6 +101,11 @@ enum gdb_regno {
GDB_REGNO_MEPC = CSR_MEPC + GDB_REGNO_CSR0,
GDB_REGNO_MCAUSE = CSR_MCAUSE + GDB_REGNO_CSR0,
GDB_REGNO_SATP = CSR_SATP + GDB_REGNO_CSR0,
+ GDB_REGNO_VSATP = CSR_VSATP + GDB_REGNO_CSR0,
+ GDB_REGNO_HGATP = CSR_HGATP + GDB_REGNO_CSR0,
+ GDB_REGNO_HSTATUS = CSR_HSTATUS + GDB_REGNO_CSR0,
+ GDB_REGNO_MTOPI = CSR_MTOPI + GDB_REGNO_CSR0,
+ GDB_REGNO_MTOPEI = CSR_MTOPEI + GDB_REGNO_CSR0,
GDB_REGNO_CSR4095 = GDB_REGNO_CSR0 + 4095,
GDB_REGNO_PRIV = 4161,
/* It's still undecided what register numbers GDB will actually use for
@@ -112,6 +123,6 @@ enum gdb_regno {
GDB_REGNO_COUNT
};
-const char *gdb_regno_name(enum gdb_regno regno);
+const char *gdb_regno_name(const struct target *target, enum gdb_regno regno);
#endif
diff --git a/src/target/riscv/opcodes.h b/src/target/riscv/opcodes.h
index 8faa154..59c3413 100644
--- a/src/target/riscv/opcodes.h
+++ b/src/target/riscv/opcodes.h
@@ -294,10 +294,11 @@ static uint32_t srli(unsigned int dest, unsigned int src, uint8_t shamt)
return inst_rs2(shamt) | inst_rs1(src) | inst_rd(dest) | MATCH_SRLI;
}
-static uint32_t fence(void) __attribute__((unused));
-static uint32_t fence(void)
+static uint32_t fence_rw_rw(void) __attribute__((unused));
+static uint32_t fence_rw_rw(void)
{
- return MATCH_FENCE;
+ /* fence rw,rw */
+ return MATCH_FENCE | 0x3300000;
}
static uint32_t auipc(unsigned int dest) __attribute__((unused));
@@ -312,6 +313,12 @@ static uint32_t vsetvli(unsigned int dest, unsigned int src, uint16_t imm)
return (bits(imm, 10, 0) << 20) | inst_rs1(src) | inst_rd(dest) | MATCH_VSETVLI;
}
+static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2) __attribute__((unused));
+static uint32_t vsetvl(unsigned int rd, unsigned int rs1, unsigned int rs2)
+{
+ return inst_rd(rd) | inst_rs1(rs1) | inst_rs2(rs2) | MATCH_VSETVL;
+}
+
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2) __attribute__((unused));
static uint32_t vmv_x_s(unsigned int rd, unsigned int vs2)
{
diff --git a/src/target/riscv/program.c b/src/target/riscv/program.c
index 0976539..c4ffb3f 100644
--- a/src/target/riscv/program.c
+++ b/src/target/riscv/program.c
@@ -11,6 +11,7 @@
#include "helper/log.h"
#include "asm.h"
+#include "debug_defines.h"
#include "encoding.h"
/* Program interface. */
@@ -19,22 +20,20 @@ int riscv_program_init(struct riscv_program *p, struct target *target)
memset(p, 0, sizeof(*p));
p->target = target;
p->instruction_count = 0;
- p->target_xlen = riscv_xlen(target);
- for (size_t i = 0; i < RISCV_REGISTER_COUNT; ++i)
- p->writes_xreg[i] = 0;
- for (size_t i = 0; i < RISCV_MAX_DEBUG_BUFFER_SIZE; ++i)
- p->debug_buffer[i] = -1;
+ for (size_t i = 0; i < RISCV_MAX_PROGBUF_SIZE; ++i)
+ p->progbuf[i] = -1;
+ p->execution_result = RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED;
return ERROR_OK;
}
int riscv_program_write(struct riscv_program *program)
{
for (unsigned i = 0; i < program->instruction_count; ++i) {
- LOG_DEBUG("debug_buffer[%02x] = DASM(0x%08x)", i, program->debug_buffer[i]);
- if (riscv_write_debug_buffer(program->target, i,
- program->debug_buffer[i]) != ERROR_OK)
+ LOG_TARGET_DEBUG(program->target, "progbuf[%02x] = DASM(0x%08x)",
+ i, program->progbuf[i]);
+ if (riscv_write_progbuf(program->target, i, program->progbuf[i]) != ERROR_OK)
return ERROR_FAIL;
}
return ERROR_OK;
@@ -45,39 +44,29 @@ int riscv_program_exec(struct riscv_program *p, struct target *t)
{
keep_alive();
- riscv_reg_t saved_registers[GDB_REGNO_XPR31 + 1];
- for (size_t i = GDB_REGNO_ZERO + 1; i <= GDB_REGNO_XPR31; ++i) {
- if (p->writes_xreg[i]) {
- LOG_DEBUG("Saving register %d as used by program", (int)i);
- int result = riscv_get_register(t, &saved_registers[i], i);
- if (result != ERROR_OK)
- return result;
- }
- }
+ p->execution_result = RISCV_PROGBUF_EXEC_RESULT_UNKNOWN;
if (riscv_program_ebreak(p) != ERROR_OK) {
- LOG_ERROR("Unable to write ebreak");
- for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
- LOG_ERROR("ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
- (int)i, p->debug_buffer[i], p->debug_buffer[i]);
+ LOG_TARGET_ERROR(t, "Unable to insert ebreak into program buffer");
+ for (size_t i = 0; i < riscv_progbuf_size(p->target); ++i)
+ LOG_TARGET_ERROR(t, "ram[%02x]: DASM(0x%08" PRIx32 ") [0x%08" PRIx32 "]",
+ (int)i, p->progbuf[i], p->progbuf[i]);
return ERROR_FAIL;
}
if (riscv_program_write(p) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_execute_debug_buffer(t) != ERROR_OK) {
- LOG_DEBUG("Unable to execute program %p", p);
+ uint32_t cmderr;
+ if (riscv_execute_progbuf(t, &cmderr) != ERROR_OK) {
+ p->execution_result = (cmderr == DM_ABSTRACTCS_CMDERR_EXCEPTION)
+ ? RISCV_PROGBUF_EXEC_RESULT_EXCEPTION
+ : RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR;
+ /* TODO: what happens if we fail here, but need to restore registers? */
+ LOG_TARGET_DEBUG(t, "Unable to execute program %p", p);
return ERROR_FAIL;
}
-
- for (size_t i = 0; i < riscv_debug_buffer_size(p->target); ++i)
- if (i >= riscv_debug_buffer_size(p->target))
- p->debug_buffer[i] = riscv_read_debug_buffer(t, i);
-
- for (size_t i = GDB_REGNO_ZERO; i <= GDB_REGNO_XPR31; ++i)
- if (p->writes_xreg[i])
- riscv_set_register(t, i, saved_registers[i]);
+ p->execution_result = RISCV_PROGBUF_EXEC_RESULT_SUCCESS;
return ERROR_OK;
}
@@ -102,6 +91,23 @@ int riscv_program_sbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
return riscv_program_insert(p, sb(d, b, offset));
}
+int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset,
+ unsigned int size)
+{
+ switch (size) {
+ case 1:
+ return riscv_program_sbr(p, d, b, offset);
+ case 2:
+ return riscv_program_shr(p, d, b, offset);
+ case 4:
+ return riscv_program_swr(p, d, b, offset);
+ case 8:
+ return riscv_program_sdr(p, d, b, offset);
+ }
+ assert(false && "Unsupported size");
+ return ERROR_FAIL;
+}
+
int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset)
{
return riscv_program_insert(p, ld(d, b, offset));
@@ -122,6 +128,23 @@ int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
return riscv_program_insert(p, lb(d, b, offset));
}
+int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int offset,
+ unsigned int size)
+{
+ switch (size) {
+ case 1:
+ return riscv_program_lbr(p, d, b, offset);
+ case 2:
+ return riscv_program_lhr(p, d, b, offset);
+ case 4:
+ return riscv_program_lwr(p, d, b, offset);
+ case 8:
+ return riscv_program_ldr(p, d, b, offset);
+ }
+ assert(false && "Unsupported size");
+ return ERROR_FAIL;
+}
+
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr)
{
assert(csr >= GDB_REGNO_CSR0 && csr <= GDB_REGNO_CSR4095);
@@ -151,16 +174,16 @@ int riscv_program_fence_i(struct riscv_program *p)
return riscv_program_insert(p, fence_i());
}
-int riscv_program_fence(struct riscv_program *p)
+int riscv_program_fence_rw_rw(struct riscv_program *p)
{
- return riscv_program_insert(p, fence());
+ return riscv_program_insert(p, fence_rw_rw());
}
int riscv_program_ebreak(struct riscv_program *p)
{
struct target *target = p->target;
RISCV_INFO(r);
- if (p->instruction_count == riscv_debug_buffer_size(p->target) &&
+ if (p->instruction_count == riscv_progbuf_size(p->target) &&
r->impebreak) {
return ERROR_OK;
}
@@ -174,14 +197,14 @@ int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_insert(struct riscv_program *p, riscv_insn_t i)
{
- if (p->instruction_count >= riscv_debug_buffer_size(p->target)) {
- LOG_ERROR("Unable to insert instruction:");
- LOG_ERROR(" instruction_count=%d", (int)p->instruction_count);
- LOG_ERROR(" buffer size =%d", (int)riscv_debug_buffer_size(p->target));
+ if (p->instruction_count >= riscv_progbuf_size(p->target)) {
+ LOG_TARGET_ERROR(p->target, "Unable to insert program into progbuf, "
+ "capacity would be exceeded (progbufsize=%d).",
+ (int)riscv_progbuf_size(p->target));
return ERROR_FAIL;
}
- p->debug_buffer[p->instruction_count] = i;
+ p->progbuf[p->instruction_count] = i;
p->instruction_count++;
return ERROR_OK;
}
diff --git a/src/target/riscv/program.h b/src/target/riscv/program.h
index 62a04f0..361191a 100644
--- a/src/target/riscv/program.h
+++ b/src/target/riscv/program.h
@@ -5,27 +5,32 @@
#include "riscv.h"
-#define RISCV_MAX_DEBUG_BUFFER_SIZE 32
+#define RISCV_MAX_PROGBUF_SIZE 32
#define RISCV_REGISTER_COUNT 32
#define RISCV_DSCRATCH_COUNT 2
+typedef enum {
+ RISCV_PROGBUF_EXEC_RESULT_NOT_EXECUTED,
+ RISCV_PROGBUF_EXEC_RESULT_UNKNOWN,
+ RISCV_PROGBUF_EXEC_RESULT_EXCEPTION,
+ RISCV_PROGBUF_EXEC_RESULT_UNKNOWN_ERROR,
+ RISCV_PROGBUF_EXEC_RESULT_SUCCESS
+} riscv_progbuf_exec_result_t;
+
/* The various RISC-V debug specifications all revolve around setting up
* program buffers and executing them on the target. This structure contains a
* single program, which can then be executed on targets. */
struct riscv_program {
struct target *target;
- uint32_t debug_buffer[RISCV_MAX_DEBUG_BUFFER_SIZE];
+ uint32_t progbuf[RISCV_MAX_PROGBUF_SIZE];
/* Number of 32-bit instructions in the program. */
size_t instruction_count;
- /* Side effects of executing this program. These must be accounted for
- * in order to maintain correct executing of the target system. */
- bool writes_xreg[RISCV_REGISTER_COUNT];
-
- /* XLEN on the target. */
- int target_xlen;
+ /* execution result of the program */
+ /* TODO: remove this field. We should make it a parameter to riscv_program_exec */
+ riscv_progbuf_exec_result_t execution_result;
};
/* Initializes a program with the header. */
@@ -51,11 +56,15 @@ int riscv_program_ldr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_lwr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lhr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
int riscv_program_lbr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno a, int o);
+int riscv_program_load(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int o,
+ unsigned int s);
int riscv_program_sdr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_swr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_shr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
int riscv_program_sbr(struct riscv_program *p, enum gdb_regno s, enum gdb_regno a, int o);
+int riscv_program_store(struct riscv_program *p, enum gdb_regno d, enum gdb_regno b, int o,
+ unsigned int s);
int riscv_program_csrrsi(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
int riscv_program_csrrci(struct riscv_program *p, enum gdb_regno d, unsigned int z, enum gdb_regno csr);
@@ -63,7 +72,7 @@ int riscv_program_csrr(struct riscv_program *p, enum gdb_regno d, enum gdb_regno
int riscv_program_csrw(struct riscv_program *p, enum gdb_regno s, enum gdb_regno csr);
int riscv_program_fence_i(struct riscv_program *p);
-int riscv_program_fence(struct riscv_program *p);
+int riscv_program_fence_rw_rw(struct riscv_program *p);
int riscv_program_ebreak(struct riscv_program *p);
int riscv_program_addi(struct riscv_program *p, enum gdb_regno d, enum gdb_regno s, int16_t i);
diff --git a/src/target/riscv/riscv-011.c b/src/target/riscv/riscv-011.c
index be296cd..64379dc 100644
--- a/src/target/riscv/riscv-011.c
+++ b/src/target/riscv/riscv-011.c
@@ -24,6 +24,7 @@
#include "riscv.h"
#include "asm.h"
#include "gdb_regs.h"
+#include "field_helpers.h"
/**
* Since almost everything can be accomplish by scanning the dbus register, all
@@ -67,8 +68,7 @@
* to the target. Afterwards use cache_get... to read results.
*/
-#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
-#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
+static int handle_halt(struct target *target, bool announce);
/* Constants for legacy SiFive hardware breakpoints. */
#define CSR_BPCONTROL_X (1<<0)
@@ -161,15 +161,6 @@ typedef enum slot {
#define DRAM_CACHE_SIZE 16
-struct trigger {
- uint64_t address;
- uint32_t length;
- uint64_t mask;
- uint64_t value;
- bool read, write, execute;
- int unique_id;
-};
-
struct memory_cache_line {
uint32_t data;
bool valid;
@@ -219,7 +210,8 @@ typedef struct {
static int poll_target(struct target *target, bool announce);
static int riscv011_poll(struct target *target);
-static int get_register(struct target *target, riscv_reg_t *value, int regid);
+static int get_register(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid);
/*** Utility functions. ***/
@@ -279,7 +271,7 @@ static uint16_t dram_address(unsigned int index)
return 0x40 + index - 0x10;
}
-static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr)
{
struct scan_field field;
uint8_t in_value[4];
@@ -306,7 +298,9 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
- return in;
+ if (in_ptr)
+ *in_ptr = in;
+ return ERROR_OK;
}
static uint32_t idcode_scan(struct target *target)
@@ -344,7 +338,7 @@ static void increase_dbus_busy_delay(struct target *target)
info->dtmcontrol_idle, info->dbus_busy_delay,
info->interrupt_high_delay);
- dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET);
+ dtmcontrol_scan(target, DTMCONTROL_DBUS_RESET, NULL /* discard value */);
}
static void increase_interrupt_high_delay(struct target *target)
@@ -431,8 +425,13 @@ static dbus_status_t dbus_scan(struct target *target, uint16_t *address_in,
.out_value = out,
.in_value = in
};
+ if (address_in)
+ *address_in = 0;
- assert(info->addrbits != 0);
+ if (info->addrbits == 0) {
+ LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0.");
+ return DBUS_STATUS_FAILED;
+ }
buf_set_u64(out, DBUS_OP_START, DBUS_OP_SIZE, op);
buf_set_u64(out, DBUS_DATA_START, DBUS_DATA_SIZE, data_out);
@@ -686,18 +685,13 @@ static void dram_write32(struct target *target, unsigned int index, uint32_t val
}
/** Read the haltnot and interrupt bits. */
-static bits_t read_bits(struct target *target)
+static int read_bits(struct target *target, bits_t *result)
{
uint64_t value;
dbus_status_t status;
uint16_t address_in;
riscv011_info_t *info = get_info(target);
- bits_t err_result = {
- .haltnot = 0,
- .interrupt = 0
- };
-
do {
unsigned i = 0;
do {
@@ -706,26 +700,23 @@ static bits_t read_bits(struct target *target)
if (address_in == (1<<info->addrbits) - 1 &&
value == (1ULL<<DBUS_DATA_SIZE) - 1) {
LOG_ERROR("TDO seems to be stuck high.");
- return err_result;
+ return ERROR_FAIL;
}
increase_dbus_busy_delay(target);
- } else if (status == DBUS_STATUS_FAILED) {
- /* TODO: return an actual error */
- return err_result;
}
} while (status == DBUS_STATUS_BUSY && i++ < 256);
- if (i >= 256) {
+ if (status != DBUS_STATUS_SUCCESS) {
LOG_ERROR("Failed to read from 0x%x; status=%d", address_in, status);
- return err_result;
+ return ERROR_FAIL;
}
} while (address_in > 0x10 && address_in != DMCONTROL);
- bits_t result = {
- .haltnot = get_field(value, DMCONTROL_HALTNOT),
- .interrupt = get_field(value, DMCONTROL_INTERRUPT)
- };
- return result;
+ if (result) {
+ result->haltnot = get_field(value, DMCONTROL_HALTNOT);
+ result->interrupt = get_field(value, DMCONTROL_INTERRUPT);
+ }
+ return ERROR_OK;
}
static int wait_for_debugint_clear(struct target *target, bool ignore_first)
@@ -736,10 +727,16 @@ static int wait_for_debugint_clear(struct target *target, bool ignore_first)
* result of the read that happened just before debugint was set.
* (Assuming the last scan before calling this function was one that
* sets debugint.) */
- read_bits(target);
+ read_bits(target, NULL);
}
while (1) {
- bits_t bits = read_bits(target);
+ bits_t bits = {
+ .haltnot = 0,
+ .interrupt = 0
+ };
+ if (read_bits(target, &bits) != ERROR_OK)
+ return ERROR_FAIL;
+
if (!bits.interrupt)
return ERROR_OK;
if (time(NULL) - start > riscv_command_timeout_sec) {
@@ -1048,7 +1045,7 @@ static int read_remote_csr(struct target *target, uint64_t *value, uint32_t csr)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when reading %s", exception,
- gdb_regno_name(GDB_REGNO_CSR0 + csr));
+ gdb_regno_name(target, GDB_REGNO_CSR0 + csr));
*value = ~0;
return ERROR_FAIL;
}
@@ -1109,10 +1106,14 @@ static int maybe_write_tselect(struct target *target)
static int execute_resume(struct target *target, bool step)
{
+ RISCV_INFO(r);
riscv011_info_t *info = get_info(target);
LOG_DEBUG("step=%d", step);
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
maybe_write_tselect(target);
/* TODO: check if dpc is dirty (which also is true if an exception was hit
@@ -1137,9 +1138,9 @@ static int execute_resume(struct target *target, bool step)
}
}
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, r->riscv_ebreakm);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, r->riscv_ebreaks);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, r->riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr &= ~DCSR_HALT;
@@ -1189,17 +1190,7 @@ static int full_step(struct target *target, bool announce)
return ERROR_FAIL;
}
}
- return ERROR_OK;
-}
-
-static int resume(struct target *target, int debug_execution, bool step)
-{
- if (debug_execution) {
- LOG_ERROR("TODO: debug_execution is true");
- return ERROR_FAIL;
- }
-
- return execute_resume(target, step);
+ return handle_halt(target, announce);
}
static uint64_t reg_cache_get(struct target *target, unsigned int number)
@@ -1256,7 +1247,7 @@ static int register_read(struct target *target, riscv_reg_t *value, int regnum)
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
- LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(regnum));
+ LOG_WARNING("Got exception 0x%x when reading %s", exception, gdb_regno_name(target, regnum));
*value = ~0;
return ERROR_FAIL;
}
@@ -1289,7 +1280,7 @@ static int register_write(struct target *target, unsigned int number,
} else if (number <= GDB_REGNO_XPR31) {
cache_set_load(target, 0, number - GDB_REGNO_ZERO, SLOT0);
cache_set_jump(target, 1);
- } else if (number == GDB_REGNO_PC) {
+ } else if (number == GDB_REGNO_PC || number == GDB_REGNO_DPC) {
info->dpc = value;
return ERROR_OK;
} else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
@@ -1331,14 +1322,15 @@ static int register_write(struct target *target, unsigned int number,
uint32_t exception = cache_get32(target, info->dramsize-1);
if (exception) {
LOG_WARNING("Got exception 0x%x when writing %s", exception,
- gdb_regno_name(number));
+ gdb_regno_name(target, number));
return ERROR_FAIL;
}
return ERROR_OK;
}
-static int get_register(struct target *target, riscv_reg_t *value, int regid)
+static int get_register(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regid)
{
riscv011_info_t *info = get_info(target);
@@ -1346,7 +1338,7 @@ static int get_register(struct target *target, riscv_reg_t *value, int regid)
if (regid <= GDB_REGNO_XPR31) {
*value = reg_cache_get(target, regid);
- } else if (regid == GDB_REGNO_PC) {
+ } else if (regid == GDB_REGNO_PC || regid == GDB_REGNO_DPC) {
*value = info->dpc;
} else if (regid >= GDB_REGNO_FPR0 && regid <= GDB_REGNO_FPR31) {
int result = update_mstatus_actual(target);
@@ -1382,7 +1374,8 @@ static int get_register(struct target *target, riscv_reg_t *value, int regid)
return ERROR_OK;
}
-static int set_register(struct target *target, int regid, uint64_t value)
+static int set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
{
return register_write(target, regid, value);
}
@@ -1468,19 +1461,20 @@ static int step(struct target *target, int current, target_addr_t address,
static int examine(struct target *target)
{
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
-
- uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
- LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
- LOG_DEBUG(" addrbits=%d", get_field(dtmcontrol, DTMCONTROL_ADDRBITS));
- LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTMCONTROL_VERSION));
- LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTMCONTROL_IDLE));
- if (dtmcontrol == 0) {
- LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+ uint32_t dtmcontrol;
+ if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) {
+ LOG_ERROR("Could not scan dtmcontrol. Check JTAG connectivity/board power.");
return ERROR_FAIL;
}
+
+ LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ LOG_DEBUG(" addrbits=%d", get_field32(dtmcontrol, DTMCONTROL_ADDRBITS));
+ LOG_DEBUG(" version=%d", get_field32(dtmcontrol, DTMCONTROL_VERSION));
+ LOG_DEBUG(" idle=%d", get_field32(dtmcontrol, DTMCONTROL_IDLE));
+
if (get_field(dtmcontrol, DTMCONTROL_VERSION) != 0) {
LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
- get_field(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
+ get_field32(dtmcontrol, DTMCONTROL_VERSION), dtmcontrol);
return ERROR_FAIL;
}
@@ -1498,22 +1492,22 @@ static int examine(struct target *target)
uint32_t dminfo = dbus_read(target, DMINFO);
LOG_DEBUG("dminfo: 0x%08x", dminfo);
- LOG_DEBUG(" abussize=0x%x", get_field(dminfo, DMINFO_ABUSSIZE));
- LOG_DEBUG(" serialcount=0x%x", get_field(dminfo, DMINFO_SERIALCOUNT));
- LOG_DEBUG(" access128=%d", get_field(dminfo, DMINFO_ACCESS128));
- LOG_DEBUG(" access64=%d", get_field(dminfo, DMINFO_ACCESS64));
- LOG_DEBUG(" access32=%d", get_field(dminfo, DMINFO_ACCESS32));
- LOG_DEBUG(" access16=%d", get_field(dminfo, DMINFO_ACCESS16));
- LOG_DEBUG(" access8=%d", get_field(dminfo, DMINFO_ACCESS8));
- LOG_DEBUG(" dramsize=0x%x", get_field(dminfo, DMINFO_DRAMSIZE));
- LOG_DEBUG(" authenticated=0x%x", get_field(dminfo, DMINFO_AUTHENTICATED));
- LOG_DEBUG(" authbusy=0x%x", get_field(dminfo, DMINFO_AUTHBUSY));
- LOG_DEBUG(" authtype=0x%x", get_field(dminfo, DMINFO_AUTHTYPE));
- LOG_DEBUG(" version=0x%x", get_field(dminfo, DMINFO_VERSION));
+ LOG_DEBUG(" abussize=0x%x", get_field32(dminfo, DMINFO_ABUSSIZE));
+ LOG_DEBUG(" serialcount=0x%x", get_field32(dminfo, DMINFO_SERIALCOUNT));
+ LOG_DEBUG(" access128=%d", get_field32(dminfo, DMINFO_ACCESS128));
+ LOG_DEBUG(" access64=%d", get_field32(dminfo, DMINFO_ACCESS64));
+ LOG_DEBUG(" access32=%d", get_field32(dminfo, DMINFO_ACCESS32));
+ LOG_DEBUG(" access16=%d", get_field32(dminfo, DMINFO_ACCESS16));
+ LOG_DEBUG(" access8=%d", get_field32(dminfo, DMINFO_ACCESS8));
+ LOG_DEBUG(" dramsize=0x%x", get_field32(dminfo, DMINFO_DRAMSIZE));
+ LOG_DEBUG(" authenticated=0x%x", get_field32(dminfo, DMINFO_AUTHENTICATED));
+ LOG_DEBUG(" authbusy=0x%x", get_field32(dminfo, DMINFO_AUTHBUSY));
+ LOG_DEBUG(" authtype=0x%x", get_field32(dminfo, DMINFO_AUTHTYPE));
+ LOG_DEBUG(" version=0x%x", get_field32(dminfo, DMINFO_VERSION));
if (get_field(dminfo, DMINFO_VERSION) != 1) {
LOG_ERROR("OpenOCD only supports Debug Module version 1, not %d "
- "(dminfo=0x%x)", get_field(dminfo, DMINFO_VERSION), dminfo);
+ "(dminfo=0x%x)", get_field32(dminfo, DMINFO_VERSION), dminfo);
return ERROR_FAIL;
}
@@ -1590,7 +1584,6 @@ static int examine(struct target *target)
return result;
target_set_examined(target);
- riscv_set_current_hartid(target, 0);
for (size_t i = 0; i < 32; ++i)
reg_cache_set(target, i, -1);
LOG_INFO("Examined RISCV core; XLEN=%d, misa=0x%" PRIx64,
@@ -1778,10 +1771,10 @@ static riscv_error_t handle_halt_routine(struct target *target)
reg = S0;
break;
case 31:
- reg = CSR_DPC;
+ reg = GDB_REGNO_DPC;
break;
case 32:
- reg = CSR_DCSR;
+ reg = GDB_REGNO_DCSR;
break;
default:
assert(0);
@@ -1815,8 +1808,8 @@ static riscv_error_t handle_halt_routine(struct target *target)
}
/* TODO: get rid of those 2 variables and talk to the cache directly. */
- info->dpc = reg_cache_get(target, CSR_DPC);
- info->dcsr = reg_cache_get(target, CSR_DCSR);
+ info->dpc = reg_cache_get(target, GDB_REGNO_DPC);
+ info->dcsr = reg_cache_get(target, GDB_REGNO_DCSR);
cache_invalidate(target);
@@ -1905,7 +1898,13 @@ static int poll_target(struct target *target, bool announce)
int old_debug_level = debug_level;
if (debug_level >= LOG_LVL_DEBUG)
debug_level = LOG_LVL_INFO;
- bits_t bits = read_bits(target);
+ bits_t bits = {
+ .haltnot = 0,
+ .interrupt = 0
+ };
+ if (read_bits(target, &bits) != ERROR_OK)
+ return ERROR_FAIL;
+
debug_level = old_debug_level;
if (bits.haltnot && bits.interrupt) {
@@ -1936,11 +1935,12 @@ static int riscv011_resume(struct target *target, int current,
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
r->prepped = false;
- return resume(target, debug_execution, false);
+ return execute_resume(target, false);
}
static int assert_reset(struct target *target)
{
+ RISCV_INFO(r);
riscv011_info_t *info = get_info(target);
/* TODO: Maybe what I implemented here is more like soft_reset_halt()? */
@@ -1954,9 +1954,9 @@ static int assert_reset(struct target *target)
/* Not sure what we should do when there are multiple cores.
* Here just reset the single hart we're talking to. */
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, riscv_ebreakm);
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, riscv_ebreaks);
- info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, riscv_ebreaku);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKM, r->riscv_ebreakm);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKS, r->riscv_ebreaks);
+ info->dcsr = set_field(info->dcsr, DCSR_EBREAKU, r->riscv_ebreaku);
info->dcsr = set_field(info->dcsr, DCSR_EBREAKH, 1);
info->dcsr |= DCSR_HALT;
if (target->reset_halt)
diff --git a/src/target/riscv/riscv-013.c b/src/target/riscv/riscv-013.c
index 2f4a8fe..2ed1dd7 100644
--- a/src/target/riscv/riscv-013.c
+++ b/src/target/riscv/riscv-013.c
@@ -28,44 +28,57 @@
#include "program.h"
#include "asm.h"
#include "batch.h"
+#include "debug_reg_printer.h"
+#include "field_helpers.h"
static int riscv013_on_step_or_resume(struct target *target, bool step);
static int riscv013_step_or_resume_current_hart(struct target *target,
- bool step, bool use_hasel);
-static void riscv013_clear_abstract_error(struct target *target);
+ bool step);
+static int riscv013_clear_abstract_error(struct target *target);
/* Implementations of the functions in struct riscv_info. */
static int riscv013_get_register(struct target *target,
- riscv_reg_t *value, int rid);
-static int riscv013_set_register(struct target *target, int regid, uint64_t value);
-static int riscv013_select_current_hart(struct target *target);
+ riscv_reg_t *value, enum gdb_regno rid);
+static int riscv013_set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value);
+static int dm013_select_hart(struct target *target, int hart_index);
static int riscv013_halt_prep(struct target *target);
static int riscv013_halt_go(struct target *target);
static int riscv013_resume_go(struct target *target);
static int riscv013_step_current_hart(struct target *target);
-static int riscv013_on_halt(struct target *target);
static int riscv013_on_step(struct target *target);
static int riscv013_resume_prep(struct target *target);
-static bool riscv013_is_halted(struct target *target);
static enum riscv_halt_reason riscv013_halt_reason(struct target *target);
-static int riscv013_write_debug_buffer(struct target *target, unsigned index,
+static int riscv013_write_progbuf(struct target *target, unsigned int index,
riscv_insn_t d);
-static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned
+static riscv_insn_t riscv013_read_progbuf(struct target *target, unsigned int
index);
-static int riscv013_execute_debug_buffer(struct target *target);
-static void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
-static void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a);
-static int riscv013_dmi_write_u64_bits(struct target *target);
-static void riscv013_fill_dmi_nop_u64(struct target *target, char *buf);
-static int register_read(struct target *target, uint64_t *value, uint32_t number);
-static int register_read_direct(struct target *target, uint64_t *value, uint32_t number);
-static int register_write_direct(struct target *target, unsigned number,
- uint64_t value);
+static int riscv013_invalidate_cached_progbuf(struct target *target);
+static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr);
+static void riscv013_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d);
+static void riscv013_fill_dmi_read(struct target *target, char *buf, uint64_t a);
+static void riscv013_fill_dmi_nop(struct target *target, char *buf);
+static int riscv013_get_dmi_scan_length(struct target *target);
+static void riscv013_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d);
+static void riscv013_fill_dm_read(struct target *target, char *buf, uint64_t a);
+static void riscv013_fill_dm_nop(struct target *target, char *buf);
+static unsigned int register_size(struct target *target, enum gdb_regno number);
+static int register_read_direct(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number);
+static int register_write_direct(struct target *target, enum gdb_regno number,
+ riscv_reg_t value);
static int read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer);
+typedef enum {
+ HALT_GROUP,
+ RESUME_GROUP
+} grouptype_t;
+static int set_group(struct target *target, bool *supported, unsigned int group,
+ grouptype_t grouptype);
+
/**
* Since almost everything can be accomplish by scanning the dbus register, all
* functions here assume dbus is already selected. The exception are functions
@@ -73,29 +86,19 @@ static int write_memory(struct target *target, target_addr_t address,
* currently in IR. They should set IR to dbus explicitly.
*/
-#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
-#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
-
-#define CSR_DCSR_CAUSE_SWBP 1
-#define CSR_DCSR_CAUSE_TRIGGER 2
-#define CSR_DCSR_CAUSE_DEBUGINT 3
-#define CSR_DCSR_CAUSE_STEP 4
-#define CSR_DCSR_CAUSE_HALT 5
-#define CSR_DCSR_CAUSE_GROUP 6
-
#define RISCV013_INFO(r) riscv013_info_t *r = get_info(target)
/*** JTAG registers. ***/
typedef enum {
- DMI_OP_NOP = 0,
- DMI_OP_READ = 1,
- DMI_OP_WRITE = 2
+ DMI_OP_NOP = DTM_DMI_OP_NOP,
+ DMI_OP_READ = DTM_DMI_OP_READ,
+ DMI_OP_WRITE = DTM_DMI_OP_WRITE
} dmi_op_t;
typedef enum {
- DMI_STATUS_SUCCESS = 0,
- DMI_STATUS_FAILED = 2,
- DMI_STATUS_BUSY = 3
+ DMI_STATUS_SUCCESS = DTM_DMI_OP_SUCCESS,
+ DMI_STATUS_FAILED = DTM_DMI_OP_FAILED,
+ DMI_STATUS_BUSY = DTM_DMI_OP_BUSY
} dmi_status_t;
typedef enum slot {
@@ -106,47 +109,45 @@ typedef enum slot {
/*** Debug Bus registers. ***/
-#define CMDERR_NONE 0
-#define CMDERR_BUSY 1
-#define CMDERR_NOT_SUPPORTED 2
-#define CMDERR_EXCEPTION 3
-#define CMDERR_HALT_RESUME 4
-#define CMDERR_OTHER 7
+/* TODO: CMDERR_* defines can removed */
+#define CMDERR_NONE DM_ABSTRACTCS_CMDERR_NONE
+#define CMDERR_BUSY DM_ABSTRACTCS_CMDERR_BUSY
+#define CMDERR_NOT_SUPPORTED DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED
+#define CMDERR_EXCEPTION DM_ABSTRACTCS_CMDERR_EXCEPTION
+#define CMDERR_HALT_RESUME DM_ABSTRACTCS_CMDERR_HALT_RESUME
+#define CMDERR_OTHER DM_ABSTRACTCS_CMDERR_OTHER
-/*** Info about the core being debugged. ***/
-
-struct trigger {
- uint64_t address;
- uint32_t length;
- uint64_t mask;
- uint64_t value;
- bool read, write, execute;
- int unique_id;
-};
-
-typedef enum {
- YNM_MAYBE,
- YNM_YES,
- YNM_NO
-} yes_no_maybe_t;
+#define HART_INDEX_MULTIPLE -1
+#define HART_INDEX_UNKNOWN -2
typedef struct {
struct list_head list;
int abs_chain_position;
-
+ /* The base address to access this DM on DMI */
+ uint32_t base;
/* The number of harts connected to this DM. */
int hart_count;
+ /* Indicates we already examined this DM, so don't need to do it again. */
+ bool was_examined;
/* Indicates we already reset this DM, so don't need to do it again. */
bool was_reset;
/* Targets that are connected to this DM. */
struct list_head target_list;
- /* The currently selected hartid on this DM. */
+ /* Contains the ID of the hart that is currently selected by this DM.
+ * If multiple harts are selected this is HART_INDEX_MULTIPLE. */
int current_hartid;
+
bool hasel_supported;
/* The program buffer stores executable code. 0 is an illegal instruction,
* so we use 0 to mean the cached value is invalid. */
uint32_t progbuf_cache[16];
+
+ /* Some operations are illegal when an abstract command is running.
+ * The field is used to track whether the last command timed out, and
+ * abstractcs.busy may have remained set. In that case we may need to
+ * re-check the busy state before executing these operations. */
+ bool abstract_cmd_maybe_busy;
} dm013_info_t;
typedef struct {
@@ -198,21 +199,23 @@ typedef struct {
yes_no_maybe_t has_aampostincrement;
- /* When a function returns some error due to a failure indicated by the
- * target in cmderr, the caller can look here to see what that error was.
- * (Compare with errno.) */
- uint8_t cmderr;
-
/* Some fields from hartinfo. */
uint8_t datasize;
uint8_t dataaccess;
int16_t dataaddr;
- /* The width of the hartsel field. */
- unsigned hartsellen;
-
/* DM that provides access to this target. */
dm013_info_t *dm;
+
+ /* This target was selected using hasel. */
+ bool selected;
+
+ /* When false, we need to set dcsr.ebreak*, halting the target if that's
+ * necessary. */
+ bool dcsr_ebreak_is_set;
+
+ /* This hart was placed into a halt group in examine(). */
+ bool haltgroup_supported;
} riscv013_info_t;
static LIST_HEAD(dm_list);
@@ -241,19 +244,25 @@ static dm013_info_t *get_dm(struct target *target)
dm013_info_t *entry;
dm013_info_t *dm = NULL;
list_for_each_entry(entry, &dm_list, list) {
- if (entry->abs_chain_position == abs_chain_position) {
+ if (entry->abs_chain_position == abs_chain_position
+ && entry->base == target->dbgbase) {
dm = entry;
break;
}
}
if (!dm) {
- LOG_DEBUG("[%d] Allocating new DM", target->coreid);
+ LOG_TARGET_DEBUG(target, "Coreid [%d] Allocating new DM", target->coreid);
dm = calloc(1, sizeof(dm013_info_t));
if (!dm)
return NULL;
dm->abs_chain_position = abs_chain_position;
- dm->current_hartid = -1;
+
+ /* Safety check for dbgbase */
+ assert(target->dbgbase_set || target->dbgbase == 0);
+
+ dm->base = target->dbgbase;
+ dm->current_hartid = 0;
dm->hart_count = -1;
INIT_LIST_HEAD(&dm->target_list);
list_add(&dm->list, &dm_list);
@@ -276,100 +285,114 @@ static dm013_info_t *get_dm(struct target *target)
return dm;
}
-static uint32_t set_hartsel(uint32_t initial, uint32_t index)
+static void riscv013_dm_free(struct target *target)
+{
+ RISCV013_INFO(info);
+ dm013_info_t *dm = info->dm;
+ if (!dm)
+ return;
+
+ target_list_t *target_entry;
+ list_for_each_entry(target_entry, &dm->target_list, list) {
+ if (target_entry->target == target) {
+ list_del(&target_entry->list);
+ free(target_entry);
+ break;
+ }
+ }
+
+ if (list_empty(&dm->target_list)) {
+ list_del(&dm->list);
+ free(dm);
+ }
+ info->dm = NULL;
+}
+
+static riscv_debug_reg_ctx_t get_riscv_debug_reg_ctx(const struct target *target)
+{
+ if (!target_was_examined(target)) {
+ const riscv_debug_reg_ctx_t default_context = {0};
+ return default_context;
+ }
+
+ RISCV013_INFO(info);
+ const riscv_debug_reg_ctx_t context = {
+ .XLEN = { .value = riscv_xlen(target), .is_set = true },
+ .DXLEN = { .value = riscv_xlen(target), .is_set = true },
+ .abits = { .value = info->abits, .is_set = true },
+ };
+ return context;
+}
+
+static void log_debug_reg(struct target *target, enum riscv_debug_reg_ordinal reg,
+ riscv_reg_t value, const char *file, unsigned int line, const char *func)
{
- initial &= ~DM_DMCONTROL_HARTSELLO;
- initial &= ~DM_DMCONTROL_HARTSELHI;
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+ const riscv_debug_reg_ctx_t context = get_riscv_debug_reg_ctx(target);
+ char * const buf = malloc(riscv_debug_reg_to_s(NULL, reg, context, value, RISCV_DEBUG_REG_HIDE_UNNAMED_0) + 1);
+ if (!buf) {
+ LOG_ERROR("Unable to allocate memory.");
+ return;
+ }
+ riscv_debug_reg_to_s(buf, reg, context, value, RISCV_DEBUG_REG_HIDE_UNNAMED_0);
+ log_printf_lf(LOG_LVL_DEBUG, file, line, func, "[%s] %s", target_name(target), buf);
+ free(buf);
+}
- uint32_t index_lo = index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1);
- initial |= index_lo << DM_DMCONTROL_HARTSELLO_OFFSET;
- uint32_t index_hi = index >> DM_DMCONTROL_HARTSELLO_LENGTH;
- assert(index_hi < 1 << DM_DMCONTROL_HARTSELHI_LENGTH);
- initial |= index_hi << DM_DMCONTROL_HARTSELHI_OFFSET;
+#define LOG_DEBUG_REG(t, r, v) log_debug_reg(t, r##_ORDINAL, v, __FILE__, __LINE__, __func__)
+
+static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index)
+{
+ assert(hart_index != HART_INDEX_UNKNOWN);
+
+ if (hart_index >= 0) {
+ initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_SINGLE);
+ uint32_t index_lo = hart_index & ((1 << DM_DMCONTROL_HARTSELLO_LENGTH) - 1);
+ initial = set_field(initial, DM_DMCONTROL_HARTSELLO, index_lo);
+ uint32_t index_hi = hart_index >> DM_DMCONTROL_HARTSELLO_LENGTH;
+ assert(index_hi < (1 << DM_DMCONTROL_HARTSELHI_LENGTH));
+ initial = set_field(initial, DM_DMCONTROL_HARTSELHI, index_hi);
+ } else if (hart_index == HART_INDEX_MULTIPLE) {
+ initial = set_field(initial, DM_DMCONTROL_HASEL, DM_DMCONTROL_HASEL_MULTIPLE);
+ /* TODO: https://github.com/riscv/riscv-openocd/issues/748 */
+ initial = set_field(initial, DM_DMCONTROL_HARTSELLO, 0);
+ initial = set_field(initial, DM_DMCONTROL_HARTSELHI, 0);
+ }
return initial;
}
-static void decode_dmi(char *text, unsigned address, unsigned data)
+static unsigned int decode_dmi(const struct target *target, char *text, uint32_t address, uint32_t data)
{
static const struct {
- unsigned address;
- uint64_t mask;
- const char *name;
+ uint32_t address;
+ enum riscv_debug_reg_ordinal ordinal;
} description[] = {
- { DM_DMCONTROL, DM_DMCONTROL_HALTREQ, "haltreq" },
- { DM_DMCONTROL, DM_DMCONTROL_RESUMEREQ, "resumereq" },
- { DM_DMCONTROL, DM_DMCONTROL_HARTRESET, "hartreset" },
- { DM_DMCONTROL, DM_DMCONTROL_HASEL, "hasel" },
- { DM_DMCONTROL, DM_DMCONTROL_HARTSELHI, "hartselhi" },
- { DM_DMCONTROL, DM_DMCONTROL_HARTSELLO, "hartsello" },
- { DM_DMCONTROL, DM_DMCONTROL_NDMRESET, "ndmreset" },
- { DM_DMCONTROL, DM_DMCONTROL_DMACTIVE, "dmactive" },
- { DM_DMCONTROL, DM_DMCONTROL_ACKHAVERESET, "ackhavereset" },
-
- { DM_DMSTATUS, DM_DMSTATUS_IMPEBREAK, "impebreak" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLHAVERESET, "allhavereset" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYHAVERESET, "anyhavereset" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLRESUMEACK, "allresumeack" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYRESUMEACK, "anyresumeack" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLNONEXISTENT, "allnonexistent" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYNONEXISTENT, "anynonexistent" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLUNAVAIL, "allunavail" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYUNAVAIL, "anyunavail" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLRUNNING, "allrunning" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYRUNNING, "anyrunning" },
- { DM_DMSTATUS, DM_DMSTATUS_ALLHALTED, "allhalted" },
- { DM_DMSTATUS, DM_DMSTATUS_ANYHALTED, "anyhalted" },
- { DM_DMSTATUS, DM_DMSTATUS_AUTHENTICATED, "authenticated" },
- { DM_DMSTATUS, DM_DMSTATUS_AUTHBUSY, "authbusy" },
- { DM_DMSTATUS, DM_DMSTATUS_HASRESETHALTREQ, "hasresethaltreq" },
- { DM_DMSTATUS, DM_DMSTATUS_CONFSTRPTRVALID, "confstrptrvalid" },
- { DM_DMSTATUS, DM_DMSTATUS_VERSION, "version" },
-
- { DM_ABSTRACTCS, DM_ABSTRACTCS_PROGBUFSIZE, "progbufsize" },
- { DM_ABSTRACTCS, DM_ABSTRACTCS_BUSY, "busy" },
- { DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR, "cmderr" },
- { DM_ABSTRACTCS, DM_ABSTRACTCS_DATACOUNT, "datacount" },
-
- { DM_COMMAND, DM_COMMAND_CMDTYPE, "cmdtype" },
-
- { DM_SBCS, DM_SBCS_SBVERSION, "sbversion" },
- { DM_SBCS, DM_SBCS_SBBUSYERROR, "sbbusyerror" },
- { DM_SBCS, DM_SBCS_SBBUSY, "sbbusy" },
- { DM_SBCS, DM_SBCS_SBREADONADDR, "sbreadonaddr" },
- { DM_SBCS, DM_SBCS_SBACCESS, "sbaccess" },
- { DM_SBCS, DM_SBCS_SBAUTOINCREMENT, "sbautoincrement" },
- { DM_SBCS, DM_SBCS_SBREADONDATA, "sbreadondata" },
- { DM_SBCS, DM_SBCS_SBERROR, "sberror" },
- { DM_SBCS, DM_SBCS_SBASIZE, "sbasize" },
- { DM_SBCS, DM_SBCS_SBACCESS128, "sbaccess128" },
- { DM_SBCS, DM_SBCS_SBACCESS64, "sbaccess64" },
- { DM_SBCS, DM_SBCS_SBACCESS32, "sbaccess32" },
- { DM_SBCS, DM_SBCS_SBACCESS16, "sbaccess16" },
- { DM_SBCS, DM_SBCS_SBACCESS8, "sbaccess8" },
+ {DM_DMCONTROL, DM_DMCONTROL_ORDINAL},
+ {DM_DMSTATUS, DM_DMSTATUS_ORDINAL},
+ {DM_ABSTRACTCS, DM_ABSTRACTCS_ORDINAL},
+ {DM_COMMAND, DM_COMMAND_ORDINAL},
+ {DM_SBCS, DM_SBCS_ORDINAL}
};
- text[0] = 0;
for (unsigned i = 0; i < ARRAY_SIZE(description); i++) {
- if (description[i].address == address) {
- uint64_t mask = description[i].mask;
- unsigned value = get_field(data, mask);
- if (value) {
- if (i > 0)
- *(text++) = ' ';
- if (mask & (mask >> 1)) {
- /* If the field is more than 1 bit wide. */
- sprintf(text, "%s=%d", description[i].name, value);
- } else {
- strcpy(text, description[i].name);
- }
- text += strlen(text);
- }
+ if (riscv_get_dmi_address(target, description[i].address) == address) {
+ const riscv_debug_reg_ctx_t context = {
+ .XLEN = { .value = 0, .is_set = false },
+ .DXLEN = { .value = 0, .is_set = false },
+ .abits = { .value = 0, .is_set = false },
+ };
+ return riscv_debug_reg_to_s(text, description[i].ordinal,
+ context, data, RISCV_DEBUG_REG_HIDE_ALL_0);
}
}
+ if (text)
+ text[0] = '\0';
+ return 0;
}
-static void dump_field(int idle, const struct scan_field *field)
+void riscv_log_dmi_scan(const struct target *target, int idle, const struct scan_field *field, bool discard_in)
{
static const char * const op_string[] = {"-", "r", "w", "?"};
static const char * const status_string[] = {"+", "?", "F", "b"};
@@ -377,29 +400,42 @@ static void dump_field(int idle, const struct scan_field *field)
if (debug_level < LOG_LVL_DEBUG)
return;
- uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
- unsigned int out_op = get_field(out, DTM_DMI_OP);
- unsigned int out_data = get_field(out, DTM_DMI_DATA);
- unsigned int out_address = out >> DTM_DMI_ADDRESS_OFFSET;
-
- uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
- unsigned int in_op = get_field(in, DTM_DMI_OP);
- unsigned int in_data = get_field(in, DTM_DMI_DATA);
- unsigned int in_address = in >> DTM_DMI_ADDRESS_OFFSET;
-
- log_printf_lf(LOG_LVL_DEBUG,
- __FILE__, __LINE__, "scan",
- "%db %s %08x @%02x -> %s %08x @%02x; %di",
- field->num_bits, op_string[out_op], out_data, out_address,
- status_string[in_op], in_data, in_address, idle);
-
- char out_text[500];
- char in_text[500];
- decode_dmi(out_text, out_address, out_data);
- decode_dmi(in_text, in_address, in_data);
- if (in_text[0] || out_text[0]) {
- log_printf_lf(LOG_LVL_DEBUG, __FILE__, __LINE__, "scan", "%s -> %s",
- out_text, in_text);
+ assert(field->out_value);
+ const uint64_t out = buf_get_u64(field->out_value, 0, field->num_bits);
+ const unsigned int out_op = get_field(out, DTM_DMI_OP);
+ const uint32_t out_data = get_field(out, DTM_DMI_DATA);
+ const uint32_t out_address = out >> DTM_DMI_ADDRESS_OFFSET;
+
+ if (field->in_value) {
+ const uint64_t in = buf_get_u64(field->in_value, 0, field->num_bits);
+ const unsigned int in_op = get_field(in, DTM_DMI_OP);
+ const uint32_t in_data = get_field(in, DTM_DMI_DATA);
+ const uint32_t in_address = in >> DTM_DMI_ADDRESS_OFFSET;
+
+ LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> %s %08" PRIx32 " @%02" PRIx32 "; %di",
+ field->num_bits, op_string[out_op], out_data, out_address,
+ status_string[in_op], in_data, in_address, idle);
+
+ if (!discard_in && in_op == DTM_DMI_OP_SUCCESS) {
+ char in_decoded[decode_dmi(target, NULL, in_address, in_data) + 1];
+ decode_dmi(target, in_decoded, in_address, in_data);
+ /* FIXME: The current code assumes that the hardware
+ * provides the read address in the dmi.address field
+ * when returning the dmi.data. That is however not
+ * required by the spec, and therefore not guaranteed.
+ * See https://github.com/riscv-collab/riscv-openocd/issues/1043
+ */
+ LOG_DEBUG("read: %s", in_decoded);
+ }
+ } else {
+ LOG_DEBUG("%db %s %08" PRIx32 " @%02" PRIx32 " -> ?; %di",
+ field->num_bits, op_string[out_op], out_data, out_address,
+ idle);
+ }
+ if (out_op == DTM_DMI_OP_WRITE) {
+ char out_decoded[decode_dmi(target, NULL, out_address, out_data) + 1];
+ decode_dmi(target, out_decoded, out_address, out_data);
+ LOG_DEBUG("write: %s", out_decoded);
}
}
@@ -414,14 +450,14 @@ static void select_dmi(struct target *target)
jtag_add_ir_scan(target->tap, &select_dbus, TAP_IDLE);
}
-static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr)
{
struct scan_field field;
uint8_t in_value[4];
uint8_t out_value[4] = { 0 };
if (bscan_tunnel_ir_width != 0)
- return dtmcontrol_scan_via_bscan(target, out);
+ return dtmcontrol_scan_via_bscan(target, out, in_ptr);
buf_set_u32(out_value, 0, 32, out);
@@ -444,20 +480,40 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
- return in;
+ if (in_ptr)
+ *in_ptr = in;
+ return ERROR_OK;
}
static void increase_dmi_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->dmi_busy_delay += info->dmi_busy_delay / 10 + 1;
- LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+ LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcs_idle, info->dmi_busy_delay,
info->ac_busy_delay);
- dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
}
+static void decrement_reset_delays_counter(struct target *target, size_t finished_scans)
+{
+ RISCV_INFO(r);
+ if (r->reset_delays_wait < 0) {
+ assert(r->reset_delays_wait == -1);
+ return;
+ }
+ if ((size_t)r->reset_delays_wait >= finished_scans) {
+ r->reset_delays_wait -= finished_scans;
+ return;
+ }
+ r->reset_delays_wait = -1;
+ LOG_TARGET_DEBUG(target,
+ "resetting learned delays (reset_delays_wait counter expired)");
+ RISCV013_INFO(info);
+ info->dmi_busy_delay = 0;
+ info->ac_busy_delay = 0;
+}
/**
* exec: If this is set, assume the scan results in an execution, so more
* run-test/idle cycles may be required.
@@ -467,7 +523,6 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
bool exec)
{
riscv013_info_t *info = get_info(target);
- RISCV_INFO(r);
unsigned num_bits = info->abits + DTM_DMI_OP_LENGTH + DTM_DMI_DATA_LENGTH;
size_t num_bytes = (num_bits + 7) / 8;
uint8_t in[num_bytes];
@@ -479,18 +534,15 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
};
riscv_bscan_tunneled_scan_context_t bscan_ctxt;
- if (r->reset_delays_wait >= 0) {
- r->reset_delays_wait--;
- if (r->reset_delays_wait < 0) {
- info->dmi_busy_delay = 0;
- info->ac_busy_delay = 0;
- }
- }
+ decrement_reset_delays_counter(target, 1);
memset(in, 0, num_bytes);
memset(out, 0, num_bytes);
- assert(info->abits != 0);
+ if (info->abits == 0) {
+ LOG_TARGET_ERROR(target, "Can't access DMI because addrbits=0.");
+ return DMI_STATUS_FAILED;
+ }
buf_set_u32(out, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, op);
buf_set_u32(out, DTM_DMI_DATA_OFFSET, DTM_DMI_DATA_LENGTH, data_out);
@@ -534,7 +586,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
if (address_in)
*address_in = buf_get_u32(in, DTM_DMI_ADDRESS_OFFSET, info->abits);
- dump_field(idle_count, &field);
+ riscv_log_dmi_scan(target, idle_count, &field, /*discard_in*/ !data_in);
return buf_get_u32(in, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH);
}
@@ -544,7 +596,7 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
* @param dmi_busy_encountered
* If non-NULL, will be updated to reflect whether DMI busy was
* encountered while executing this operation or not.
- * @param dmi_op The operation to perform (read/write/nop).
+ * @param op The operation to perform (read/write/nop).
* @param address The address argument to that operation.
* @param data_out The data to send to the target.
* @param timeout_sec
@@ -555,19 +607,18 @@ static dmi_status_t dmi_scan(struct target *target, uint32_t *address_in,
* DMI operation succeeded.
*/
static int dmi_op_timeout(struct target *target, uint32_t *data_in,
- bool *dmi_busy_encountered, int dmi_op, uint32_t address,
+ bool *dmi_busy_encountered, int op, uint32_t address,
uint32_t data_out, int timeout_sec, bool exec, bool ensure_success)
{
select_dmi(target);
dmi_status_t status;
- uint32_t address_in;
if (dmi_busy_encountered)
*dmi_busy_encountered = false;
const char *op_name;
- switch (dmi_op) {
+ switch (op) {
case DMI_OP_NOP:
op_name = "nop";
break;
@@ -578,7 +629,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
op_name = "write";
break;
default:
- LOG_ERROR("Invalid DMI operation: %d", dmi_op);
+ LOG_ERROR("Invalid DMI operation: %d", op);
return ERROR_FAIL;
}
@@ -588,7 +639,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
/* This first loop performs the request. Note that if for some reason this
* stays busy, it is actually due to the previous access. */
while (1) {
- status = dmi_scan(target, NULL, NULL, dmi_op, address, data_out,
+ status = dmi_scan(target, NULL, NULL, op, address, data_out,
exec);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
@@ -597,16 +648,15 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
} else if (status == DMI_STATUS_SUCCESS) {
break;
} else {
- LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status);
- dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
- return ERROR_FAIL;
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
+ break;
}
if (time(NULL) - start > timeout_sec)
return ERROR_TIMEOUT_REACHED;
}
if (status != DMI_STATUS_SUCCESS) {
- LOG_ERROR("Failed %s at 0x%x; status=%d", op_name, address, status);
+ LOG_TARGET_ERROR(target, "Failed DMI %s at 0x%x; status=%d", op_name, address, status);
return ERROR_FAIL;
}
@@ -615,7 +665,7 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
* Note that NOP can result in a 'busy' result as well, but that would be
* noticed on the next DMI access we do. */
while (1) {
- status = dmi_scan(target, &address_in, data_in, DMI_OP_NOP, address, 0,
+ status = dmi_scan(target, NULL, data_in, DMI_OP_NOP, address, 0,
false);
if (status == DMI_STATUS_BUSY) {
increase_dmi_busy_delay(target);
@@ -625,13 +675,15 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
break;
} else {
if (data_in) {
- LOG_ERROR("Failed %s (NOP) at 0x%x; value=0x%x, status=%d",
+ LOG_TARGET_ERROR(target,
+ "Failed DMI %s (NOP) at 0x%x; value=0x%x, status=%d",
op_name, address, *data_in, status);
} else {
- LOG_ERROR("Failed %s (NOP) at 0x%x; status=%d", op_name, address,
+ LOG_TARGET_ERROR(target,
+ "Failed DMI %s (NOP) at 0x%x; status=%d", op_name, address,
status);
}
- dtmcontrol_scan(target, DTM_DTMCS_DMIRESET);
+ dtmcontrol_scan(target, DTM_DTMCS_DMIRESET, NULL /* discard result */);
return ERROR_FAIL;
}
if (time(NULL) - start > timeout_sec)
@@ -643,13 +695,13 @@ static int dmi_op_timeout(struct target *target, uint32_t *data_in,
}
static int dmi_op(struct target *target, uint32_t *data_in,
- bool *dmi_busy_encountered, int dmi_op, uint32_t address,
+ bool *dmi_busy_encountered, int op, uint32_t address,
uint32_t data_out, bool exec, bool ensure_success)
{
- int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, dmi_op,
+ int result = dmi_op_timeout(target, data_in, dmi_busy_encountered, op,
address, data_out, riscv_command_timeout_sec, exec, ensure_success);
if (result == ERROR_TIMEOUT_REACHED) {
- LOG_ERROR("DMI operation didn't complete in %d seconds. The target is "
+ LOG_TARGET_ERROR(target, "DMI operation didn't complete in %d seconds. The target is "
"either really slow or broken. You could increase the "
"timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec);
@@ -679,19 +731,113 @@ static int dmi_write_exec(struct target *target, uint32_t address,
return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success);
}
+static uint32_t riscv013_get_dmi_address(const struct target *target, uint32_t address)
+{
+ assert(target);
+ uint32_t base = 0;
+ RISCV013_INFO(info);
+ if (info && info->dm)
+ base = info->dm->base;
+ return address + base;
+}
+
+static int dm_op_timeout(struct target *target, uint32_t *data_in,
+ bool *dmi_busy_encountered, int op, uint32_t address,
+ uint32_t data_out, int timeout_sec, bool exec, bool ensure_success)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ return dmi_op_timeout(target, data_in, dmi_busy_encountered, op, address + dm->base,
+ data_out, timeout_sec, exec, ensure_success);
+}
+
+static int dm_op(struct target *target, uint32_t *data_in,
+ bool *dmi_busy_encountered, int op, uint32_t address,
+ uint32_t data_out, bool exec, bool ensure_success)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ return dmi_op(target, data_in, dmi_busy_encountered, op, address + dm->base,
+ data_out, exec, ensure_success);
+}
+
+static int dm_read(struct target *target, uint32_t *value, uint32_t address)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ return dmi_read(target, value, address + dm->base);
+}
+
+static int dm_read_exec(struct target *target, uint32_t *value, uint32_t address)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ dm->abstract_cmd_maybe_busy = true;
+ return dmi_read_exec(target, value, address + dm->base);
+}
+
+static int dm_write(struct target *target, uint32_t address, uint32_t value)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ return dmi_write(target, address + dm->base, value);
+}
+
+static int dm_write_exec(struct target *target, uint32_t address,
+ uint32_t value, bool ensure_success)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ dm->abstract_cmd_maybe_busy = true;
+ return dmi_write_exec(target, address + dm->base, value, ensure_success);
+}
+
+static bool check_dbgbase_exists(struct target *target)
+{
+ uint32_t next_dm = 0;
+ unsigned int count = 1;
+
+ LOG_TARGET_DEBUG(target, "Searching for DM with DMI base address (dbgbase) = 0x%x", target->dbgbase);
+ while (1) {
+ uint32_t current_dm = next_dm;
+ if (current_dm == target->dbgbase)
+ return true;
+ if (dmi_read(target, &next_dm, DM_NEXTDM + current_dm) != ERROR_OK)
+ break;
+ LOG_TARGET_DEBUG(target, "dm @ 0x%x --> nextdm=0x%x", current_dm, next_dm);
+ /* Check if it's last one in the chain. */
+ if (next_dm == 0) {
+ LOG_TARGET_ERROR(target, "Reached the end of DM chain (detected %u DMs in total).", count);
+ break;
+ }
+ /* Safety: Avoid looping forever in case of buggy nextdm values in the hardware. */
+ if (count++ > RISCV_MAX_DMS) {
+ LOG_TARGET_ERROR(target, "Supporting no more than %d DMs on a DMI bus. Aborting", RISCV_MAX_DMS);
+ break;
+ }
+ }
+ return false;
+}
+
static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
bool authenticated, unsigned timeout_sec)
{
- int result = dmi_op_timeout(target, dmstatus, NULL, DMI_OP_READ,
+ int result = dm_op_timeout(target, dmstatus, NULL, DMI_OP_READ,
DM_DMSTATUS, 0, timeout_sec, false, true);
if (result != ERROR_OK)
return result;
int dmstatus_version = get_field(*dmstatus, DM_DMSTATUS_VERSION);
if (dmstatus_version != 2 && dmstatus_version != 3) {
LOG_ERROR("OpenOCD only supports Debug Module version 2 (0.13) and 3 (1.0), not "
- "%d (dmstatus=0x%x). This error might be caused by a JTAG "
+ "%" PRId32 " (dmstatus=0x%" PRIx32 "). This error might be caused by a JTAG "
"signal issue. Try reducing the JTAG clock speed.",
- get_field(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus);
+ get_field32(*dmstatus, DM_DMSTATUS_VERSION), *dmstatus);
} else if (authenticated && !get_field(*dmstatus, DM_DMSTATUS_AUTHENTICATED)) {
LOG_ERROR("Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
@@ -704,15 +850,21 @@ static int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus,
static int dmstatus_read(struct target *target, uint32_t *dmstatus,
bool authenticated)
{
- return dmstatus_read_timeout(target, dmstatus, authenticated,
+ int result = dmstatus_read_timeout(target, dmstatus, authenticated,
riscv_command_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_TARGET_ERROR(target, "DMSTATUS read didn't complete in %d seconds. The target is "
+ "either really slow or broken. You could increase the "
+ "timeout with `riscv set_command_timeout_sec`.",
+ riscv_command_timeout_sec);
+ return result;
}
static void increase_ac_busy_delay(struct target *target)
{
riscv013_info_t *info = get_info(target);
info->ac_busy_delay += info->ac_busy_delay / 10 + 1;
- LOG_DEBUG("dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
+ LOG_TARGET_DEBUG(target, "dtmcs_idle=%d, dmi_busy_delay=%d, ac_busy_delay=%d",
info->dtmcs_idle, info->dmi_busy_delay,
info->ac_busy_delay);
}
@@ -734,113 +886,156 @@ static uint32_t __attribute__((unused)) abstract_register_size(unsigned width)
static int wait_for_idle(struct target *target, uint32_t *abstractcs)
{
- RISCV013_INFO(info);
+ assert(target);
+ assert(abstractcs);
+
+ dm013_info_t *dm = get_dm(target);
+ if (!dm) {
+ LOG_ERROR("BUG: Target %s is not assigned to any RISC-V debug module",
+ target_name(target));
+ *abstractcs = 0;
+ return ERROR_FAIL;
+ }
+
time_t start = time(NULL);
- while (1) {
- if (dmi_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK)
+ do {
+ if (dm_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) {
+ /* We couldn't read abstractcs. For safety, overwrite the output value to
+ * prevent the caller working with a stale value of abstractcs. */
+ *abstractcs = 0;
+ LOG_TARGET_ERROR(target,
+ "potentially unrecoverable error detected - could not read abstractcs");
return ERROR_FAIL;
+ }
- if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0)
+ if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) {
+ dm->abstract_cmd_maybe_busy = false;
return ERROR_OK;
+ }
+ } while ((time(NULL) - start) < riscv_command_timeout_sec);
- if (time(NULL) - start > riscv_command_timeout_sec) {
- info->cmderr = get_field(*abstractcs, DM_ABSTRACTCS_CMDERR);
- if (info->cmderr != CMDERR_NONE) {
- const char *errors[8] = {
- "none",
- "busy",
- "not supported",
- "exception",
- "halt/resume",
- "reserved",
- "reserved",
- "other" };
-
- LOG_ERROR("Abstract command ended in error '%s' (abstractcs=0x%x)",
- errors[info->cmderr], *abstractcs);
- }
+ LOG_TARGET_ERROR(target,
+ "Timed out after %ds waiting for busy to go low (abstractcs=0x%" PRIx32 "). "
+ "Increase the timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec,
+ *abstractcs);
- LOG_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). "
- "Increase the timeout with riscv set_command_timeout_sec.",
- riscv_command_timeout_sec,
- *abstractcs);
- return ERROR_FAIL;
- }
- }
+ if (!dm->abstract_cmd_maybe_busy)
+ LOG_TARGET_ERROR(target,
+ "BUG: dm->abstract_cmd_maybe_busy had not been set when starting an abstract command.");
+ dm->abstract_cmd_maybe_busy = true;
+
+ return ERROR_TIMEOUT_REACHED;
}
-static int execute_abstract_command(struct target *target, uint32_t command)
+static int dm013_select_target(struct target *target)
{
- RISCV013_INFO(info);
+ riscv013_info_t *info = get_info(target);
+ return dm013_select_hart(target, info->index);
+}
+
+static int execute_abstract_command(struct target *target, uint32_t command,
+ uint32_t *cmderr)
+{
+ assert(cmderr);
+ *cmderr = CMDERR_NONE;
if (debug_level >= LOG_LVL_DEBUG) {
switch (get_field(command, DM_COMMAND_CMDTYPE)) {
case 0:
- LOG_DEBUG("command=0x%x; access register, size=%d, postexec=%d, "
- "transfer=%d, write=%d, regno=0x%x",
- command,
- 8 << get_field(command, AC_ACCESS_REGISTER_AARSIZE),
- get_field(command, AC_ACCESS_REGISTER_POSTEXEC),
- get_field(command, AC_ACCESS_REGISTER_TRANSFER),
- get_field(command, AC_ACCESS_REGISTER_WRITE),
- get_field(command, AC_ACCESS_REGISTER_REGNO));
+ LOG_DEBUG_REG(target, AC_ACCESS_REGISTER, command);
break;
default:
- LOG_DEBUG("command=0x%x", command);
+ LOG_TARGET_DEBUG(target, "command=0x%x", command);
break;
}
}
- if (dmi_write_exec(target, DM_COMMAND, command, false) != ERROR_OK)
+ if (dm_write_exec(target, DM_COMMAND, command, false /* ensure success */) != ERROR_OK)
return ERROR_FAIL;
- uint32_t abstractcs = 0;
- int result = wait_for_idle(target, &abstractcs);
-
- info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR);
- if (info->cmderr != 0 || result != ERROR_OK) {
- LOG_DEBUG("command 0x%x failed; abstractcs=0x%x", command, abstractcs);
- /* Clear the error. */
- dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
+ uint32_t abstractcs;
+ int wait_result = wait_for_idle(target, &abstractcs);
+ if (wait_result != ERROR_OK) {
+ /* TODO: can we recover from this? */
+ if (wait_result == ERROR_TIMEOUT_REACHED)
+ LOG_TARGET_DEBUG(target, "command 0x%" PRIx32 " failed (timeout)", command);
+ else
+ LOG_TARGET_DEBUG(target, "command 0x%" PRIx32 " failed (unknown fatal error %d)", command, wait_result);
+ return wait_result;
+ }
+ *cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR);
+ if (*cmderr != CMDERR_NONE) {
+ LOG_TARGET_DEBUG(target, "command 0x%" PRIx32 " failed; abstractcs=0x%" PRIx32,
+ command, abstractcs);
+ /* Attempt to clear the error. */
+ /* TODO: can we add a more substantial recovery if the clear operation fails ? */
+ if (dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR) != ERROR_OK)
+ LOG_TARGET_ERROR(target, "could not clear abstractcs error");
return ERROR_FAIL;
}
return ERROR_OK;
}
-static riscv_reg_t read_abstract_arg(struct target *target, unsigned index,
- unsigned size_bits)
+static void abstract_data_read_fill_batch(struct riscv_batch *batch, unsigned int index,
+ unsigned int size_bits)
+{
+ assert(size_bits >= 32);
+ assert(size_bits % 32 == 0);
+ const unsigned int size_in_words = size_bits / 32;
+ const unsigned int offset = index * size_in_words;
+ for (unsigned int i = 0; i < size_in_words; ++i) {
+ const unsigned int reg_address = DM_DATA0 + offset + i;
+ riscv_batch_add_dm_read(batch, reg_address);
+ }
+}
+
+static riscv_reg_t abstract_data_get_from_batch(struct riscv_batch *batch,
+ unsigned int index, unsigned int size_bits)
{
+ assert(size_bits >= 32);
+ assert(size_bits % 32 == 0);
+ const unsigned int size_in_words = size_bits / 32;
+ assert(size_in_words * sizeof(uint32_t) <= sizeof(riscv_reg_t));
riscv_reg_t value = 0;
- uint32_t v;
- unsigned offset = index * size_bits / 32;
- switch (size_bits) {
- default:
- LOG_ERROR("Unsupported size: %d bits", size_bits);
- return ~0;
- case 64:
- dmi_read(target, &v, DM_DATA0 + offset + 1);
- value |= ((uint64_t) v) << 32;
- /* falls through */
- case 32:
- dmi_read(target, &v, DM_DATA0 + offset);
- value |= v;
+ for (unsigned int i = 0; i < size_in_words; ++i) {
+ const uint32_t v = riscv_batch_get_dmi_read_data(batch, i);
+ value |= ((riscv_reg_t)v) << (i * 32);
}
return value;
}
+static int batch_run_timeout(struct target *target, struct riscv_batch *batch);
+
+static int read_abstract_arg(struct target *target, riscv_reg_t *value,
+ unsigned int index, unsigned int size_bits)
+{
+ assert(value);
+ assert(size_bits >= 32);
+ assert(size_bits % 32 == 0);
+ const unsigned char size_in_words = size_bits / 32;
+ struct riscv_batch * const batch = riscv_batch_alloc(target, size_in_words);
+ abstract_data_read_fill_batch(batch, index, size_bits);
+ int result = batch_run_timeout(target, batch);
+ if (result == ERROR_OK)
+ *value = abstract_data_get_from_batch(batch, index, size_bits);
+ riscv_batch_free(batch);
+ return result;
+}
+
static int write_abstract_arg(struct target *target, unsigned index,
riscv_reg_t value, unsigned size_bits)
{
unsigned offset = index * size_bits / 32;
switch (size_bits) {
default:
- LOG_ERROR("Unsupported size: %d bits", size_bits);
+ LOG_TARGET_ERROR(target, "Unsupported size: %d bits", size_bits);
return ERROR_FAIL;
case 64:
- dmi_write(target, DM_DATA0 + offset + 1, value >> 32);
+ dm_write(target, DM_DATA0 + offset + 1, (uint32_t)(value >> 32));
/* falls through */
case 32:
- dmi_write(target, DM_DATA0 + offset, value);
+ dm_write(target, DM_DATA0 + offset, (uint32_t)value);
}
return ERROR_OK;
}
@@ -860,8 +1055,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3);
break;
default:
- LOG_ERROR("%d-bit register %s not supported.", size,
- gdb_regno_name(number));
+ LOG_TARGET_ERROR(target, "%d-bit register %s not supported.",
+ size, gdb_regno_name(target, number));
assert(0);
}
@@ -890,8 +1085,8 @@ static uint32_t access_register_command(struct target *target, uint32_t number,
return command;
}
-static int register_read_abstract(struct target *target, uint64_t *value,
- uint32_t number, unsigned size)
+static int register_read_abstract_with_size(struct target *target,
+ riscv_reg_t *value, enum gdb_regno number, unsigned int size)
{
RISCV013_INFO(info);
@@ -908,30 +1103,40 @@ static int register_read_abstract(struct target *target, uint64_t *value,
uint32_t command = access_register_command(target, number, size,
AC_ACCESS_REGISTER_TRANSFER);
- int result = execute_abstract_command(target, command);
+ uint32_t cmderr;
+ int result = execute_abstract_command(target, command, &cmderr);
if (result != ERROR_OK) {
- if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+ if (cmderr == CMDERR_NOT_SUPPORTED) {
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
info->abstract_read_fpr_supported = false;
- LOG_INFO("Disabling abstract command reads from FPRs.");
+ LOG_TARGET_INFO(target, "Disabling abstract command reads from FPRs.");
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
info->abstract_read_csr_supported = false;
- LOG_INFO("Disabling abstract command reads from CSRs.");
+ LOG_TARGET_INFO(target, "Disabling abstract command reads from CSRs.");
}
}
return result;
}
if (value)
- *value = read_abstract_arg(target, 0, size);
+ return read_abstract_arg(target, value, 0, size);
return ERROR_OK;
}
-static int register_write_abstract(struct target *target, uint32_t number,
- uint64_t value, unsigned size)
+static int register_read_abstract(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number)
+{
+ const unsigned int size = register_size(target, number);
+
+ return register_read_abstract_with_size(target, value, number, size);
+}
+
+static int register_write_abstract(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
{
RISCV013_INFO(info);
+ const unsigned int size = register_size(target, number);
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
!info->abstract_write_fpr_supported)
@@ -947,15 +1152,16 @@ static int register_write_abstract(struct target *target, uint32_t number,
if (write_abstract_arg(target, 0, value, size) != ERROR_OK)
return ERROR_FAIL;
- int result = execute_abstract_command(target, command);
+ uint32_t cmderr;
+ int result = execute_abstract_command(target, command, &cmderr);
if (result != ERROR_OK) {
- if (info->cmderr == CMDERR_NOT_SUPPORTED) {
+ if (cmderr == CMDERR_NOT_SUPPORTED) {
if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
info->abstract_write_fpr_supported = false;
- LOG_INFO("Disabling abstract command writes to FPRs.");
+ LOG_TARGET_INFO(target, "Disabling abstract command writes to FPRs.");
} else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
info->abstract_write_csr_supported = false;
- LOG_INFO("Disabling abstract command writes to CSRs.");
+ LOG_TARGET_INFO(target, "Disabling abstract command writes to CSRs.");
}
}
return result;
@@ -991,14 +1197,14 @@ static uint32_t abstract_memory_size(unsigned width)
* Creates a memory access abstract command.
*/
static uint32_t access_memory_command(struct target *target, bool virtual,
- unsigned width, bool postincrement, bool write)
+ unsigned int width, bool postincrement, bool is_write)
{
uint32_t command = set_field(0, AC_ACCESS_MEMORY_CMDTYPE, 2);
command = set_field(command, AC_ACCESS_MEMORY_AAMVIRTUAL, virtual);
command |= abstract_memory_size(width);
command = set_field(command, AC_ACCESS_MEMORY_AAMPOSTINCREMENT,
postincrement);
- command = set_field(command, AC_ACCESS_MEMORY_WRITE, write);
+ command = set_field(command, AC_ACCESS_MEMORY_WRITE, is_write);
return command;
}
@@ -1014,12 +1220,11 @@ static int examine_progbuf(struct target *target)
if (info->progbufsize < 1) {
info->progbuf_writable = YNM_NO;
- LOG_INFO("No program buffer present.");
+ LOG_TARGET_INFO(target, "No program buffer present.");
return ERROR_OK;
}
- uint64_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
struct riscv_program program;
@@ -1035,9 +1240,6 @@ static int examine_progbuf(struct target *target)
riscv_program_insert(&program, sw(S0, S0, 0));
int result = riscv_program_exec(&program, target);
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
- return ERROR_FAIL;
-
if (result != ERROR_OK) {
/* This program might have failed if the program buffer is not
* writable. */
@@ -1046,15 +1248,15 @@ static int examine_progbuf(struct target *target)
}
uint32_t written;
- if (dmi_read(target, &written, DM_PROGBUF0) != ERROR_OK)
+ if (dm_read(target, &written, DM_PROGBUF0) != ERROR_OK)
return ERROR_FAIL;
if (written == (uint32_t) info->progbuf_address) {
- LOG_INFO("progbuf is writable at 0x%" PRIx64,
+ LOG_TARGET_INFO(target, "progbuf is writable at 0x%" PRIx64,
info->progbuf_address);
info->progbuf_writable = YNM_YES;
} else {
- LOG_INFO("progbuf is not writeable at 0x%" PRIx64,
+ LOG_TARGET_INFO(target, "progbuf is not writeable at 0x%" PRIx64,
info->progbuf_address);
info->progbuf_writable = YNM_NO;
}
@@ -1062,7 +1264,7 @@ static int examine_progbuf(struct target *target)
return ERROR_OK;
}
-static int is_fpu_reg(uint32_t gdb_regno)
+static int is_fpu_reg(enum gdb_regno gdb_regno)
{
return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) ||
(gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) ||
@@ -1070,46 +1272,66 @@ static int is_fpu_reg(uint32_t gdb_regno)
(gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR);
}
-static int is_vector_reg(uint32_t gdb_regno)
+static int is_vector_reg(enum gdb_regno gdb_regno)
{
return (gdb_regno >= GDB_REGNO_V0 && gdb_regno <= GDB_REGNO_V31) ||
gdb_regno == GDB_REGNO_VSTART ||
gdb_regno == GDB_REGNO_VXSAT ||
gdb_regno == GDB_REGNO_VXRM ||
+ gdb_regno == GDB_REGNO_VCSR ||
gdb_regno == GDB_REGNO_VL ||
gdb_regno == GDB_REGNO_VTYPE ||
gdb_regno == GDB_REGNO_VLENB;
}
-static int prep_for_register_access(struct target *target, uint64_t *mstatus,
- int regno)
+static int prep_for_register_access(struct target *target,
+ riscv_reg_t *orig_mstatus, enum gdb_regno regno)
{
- if (is_fpu_reg(regno) || is_vector_reg(regno)) {
- if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
- return ERROR_FAIL;
- if (is_fpu_reg(regno) && (*mstatus & MSTATUS_FS) == 0) {
- if (register_write_direct(target, GDB_REGNO_MSTATUS,
- set_field(*mstatus, MSTATUS_FS, 1)) != ERROR_OK)
- return ERROR_FAIL;
- } else if (is_vector_reg(regno) && (*mstatus & MSTATUS_VS) == 0) {
- if (register_write_direct(target, GDB_REGNO_MSTATUS,
- set_field(*mstatus, MSTATUS_VS, 1)) != ERROR_OK)
- return ERROR_FAIL;
- }
- } else {
- *mstatus = 0;
+ assert(orig_mstatus);
+
+ if (!is_fpu_reg(regno) && !is_vector_reg(regno)) {
+ /* If we don't assign orig_mstatus, clang static analysis
+ * complains when this value is passed to
+ * cleanup_after_register_access(). */
+ *orig_mstatus = 0;
+ /* No special preparation needed */
+ return ERROR_OK;
}
+
+ LOG_TARGET_DEBUG(target, "Preparing mstatus to access %s",
+ gdb_regno_name(target, regno));
+
+ assert(target->state == TARGET_HALTED &&
+ "The target must be halted to modify and then restore mstatus");
+
+ if (riscv_get_register(target, orig_mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ return ERROR_FAIL;
+
+ riscv_reg_t new_mstatus = *orig_mstatus;
+ riscv_reg_t field_mask = is_fpu_reg(regno) ? MSTATUS_FS : MSTATUS_VS;
+
+ if ((new_mstatus & field_mask) != 0)
+ return ERROR_OK;
+
+ new_mstatus = set_field(new_mstatus, field_mask, 1);
+
+ if (riscv_write_register(target, GDB_REGNO_MSTATUS, new_mstatus) != ERROR_OK)
+ return ERROR_FAIL;
+
+ LOG_TARGET_DEBUG(target, "Prepared to access %s (mstatus=0x%" PRIx64 ")",
+ gdb_regno_name(target, regno), new_mstatus);
return ERROR_OK;
}
static int cleanup_after_register_access(struct target *target,
- uint64_t mstatus, int regno)
+ riscv_reg_t mstatus, enum gdb_regno regno)
{
- if ((is_fpu_reg(regno) && (mstatus & MSTATUS_FS) == 0) ||
- (is_vector_reg(regno) && (mstatus & MSTATUS_VS) == 0))
- if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus) != ERROR_OK)
- return ERROR_FAIL;
- return ERROR_OK;
+ if (!is_fpu_reg(regno) && !is_vector_reg(regno))
+ /* Mstatus was not changed for this register access. No need to restore it. */
+ return ERROR_OK;
+
+ LOG_TARGET_DEBUG(target, "Restoring mstatus to 0x%" PRIx64, mstatus);
+ return riscv_write_register(target, GDB_REGNO_MSTATUS, mstatus);
}
typedef enum {
@@ -1187,7 +1409,7 @@ static int scratch_reserve(struct target *target,
return ERROR_OK;
}
- LOG_ERROR("Couldn't find %d bytes of scratch RAM to use. Please configure "
+ LOG_TARGET_ERROR(target, "Couldn't find %d bytes of scratch RAM to use. Please configure "
"a work area with 'configure -work-area-phys'.", size_bytes);
return ERROR_FAIL;
}
@@ -1204,18 +1426,18 @@ static int scratch_read64(struct target *target, scratch_mem_t *scratch,
uint32_t v;
switch (scratch->memory_space) {
case SPACE_DM_DATA:
- if (dmi_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK)
+ if (dm_read(target, &v, DM_DATA0 + scratch->debug_address) != ERROR_OK)
return ERROR_FAIL;
*value = v;
- if (dmi_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK)
+ if (dm_read(target, &v, DM_DATA1 + scratch->debug_address) != ERROR_OK)
return ERROR_FAIL;
*value |= ((uint64_t) v) << 32;
break;
case SPACE_DMI_PROGBUF:
- if (dmi_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK)
+ if (dm_read(target, &v, DM_PROGBUF0 + scratch->debug_address) != ERROR_OK)
return ERROR_FAIL;
*value = v;
- if (dmi_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK)
+ if (dm_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK)
return ERROR_FAIL;
*value |= ((uint64_t) v) << 32;
break;
@@ -1243,12 +1465,13 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
{
switch (scratch->memory_space) {
case SPACE_DM_DATA:
- dmi_write(target, DM_DATA0 + scratch->debug_address, value);
- dmi_write(target, DM_DATA1 + scratch->debug_address, value >> 32);
+ dm_write(target, DM_DATA0 + scratch->debug_address, (uint32_t)value);
+ dm_write(target, DM_DATA1 + scratch->debug_address, (uint32_t)(value >> 32));
break;
case SPACE_DMI_PROGBUF:
- dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value);
- dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32);
+ dm_write(target, DM_PROGBUF0 + scratch->debug_address, (uint32_t)value);
+ dm_write(target, DM_PROGBUF1 + scratch->debug_address, (uint32_t)(value >> 32));
+ riscv013_invalidate_cached_progbuf(target);
break;
case SPACE_DMI_RAM:
{
@@ -1271,7 +1494,7 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch,
}
/** Return register size in bits. */
-static unsigned register_size(struct target *target, unsigned number)
+static unsigned int register_size(struct target *target, enum gdb_regno number)
{
/* If reg_cache hasn't been initialized yet, make a guess. We need this for
* when this function is called during examine(). */
@@ -1290,204 +1513,308 @@ static bool has_sufficient_progbuf(struct target *target, unsigned size)
}
/**
- * Immediately write the new value to the requested register. This mechanism
- * bypasses any caches.
+ * This function is used to read a 64-bit value from a register by executing a
+ * program.
+ * The program stores a register to address located in S0.
+ * The caller should save S0.
*/
-static int register_write_direct(struct target *target, unsigned number,
- uint64_t value)
+static int internal_register_read64_progbuf_scratch(struct target *target,
+ struct riscv_program *program, riscv_reg_t *value)
{
- LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target),
- gdb_regno_name(number), value);
+ scratch_mem_t scratch;
- int result = register_write_abstract(target, number, value,
- register_size(target, number));
- if (result == ERROR_OK || !has_sufficient_progbuf(target, 2) ||
- !riscv_is_halted(target))
- return result;
+ if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ if (riscv_program_exec(program, target) != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+
+ int result = scratch_read64(target, &scratch, value);
+
+ scratch_release(target, &scratch);
+ return result;
+}
+
+static int fpr_read_progbuf(struct target *target, uint64_t *value,
+ enum gdb_regno number)
+{
+ assert(target->state == TARGET_HALTED);
+ assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31);
+
+ const unsigned int freg = number - GDB_REGNO_FPR0;
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
struct riscv_program program;
riscv_program_init(&program, target);
+ if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a
+ * register, so we need to use some scratch RAM.
+ */
+ if (riscv_program_insert(&program, fsd(freg, S0, 0)) != ERROR_OK)
+ return ERROR_FAIL;
+ return internal_register_read64_progbuf_scratch(target, &program, value);
+ }
+ if (riscv_program_insert(&program,
+ riscv_supports_extension(target, 'D') ?
+ fmv_x_d(S0, freg) : fmv_x_w(S0, freg)) != ERROR_OK)
+ return ERROR_FAIL;
- uint64_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+ if (riscv_program_exec(&program, target) != ERROR_OK)
return ERROR_FAIL;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK;
+}
+
+static int csr_read_progbuf(struct target *target, uint64_t *value,
+ enum gdb_regno number)
+{
+ assert(target->state == TARGET_HALTED);
+ assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ if (riscv_program_csrr(&program, S0, number) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_exec(&program, target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return register_read_abstract(target, value, GDB_REGNO_S0) != ERROR_OK;
+}
+
+/**
+ * This function reads a register by writing a program to program buffer and
+ * executing it.
+ */
+static int register_read_progbuf(struct target *target, uint64_t *value,
+ enum gdb_regno number)
+{
+ assert(target->state == TARGET_HALTED);
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31)
+ return fpr_read_progbuf(target, value, number);
+ else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095)
+ return csr_read_progbuf(target, value, number);
+
+ LOG_TARGET_ERROR(target, "Unexpected read of %s via program buffer.",
+ gdb_regno_name(target, number));
+ return ERROR_FAIL;
+}
+
+/**
+ * This function is used to write a 64-bit value to a register by executing a
+ * program.
+ * The program loads a value from address located in S0 to a register.
+ * The caller should save S0.
+ */
+static int internal_register_write64_progbuf_scratch(struct target *target,
+ struct riscv_program *program, riscv_reg_t value)
+{
scratch_mem_t scratch;
- bool use_scratch = false;
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 &&
- riscv_supports_extension(target, 'D') &&
- riscv_xlen(target) < 64) {
- /* There are no instructions to move all the bits from a register, so
- * we need to use some scratch RAM. */
- use_scratch = true;
- riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0));
-
- if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, scratch.hart_address)
- != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
+ if (scratch_reserve(target, &scratch, program, 8) != ERROR_OK)
+ return ERROR_FAIL;
- if (scratch_write64(target, &scratch, value) != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
+ if (register_write_abstract(target, GDB_REGNO_S0, scratch.hart_address)
+ != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ if (scratch_write64(target, &scratch, value) != ERROR_OK) {
+ scratch_release(target, &scratch);
+ return ERROR_FAIL;
+ }
+ int result = riscv_program_exec(program, target);
- } else if (number == GDB_REGNO_VTYPE) {
- riscv_program_insert(&program, csrr(S0, CSR_VL));
- riscv_program_insert(&program, vsetvli(ZERO, S0, value));
+ scratch_release(target, &scratch);
+ return result;
+}
- } else {
- if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK)
- return ERROR_FAIL;
+static int fpr_write_progbuf(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
+{
+ assert(target->state == TARGET_HALTED);
+ assert(number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31);
+ const unsigned int freg = number - GDB_REGNO_FPR0;
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (riscv_supports_extension(target, 'D'))
- riscv_program_insert(&program, fmv_d_x(number - GDB_REGNO_FPR0, S0));
- else
- riscv_program_insert(&program, fmv_w_x(number - GDB_REGNO_FPR0, S0));
- } else if (number == GDB_REGNO_VL) {
- /* "The XLEN-bit-wide read-only vl CSR can only be updated by the
- * vsetvli and vsetvl instructions, and the fault-only-rst vector
- * load instruction variants." */
- riscv_reg_t vtype;
- if (register_read(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_program_insert(&program, vsetvli(ZERO, S0, vtype)) != ERROR_OK)
- return ERROR_FAIL;
- } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
- riscv_program_csrw(&program, S0, number);
- } else {
- LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number);
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+
+ if (riscv_supports_extension(target, 'D') && riscv_xlen(target) < 64) {
+ /* There are no instructions to move all the bits from a register,
+ * so we need to use some scratch RAM.
+ */
+ if (riscv_program_insert(&program, fld(freg, S0, 0)) != ERROR_OK)
return ERROR_FAIL;
- }
+ return internal_register_write64_progbuf_scratch(target, &program, value);
}
- int exec_out = riscv_program_exec(&program, target);
- /* Don't message on error. Probably the register doesn't exist. */
- if (exec_out == ERROR_OK && target->reg_cache) {
- struct reg *reg = &target->reg_cache->reg_list[number];
- buf_set_u64(reg->value, 0, reg->size, value);
- }
+ if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
- if (use_scratch)
- scratch_release(target, &scratch);
+ if (riscv_program_insert(&program,
+ riscv_supports_extension(target, 'D') ?
+ fmv_d_x(freg, S0) : fmv_w_x(freg, S0)) != ERROR_OK)
+ return ERROR_FAIL;
- if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return riscv_program_exec(&program, target);
+}
+
+static int vtype_write_progbuf(struct target *target, riscv_reg_t value)
+{
+ assert(target->state == TARGET_HALTED);
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
- /* Restore S0. */
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ if (riscv_program_insert(&program, csrr(S1, CSR_VL)) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, vsetvl(ZERO, S1, S0)) != ERROR_OK)
return ERROR_FAIL;
- return exec_out;
+ return riscv_program_exec(&program, target);
}
-/** Read register value from the target. Also update the cached value. */
-static int register_read(struct target *target, uint64_t *value, uint32_t number)
+static int vl_write_progbuf(struct target *target, riscv_reg_t value)
{
- if (number == GDB_REGNO_ZERO) {
- *value = 0;
- return ERROR_OK;
- }
- int result = register_read_direct(target, value, number);
- if (result != ERROR_OK)
+ assert(target->state == TARGET_HALTED);
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- if (target->reg_cache) {
- struct reg *reg = &target->reg_cache->reg_list[number];
- buf_set_u64(reg->value, 0, reg->size, *value);
- }
- return ERROR_OK;
+ if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ if (riscv_program_insert(&program, csrr(S1, CSR_VTYPE)) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_insert(&program, vsetvl(ZERO, S0, S1)) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return riscv_program_exec(&program, target);
}
-/** Actually read registers from the target right now. */
-static int register_read_direct(struct target *target, uint64_t *value, uint32_t number)
+static int csr_write_progbuf(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
{
- int result = register_read_abstract(target, value, number,
- register_size(target, number));
+ assert(target->state == TARGET_HALTED);
+ assert(number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095);
- if (result != ERROR_OK &&
- has_sufficient_progbuf(target, 2) &&
- number > GDB_REGNO_XPR31) {
- struct riscv_program program;
- riscv_program_init(&program, target);
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (register_write_abstract(target, GDB_REGNO_S0, value) != ERROR_OK)
+ return ERROR_FAIL;
- scratch_mem_t scratch;
- bool use_scratch = false;
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ if (riscv_program_csrw(&program, S0, number) != ERROR_OK)
+ return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
+ return riscv_program_exec(&program, target);
+}
- /* Write program to move data into s0. */
+/**
+ * This function writes a register by writing a program to program buffer and
+ * executing it.
+ */
+static int register_write_progbuf(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
+{
+ assert(target->state == TARGET_HALTED);
+
+ if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31)
+ return fpr_write_progbuf(target, number, value);
+ else if (number == GDB_REGNO_VTYPE)
+ return vtype_write_progbuf(target, value);
+ else if (number == GDB_REGNO_VL)
+ return vl_write_progbuf(target, value);
+ else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095)
+ return csr_write_progbuf(target, number, value);
+
+ LOG_TARGET_ERROR(target, "Unexpected write to %s via program buffer.",
+ gdb_regno_name(target, number));
+ return ERROR_FAIL;
+}
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
- return ERROR_FAIL;
+/**
+ * Immediately write the new value to the requested register. This mechanism
+ * bypasses any caches.
+ */
+static int register_write_direct(struct target *target, enum gdb_regno number,
+ riscv_reg_t value)
+{
+ LOG_TARGET_DEBUG(target, "Writing 0x%" PRIx64 " to %s", value,
+ gdb_regno_name(target, number));
- if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- if (riscv_supports_extension(target, 'D')
- && riscv_xlen(target) < 64) {
- /* There are no instructions to move all the bits from a
- * register, so we need to use some scratch RAM. */
- riscv_program_insert(&program, fsd(number - GDB_REGNO_FPR0, S0,
- 0));
+ if (target->state != TARGET_HALTED)
+ return register_write_abstract(target, number, value);
- if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK)
- return ERROR_FAIL;
- use_scratch = true;
+ riscv_reg_t mstatus;
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0,
- scratch.hart_address) != ERROR_OK) {
- scratch_release(target, &scratch);
- return ERROR_FAIL;
- }
- } else if (riscv_supports_extension(target, 'D')) {
- riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0));
- } else {
- riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0));
- }
- } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
- riscv_program_csrr(&program, S0, number);
- } else {
- LOG_ERROR("Unsupported register: %s", gdb_regno_name(number));
- return ERROR_FAIL;
- }
+ int result = register_write_abstract(target, number, value);
- /* Execute program. */
- result = riscv_program_exec(&program, target);
- /* Don't message on error. Probably the register doesn't exist. */
+ if (result != ERROR_OK && target->state == TARGET_HALTED)
+ result = register_write_progbuf(target, number, value);
- if (use_scratch) {
- result = scratch_read64(target, &scratch, value);
- scratch_release(target, &scratch);
- if (result != ERROR_OK)
- return result;
- } else {
- /* Read S0 */
- if (register_read_direct(target, value, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
- }
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
- if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
- return ERROR_FAIL;
+ if (result == ERROR_OK)
+ LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(target, number),
+ value);
- /* Restore S0. */
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
- return ERROR_FAIL;
- }
+ return result;
+}
- if (result == ERROR_OK) {
- LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target),
- gdb_regno_name(number), *value);
- }
+/** Actually read registers from the target right now. */
+static int register_read_direct(struct target *target, riscv_reg_t *value,
+ enum gdb_regno number)
+{
+ LOG_TARGET_DEBUG(target, "Reading %s", gdb_regno_name(target, number));
+
+ if (target->state != TARGET_HALTED)
+ return register_read_abstract(target, value, number);
+
+ riscv_reg_t mstatus;
+
+ if (prep_for_register_access(target, &mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
+
+ int result = register_read_abstract(target, value, number);
+
+ if (result != ERROR_OK && target->state == TARGET_HALTED)
+ result = register_read_progbuf(target, value, number);
+
+ if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (result == ERROR_OK)
+ LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(target, number),
+ *value);
return result;
}
@@ -1504,7 +1831,7 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
if (!get_field(value, DM_DMSTATUS_AUTHBUSY))
break;
if (time(NULL) - start > riscv_command_timeout_sec) {
- LOG_ERROR("Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). "
+ LOG_TARGET_ERROR(target, "Timed out after %ds waiting for authbusy to go low (dmstatus=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec,
value);
@@ -1515,125 +1842,374 @@ static int wait_for_authbusy(struct target *target, uint32_t *dmstatus)
return ERROR_OK;
}
+static int set_dcsr_ebreak(struct target *target, bool step)
+{
+ LOG_TARGET_DEBUG(target, "Set dcsr.ebreak*");
+
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
+ riscv_reg_t original_dcsr, dcsr;
+ /* We want to twiddle some bits in the debug CSR so debugging works. */
+ if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+ original_dcsr = dcsr;
+ dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, r->riscv_ebreakm);
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, r->riscv_ebreaks && riscv_supports_extension(target, 'S'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, r->riscv_ebreaku && riscv_supports_extension(target, 'U'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKVS, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
+ dcsr = set_field(dcsr, CSR_DCSR_EBREAKVU, r->riscv_ebreaku && riscv_supports_extension(target, 'H'));
+ if (dcsr != original_dcsr &&
+ riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != ERROR_OK)
+ return ERROR_FAIL;
+ info->dcsr_ebreak_is_set = true;
+ return ERROR_OK;
+}
+
+static int halt_set_dcsr_ebreak(struct target *target)
+{
+ RISCV_INFO(r);
+ RISCV013_INFO(info);
+ LOG_TARGET_DEBUG(target, "Halt to set DCSR.ebreak*");
+
+ /* Remove this hart from the halt group. This won't work on all targets
+ * because the debug spec allows halt groups to be hard-coded, but I
+ * haven't actually encountered those in the wild yet.
+ *
+ * There is a possible race condition when another hart halts, and
+ * this one is expected to also halt because it's supposed to be in the
+ * same halt group. Or when this hart is halted when that happens.
+ *
+ * A better solution might be to leave the halt groups alone, and track
+ * why we're halting when a halt occurs. When there are halt groups,
+ * that leads to extra halting if not all harts need to set dcsr.ebreak
+ * at the same time. It also makes for more complicated code.
+ *
+ * The perfect solution would be Quick Access, but I'm not aware of any
+ * hardware that implements it.
+ *
+ * We don't need a perfect solution, because we only get here when a
+ * hart spontaneously resets, or when it powers down and back up again.
+ * Those are both relatively rare. (At least I hope so. Maybe some
+ * design just powers each hart down for 90ms out of every 100ms)
+ */
+
+
+ if (info->haltgroup_supported) {
+ bool supported;
+ if (set_group(target, &supported, 0, HALT_GROUP) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!supported)
+ LOG_TARGET_ERROR(target, "Couldn't place hart in halt group 0. "
+ "Some harts may be unexpectedly halted.");
+ }
+
+ int result = ERROR_OK;
+
+ r->prepped = true;
+ if (riscv013_halt_go(target) != ERROR_OK ||
+ set_dcsr_ebreak(target, false) != ERROR_OK ||
+ riscv013_step_or_resume_current_hart(target, false) != ERROR_OK) {
+ result = ERROR_FAIL;
+ } else {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ }
+
+ /* Add it back to the halt group. */
+ if (info->haltgroup_supported) {
+ bool supported;
+ if (set_group(target, &supported, target->smp, HALT_GROUP) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!supported)
+ LOG_TARGET_ERROR(target, "Couldn't place hart back in halt group %d. "
+ "Some harts may be unexpectedly halted.", target->smp);
+ }
+
+ return result;
+}
+
/*** OpenOCD target functions. ***/
static void deinit_target(struct target *target)
{
- LOG_DEBUG("riscv_deinit_target()");
+ LOG_TARGET_DEBUG(target, "Deinitializing target.");
struct riscv_info *info = target->arch_info;
if (!info)
return;
+ riscv013_dm_free(target);
+
free(info->version_specific);
/* TODO: free register arch_info */
info->version_specific = NULL;
}
-static int set_haltgroup(struct target *target, bool *supported)
+static int set_group(struct target *target, bool *supported, unsigned int group,
+ grouptype_t grouptype)
{
- uint32_t write = set_field(DM_DMCS2_HGWRITE, DM_DMCS2_GROUP, target->smp);
- if (dmi_write(target, DM_DMCS2, write) != ERROR_OK)
+ uint32_t write_val = DM_DMCS2_HGWRITE;
+ assert(group <= 31);
+ write_val = set_field(write_val, DM_DMCS2_GROUP, group);
+ write_val = set_field(write_val, DM_DMCS2_GROUPTYPE, (grouptype == HALT_GROUP) ? 0 : 1);
+ if (dm_write(target, DM_DMCS2, write_val) != ERROR_OK)
return ERROR_FAIL;
- uint32_t read;
- if (dmi_read(target, &read, DM_DMCS2) != ERROR_OK)
+ uint32_t read_val;
+ if (dm_read(target, &read_val, DM_DMCS2) != ERROR_OK)
return ERROR_FAIL;
- *supported = get_field(read, DM_DMCS2_GROUP) == (unsigned)target->smp;
+ if (supported)
+ *supported = (get_field(read_val, DM_DMCS2_GROUP) == group);
return ERROR_OK;
}
-static int discover_vlenb(struct target *target)
+static int wait_for_idle_if_needed(struct target *target)
{
- RISCV_INFO(r);
- riscv_reg_t vlenb;
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ if (!dm->abstract_cmd_maybe_busy)
+ /* The previous abstract command ended correctly
+ * and busy was cleared. No need to do anything. */
+ return ERROR_OK;
- if (register_read(target, &vlenb, GDB_REGNO_VLENB) != ERROR_OK) {
- LOG_WARNING("Couldn't read vlenb for %s; vector register access won't work.",
- target_name(target));
- r->vlenb = 0;
+ /* The previous abstract command timed out and abstractcs.busy
+ * may have remained set. Wait for it to get cleared. */
+ uint32_t abstractcs;
+ int result = wait_for_idle(target, &abstractcs);
+ if (result != ERROR_OK)
+ return result;
+ LOG_DEBUG_REG(target, DM_ABSTRACTCS, abstractcs);
+ return ERROR_OK;
+}
+
+static int reset_dm(struct target *target)
+{
+ /* TODO: This function returns an error when a DMI operation fails.
+ * However, [3.14.2. Debug Module Control] states:
+ * > 0 (inactive): ... Any accesses to the module may fail.
+ *
+ * Ignoring failures may introduce incompatibility with 0.13.
+ * See https://github.com/riscv/riscv-debug-spec/issues/1021
+ */
+ dm013_info_t *dm = get_dm(target);
+ assert(dm && "DM is expected to be already allocated.");
+ assert(!dm->was_reset && "Attempt to reset an already-reset debug module.");
+ /* `dmcontrol.hartsel` should be read first, in order not to
+ * change it when requesting the reset, since changing it
+ * without checking that `abstractcs.busy` is low is
+ * prohibited.
+ */
+ uint32_t dmcontrol;
+ int result = dm_read(target, &dmcontrol, DM_DMCONTROL);
+ if (result != ERROR_OK)
+ return result;
+
+ if (get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE)) {
+ /* `dmcontrol.hartsel` is not changed. */
+ dmcontrol = (dmcontrol & DM_DMCONTROL_HARTSELLO) |
+ (dmcontrol & DM_DMCONTROL_HARTSELHI);
+ LOG_TARGET_DEBUG(target, "Initiating DM reset.");
+ result = dm_write(target, DM_DMCONTROL, dmcontrol);
+ if (result != ERROR_OK)
+ return result;
+
+ const time_t start = time(NULL);
+ LOG_TARGET_DEBUG(target, "Waiting for the DM to acknowledge reset.");
+ do {
+ result = dm_read(target, &dmcontrol, DM_DMCONTROL);
+ if (result != ERROR_OK)
+ return result;
+
+ if (time(NULL) - start > riscv_reset_timeout_sec) {
+ /* TODO: Introduce a separate timeout for this. */
+ LOG_TARGET_ERROR(target, "DM didn't acknowledge reset in %d s. "
+ "Increase the timeout with 'riscv set_reset_timeout_sec'.",
+ riscv_reset_timeout_sec);
+ return ERROR_TIMEOUT_REACHED;
+ }
+ } while (get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE));
+ LOG_TARGET_DEBUG(target, "DM reset initiated.");
+ }
+
+ LOG_TARGET_DEBUG(target, "Activating the DM.");
+ result = dm_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
+ if (result != ERROR_OK)
+ return result;
+
+ const time_t start = time(NULL);
+ LOG_TARGET_DEBUG(target, "Waiting for the DM to come out of reset.");
+ do {
+ result = dm_read(target, &dmcontrol, DM_DMCONTROL);
+ if (result != ERROR_OK)
+ return result;
+
+ if (time(NULL) - start > riscv_reset_timeout_sec) {
+ /* TODO: Introduce a separate timeout for this. */
+ LOG_TARGET_ERROR(target, "Debug Module did not become active in %d s. "
+ "Increase the timeout with 'riscv set_reset_timeout_sec'.",
+ riscv_reset_timeout_sec);
+ return ERROR_TIMEOUT_REACHED;
+ }
+ } while (!get_field32(dmcontrol, DM_DMCONTROL_DMACTIVE));
+
+ LOG_TARGET_DEBUG(target, "DM successfully reset.");
+ dm->was_reset = true;
+ return ERROR_OK;
+}
+
+static int examine_dm(struct target *target)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ if (dm->was_examined)
return ERROR_OK;
+
+ int result = ERROR_FAIL;
+
+ if (dm->was_reset) {
+ /* The DM was already reset when examining a different hart.
+ * No need to reset it again. But for safety, assume that an abstract
+ * command might be in progress at the moment.
+ */
+ dm->abstract_cmd_maybe_busy = true;
+ } else {
+ result = reset_dm(target);
+ if (result != ERROR_OK)
+ return result;
+ }
+
+ dm->current_hartid = HART_INDEX_UNKNOWN;
+
+ result = dm_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO |
+ DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE |
+ DM_DMCONTROL_HASEL);
+ if (result != ERROR_OK)
+ return result;
+
+ uint32_t dmcontrol;
+ result = dm_read(target, &dmcontrol, DM_DMCONTROL);
+ if (result != ERROR_OK)
+ return result;
+
+ dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL);
+
+ uint32_t hartsel =
+ (get_field(dmcontrol, DM_DMCONTROL_HARTSELHI) <<
+ DM_DMCONTROL_HARTSELLO_LENGTH) |
+ get_field(dmcontrol, DM_DMCONTROL_HARTSELLO);
+
+ /* Before doing anything else we must first enumerate the harts. */
+ const int max_hart_count = MIN(RISCV_MAX_HARTS, hartsel + 1);
+ if (dm->hart_count < 0) {
+ for (int i = 0; i < max_hart_count; ++i) {
+ /* TODO: This is extremely similar to
+ * riscv013_get_hart_state().
+ * It would be best to reuse the code.
+ */
+ result = dm013_select_hart(target, i);
+ if (result != ERROR_OK)
+ return result;
+
+ uint32_t s;
+ result = dmstatus_read(target, &s, /*authenticated*/ true);
+ if (result != ERROR_OK)
+ return result;
+
+ if (get_field(s, DM_DMSTATUS_ANYNONEXISTENT))
+ break;
+
+ dm->hart_count = i + 1;
+
+ if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) {
+ dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET;
+ /* If `abstractcs.busy` is set, debugger should not
+ * change `hartsel`.
+ */
+ result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, i);
+ result = dm_write(target, DM_DMCONTROL, dmcontrol);
+ if (result != ERROR_OK)
+ return result;
+ }
+ }
+ LOG_TARGET_DEBUG(target, "Detected %d harts.", dm->hart_count);
}
- r->vlenb = vlenb;
- LOG_INFO("Vector support with vlenb=%d", r->vlenb);
+ if (dm->hart_count <= 0) {
+ LOG_TARGET_ERROR(target, "No harts found!");
+ return ERROR_FAIL;
+ }
+ dm->was_examined = true;
return ERROR_OK;
}
static int examine(struct target *target)
{
+ /* We reset target state in case if something goes wrong during examine:
+ * DTM/DM scans could fail or hart may fail to halt. */
+ target->state = TARGET_UNKNOWN;
+ target->debug_reason = DBG_REASON_UNDEFINED;
+
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
+ LOG_TARGET_DEBUG(target, "dbgbase=0x%x", target->dbgbase);
- uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
- LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
- LOG_DEBUG(" dmireset=%d", get_field(dtmcontrol, DTM_DTMCS_DMIRESET));
- LOG_DEBUG(" idle=%d", get_field(dtmcontrol, DTM_DTMCS_IDLE));
- LOG_DEBUG(" dmistat=%d", get_field(dtmcontrol, DTM_DTMCS_DMISTAT));
- LOG_DEBUG(" abits=%d", get_field(dtmcontrol, DTM_DTMCS_ABITS));
- LOG_DEBUG(" version=%d", get_field(dtmcontrol, DTM_DTMCS_VERSION));
- if (dtmcontrol == 0) {
- LOG_ERROR("dtmcontrol is 0. Check JTAG connectivity/board power.");
+ uint32_t dtmcontrol;
+ if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) {
+ LOG_TARGET_ERROR(target, "Could not scan dtmcontrol. Check JTAG connectivity/board power.");
return ERROR_FAIL;
}
+
+ LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol);
+ LOG_DEBUG_REG(target, DTM_DTMCS, dtmcontrol);
+
if (get_field(dtmcontrol, DTM_DTMCS_VERSION) != 1) {
- LOG_ERROR("Unsupported DTM version %d. (dtmcontrol=0x%x)",
- get_field(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
+ LOG_TARGET_ERROR(target, "Unsupported DTM version %" PRIu32 ". (dtmcontrol=0x%" PRIx32 ")",
+ get_field32(dtmcontrol, DTM_DTMCS_VERSION), dtmcontrol);
return ERROR_FAIL;
}
riscv013_info_t *info = get_info(target);
- /* TODO: This won't be true if there are multiple DMs. */
+
info->index = target->coreid;
info->abits = get_field(dtmcontrol, DTM_DTMCS_ABITS);
info->dtmcs_idle = get_field(dtmcontrol, DTM_DTMCS_IDLE);
- /* Reset the Debug Module. */
- dm013_info_t *dm = get_dm(target);
- if (!dm)
+ if (!check_dbgbase_exists(target)) {
+ LOG_TARGET_ERROR(target, "Could not find debug module with DMI base address (dbgbase) = 0x%x", target->dbgbase);
return ERROR_FAIL;
- if (!dm->was_reset) {
- dmi_write(target, DM_DMCONTROL, 0);
- dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
- dm->was_reset = true;
}
- dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO |
- DM_DMCONTROL_HARTSELHI | DM_DMCONTROL_DMACTIVE |
- DM_DMCONTROL_HASEL);
- uint32_t dmcontrol;
- if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
- return ERROR_FAIL;
+ int result = examine_dm(target);
+ if (result != ERROR_OK)
+ return result;
- if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) {
- LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x",
- dmcontrol);
- return ERROR_FAIL;
- }
+ result = dm013_select_target(target);
+ if (result != ERROR_OK)
+ return result;
- dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL);
+ /* We're here because we're uncertain about the state of the target. That
+ * includes our progbuf cache. */
+ riscv013_invalidate_cached_progbuf(target);
uint32_t dmstatus;
if (dmstatus_read(target, &dmstatus, false) != ERROR_OK)
return ERROR_FAIL;
- LOG_DEBUG("dmstatus: 0x%08x", dmstatus);
+ LOG_TARGET_DEBUG(target, "dmstatus: 0x%08x", dmstatus);
int dmstatus_version = get_field(dmstatus, DM_DMSTATUS_VERSION);
if (dmstatus_version != 2 && dmstatus_version != 3) {
/* Error was already printed out in dmstatus_read(). */
return ERROR_FAIL;
}
- uint32_t hartsel =
- (get_field(dmcontrol, DM_DMCONTROL_HARTSELHI) <<
- DM_DMCONTROL_HARTSELLO_LENGTH) |
- get_field(dmcontrol, DM_DMCONTROL_HARTSELLO);
- info->hartsellen = 0;
- while (hartsel & 1) {
- info->hartsellen++;
- hartsel >>= 1;
- }
- LOG_DEBUG("hartsellen=%d", info->hartsellen);
-
uint32_t hartinfo;
- if (dmi_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK)
+ if (dm_read(target, &hartinfo, DM_HARTINFO) != ERROR_OK)
return ERROR_FAIL;
info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE);
@@ -1641,161 +2217,178 @@ static int examine(struct target *target)
info->dataaddr = get_field(hartinfo, DM_HARTINFO_DATAADDR);
if (!get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)) {
- LOG_ERROR("Debugger is not authenticated to target Debug Module. "
+ LOG_TARGET_ERROR(target, "Debugger is not authenticated to target Debug Module. "
"(dmstatus=0x%x). Use `riscv authdata_read` and "
"`riscv authdata_write` commands to authenticate.", dmstatus);
- /* If we return ERROR_FAIL here, then in a multicore setup the next
- * core won't be examined, which means we won't set up the
- * authentication commands for them, which means the config script
- * needs to be a lot more complex. */
- return ERROR_OK;
+ return ERROR_FAIL;
}
- if (dmi_read(target, &info->sbcs, DM_SBCS) != ERROR_OK)
+ if (dm_read(target, &info->sbcs, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
/* Check that abstract data registers are accessible. */
uint32_t abstractcs;
- if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK)
+ if (dm_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK)
return ERROR_FAIL;
info->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT);
info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE);
- LOG_INFO("datacount=%d progbufsize=%d", info->datacount, info->progbufsize);
+ LOG_TARGET_INFO(target, "datacount=%d progbufsize=%d",
+ info->datacount, info->progbufsize);
RISCV_INFO(r);
r->impebreak = get_field(dmstatus, DM_DMSTATUS_IMPEBREAK);
if (!has_sufficient_progbuf(target, 2)) {
- LOG_WARNING("We won't be able to execute fence instructions on this "
+ LOG_TARGET_WARNING(target, "We won't be able to execute fence instructions on this "
"target. Memory may not always appear consistent. "
"(progbufsize=%d, impebreak=%d)", info->progbufsize,
r->impebreak);
}
if (info->progbufsize < 4 && riscv_enable_virtual) {
- LOG_ERROR("set_enable_virtual is not available on this target. It "
+ LOG_TARGET_ERROR(target, "set_enable_virtual is not available on this target. It "
"requires a program buffer size of at least 4. (progbufsize=%d) "
"Use `riscv set_enable_virtual off` to continue."
, info->progbufsize);
}
- /* Before doing anything else we must first enumerate the harts. */
- if (dm->hart_count < 0) {
- for (int i = 0; i < MIN(RISCV_MAX_HARTS, 1 << info->hartsellen); ++i) {
- r->current_hartid = i;
- if (riscv013_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
- uint32_t s;
- if (dmstatus_read(target, &s, true) != ERROR_OK)
- return ERROR_FAIL;
- if (get_field(s, DM_DMSTATUS_ANYNONEXISTENT))
- break;
- dm->hart_count = i + 1;
-
- if (get_field(s, DM_DMSTATUS_ANYHAVERESET))
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i));
- }
-
- LOG_DEBUG("Detected %d harts.", dm->hart_count);
- }
-
- r->current_hartid = target->coreid;
-
- if (dm->hart_count == 0) {
- LOG_ERROR("No harts found!");
- return ERROR_FAIL;
- }
-
/* Don't call any riscv_* functions until after we've counted the number of
* cores and initialized registers. */
- if (riscv013_select_current_hart(target) != ERROR_OK)
+ enum riscv_hart_state state_at_examine_start;
+ if (riscv_get_hart_state(target, &state_at_examine_start) != ERROR_OK)
return ERROR_FAIL;
-
- bool halted = riscv_is_halted(target);
- if (!halted) {
+ const bool hart_halted_at_examine_start = state_at_examine_start == RISCV_STATE_HALTED;
+ if (!hart_halted_at_examine_start) {
+ r->prepped = true;
if (riscv013_halt_go(target) != ERROR_OK) {
- LOG_ERROR("Fatal: Hart %d failed to halt during examine()", r->current_hartid);
+ LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during %s",
+ info->index, __func__);
return ERROR_FAIL;
}
}
+ target->state = TARGET_HALTED;
+ target->debug_reason = hart_halted_at_examine_start ? DBG_REASON_UNDEFINED : DBG_REASON_DBGRQ;
+
/* Without knowing anything else we can at least mess with the
- * program buffer. */
- r->debug_buffer_size = info->progbufsize;
+ * program buffer. */
+ r->progbuf_size = info->progbufsize;
- int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64);
+ result = register_read_abstract_with_size(target, NULL, GDB_REGNO_S0, 64);
if (result == ERROR_OK)
r->xlen = 64;
else
r->xlen = 32;
- if (register_read(target, &r->misa, GDB_REGNO_MISA)) {
- LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid);
+ /* Save s0 and s1. The register cache hasn't be initialized yet so we
+ * need to take care of this manually. */
+ uint64_t s0, s1;
+ if (register_read_abstract(target, &s0, GDB_REGNO_S0) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read s0.");
+ return ERROR_FAIL;
+ }
+ if (register_read_abstract(target, &s1, GDB_REGNO_S1) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read s1.");
return ERROR_FAIL;
}
- if (riscv_supports_extension(target, 'V')) {
- if (discover_vlenb(target) != ERROR_OK)
- return ERROR_FAIL;
+ if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to read MISA.");
+ return ERROR_FAIL;
+ }
+
+ uint64_t value;
+ if (register_read_direct(target, &value, GDB_REGNO_VLENB) != ERROR_OK) {
+ if (riscv_supports_extension(target, 'V'))
+ LOG_TARGET_WARNING(target, "Couldn't read vlenb; vector register access won't work.");
+ r->vlenb = 0;
+ } else {
+ r->vlenb = value;
+ LOG_TARGET_INFO(target, "Vector support with vlenb=%d", r->vlenb);
+ }
+
+ if (register_read_direct(target, &value, GDB_REGNO_MTOPI) == ERROR_OK) {
+ r->mtopi_readable = true;
+
+ if (register_read_direct(target, &value, GDB_REGNO_MTOPEI) == ERROR_OK) {
+ LOG_TARGET_INFO(target, "S?aia detected with IMSIC");
+ r->mtopei_readable = true;
+ } else {
+ r->mtopei_readable = false;
+ LOG_TARGET_INFO(target, "S?aia detected without IMSIC");
+ }
+ } else {
+ r->mtopi_readable = false;
+ }
+
+ /* Display this as early as possible to help people who are using
+ * really slow simulators. */
+ LOG_TARGET_DEBUG(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa);
+
+ /* Restore s0 and s1. */
+ if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to write back s0.");
+ return ERROR_FAIL;
+ }
+ if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Fatal: Failed to write back s1.");
+ return ERROR_FAIL;
}
/* Now init registers based on what we discovered. */
if (riscv_init_registers(target) != ERROR_OK)
return ERROR_FAIL;
- /* Display this as early as possible to help people who are using
- * really slow simulators. */
- LOG_DEBUG(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
- r->misa);
-
- if (!halted)
- riscv013_step_or_resume_current_hart(target, false, false);
+ if (set_dcsr_ebreak(target, false) != ERROR_OK)
+ return ERROR_FAIL;
- target_set_examined(target);
+ if (state_at_examine_start == RISCV_STATE_RUNNING) {
+ riscv013_step_or_resume_current_hart(target, false);
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ } else if (state_at_examine_start == RISCV_STATE_HALTED) {
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ }
if (target->smp) {
- bool haltgroup_supported;
- if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK)
+ if (set_group(target, &info->haltgroup_supported, target->smp, HALT_GROUP) != ERROR_OK)
return ERROR_FAIL;
- if (haltgroup_supported)
- LOG_INFO("Core %d made part of halt group %d.", target->coreid,
+ if (info->haltgroup_supported)
+ LOG_TARGET_INFO(target, "Core %d made part of halt group %d.", info->index,
target->smp);
else
- LOG_INFO("Core %d could not be made part of halt group %d.",
- target->coreid, target->smp);
+ LOG_TARGET_INFO(target, "Core %d could not be made part of halt group %d.",
+ info->index, target->smp);
}
/* Some regression suites rely on seeing 'Examined RISC-V core' to know
* when they can connect with gdb/telnet.
* We will need to update those suites if we want to change that text. */
- LOG_INFO("Examined RISC-V core; found %d harts",
- riscv_count_harts(target));
- LOG_INFO(" hart %d: XLEN=%d, misa=0x%" PRIx64, r->current_hartid, r->xlen,
- r->misa);
+ LOG_TARGET_INFO(target, "Examined RISC-V core");
+ LOG_TARGET_INFO(target, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa);
return ERROR_OK;
}
static int riscv013_authdata_read(struct target *target, uint32_t *value, unsigned int index)
{
if (index > 0) {
- LOG_ERROR("Spec 0.13 only has a single authdata register.");
+ LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register.");
return ERROR_FAIL;
}
if (wait_for_authbusy(target, NULL) != ERROR_OK)
return ERROR_FAIL;
- return dmi_read(target, value, DM_AUTHDATA);
+ return dm_read(target, value, DM_AUTHDATA);
}
static int riscv013_authdata_write(struct target *target, uint32_t value, unsigned int index)
{
if (index > 0) {
- LOG_ERROR("Spec 0.13 only has a single authdata register.");
+ LOG_TARGET_ERROR(target, "Spec 0.13 only has a single authdata register.");
return ERROR_FAIL;
}
@@ -1803,21 +2396,21 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign
if (wait_for_authbusy(target, &before) != ERROR_OK)
return ERROR_FAIL;
- dmi_write(target, DM_AUTHDATA, value);
+ dm_write(target, DM_AUTHDATA, value);
if (wait_for_authbusy(target, &after) != ERROR_OK)
return ERROR_FAIL;
if (!get_field(before, DM_DMSTATUS_AUTHENTICATED) &&
get_field(after, DM_DMSTATUS_AUTHENTICATED)) {
- LOG_INFO("authdata_write resulted in successful authentication");
+ LOG_TARGET_INFO(target, "authdata_write resulted in successful authentication");
int result = ERROR_OK;
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
target_list_t *entry;
list_for_each_entry(entry, &dm->target_list, list) {
- if (examine(entry->target) != ERROR_OK)
+ if (target_examine_one(entry->target) != ERROR_OK)
result = ERROR_FAIL;
}
return result;
@@ -1826,13 +2419,6 @@ static int riscv013_authdata_write(struct target *target, uint32_t value, unsign
return ERROR_OK;
}
-static int riscv013_hart_count(struct target *target)
-{
- dm013_info_t *dm = get_dm(target);
- assert(dm);
- return dm->hart_count;
-}
-
/* Try to find out the widest memory access size depending on the selected memory access methods. */
static unsigned riscv013_data_bits(struct target *target)
{
@@ -1865,7 +2451,7 @@ static unsigned riscv013_data_bits(struct target *target)
/* No further mem access method to try. */
break;
}
- LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits.");
+ LOG_TARGET_ERROR(target, "Unable to determine supported data bits on this target. Assuming 32 bits.");
return 32;
}
@@ -1903,82 +2489,109 @@ static COMMAND_HELPER(riscv013_print_info, struct target *target)
return 0;
}
-static int prep_for_vector_access(struct target *target, uint64_t *vtype,
- uint64_t *vl, unsigned *debug_vl)
+static int try_set_vsew(struct target *target, unsigned int *debug_vsew)
{
RISCV_INFO(r);
- /* TODO: this continuous save/restore is terrible for performance. */
- /* Write vtype and vl. */
- unsigned encoded_vsew;
- switch (riscv_xlen(target)) {
- case 32:
- encoded_vsew = 2;
- break;
- case 64:
- encoded_vsew = 3;
- break;
- default:
- LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+ unsigned int encoded_vsew =
+ (riscv_xlen(target) == 64 && r->vsew64_supported != YNM_NO) ? 3 : 2;
+
+ /* Set standard element width to match XLEN, for vmv instruction to move
+ * the least significant bits into a GPR.
+ */
+ if (riscv_write_register(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (encoded_vsew == 3 && r->vsew64_supported == YNM_MAYBE) {
+ /* Check that it's supported. */
+ riscv_reg_t vtype;
+
+ if (riscv_get_register(target, &vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
+ if (vtype >> (riscv_xlen(target) - 1)) {
+ r->vsew64_supported = YNM_NO;
+ /* Try again. */
+ return try_set_vsew(target, debug_vsew);
+ }
+ r->vsew64_supported = YNM_YES;
}
+ *debug_vsew = encoded_vsew == 3 ? 64 : 32;
+ return ERROR_OK;
+}
- /* Save vtype and vl. */
- if (register_read(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK)
+static int prep_for_vector_access(struct target *target,
+ riscv_reg_t *orig_mstatus, riscv_reg_t *orig_vtype, riscv_reg_t *orig_vl,
+ unsigned int *debug_vl, unsigned int *debug_vsew)
+{
+ assert(orig_mstatus);
+ assert(orig_vtype);
+ assert(orig_vl);
+ assert(debug_vl);
+ assert(debug_vsew);
+
+ RISCV_INFO(r);
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target,
+ "Unable to access vector register: target not halted");
return ERROR_FAIL;
- if (register_read(target, vl, GDB_REGNO_VL) != ERROR_OK)
+ }
+ if (prep_for_register_access(target, orig_mstatus, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != ERROR_OK)
+ /* Save vtype and vl. */
+ if (riscv_get_register(target, orig_vtype, GDB_REGNO_VTYPE) != ERROR_OK)
return ERROR_FAIL;
- *debug_vl = DIV_ROUND_UP(r->vlenb * 8, riscv_xlen(target));
- if (register_write_direct(target, GDB_REGNO_VL, *debug_vl) != ERROR_OK)
+ if (riscv_get_register(target, orig_vl, GDB_REGNO_VL) != ERROR_OK)
return ERROR_FAIL;
- return ERROR_OK;
+ if (try_set_vsew(target, debug_vsew) != ERROR_OK)
+ return ERROR_FAIL;
+ /* Set the number of elements to be updated with results from a vector
+ * instruction, for the vslide1down instruction.
+ * Set it so the entire V register is updated. */
+ *debug_vl = DIV_ROUND_UP(r->vlenb * 8, *debug_vsew);
+ return riscv_write_register(target, GDB_REGNO_VL, *debug_vl);
}
-static int cleanup_after_vector_access(struct target *target, uint64_t vtype,
- uint64_t vl)
+static int cleanup_after_vector_access(struct target *target,
+ riscv_reg_t mstatus, riscv_reg_t vtype, riscv_reg_t vl)
{
/* Restore vtype and vl. */
- if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
+ if (riscv_write_register(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK)
return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK)
+ if (riscv_write_register(target, GDB_REGNO_VL, vl) != ERROR_OK)
return ERROR_FAIL;
- return ERROR_OK;
+ return cleanup_after_register_access(target, mstatus, GDB_REGNO_VL);
}
static int riscv013_get_register_buf(struct target *target,
- uint8_t *value, int regno)
+ uint8_t *value, enum gdb_regno regno)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
+ riscv_reg_t mstatus, vtype, vl;
+ unsigned int debug_vl, debug_vsew;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
+ if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
+ &debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- uint64_t vtype, vl;
- unsigned debug_vl;
- if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- unsigned vnum = regno - GDB_REGNO_V0;
- unsigned xlen = riscv_xlen(target);
-
- struct riscv_program program;
- riscv_program_init(&program, target);
- riscv_program_insert(&program, vmv_x_s(S0, vnum));
- riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+ unsigned int vnum = regno - GDB_REGNO_V0;
int result = ERROR_OK;
- for (unsigned i = 0; i < debug_vl; i++) {
+ for (unsigned int i = 0; i < debug_vl; i++) {
+ /* Can't reuse the same program because riscv_program_exec() adds
+ * ebreak to the end every time. */
+ struct riscv_program program;
+ riscv_program_init(&program, target);
+ riscv_program_insert(&program, vmv_x_s(S0, vnum));
+ riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
+
/* Executing the program might result in an exception if there is some
* issue with the vector implementation/instructions we're using. If that
* happens, attempt to restore as usual. We may have clobbered the
@@ -1987,69 +2600,58 @@ static int riscv013_get_register_buf(struct target *target,
* so messed up that attempting to restore isn't going to help. */
result = riscv_program_exec(&program, target);
if (result == ERROR_OK) {
- uint64_t v;
+ riscv_reg_t v;
if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- buf_set_u64(value, xlen * i, xlen, v);
+ buf_set_u64(value, debug_vsew * i, debug_vsew, v);
} else {
+ LOG_TARGET_ERROR(target,
+ "Failed to execute vmv/vslide1down while reading %s",
+ gdb_regno_name(target, regno));
break;
}
}
- if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
- return ERROR_FAIL;
-
- if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;
}
static int riscv013_set_register_buf(struct target *target,
- int regno, const uint8_t *value)
+ enum gdb_regno regno, const uint8_t *value)
{
assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (dm013_select_target(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_reg_t s0;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
+ riscv_reg_t mstatus, vtype, vl;
+ unsigned int debug_vl, debug_vsew;
- uint64_t mstatus;
- if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK)
+ if (prep_for_vector_access(target, &mstatus, &vtype, &vl,
+ &debug_vl, &debug_vsew) != ERROR_OK)
return ERROR_FAIL;
- uint64_t vtype, vl;
- unsigned debug_vl;
- if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != ERROR_OK)
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
return ERROR_FAIL;
- unsigned vnum = regno - GDB_REGNO_V0;
- unsigned xlen = riscv_xlen(target);
+ unsigned int vnum = regno - GDB_REGNO_V0;
struct riscv_program program;
riscv_program_init(&program, target);
riscv_program_insert(&program, vslide1down_vx(vnum, vnum, S0, true));
int result = ERROR_OK;
- for (unsigned i = 0; i < debug_vl; i++) {
+ for (unsigned int i = 0; i < debug_vl; i++) {
if (register_write_direct(target, GDB_REGNO_S0,
- buf_get_u64(value, xlen * i, xlen)) != ERROR_OK)
+ buf_get_u64(value, debug_vsew * i, debug_vsew)) != ERROR_OK)
return ERROR_FAIL;
result = riscv_program_exec(&program, target);
if (result != ERROR_OK)
break;
}
- if (cleanup_after_vector_access(target, vtype, vl) != ERROR_OK)
- return ERROR_FAIL;
-
- if (cleanup_after_register_access(target, mstatus, regno) != ERROR_OK)
- return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ if (cleanup_after_vector_access(target, mstatus, vtype, vl) != ERROR_OK)
return ERROR_FAIL;
return result;
@@ -2080,28 +2682,77 @@ static int sb_write_address(struct target *target, target_addr_t address,
unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
/* There currently is no support for >64-bit addresses in OpenOCD. */
if (sbasize > 96)
- dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false);
+ dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false);
if (sbasize > 64)
- dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false);
+ dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false);
if (sbasize > 32)
- dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false);
- return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address,
- false, ensure_success);
+ dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1,
+ (uint32_t)(address >> 32), false, false);
+ return dm_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0,
+ (uint32_t)address, false, ensure_success);
}
-static int batch_run(const struct target *target, struct riscv_batch *batch)
+static int batch_run(struct target *target, struct riscv_batch *batch,
+ size_t idle_count)
{
- RISCV013_INFO(info);
RISCV_INFO(r);
- if (r->reset_delays_wait >= 0) {
- r->reset_delays_wait -= batch->used_scans;
- if (r->reset_delays_wait <= 0) {
- batch->idle_count = 0;
- info->dmi_busy_delay = 0;
- info->ac_busy_delay = 0;
+ riscv_batch_add_nop(batch);
+ const int result = riscv_batch_run_from(batch, 0, idle_count,
+ /*resets_delays*/ r->reset_delays_wait >= 0,
+ r->reset_delays_wait);
+ /* TODO: To use `riscv_batch_finished_scans()` here, it is needed for
+ * all scans to not discard input, meaning
+ * "riscv_batch_add_dm_write(..., false)" should not be used. */
+ const size_t finished_scans = batch->used_scans;
+ decrement_reset_delays_counter(target, finished_scans);
+ return result;
+}
+
+/* It is expected that during creation of the batch
+ * "riscv_batch_add_dm_write(..., false)" was not used.
+ */
+static int batch_run_timeout(struct target *target, struct riscv_batch *batch)
+{
+ RISCV013_INFO(info);
+
+ riscv_batch_add_nop(batch);
+
+ size_t finished_scans = 0;
+ const time_t start = time(NULL);
+ const size_t old_dmi_busy_delay = info->dmi_busy_delay;
+ int result;
+ do {
+ RISCV_INFO(r);
+ result = riscv_batch_run_from(batch, finished_scans,
+ info->dmi_busy_delay,
+ /*resets_delays*/ r->reset_delays_wait >= 0,
+ r->reset_delays_wait);
+ const size_t new_finished_scans = riscv_batch_finished_scans(batch);
+ assert(new_finished_scans >= finished_scans);
+ decrement_reset_delays_counter(target, new_finished_scans - finished_scans);
+ finished_scans = new_finished_scans;
+ if (result != ERROR_OK)
+ return result;
+ if (!riscv_batch_was_batch_busy(batch)) {
+ assert(finished_scans == batch->used_scans);
+ return ERROR_OK;
}
- }
- return riscv_batch_run(batch);
+ increase_dmi_busy_delay(target);
+ } while (time(NULL) - start < riscv_command_timeout_sec);
+
+ assert(result == ERROR_OK);
+ assert(riscv_batch_was_batch_busy(batch));
+
+ /* Reset dmi_busy_delay, so the value doesn't get too big. */
+ LOG_TARGET_DEBUG(target, "dmi_busy_delay is restored to %zu.",
+ old_dmi_busy_delay);
+ info->dmi_busy_delay = old_dmi_busy_delay;
+
+ LOG_TARGET_ERROR(target, "DMI operation didn't complete in %d seconds. "
+ "The target is either really slow or broken. You could increase "
+ "the timeout with riscv set_command_timeout_sec.",
+ riscv_command_timeout_sec);
+ return ERROR_TIMEOUT_REACHED;
}
static int sba_supports_access(struct target *target, unsigned int size_bytes)
@@ -2131,12 +2782,12 @@ static int sample_memory_bus_v1(struct target *target,
RISCV013_INFO(info);
unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
if (sbasize > 64) {
- LOG_ERROR("Memory sampling is only implemented for sbasize <= 64.");
+ LOG_TARGET_ERROR(target, "Memory sampling is only implemented for sbasize <= 64.");
return ERROR_NOT_IMPLEMENTED;
}
if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) {
- LOG_ERROR("Memory sampling is only implemented for SBA version 1.");
+ LOG_TARGET_ERROR(target, "Memory sampling is only implemented for SBA version 1.");
return ERROR_NOT_IMPLEMENTED;
}
@@ -2164,8 +2815,7 @@ static int sample_memory_bus_v1(struct target *target,
* loop.
*/
struct riscv_batch *batch = riscv_batch_alloc(
- target, 1 + enabled_count * 5 * repeat,
- info->dmi_busy_delay + info->bus_master_read_delay);
+ target, 1 + enabled_count * 5 * repeat);
if (!batch)
return ERROR_FAIL;
@@ -2174,7 +2824,7 @@ static int sample_memory_bus_v1(struct target *target,
for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
if (config->bucket[i].enabled) {
if (!sba_supports_access(target, config->bucket[i].size_bytes)) {
- LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.",
+ LOG_TARGET_ERROR(target, "Hardware does not support SBA access for %d-byte memory sampling.",
config->bucket[i].size_bytes);
return ERROR_NOT_IMPLEMENTED;
}
@@ -2184,7 +2834,7 @@ static int sample_memory_bus_v1(struct target *target,
sbcs_write |= DM_SBCS_SBREADONDATA;
sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes);
if (!sbcs_valid || sbcs_write != sbcs) {
- riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write);
+ riscv_batch_add_dm_write(batch, DM_SBCS, sbcs_write, true);
sbcs = sbcs_write;
sbcs_valid = true;
}
@@ -2193,18 +2843,18 @@ static int sample_memory_bus_v1(struct target *target,
(!sbaddress1_valid ||
sbaddress1 != config->bucket[i].address >> 32)) {
sbaddress1 = config->bucket[i].address >> 32;
- riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1);
+ riscv_batch_add_dm_write(batch, DM_SBADDRESS1, sbaddress1, true);
sbaddress1_valid = true;
}
if (!sbaddress0_valid ||
sbaddress0 != (config->bucket[i].address & 0xffffffff)) {
sbaddress0 = config->bucket[i].address;
- riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0);
+ riscv_batch_add_dm_write(batch, DM_SBADDRESS0, sbaddress0, true);
sbaddress0_valid = true;
}
if (config->bucket[i].size_bytes > 4)
- riscv_batch_add_dmi_read(batch, DM_SBDATA1);
- riscv_batch_add_dmi_read(batch, DM_SBDATA0);
+ riscv_batch_add_dm_read(batch, DM_SBDATA1);
+ riscv_batch_add_dm_read(batch, DM_SBDATA0);
result_bytes += 1 + config->bucket[i].size_bytes;
}
}
@@ -2215,37 +2865,49 @@ static int sample_memory_bus_v1(struct target *target,
break;
}
- size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS);
+ size_t sbcs_read_index = riscv_batch_add_dm_read(batch, DM_SBCS);
- int result = batch_run(target, batch);
- if (result != ERROR_OK)
+ int result = batch_run(target, batch,
+ info->dmi_busy_delay + info->bus_master_read_delay);
+ if (result != ERROR_OK) {
+ riscv_batch_free(batch);
return result;
+ }
- uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key);
+ /* Discard the batch when we encounter a busy state on the DMI level.
+ * It's too much hassle to try to recover partial data. We'll try again
+ * with a larger DMI delay. */
+ unsigned int sbcs_read_op = riscv_batch_get_dmi_read_op(batch, sbcs_read_index);
+ if (sbcs_read_op == DTM_DMI_OP_BUSY) {
+ increase_dmi_busy_delay(target);
+ continue;
+ }
+
+ uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_read_index);
if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
- /* Discard this batch (too much hassle to try to recover partial
- * data) and try again with a larger delay. */
+ /* Discard this batch when we encounter "busy error" state on the System Bus level.
+ * We'll try next time with a larger System Bus read delay. */
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
- dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+ dm_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
riscv_batch_free(batch);
continue;
}
if (get_field(sbcs_read, DM_SBCS_SBERROR)) {
/* The memory we're sampling was unreadable, somehow. Give up. */
- dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
+ dm_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR);
riscv_batch_free(batch);
return ERROR_FAIL;
}
- unsigned int read = 0;
+ unsigned int read_count = 0;
for (unsigned int n = 0; n < repeat; n++) {
for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) {
if (config->bucket[i].enabled) {
assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE);
uint64_t value = 0;
if (config->bucket[i].size_bytes > 4)
- value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32;
- value |= riscv_batch_get_dmi_read_data(batch, read++);
+ value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read_count++)) << 32;
+ value |= riscv_batch_get_dmi_read_data(batch, read_count++);
buf->buf[buf->used] = i;
buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value);
@@ -2271,41 +2933,114 @@ static int sample_memory(struct target *target,
return sample_memory_bus_v1(target, buf, config, until_ms);
}
+static int riscv013_get_hart_state(struct target *target, enum riscv_hart_state *state)
+{
+ RISCV013_INFO(info);
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint32_t dmstatus;
+ if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) {
+ LOG_TARGET_INFO(target, "Hart unexpectedly reset!");
+ info->dcsr_ebreak_is_set = false;
+ /* TODO: Can we make this more obvious to eg. a gdb user? */
+ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE |
+ DM_DMCONTROL_ACKHAVERESET;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, info->index);
+ /* If we had been halted when we reset, request another halt. If we
+ * ended up running out of reset, then the user will (hopefully) get a
+ * message that a reset happened, that the target is running, and then
+ * that it is halted again once the request goes through.
+ */
+ if (target->state == TARGET_HALTED) {
+ dmcontrol |= DM_DMCONTROL_HALTREQ;
+ /* `haltreq` should not be issued if `abstractcs.busy`
+ * is set. */
+ int result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+ }
+ dm_write(target, DM_DMCONTROL, dmcontrol);
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLNONEXISTENT)) {
+ *state = RISCV_STATE_NON_EXISTENT;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ *state = RISCV_STATE_UNAVAILABLE;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ *state = RISCV_STATE_HALTED;
+ return ERROR_OK;
+ }
+ if (get_field(dmstatus, DM_DMSTATUS_ALLRUNNING)) {
+ *state = RISCV_STATE_RUNNING;
+ return ERROR_OK;
+ }
+ LOG_TARGET_ERROR(target, "Couldn't determine state. dmstatus=0x%x", dmstatus);
+ return ERROR_FAIL;
+}
+
+static int handle_became_unavailable(struct target *target,
+ enum riscv_hart_state previous_riscv_state)
+{
+ RISCV013_INFO(info);
+ info->dcsr_ebreak_is_set = false;
+ return ERROR_OK;
+}
+
+static int tick(struct target *target)
+{
+ RISCV013_INFO(info);
+ if (!info->dcsr_ebreak_is_set &&
+ target->state == TARGET_RUNNING &&
+ target_was_examined(target))
+ return halt_set_dcsr_ebreak(target);
+ return ERROR_OK;
+}
+
static int init_target(struct command_context *cmd_ctx,
struct target *target)
{
- LOG_DEBUG("init");
+ LOG_TARGET_DEBUG(target, "Init.");
RISCV_INFO(generic_info);
generic_info->get_register = &riscv013_get_register;
generic_info->set_register = &riscv013_set_register;
generic_info->get_register_buf = &riscv013_get_register_buf;
generic_info->set_register_buf = &riscv013_set_register_buf;
- generic_info->select_current_hart = &riscv013_select_current_hart;
- generic_info->is_halted = &riscv013_is_halted;
+ generic_info->select_target = &dm013_select_target;
+ generic_info->get_hart_state = &riscv013_get_hart_state;
generic_info->resume_go = &riscv013_resume_go;
generic_info->step_current_hart = &riscv013_step_current_hart;
- generic_info->on_halt = &riscv013_on_halt;
generic_info->resume_prep = &riscv013_resume_prep;
generic_info->halt_prep = &riscv013_halt_prep;
generic_info->halt_go = &riscv013_halt_go;
generic_info->on_step = &riscv013_on_step;
generic_info->halt_reason = &riscv013_halt_reason;
- generic_info->read_debug_buffer = &riscv013_read_debug_buffer;
- generic_info->write_debug_buffer = &riscv013_write_debug_buffer;
- generic_info->execute_debug_buffer = &riscv013_execute_debug_buffer;
- generic_info->fill_dmi_write_u64 = &riscv013_fill_dmi_write_u64;
- generic_info->fill_dmi_read_u64 = &riscv013_fill_dmi_read_u64;
- generic_info->fill_dmi_nop_u64 = &riscv013_fill_dmi_nop_u64;
- generic_info->dmi_write_u64_bits = &riscv013_dmi_write_u64_bits;
+ generic_info->read_progbuf = &riscv013_read_progbuf;
+ generic_info->write_progbuf = &riscv013_write_progbuf;
+ generic_info->execute_progbuf = &riscv013_execute_progbuf;
+ generic_info->invalidate_cached_progbuf = &riscv013_invalidate_cached_progbuf;
+ generic_info->fill_dm_write = &riscv013_fill_dm_write;
+ generic_info->fill_dm_read = &riscv013_fill_dm_read;
+ generic_info->fill_dm_nop = &riscv013_fill_dm_nop;
+ generic_info->get_dmi_scan_length = &riscv013_get_dmi_scan_length;
generic_info->authdata_read = &riscv013_authdata_read;
generic_info->authdata_write = &riscv013_authdata_write;
generic_info->dmi_read = &dmi_read;
generic_info->dmi_write = &dmi_write;
+ generic_info->get_dmi_address = &riscv013_get_dmi_address;
generic_info->read_memory = read_memory;
- generic_info->hart_count = &riscv013_hart_count;
generic_info->data_bits = &riscv013_data_bits;
generic_info->print_info = &riscv013_print_info;
+
+ generic_info->handle_became_unavailable = &handle_became_unavailable;
+ generic_info->tick = &tick;
+
if (!generic_info->version_specific) {
generic_info->version_specific = calloc(1, sizeof(riscv013_info_t));
if (!generic_info->version_specific)
@@ -2338,152 +3073,204 @@ static int init_target(struct command_context *cmd_ctx,
static int assert_reset(struct target *target)
{
- RISCV_INFO(r);
+ RISCV013_INFO(info);
+ int result;
select_dmi(target);
- uint32_t control_base = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
-
if (target_has_event_action(target, TARGET_EVENT_RESET_ASSERT)) {
/* Run the user-supplied script if there is one. */
target_handle_event(target, TARGET_EVENT_RESET_ASSERT);
- } else if (target->rtos) {
- /* There's only one target, and OpenOCD thinks each hart is a thread.
- * We must reset them all. */
-
- /* TODO: Try to use hasel in dmcontrol */
-
- /* Set haltreq for each hart. */
- uint32_t control = set_hartsel(control_base, target->coreid);
- control = set_field(control, DM_DMCONTROL_HALTREQ,
- target->reset_halt ? 1 : 0);
- dmi_write(target, DM_DMCONTROL, control);
-
- /* Assert ndmreset */
- control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
- dmi_write(target, DM_DMCONTROL, control);
-
} else {
- /* Reset just this hart. */
- uint32_t control = set_hartsel(control_base, r->current_hartid);
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+
+ uint32_t control = set_field(0, DM_DMCONTROL_DMACTIVE, 1);
+ control = set_dmcontrol_hartsel(control, info->index);
control = set_field(control, DM_DMCONTROL_HALTREQ,
target->reset_halt ? 1 : 0);
control = set_field(control, DM_DMCONTROL_NDMRESET, 1);
- dmi_write(target, DM_DMCONTROL, control);
+ /* If `abstractcs.busy` is set, debugger should not
+ * change `hartsel` or set `haltreq`
+ */
+ const bool hartsel_changed = (int)info->index != dm->current_hartid;
+ if (hartsel_changed || target->reset_halt) {
+ result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+ }
+ result = dm_write(target, DM_DMCONTROL, control);
+ if (result != ERROR_OK)
+ return result;
}
target->state = TARGET_RESET;
- dm013_info_t *dm = get_dm(target);
- if (!dm)
- return ERROR_FAIL;
-
/* The DM might have gotten reset if OpenOCD called us in some reset that
* involves SRST being toggled. So clear our cache which may be out of
* date. */
- memset(dm->progbuf_cache, 0, sizeof(dm->progbuf_cache));
-
- return ERROR_OK;
+ return riscv013_invalidate_cached_progbuf(target);
}
static int deassert_reset(struct target *target)
{
- RISCV_INFO(r);
RISCV013_INFO(info);
- select_dmi(target);
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+ int result;
+ select_dmi(target);
/* Clear the reset, but make sure haltreq is still set */
- uint32_t control = 0, control_haltreq;
+ uint32_t control = 0;
control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
- control_haltreq = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control_haltreq, r->current_hartid));
+ control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0);
+ control = set_dmcontrol_hartsel(control, info->index);
+ /* If `abstractcs.busy` is set, debugger should not
+ * change `hartsel`.
+ */
+ const bool hartsel_changed = (int)info->index != dm->current_hartid;
+ if (hartsel_changed) {
+ result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+ }
+ result = dm_write(target, DM_DMCONTROL, control);
+ if (result != ERROR_OK)
+ return result;
uint32_t dmstatus;
- int dmi_busy_delay = info->dmi_busy_delay;
+ const int orig_dmi_busy_delay = info->dmi_busy_delay;
time_t start = time(NULL);
-
- for (int i = 0; i < riscv_count_harts(target); ++i) {
- int index = i;
- if (target->rtos) {
- if (index != target->coreid)
- continue;
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control_haltreq, index));
- } else {
- index = r->current_hartid;
- }
-
- LOG_DEBUG("Waiting for hart %d to come out of reset.", index);
- while (1) {
- int result = dmstatus_read_timeout(target, &dmstatus, true,
+ LOG_TARGET_DEBUG(target, "Waiting for hart to come out of reset.");
+ do {
+ result = dmstatus_read_timeout(target, &dmstatus, true,
+ riscv_reset_timeout_sec);
+ if (result == ERROR_TIMEOUT_REACHED)
+ LOG_TARGET_ERROR(target, "Hart didn't complete a DMI read coming "
+ "out of reset in %ds; Increase the timeout with riscv "
+ "set_reset_timeout_sec.",
riscv_reset_timeout_sec);
- if (result == ERROR_TIMEOUT_REACHED)
- LOG_ERROR("Hart %d didn't complete a DMI read coming out of "
- "reset in %ds; Increase the timeout with riscv "
- "set_reset_timeout_sec.",
- index, riscv_reset_timeout_sec);
- if (result != ERROR_OK)
- return result;
- /* Certain debug modules, like the one in GD32VF103
- * MCUs, violate the specification's requirement that
- * each hart is in "exactly one of four states" and,
- * during reset, report harts as both unavailable and
- * halted/running. To work around this, we check for
- * the absence of the unavailable state rather than
- * the presence of any other state. */
- if (!get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
- break;
- if (time(NULL) - start > riscv_reset_timeout_sec) {
- LOG_ERROR("Hart %d didn't leave reset in %ds; "
- "dmstatus=0x%x; "
- "Increase the timeout with riscv set_reset_timeout_sec.",
- index, riscv_reset_timeout_sec, dmstatus);
- return ERROR_FAIL;
- }
- }
- target->state = TARGET_HALTED;
+ if (result != ERROR_OK)
+ return result;
- if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) {
- /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */
- dmi_write(target, DM_DMCONTROL,
- set_hartsel(control, index) |
- DM_DMCONTROL_ACKHAVERESET);
+ if (time(NULL) - start > riscv_reset_timeout_sec) {
+ LOG_TARGET_ERROR(target, "Hart didn't leave reset in %ds; "
+ "dmstatus=0x%x (allunavail=%s, allhavereset=%s); "
+ "Increase the timeout with riscv set_reset_timeout_sec.",
+ riscv_reset_timeout_sec, dmstatus,
+ get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) ? "true" : "false",
+ get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET) ? "true" : "false");
+ return ERROR_TIMEOUT_REACHED;
}
-
- if (!target->rtos)
- break;
+ /* Certain debug modules, like the one in GD32VF103
+ * MCUs, violate the specification's requirement that
+ * each hart is in "exactly one of four states" and,
+ * during reset, report harts as both unavailable and
+ * halted/running. To work around this, we check for
+ * the absence of the unavailable state rather than
+ * the presence of any other state. */
+ } while (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL) &&
+ !get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET));
+
+ info->dmi_busy_delay = orig_dmi_busy_delay;
+
+ if (target->reset_halt) {
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_DBGRQ;
+ } else {
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
}
- info->dmi_busy_delay = dmi_busy_delay;
- return ERROR_OK;
+ info->dcsr_ebreak_is_set = false;
+
+ /* Ack reset and clear DM_DMCONTROL_HALTREQ if previously set */
+ control = 0;
+ control = set_field(control, DM_DMCONTROL_DMACTIVE, 1);
+ control = set_field(control, DM_DMCONTROL_ACKHAVERESET, 1);
+ control = set_dmcontrol_hartsel(control, info->index);
+ return dm_write(target, DM_DMCONTROL, control);
}
static int execute_fence(struct target *target)
{
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
/* FIXME: For non-coherent systems we need to flush the caches right
* here, but there's no ISA-defined way of doing that. */
- {
- struct riscv_program program;
+ struct riscv_program program;
+
+ /* program.execution_result may indicate RISCV_PROGBUF_EXEC_RESULT_EXCEPTION -
+ * currently, we ignore this error since most likely this is an indication
+ * that target does not support a fence instruction (execution of an
+ * unsupported instruction results in "Illegal instruction" exception on
+ * targets that comply with riscv-privilege spec).
+ * Currently, RISC-V specification does not provide us with a portable and
+ * less invasive way to detect if a fence is supported by the target. We may
+ * revise this code once the spec allows us to do this */
+ if (has_sufficient_progbuf(target, 3)) {
riscv_program_init(&program, target);
riscv_program_fence_i(&program);
- riscv_program_fence(&program);
- int result = riscv_program_exec(&program, target);
- if (result != ERROR_OK)
- LOG_DEBUG("Unable to execute pre-fence");
+ riscv_program_fence_rw_rw(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) {
+ LOG_TARGET_ERROR(target, "Unexpected error during fence execution");
+ return ERROR_FAIL;
+ }
+ LOG_TARGET_DEBUG(target, "Unable to execute fence");
+ }
+ return ERROR_OK;
}
- return ERROR_OK;
+ if (has_sufficient_progbuf(target, 2)) {
+ riscv_program_init(&program, target);
+ riscv_program_fence_i(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) {
+ LOG_TARGET_ERROR(target, "Unexpected error during fence.i execution");
+ return ERROR_FAIL;
+ }
+ LOG_TARGET_DEBUG(target, "Unable to execute fence.i");
+ }
+
+ riscv_program_init(&program, target);
+ riscv_program_fence_rw_rw(&program);
+ if (riscv_program_exec(&program, target) != ERROR_OK) {
+ if (program.execution_result != RISCV_PROGBUF_EXEC_RESULT_EXCEPTION) {
+ LOG_TARGET_ERROR(target, "Unexpected error during fence rw, rw execution");
+ return ERROR_FAIL;
+ }
+ LOG_TARGET_DEBUG(target, "Unable to execute fence rw, rw");
+ }
+ return ERROR_OK;
+ }
+
+ return ERROR_FAIL;
}
-static void log_memory_access(target_addr_t address, uint64_t value,
- unsigned size_bytes, bool read)
+static void log_memory_access128(target_addr_t address, uint64_t value_h,
+ uint64_t value_l, bool is_read)
+{
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ char fmt[80];
+ sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%016" PRIx64 "%%016" PRIx64,
+ address, is_read ? "read" : "write");
+ LOG_DEBUG(fmt, value_h, value_l);
+}
+
+static void log_memory_access64(target_addr_t address, uint64_t value,
+ unsigned int size_bytes, bool is_read)
{
if (debug_level < LOG_LVL_DEBUG)
return;
char fmt[80];
sprintf(fmt, "M[0x%" TARGET_PRIxADDR "] %ss 0x%%0%d" PRIx64,
- address, read ? "read" : "write", size_bytes * 2);
+ address, is_read ? "read" : "write", size_bytes * 2);
switch (size_bytes) {
case 1:
value &= 0xff;
@@ -2501,23 +3288,35 @@ static void log_memory_access(target_addr_t address, uint64_t value,
}
LOG_DEBUG(fmt, value);
}
+static void log_memory_access(target_addr_t address, uint32_t *sbvalue,
+ unsigned int size_bytes, bool is_read)
+{
+ if (size_bytes == 16) {
+ uint64_t value_h = ((uint64_t)sbvalue[3] << 32) | sbvalue[2];
+ uint64_t value_l = ((uint64_t)sbvalue[1] << 32) | sbvalue[0];
+ log_memory_access128(address, value_h, value_l, is_read);
+ } else {
+ uint64_t value = ((uint64_t)sbvalue[1] << 32) | sbvalue[0];
+ log_memory_access64(address, value, size_bytes, is_read);
+ }
+}
/* Read the relevant sbdata regs depending on size, and put the results into
* buffer. */
static int read_memory_bus_word(struct target *target, target_addr_t address,
uint32_t size, uint8_t *buffer)
{
- uint32_t value;
int result;
+ uint32_t sbvalue[4] = { 0 };
static int sbdata[4] = { DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3 };
assert(size <= 16);
for (int i = (size - 1) / 4; i >= 0; i--) {
- result = dmi_op(target, &value, NULL, DMI_OP_READ, sbdata[i], 0, false, true);
+ result = dm_read(target, &sbvalue[i], sbdata[i]);
if (result != ERROR_OK)
return result;
- buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), value);
- log_memory_access(address + i * 4, value, MIN(size, 4), true);
+ buf_set_u32(buffer + i * 4, 0, 8 * MIN(size, 4), sbvalue[i]);
}
+ log_memory_access(address, sbvalue, size, true);
return ERROR_OK;
}
@@ -2528,12 +3327,12 @@ static target_addr_t sb_read_address(struct target *target)
target_addr_t address = 0;
uint32_t v;
if (sbasize > 32) {
- dmi_read(target, &v, DM_SBADDRESS1);
- address |= v;
+ if (dm_read(target, &v, DM_SBADDRESS1) == ERROR_OK)
+ address |= v;
address <<= 32;
}
- dmi_read(target, &v, DM_SBADDRESS0);
- address |= v;
+ if (dm_read(target, &v, DM_SBADDRESS0) == ERROR_OK)
+ address |= v;
return address;
}
@@ -2541,12 +3340,12 @@ static int read_sbcs_nonbusy(struct target *target, uint32_t *sbcs)
{
time_t start = time(NULL);
while (1) {
- if (dmi_read(target, sbcs, DM_SBCS) != ERROR_OK)
+ if (dm_read(target, sbcs, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
if (!get_field(*sbcs, DM_SBCS_SBBUSY))
return ERROR_OK;
if (time(NULL) - start > riscv_command_timeout_sec) {
- LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
+ LOG_TARGET_ERROR(target, "Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec, *sbcs);
return ERROR_FAIL;
@@ -2559,18 +3358,18 @@ static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *
if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) {
/* Read DCSR */
uint64_t dcsr;
- if (register_read(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
return ERROR_FAIL;
/* Read and save MSTATUS */
- if (register_read(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
+ if (register_read_direct(target, mstatus, GDB_REGNO_MSTATUS) != ERROR_OK)
return ERROR_FAIL;
*mstatus_old = *mstatus;
/* If we come from m-mode with mprv set, we want to keep mpp */
- if (get_field(dcsr, DCSR_PRV) < 3) {
+ if (get_field(dcsr, CSR_DCSR_PRV) < 3) {
/* MPP = PRIV */
- *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, DCSR_PRV));
+ *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, CSR_DCSR_PRV));
/* MPRV = 1 */
*mstatus = set_field(*mstatus, MSTATUS_MPRV, 1);
@@ -2589,11 +3388,11 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
{
if (size != increment) {
- LOG_ERROR("sba v0 reads only support size==increment");
+ LOG_TARGET_ERROR(target, "sba v0 reads only support size==increment");
return ERROR_NOT_IMPLEMENTED;
}
- LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+ LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
TARGET_PRIxADDR, size, count, address);
uint8_t *t_buffer = buffer;
riscv_addr_t cur_addr = address;
@@ -2609,19 +3408,19 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
/* ww favorise one off reading if there is an issue */
if (count == 1) {
for (uint32_t i = 0; i < count; i++) {
- if (dmi_read(target, &access, DM_SBCS) != ERROR_OK)
+ if (dm_read(target, &access, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
- dmi_write(target, DM_SBADDRESS0, cur_addr);
+ dm_write(target, DM_SBADDRESS0, cur_addr);
/* size/2 matching the bit access of the spec 0.13 */
access = set_field(access, DM_SBCS_SBACCESS, size/2);
access = set_field(access, DM_SBCS_SBSINGLEREAD, 1);
- LOG_DEBUG("\r\nread_memory: sab: access: 0x%08x", access);
- dmi_write(target, DM_SBCS, access);
+ LOG_TARGET_DEBUG(target, "read_memory: sab: access: 0x%08x", access);
+ dm_write(target, DM_SBCS, access);
/* 3) read */
uint32_t value;
- if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK)
+ if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
return ERROR_FAIL;
- LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value);
+ LOG_TARGET_DEBUG(target, "read_memory: sab: value: 0x%08x", value);
buf_set_u32(t_buffer, 0, 8 * size, value);
t_buffer += size;
cur_addr += size;
@@ -2630,26 +3429,26 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
}
/* has to be the same size if we want to read a block */
- LOG_DEBUG("reading block until final address 0x%" PRIx64, fin_addr);
- if (dmi_read(target, &access, DM_SBCS) != ERROR_OK)
+ LOG_TARGET_DEBUG(target, "Reading block until final address 0x%" PRIx64, fin_addr);
+ if (dm_read(target, &access, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
/* set current address */
- dmi_write(target, DM_SBADDRESS0, cur_addr);
+ dm_write(target, DM_SBADDRESS0, cur_addr);
/* 2) write sbaccess=2, sbsingleread,sbautoread,sbautoincrement
* size/2 matching the bit access of the spec 0.13 */
access = set_field(access, DM_SBCS_SBACCESS, size/2);
access = set_field(access, DM_SBCS_SBAUTOREAD, 1);
access = set_field(access, DM_SBCS_SBSINGLEREAD, 1);
access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1);
- LOG_DEBUG("\r\naccess: 0x%08x", access);
- dmi_write(target, DM_SBCS, access);
+ LOG_TARGET_DEBUG(target, "access: 0x%08x", access);
+ dm_write(target, DM_SBCS, access);
while (cur_addr < fin_addr) {
- LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08"
+ LOG_TARGET_DEBUG(target, "sab:autoincrement:\r\n\tsize: %d\tcount:%d\taddress: 0x%08"
PRIx64, size, count, cur_addr);
/* read */
uint32_t value;
- if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK)
+ if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
return ERROR_FAIL;
buf_set_u32(t_buffer, 0, 8 * size, value);
cur_addr += size;
@@ -2657,15 +3456,15 @@ static int read_memory_bus_v0(struct target *target, target_addr_t address,
/* if we are reaching last address, we must clear autoread */
if (cur_addr == fin_addr && count != 1) {
- dmi_write(target, DM_SBCS, 0);
- if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK)
+ dm_write(target, DM_SBCS, 0);
+ if (dm_read(target, &value, DM_SBDATA0) != ERROR_OK)
return ERROR_FAIL;
buf_set_u32(t_buffer, 0, 8 * size, value);
}
}
uint32_t sbcs;
- if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
+ if (dm_read(target, &sbcs, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
return ERROR_OK;
@@ -2678,13 +3477,17 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
{
if (increment != size && increment != 0) {
- LOG_ERROR("sba v1 reads only support increment of size or 0");
+ LOG_TARGET_ERROR(target, "sba v1 reads only support increment of size or 0");
return ERROR_NOT_IMPLEMENTED;
}
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
+
RISCV013_INFO(info);
target_addr_t next_address = address;
- target_addr_t end_address = address + count * size;
+ target_addr_t end_address = address + (increment ? count : 1) * size;
while (next_address < end_address) {
uint32_t sbcs_write = set_field(0, DM_SBCS_SBREADONADDR, 1);
@@ -2693,7 +3496,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
sbcs_write = set_field(sbcs_write, DM_SBCS_SBAUTOINCREMENT, 1);
if (count > 1)
sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, count > 1);
- if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK)
+ if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK)
return ERROR_FAIL;
/* This address write will trigger the first read. */
@@ -2701,32 +3504,48 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
if (info->bus_master_read_delay) {
+ LOG_TARGET_DEBUG(target, "Waiting %d cycles for bus master read delay",
+ info->bus_master_read_delay);
jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
if (jtag_execute_queue() != ERROR_OK) {
- LOG_ERROR("Failed to scan idle sequence");
+ LOG_TARGET_ERROR(target, "Failed to scan idle sequence");
return ERROR_FAIL;
}
}
- /* First value has been read, and is waiting for us to issue a DMI read
- * to get it. */
+ /* First read has been started. Optimistically assume that it has
+ * completed. */
static int sbdata[4] = {DM_SBDATA0, DM_SBDATA1, DM_SBDATA2, DM_SBDATA3};
+ uint32_t sbvalue[4] = {0};
assert(size <= 16);
target_addr_t next_read = address - 1;
+ uint32_t buffer_offset = 0;
+ int next_read_j = 0;
for (uint32_t i = (next_address - address) / size; i < count - 1; i++) {
for (int j = (size - 1) / 4; j >= 0; j--) {
- uint32_t value;
unsigned attempt = 0;
while (1) {
if (attempt++ > 100) {
- LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT,
- next_read);
+ LOG_TARGET_ERROR(target, "DMI keeps being busy in while reading memory"
+ " just past " TARGET_ADDR_FMT, next_read);
return ERROR_FAIL;
}
keep_alive();
- dmi_status_t status = dmi_scan(target, NULL, &value,
- DMI_OP_READ, sbdata[j], 0, false);
+ dmi_status_t status = dmi_scan(target, NULL, &sbvalue[next_read_j],
+ DMI_OP_READ, sbdata[j] + dm->base, 0, false);
+ /* By reading from sbdata0, we have just initiated another system bus read.
+ * If necessary add a delay so the read can finish. */
+ if (j == 0 && info->bus_master_read_delay) {
+ LOG_TARGET_DEBUG(target, "Waiting %d cycles for bus master read delay",
+ info->bus_master_read_delay);
+ jtag_add_runtest(info->bus_master_read_delay, TAP_IDLE);
+ if (jtag_execute_queue() != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to scan idle sequence");
+ return ERROR_FAIL;
+ }
+ }
+
if (status == DMI_STATUS_BUSY)
increase_dmi_busy_delay(target);
else if (status == DMI_STATUS_SUCCESS)
@@ -2735,24 +3554,28 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
}
if (next_read != address - 1) {
- buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value);
- log_memory_access(next_read, value, MIN(size, 4), true);
+ buf_set_u32(buffer + buffer_offset, 0, 8 * MIN(size, 4), sbvalue[next_read_j]);
+ if (next_read_j == 0) {
+ log_memory_access(next_read, sbvalue, size, true);
+ memset(sbvalue, 0, size);
+ }
}
- next_read = address + i * size + j * 4;
+ next_read_j = j;
+ next_read = address + i * increment + next_read_j * 4;
+ buffer_offset = i * size + next_read_j * 4;
}
}
uint32_t sbcs_read = 0;
if (count > 1) {
- uint32_t value;
unsigned attempt = 0;
while (1) {
if (attempt++ > 100) {
- LOG_ERROR("DMI keeps being busy in while reading memory just past " TARGET_ADDR_FMT,
- next_read);
+ LOG_TARGET_ERROR(target, "DMI keeps being busy in while reading memory"
+ " just past " TARGET_ADDR_FMT, next_read);
return ERROR_FAIL;
}
- dmi_status_t status = dmi_scan(target, NULL, &value, DMI_OP_NOP, 0, 0, false);
+ dmi_status_t status = dmi_scan(target, NULL, &sbvalue[0], DMI_OP_NOP, 0, 0, false);
if (status == DMI_STATUS_BUSY)
increase_dmi_busy_delay(target);
else if (status == DMI_STATUS_SUCCESS)
@@ -2760,8 +3583,8 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
else
return ERROR_FAIL;
}
- buf_set_u32(buffer + next_read - address, 0, 8 * MIN(size, 4), value);
- log_memory_access(next_read, value, MIN(size, 4), true);
+ buf_set_u32(buffer + buffer_offset, 0, 8 * MIN(size, 4), sbvalue[0]);
+ log_memory_access(next_read, sbvalue, size, true);
/* "Writes to sbcs while sbbusy is high result in undefined behavior.
* A debugger must not write to sbcs until it reads sbbusy as 0." */
@@ -2769,14 +3592,14 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_FAIL;
sbcs_write = set_field(sbcs_write, DM_SBCS_SBREADONDATA, 0);
- if (dmi_write(target, DM_SBCS, sbcs_write) != ERROR_OK)
+ if (dm_write(target, DM_SBCS, sbcs_write) != ERROR_OK)
return ERROR_FAIL;
}
/* Read the last word, after we disabled sbreadondata if necessary. */
if (!get_field(sbcs_read, DM_SBCS_SBERROR) &&
!get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
- if (read_memory_bus_word(target, address + (count - 1) * size, size,
+ if (read_memory_bus_word(target, address + (count - 1) * increment, size,
buffer + (count - 1) * size) != ERROR_OK)
return ERROR_FAIL;
@@ -2785,21 +3608,37 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
}
if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) {
- /* We read while the target was busy. Slow down and try again. */
- if (dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR) != ERROR_OK)
+ /* We read while the target was busy. Slow down and try again.
+ * Clear sbbusyerror, as well as readondata or readonaddr. */
+ if (dm_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR) != ERROR_OK)
return ERROR_FAIL;
- next_address = sb_read_address(target);
+
+ if (get_field(sbcs_read, DM_SBCS_SBERROR) == DM_SBCS_SBERROR_NONE) {
+ /* Read the address whose read was last completed. */
+ next_address = sb_read_address(target);
+
+ /* Read the value for the last address. It's
+ * sitting in the register for us, but we read it
+ * too early (sbbusyerror became set). */
+ target_addr_t current_address = next_address - (increment ? size : 0);
+ if (read_memory_bus_word(target, current_address, size,
+ buffer + current_address - address) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1;
+ LOG_TARGET_DEBUG(target, "Increasing bus_master_read_delay to %d.",
+ info->bus_master_read_delay);
continue;
}
unsigned error = get_field(sbcs_read, DM_SBCS_SBERROR);
- if (error == 0) {
+ if (error == DM_SBCS_SBERROR_NONE) {
next_address = end_address;
} else {
/* Some error indicating the bus access failed, but not because of
* something we did wrong. */
- if (dmi_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK)
+ if (dm_write(target, DM_SBCS, DM_SBCS_SBERROR) != ERROR_OK)
return ERROR_FAIL;
return ERROR_FAIL;
}
@@ -2808,7 +3647,7 @@ static int read_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_OK;
}
-static void log_mem_access_result(struct target *target, bool success, int method, bool read)
+static void log_mem_access_result(struct target *target, bool success, int method, bool is_read)
{
RISCV_INFO(r);
bool warn = false;
@@ -2817,7 +3656,7 @@ static void log_mem_access_result(struct target *target, bool success, int metho
/* Compose the message */
snprintf(msg, 60, "%s to %s memory via %s.",
success ? "Succeeded" : "Failed",
- read ? "read" : "write",
+ is_read ? "read" : "write",
(method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" :
(method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access");
@@ -2838,43 +3677,43 @@ static void log_mem_access_result(struct target *target, bool success, int metho
}
if (warn)
- LOG_WARNING("%s", msg);
+ LOG_TARGET_WARNING(target, "%s", msg);
else
- LOG_DEBUG("%s", msg);
+ LOG_TARGET_DEBUG(target, "%s", msg);
}
static bool mem_should_skip_progbuf(struct target *target, target_addr_t address,
- uint32_t size, bool read, char **skip_reason)
+ uint32_t size, bool is_read, char **skip_reason)
{
assert(skip_reason);
if (!has_sufficient_progbuf(target, 3)) {
- LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.",
- read ? "read" : "write");
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - insufficient progbuf size.",
+ is_read ? "read" : "write");
*skip_reason = "skipped (insufficient progbuf)";
return true;
}
if (target->state != TARGET_HALTED) {
- LOG_DEBUG("Skipping mem %s via progbuf - target not halted.",
- read ? "read" : "write");
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - target not halted.",
+ is_read ? "read" : "write");
*skip_reason = "skipped (target not halted)";
return true;
}
if (riscv_xlen(target) < size * 8) {
- LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.",
- read ? "read" : "write", riscv_xlen(target), size * 8);
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.",
+ is_read ? "read" : "write", riscv_xlen(target), size * 8);
*skip_reason = "skipped (XLEN too short)";
return true;
}
if (size > 8) {
- LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.",
- read ? "read" : "write");
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - unsupported size.",
+ is_read ? "read" : "write");
*skip_reason = "skipped (unsupported size)";
return true;
}
if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
- LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
- read ? "read" : "write", riscv_xlen(target));
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via progbuf - progbuf only supports %u-bit address.",
+ is_read ? "read" : "write", riscv_xlen(target));
*skip_reason = "skipped (too large address)";
return true;
}
@@ -2883,26 +3722,26 @@ static bool mem_should_skip_progbuf(struct target *target, target_addr_t address
}
static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
- uint32_t size, uint32_t increment, bool read, char **skip_reason)
+ uint32_t size, uint32_t increment, bool is_read, char **skip_reason)
{
assert(skip_reason);
RISCV013_INFO(info);
if (!sba_supports_access(target, size)) {
- LOG_DEBUG("Skipping mem %s via system bus - unsupported size.",
- read ? "read" : "write");
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - unsupported size.",
+ is_read ? "read" : "write");
*skip_reason = "skipped (unsupported size)";
return true;
}
unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE);
if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) {
- LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.",
- read ? "read" : "write", sbasize);
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via system bus - sba only supports %u-bit address.",
+ is_read ? "read" : "write", sbasize);
*skip_reason = "skipped (too large address)";
return true;
}
- if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
- LOG_DEBUG("Skipping mem read via system bus - "
+ if (is_read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) {
+ LOG_TARGET_DEBUG(target, "Skipping mem read via system bus - "
"sba reads only support size==increment or also size==0 for sba v1.");
*skip_reason = "skipped (unsupported increment)";
return true;
@@ -2912,26 +3751,26 @@ static bool mem_should_skip_sysbus(struct target *target, target_addr_t address,
}
static bool mem_should_skip_abstract(struct target *target, target_addr_t address,
- uint32_t size, uint32_t increment, bool read, char **skip_reason)
+ uint32_t size, uint32_t increment, bool is_read, char **skip_reason)
{
assert(skip_reason);
if (size > 8) {
/* TODO: Add 128b support if it's ever used. Involves modifying
read/write_abstract_arg() to work on two 64b values. */
- LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits",
- read ? "read" : "write", size * 8);
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - unsupported size: %d bits",
+ is_read ? "read" : "write", size * 8);
*skip_reason = "skipped (unsupported size)";
return true;
}
if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) {
- LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
- read ? "read" : "write", riscv_xlen(target));
+ LOG_TARGET_DEBUG(target, "Skipping mem %s via abstract access - abstract access only supports %u-bit address.",
+ is_read ? "read" : "write", riscv_xlen(target));
*skip_reason = "skipped (too large address)";
return true;
}
- if (read && size != increment) {
- LOG_ERROR("Skipping mem read via abstract access - "
+ if (is_read && size != increment) {
+ LOG_TARGET_ERROR(target, "Skipping mem read via abstract access - "
"abstract command reads only support size==increment.");
*skip_reason = "skipped (unsupported increment)";
return true;
@@ -2953,7 +3792,7 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
int result = ERROR_OK;
bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
- LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
+ LOG_TARGET_DEBUG(target, "Reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
size, address);
memset(buffer, 0, count * size);
@@ -2974,31 +3813,38 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
/* Set arg1 to the address: address + c * size */
result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
if (result != ERROR_OK) {
- LOG_ERROR("Failed to write arg1 during read_memory_abstract().");
+ LOG_TARGET_ERROR(target, "Failed to write arg1.");
return result;
}
}
/* Execute the command */
- result = execute_abstract_command(target, command);
+ uint32_t cmderr;
+ result = execute_abstract_command(target, command, &cmderr);
+ /* TODO: we need to modify error handling here. */
+ /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
if (info->has_aampostincrement == YNM_MAYBE) {
if (result == ERROR_OK) {
/* Safety: double-check that the address was really auto-incremented */
- riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+ riscv_reg_t new_address;
+ result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target));
+ if (result != ERROR_OK)
+ return result;
+
if (new_address == address + size) {
- LOG_DEBUG("aampostincrement is supported on this target.");
+ LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
info->has_aampostincrement = YNM_YES;
} else {
- LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+ LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly.");
info->has_aampostincrement = YNM_NO;
}
} else {
/* Try the same access but with postincrement disabled. */
command = access_memory_command(target, false, width, false, false);
- result = execute_abstract_command(target, command);
+ result = execute_abstract_command(target, command, &cmderr);
if (result == ERROR_OK) {
- LOG_DEBUG("aampostincrement is not supported on this target.");
+ LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target.");
info->has_aampostincrement = YNM_NO;
}
}
@@ -3008,7 +3854,10 @@ static int read_memory_abstract(struct target *target, target_addr_t address,
return result;
/* Copy arg0 to buffer (rounded width up to nearest 32) */
- riscv_reg_t value = read_abstract_arg(target, 0, width32);
+ riscv_reg_t value;
+ result = read_abstract_arg(target, &value, 0, width32);
+ if (result != ERROR_OK)
+ return result;
buf_set_u64(p, 0, 8 * size, value);
if (info->has_aampostincrement == YNM_YES)
@@ -3031,7 +3880,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
int result = ERROR_OK;
bool use_aampostincrement = info->has_aampostincrement != YNM_NO;
- LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
+ LOG_TARGET_DEBUG(target, "writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
size, address);
/* Convert the size (bytes) to width (bits) */
@@ -3048,7 +3897,7 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
riscv_reg_t value = buf_get_u64(p, 0, 8 * size);
result = write_abstract_arg(target, 0, value, riscv_xlen(target));
if (result != ERROR_OK) {
- LOG_ERROR("Failed to write arg0 during write_memory_abstract().");
+ LOG_TARGET_ERROR(target, "Failed to write arg0.");
return result;
}
@@ -3057,31 +3906,38 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
/* Set arg1 to the address: address + c * size */
result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target));
if (result != ERROR_OK) {
- LOG_ERROR("Failed to write arg1 during write_memory_abstract().");
+ LOG_TARGET_ERROR(target, "Failed to write arg1.");
return result;
}
}
/* Execute the command */
- result = execute_abstract_command(target, command);
+ uint32_t cmderr;
+ result = execute_abstract_command(target, command, &cmderr);
+ /* TODO: we need to modify error handling here. */
+ /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
if (info->has_aampostincrement == YNM_MAYBE) {
if (result == ERROR_OK) {
/* Safety: double-check that the address was really auto-incremented */
- riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target));
+ riscv_reg_t new_address;
+ result = read_abstract_arg(target, &new_address, 1, riscv_xlen(target));
+ if (result != ERROR_OK)
+ return result;
+
if (new_address == address + size) {
- LOG_DEBUG("aampostincrement is supported on this target.");
+ LOG_TARGET_DEBUG(target, "aampostincrement is supported on this target.");
info->has_aampostincrement = YNM_YES;
} else {
- LOG_WARNING("Buggy aampostincrement! Address not incremented correctly.");
+ LOG_TARGET_WARNING(target, "Buggy aampostincrement! Address not incremented correctly.");
info->has_aampostincrement = YNM_NO;
}
} else {
/* Try the same access but with postincrement disabled. */
command = access_memory_command(target, false, width, false, true);
- result = execute_abstract_command(target, command);
+ result = execute_abstract_command(target, command, &cmderr);
if (result == ERROR_OK) {
- LOG_DEBUG("aampostincrement is not supported on this target.");
+ LOG_TARGET_DEBUG(target, "aampostincrement is not supported on this target.");
info->has_aampostincrement = YNM_NO;
}
}
@@ -3099,315 +3955,518 @@ static int write_memory_abstract(struct target *target, target_addr_t address,
}
/**
- * Read the requested memory, taking care to execute every read exactly once,
- * even if cmderr=busy is encountered.
+ * This function is used to start the memory-reading pipeline.
+ * The pipeline looks like this:
+ * memory -> s1 -> dm_data[0:1] -> debugger
+ * Prior to calling it, the program buffer should contain the appropriate
+ * program.
+ * This function sets DM_ABSTRACTAUTO_AUTOEXECDATA to trigger second stage of the
+ * pipeline (s1 -> dm_data[0:1]) whenever dm_data is read.
*/
-static int read_memory_progbuf_inner(struct target *target, target_addr_t address,
- uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
+static int read_memory_progbuf_inner_startup(struct target *target,
+ target_addr_t address, uint32_t increment, uint32_t index)
{
- RISCV013_INFO(info);
-
- int result = ERROR_OK;
-
- /* Write address to S0. */
- result = register_write_direct(target, GDB_REGNO_S0, address);
- if (result != ERROR_OK)
- return result;
+ /* s0 holds the next address to read from.
+ * s1 holds the next data value read.
+ * a0 is a counter in case increment is 0.
+ */
+ if (register_write_direct(target, GDB_REGNO_S0, address + index * increment)
+ != ERROR_OK)
+ return ERROR_FAIL;
- if (increment == 0 &&
- register_write_direct(target, GDB_REGNO_S2, 0) != ERROR_OK)
+ if (/*is_repeated_read*/ increment == 0 &&
+ register_write_direct(target, GDB_REGNO_A0, index) != ERROR_OK)
return ERROR_FAIL;
- uint32_t command = access_register_command(target, GDB_REGNO_S1,
- riscv_xlen(target),
+ /* AC_ACCESS_REGISTER_POSTEXEC is used to trigger first stage of the
+ * pipeline (memory -> s1) whenever this command is executed.
+ */
+ const uint32_t startup_command = access_register_command(target,
+ GDB_REGNO_S1, riscv_xlen(target),
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
- if (execute_abstract_command(target, command) != ERROR_OK)
+ uint32_t cmderr;
+ if (execute_abstract_command(target, startup_command, &cmderr) != ERROR_OK)
return ERROR_FAIL;
+ /* TODO: we need to modify error handling here. */
+ /* NOTE: in case of timeout cmderr is set to CMDERR_NONE */
- /* First read has just triggered. Result is in s1. */
- if (count == 1) {
- uint64_t value;
- if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK)
- return ERROR_FAIL;
- buf_set_u64(buffer, 0, 8 * size, value);
- log_memory_access(address, value, size, true);
+ /* First read has just triggered. Result is in s1.
+ * dm_data registers contain the previous value of s1 (garbage).
+ */
+ if (dm_write(target, DM_ABSTRACTAUTO,
+ set_field(0, DM_ABSTRACTAUTO_AUTOEXECDATA, 1)) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* Read garbage from dm_data0, which triggers another execution of the
+ * program. Now dm_data contains the first good result (from s1),
+ * and s1 the next memory value.
+ */
+ if (dm_read_exec(target, NULL, DM_DATA0) != ERROR_OK)
+ goto clear_abstractauto_and_fail;
+
+ uint32_t abstractcs;
+ if (wait_for_idle(target, &abstractcs) != ERROR_OK)
+ goto clear_abstractauto_and_fail;
+
+ cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR);
+ switch (cmderr) {
+ case CMDERR_NONE:
return ERROR_OK;
- }
+ case CMDERR_BUSY:
+ LOG_TARGET_ERROR(target, "Unexpected busy error. This is probably a hardware bug.");
+ /* fall through */
+ default:
+ LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, cmderr);
+ riscv013_clear_abstract_error(target);
+ goto clear_abstractauto_and_fail;
+ }
+clear_abstractauto_and_fail:
+ dm_write(target, DM_ABSTRACTAUTO, 0);
+ return ERROR_FAIL;
+}
- if (dmi_write(target, DM_ABSTRACTAUTO,
- 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) != ERROR_OK)
- goto error;
- /* Read garbage from dmi_data0, which triggers another execution of the
- * program. Now dmi_data0 contains the first good result, and s1 the next
- * memory value. */
- if (dmi_read_exec(target, NULL, DM_DATA0) != ERROR_OK)
- goto error;
-
- /* read_addr is the next address that the hart will read from, which is the
- * value in s0. */
- unsigned index = 2;
- while (index < count) {
- riscv_addr_t read_addr = address + index * increment;
- LOG_DEBUG("i=%d, count=%d, read_addr=0x%" PRIx64, index, count, read_addr);
- /* The pipeline looks like this:
- * memory -> s1 -> dm_data0 -> debugger
- * Right now:
- * s0 contains read_addr
- * s1 contains mem[read_addr-size]
- * dm_data0 contains[read_addr-size*2]
- */
+struct memory_access_info {
+ uint8_t *buffer_address;
+ target_addr_t target_address;
+ uint32_t element_size;
+ uint32_t increment;
+};
- struct riscv_batch *batch = riscv_batch_alloc(target, 32,
- info->dmi_busy_delay + info->ac_busy_delay);
- if (!batch)
- return ERROR_FAIL;
+/**
+ * This function attempts to restore the pipeline after a busy on abstract
+ * access.
+ * Target's state is as follows:
+ * s0 contains address + index_on_target * increment
+ * s1 contains mem[address + (index_on_target - 1) * increment]
+ * dm_data[0:1] contains mem[address + (index_on_target - 2) * increment]
+ */
+static int read_memory_progbuf_inner_on_ac_busy(struct target *target,
+ uint32_t start_index, uint32_t *elements_read,
+ struct memory_access_info access)
+{
+ increase_ac_busy_delay(target);
+ riscv013_clear_abstract_error(target);
- unsigned reads = 0;
- for (unsigned j = index; j < count; j++) {
- if (size > 4)
- riscv_batch_add_dmi_read(batch, DM_DATA1);
- riscv_batch_add_dmi_read(batch, DM_DATA0);
+ if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK)
+ return ERROR_FAIL;
- reads++;
- if (riscv_batch_full(batch))
- break;
- }
+ /* See how far we got by reading s0/a0 */
+ uint32_t index_on_target;
- batch_run(target, batch);
+ if (/*is_repeated_read*/ access.increment == 0) {
+ /* s0 is constant, a0 is incremented by one each execution */
+ riscv_reg_t counter;
- /* Wait for the target to finish performing the last abstract command,
- * and update our copy of cmderr. If we see that DMI is busy here,
- * dmi_busy_delay will be incremented. */
- uint32_t abstractcs;
- if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK)
+ if (register_read_direct(target, &counter, GDB_REGNO_A0) != ERROR_OK)
return ERROR_FAIL;
- while (get_field(abstractcs, DM_ABSTRACTCS_BUSY))
- if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK)
- return ERROR_FAIL;
- info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR);
-
- unsigned next_index;
- unsigned ignore_last = 0;
- switch (info->cmderr) {
- case CMDERR_NONE:
- LOG_DEBUG("successful (partial?) memory read");
- next_index = index + reads;
- break;
- case CMDERR_BUSY:
- LOG_DEBUG("memory read resulted in busy response");
+ index_on_target = counter;
+ } else {
+ target_addr_t address_on_target;
- increase_ac_busy_delay(target);
- riscv013_clear_abstract_error(target);
+ if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ index_on_target = (address_on_target - access.target_address) /
+ access.increment;
+ }
- dmi_write(target, DM_ABSTRACTAUTO, 0);
+ /* According to the spec, if an abstract command fails, one can't make any
+ * assumptions about dm_data registers, so all the values in the pipeline
+ * are clobbered now and need to be reread.
+ */
+ const uint32_t min_index_on_target = start_index + 2;
+ if (index_on_target < min_index_on_target) {
+ LOG_TARGET_ERROR(target, "Arithmetic does not work correctly on the target");
+ return ERROR_FAIL;
+ } else if (index_on_target == min_index_on_target) {
+ LOG_TARGET_DEBUG(target, "No forward progress");
+ }
+ const uint32_t next_index = (index_on_target - 2);
+ *elements_read = next_index - start_index;
+ LOG_TARGET_WARNING(target, "Re-reading memory from addresses 0x%"
+ TARGET_PRIxADDR " and 0x%" TARGET_PRIxADDR ".",
+ access.target_address + access.increment * next_index,
+ access.target_address + access.increment * (next_index + 1));
+ return read_memory_progbuf_inner_startup(target, access.target_address,
+ access.increment, next_index);
+}
- uint32_t dmi_data0, dmi_data1 = 0;
- /* This is definitely a good version of the value that we
- * attempted to read when we discovered that the target was
- * busy. */
- if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK) {
- riscv_batch_free(batch);
- goto error;
- }
- if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK) {
- riscv_batch_free(batch);
- goto error;
- }
+/**
+ * This function attempts to restore the pipeline after a dmi busy.
+ */
+static int read_memory_progbuf_inner_on_dmi_busy(struct target *target,
+ uint32_t start_index, uint32_t next_start_index,
+ struct memory_access_info access)
+{
+ LOG_TARGET_DEBUG(target, "DMI_STATUS_BUSY encountered in batch. Memory read [%"
+ PRIu32 ", %" PRIu32 ")", start_index, next_start_index);
+ if (start_index == next_start_index)
+ LOG_TARGET_DEBUG(target, "No forward progress");
- /* See how far we got, clobbering dmi_data0. */
- if (increment == 0) {
- uint64_t counter;
- result = register_read_direct(target, &counter, GDB_REGNO_S2);
- next_index = counter;
- } else {
- uint64_t next_read_addr;
- result = register_read_direct(target, &next_read_addr,
- GDB_REGNO_S0);
- next_index = (next_read_addr - address) / increment;
- }
- if (result != ERROR_OK) {
- riscv_batch_free(batch);
- goto error;
- }
+ if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK)
+ return ERROR_FAIL;
+ return read_memory_progbuf_inner_startup(target, access.target_address,
+ access.increment, next_start_index);
+}
- uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0;
- buf_set_u64(buffer + (next_index - 2) * size, 0, 8 * size, value64);
- log_memory_access(address + (next_index - 2) * size, value64, size, true);
+/**
+ * This function extracts the data from the batch.
+ */
+static int read_memory_progbuf_inner_extract_batch_data(struct target *target,
+ const struct riscv_batch *batch,
+ uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read,
+ struct memory_access_info access)
+{
+ const bool two_reads_per_element = access.element_size > 4;
+ const uint32_t reads_per_element = (two_reads_per_element ? 2 : 1);
+ assert(!two_reads_per_element || riscv_xlen(target) == 64);
+ assert(elements_to_read <= UINT32_MAX / reads_per_element);
+ const uint32_t nreads = elements_to_read * reads_per_element;
+ for (uint32_t curr_idx = start_index, read = 0; read < nreads; ++read) {
+ switch (riscv_batch_get_dmi_read_op(batch, read)) {
+ case DMI_STATUS_BUSY:
+ *elements_read = curr_idx - start_index;
+ return read_memory_progbuf_inner_on_dmi_busy(target, start_index, curr_idx
+ , access);
+ case DMI_STATUS_FAILED:
+ LOG_TARGET_DEBUG(target,
+ "Batch memory read encountered DMI_STATUS_FAILED on read %"
+ PRIu32, read);
+ return ERROR_FAIL;
+ case DMI_STATUS_SUCCESS:
+ break;
+ default:
+ assert(0);
+ }
+ const uint32_t value = riscv_batch_get_dmi_read_data(batch, read);
+ uint8_t * const curr_buff = access.buffer_address +
+ curr_idx * access.element_size;
+ const target_addr_t curr_addr = access.target_address +
+ curr_idx * access.increment;
+ const uint32_t size = access.element_size;
+
+ assert(size <= 8);
+ const bool is_odd_read = read % 2;
+
+ if (two_reads_per_element && !is_odd_read) {
+ buf_set_u32(curr_buff + 4, 0, (size * 8) - 32, value);
+ continue;
+ }
+ const bool is_second_read = two_reads_per_element;
- /* Restore the command, and execute it.
- * Now DM_DATA0 contains the next value just as it would if no
- * error had occurred. */
- dmi_write_exec(target, DM_COMMAND, command, true);
- next_index++;
+ buf_set_u32(curr_buff, 0, is_second_read ? 32 : (size * 8), value);
+ log_memory_access64(curr_addr, buf_get_u64(curr_buff, 0, size * 8),
+ size, /*is_read*/ true);
+ ++curr_idx;
+ }
+ *elements_read = elements_to_read;
+ return ERROR_OK;
+}
+
+/**
+ * This function reads a batch of elements from memory.
+ * Prior to calling this function the folowing conditions should be met:
+ * - Appropriate program loaded to program buffer.
+ * - DM_ABSTRACTAUTO_AUTOEXECDATA is set.
+ */
+static int read_memory_progbuf_inner_run_and_process_batch(struct target *target,
+ struct riscv_batch *batch, struct memory_access_info access,
+ uint32_t start_index, uint32_t elements_to_read, uint32_t *elements_read)
+{
+ RISCV013_INFO(info);
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return ERROR_FAIL;
- dmi_write(target, DM_ABSTRACTAUTO,
- 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+ /* Abstract commands are executed while running the batch. */
+ dm->abstract_cmd_maybe_busy = true;
+ if (batch_run(target, batch, info->dmi_busy_delay + info->ac_busy_delay) != ERROR_OK)
+ return ERROR_FAIL;
- ignore_last = 1;
+ uint32_t abstractcs;
+ if (wait_for_idle(target, &abstractcs) != ERROR_OK)
+ return ERROR_FAIL;
- break;
- default:
- LOG_DEBUG("error when reading memory, abstractcs=0x%08lx", (long)abstractcs);
- riscv013_clear_abstract_error(target);
- riscv_batch_free(batch);
- result = ERROR_FAIL;
- goto error;
- }
+ uint32_t elements_to_extract_from_batch;
+
+ uint32_t cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR);
+ switch (cmderr) {
+ case CMDERR_NONE:
+ LOG_TARGET_DEBUG(target, "successful (partial?) memory read [%"
+ PRIu32 ", %" PRIu32 ")", start_index, start_index + elements_to_read);
+ elements_to_extract_from_batch = elements_to_read;
+ break;
+ case CMDERR_BUSY:
+ LOG_TARGET_DEBUG(target, "memory read resulted in busy response");
+ if (read_memory_progbuf_inner_on_ac_busy(target, start_index,
+ &elements_to_extract_from_batch, access)
+ != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+ default:
+ LOG_TARGET_DEBUG(target, "error when reading memory, cmderr=0x%" PRIx32, cmderr);
+ riscv013_clear_abstract_error(target);
+ return ERROR_FAIL;
+ }
- /* Now read whatever we got out of the batch. */
- dmi_status_t status = DMI_STATUS_SUCCESS;
- unsigned read = 0;
- assert(index >= 2);
- for (unsigned j = index - 2; j < index + reads; j++) {
- assert(j < count);
- LOG_DEBUG("index=%d, reads=%d, next_index=%d, ignore_last=%d, j=%d",
- index, reads, next_index, ignore_last, j);
- if (j + 3 + ignore_last > next_index)
- break;
+ if (read_memory_progbuf_inner_extract_batch_data(target, batch, start_index,
+ elements_to_extract_from_batch, elements_read, access) != ERROR_OK)
+ return ERROR_FAIL;
- status = riscv_batch_get_dmi_read_op(batch, read);
- uint64_t value = riscv_batch_get_dmi_read_data(batch, read);
- read++;
- if (status != DMI_STATUS_SUCCESS) {
- /* If we're here because of busy count, dmi_busy_delay will
- * already have been increased and busy state will have been
- * cleared in dmi_read(). */
- /* In at least some implementations, we issue a read, and then
- * can get busy back when we try to scan out the read result,
- * and the actual read value is lost forever. Since this is
- * rare in any case, we return error here and rely on our
- * caller to reread the entire block. */
- LOG_WARNING("Batch memory read encountered DMI error %d. "
- "Falling back on slower reads.", status);
- riscv_batch_free(batch);
- result = ERROR_FAIL;
- goto error;
- }
- if (size > 4) {
- status = riscv_batch_get_dmi_read_op(batch, read);
- if (status != DMI_STATUS_SUCCESS) {
- LOG_WARNING("Batch memory read encountered DMI error %d. "
- "Falling back on slower reads.", status);
- riscv_batch_free(batch);
- result = ERROR_FAIL;
- goto error;
- }
- value <<= 32;
- value |= riscv_batch_get_dmi_read_data(batch, read);
- read++;
- }
- riscv_addr_t offset = j * size;
- buf_set_u64(buffer + offset, 0, 8 * size, value);
- log_memory_access(address + j * increment, value, size, true);
- }
+ return ERROR_OK;
+}
- index = next_index;
+static uint32_t read_memory_progbuf_inner_fill_batch(struct riscv_batch *batch,
+ uint32_t count, uint32_t size)
+{
+ assert(size <= 8);
+ const uint32_t two_regs_used[] = {DM_DATA1, DM_DATA0};
+ const uint32_t one_reg_used[] = {DM_DATA0};
+ const uint32_t reads_per_element = size > 4 ? 2 : 1;
+ const uint32_t * const used_regs = size > 4 ? two_regs_used : one_reg_used;
+ const uint32_t batch_capacity = riscv_batch_available_scans(batch) / reads_per_element;
+ const uint32_t end = MIN(batch_capacity, count);
+
+ for (uint32_t j = 0; j < end; ++j)
+ for (uint32_t i = 0; i < reads_per_element; ++i)
+ riscv_batch_add_dm_read(batch, used_regs[i]);
+ return end;
+}
- riscv_batch_free(batch);
- }
+static int read_memory_progbuf_inner_try_to_read(struct target *target,
+ struct memory_access_info access, uint32_t *elements_read,
+ uint32_t index, uint32_t loop_count)
+{
+ struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE);
+ if (!batch)
+ return ERROR_FAIL;
- dmi_write(target, DM_ABSTRACTAUTO, 0);
+ const uint32_t elements_to_read = read_memory_progbuf_inner_fill_batch(batch,
+ loop_count - index, access.element_size);
- if (count > 1) {
- /* Read the penultimate word. */
- uint32_t dmi_data0, dmi_data1 = 0;
- if (dmi_read(target, &dmi_data0, DM_DATA0) != ERROR_OK)
- return ERROR_FAIL;
- if (size > 4 && dmi_read(target, &dmi_data1, DM_DATA1) != ERROR_OK)
- return ERROR_FAIL;
- uint64_t value64 = (((uint64_t)dmi_data1) << 32) | dmi_data0;
- buf_set_u64(buffer + size * (count - 2), 0, 8 * size, value64);
- log_memory_access(address + size * (count - 2), value64, size, true);
- }
+ int result = read_memory_progbuf_inner_run_and_process_batch(target, batch,
+ access, index, elements_to_read, elements_read);
+ riscv_batch_free(batch);
+ return result;
+}
- /* Read the last word. */
- uint64_t value;
- result = register_read_direct(target, &value, GDB_REGNO_S1);
- if (result != ERROR_OK)
- goto error;
- buf_set_u64(buffer + size * (count-1), 0, 8 * size, value);
- log_memory_access(address + size * (count-1), value, size, true);
+/**
+ * read_memory_progbuf_inner_startup() must be called before calling this function
+ * with the address argument equal to curr_target_address.
+ */
+static int read_memory_progbuf_inner_ensure_forward_progress(struct target *target,
+ struct memory_access_info access, uint32_t start_index)
+{
+ LOG_TARGET_DEBUG(target,
+ "Executing one loop iteration to ensure forward progress (index=%"
+ PRIu32 ")", start_index);
+ const target_addr_t curr_target_address = access.target_address +
+ start_index * access.increment;
+ uint8_t * const curr_buffer_address = access.buffer_address +
+ start_index * access.element_size;
+ const struct memory_access_info curr_access = {
+ .buffer_address = curr_buffer_address,
+ .target_address = curr_target_address,
+ .element_size = access.element_size,
+ .increment = access.increment,
+ };
+ uint32_t elements_read;
+ if (read_memory_progbuf_inner_try_to_read(target, curr_access, &elements_read,
+ /*index*/ 0, /*loop_count*/ 1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (elements_read != 1) {
+ assert(elements_read == 0);
+ LOG_TARGET_DEBUG(target, "Can not ensure forward progress");
+ /* FIXME: Here it would be better to retry the read and fail only if the
+ * delay is greater then some threshold.
+ */
+ return ERROR_FAIL;
+ }
return ERROR_OK;
+}
-error:
- dmi_write(target, DM_ABSTRACTAUTO, 0);
+static void set_buffer_and_log_read(struct memory_access_info access,
+ uint32_t index, uint64_t value)
+{
+ uint8_t * const buffer = access.buffer_address;
+ const uint32_t size = access.element_size;
+ const uint32_t increment = access.increment;
+ const target_addr_t address = access.target_address;
+
+ assert(size <= 8);
+ buf_set_u64(buffer + index * size, 0, 8 * size, value);
+ log_memory_access64(address + index * increment, value, size,
+ /*is_read*/ true);
+}
+static int read_word_from_dm_data_regs(struct target *target,
+ struct memory_access_info access, uint32_t index)
+{
+ assert(access.element_size <= 8);
+ uint64_t value;
+ int result = read_abstract_arg(target, &value, /*index*/ 0,
+ access.element_size > 4 ? 64 : 32);
+ if (result == ERROR_OK)
+ set_buffer_and_log_read(access, index, value);
return result;
}
-/* Only need to save/restore one GPR to read a single word, and the progbuf
- * program doesn't need to increment. */
-static int read_memory_progbuf_one(struct target *target, target_addr_t address,
- uint32_t size, uint8_t *buffer)
+static int read_word_from_s1(struct target *target,
+ struct memory_access_info access, uint32_t index)
{
- uint64_t mstatus = 0;
- uint64_t mstatus_old = 0;
- if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
+ uint64_t value;
+
+ if (register_read_direct(target, &value, GDB_REGNO_S1) != ERROR_OK)
return ERROR_FAIL;
+ set_buffer_and_log_read(access, index, value);
+ return ERROR_OK;
+}
- uint64_t s0;
- int result = ERROR_FAIL;
+static int riscv_program_load_mprv(struct riscv_program *p, enum gdb_regno d,
+ enum gdb_regno b, int offset, unsigned int size, bool mprven)
+{
+ if (mprven && riscv_program_csrrsi(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN,
+ GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (riscv_program_load(p, d, b, offset, size) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (mprven && riscv_program_csrrci(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN,
+ GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- goto restore_mstatus;
+static int read_memory_progbuf_inner_fill_progbuf(struct target *target,
+ uint32_t increment, uint32_t size, bool mprven)
+{
+ const bool is_repeated_read = increment == 0;
+
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (is_repeated_read && riscv_save_register(target, GDB_REGNO_A0) != ERROR_OK)
+ return ERROR_FAIL;
- /* Write the program (load, increment) */
struct riscv_program program;
+
riscv_program_init(&program, target);
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
- switch (size) {
- case 1:
- riscv_program_lbr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
- break;
- case 2:
- riscv_program_lhr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
- break;
- case 4:
- riscv_program_lwr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
- break;
- case 8:
- riscv_program_ldr(&program, GDB_REGNO_S0, GDB_REGNO_S0, 0);
- break;
- default:
- LOG_ERROR("Unsupported size: %d", size);
- goto restore_mstatus;
+ if (riscv_program_load_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0, size,
+ mprven) != ERROR_OK)
+ return ERROR_FAIL;
+ if (is_repeated_read) {
+ if (riscv_program_addi(&program, GDB_REGNO_A0, GDB_REGNO_A0, 1)
+ != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0,
+ increment)
+ != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ if (riscv_program_ebreak(&program) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_program_write(&program) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
+/**
+ * Read the requested memory, taking care to minimize the number of reads and
+ * re-read the data only if `abstract command busy` or `DMI busy`
+ * is encountered in the process.
+ */
+static int read_memory_progbuf_inner(struct target *target,
+ struct memory_access_info access, uint32_t count, bool mprven)
+{
+ assert(count > 1 && "If count == 1, read_memory_progbuf_inner_one must be called");
+
+ if (read_memory_progbuf_inner_fill_progbuf(target, access.increment,
+ access.element_size, mprven) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (read_memory_progbuf_inner_startup(target, access.target_address,
+ access.increment, /*index*/ 0)
+ != ERROR_OK)
+ return ERROR_FAIL;
+ /* The program in program buffer is executed twice during
+ * read_memory_progbuf_inner_startup().
+ * Here:
+ * dm_data[0:1] == M[address]
+ * s1 == M[address + increment]
+ * s0 == address + increment * 2
+ * `count - 2` program executions are performed in this loop.
+ * No need to execute the program any more, since S1 will already contain
+ * M[address + increment * (count - 1)] and we can read it directly.
+ */
+ const uint32_t loop_count = count - 2;
+
+ for (uint32_t index = 0; index < loop_count;) {
+ uint32_t elements_read;
+ if (read_memory_progbuf_inner_try_to_read(target, access, &elements_read,
+ index, loop_count) != ERROR_OK) {
+ dm_write(target, DM_ABSTRACTAUTO, 0);
+ return ERROR_FAIL;
+ }
+ if (elements_read == 0) {
+ if (read_memory_progbuf_inner_ensure_forward_progress(target, access,
+ index) != ERROR_OK) {
+ dm_write(target, DM_ABSTRACTAUTO, 0);
+ return ERROR_FAIL;
+ }
+ elements_read = 1;
+ }
+ index += elements_read;
+ assert(index <= loop_count);
}
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
+ if (dm_write(target, DM_ABSTRACTAUTO, 0) != ERROR_OK)
+ return ERROR_FAIL;
+ /* Read the penultimate word. */
+ if (read_word_from_dm_data_regs(target, access, count - 2)
+ != ERROR_OK)
+ return ERROR_FAIL;
+ /* Read the last word. */
+ return read_word_from_s1(target, access, count - 1);
+}
+
+/**
+ * Only need to save/restore one GPR to read a single word, and the progbuf
+ * program doesn't need to increment.
+ */
+static int read_memory_progbuf_inner_one(struct target *target,
+ struct memory_access_info access, bool mprven)
+{
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ struct riscv_program program;
+
+ riscv_program_init(&program, target);
+ if (riscv_program_load_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S1, 0,
+ access.element_size, mprven) != ERROR_OK)
+ return ERROR_FAIL;
if (riscv_program_ebreak(&program) != ERROR_OK)
- goto restore_mstatus;
+ return ERROR_FAIL;
if (riscv_program_write(&program) != ERROR_OK)
- goto restore_mstatus;
+ return ERROR_FAIL;
- /* Write address to S0, and execute buffer. */
- if (write_abstract_arg(target, 0, address, riscv_xlen(target)) != ERROR_OK)
- goto restore_mstatus;
- uint32_t command = access_register_command(target, GDB_REGNO_S0,
+ /* Write address to S1, and execute buffer. */
+ if (write_abstract_arg(target, 0, access.target_address, riscv_xlen(target))
+ != ERROR_OK)
+ return ERROR_FAIL;
+ uint32_t command = access_register_command(target, GDB_REGNO_S1,
riscv_xlen(target), AC_ACCESS_REGISTER_WRITE |
AC_ACCESS_REGISTER_TRANSFER | AC_ACCESS_REGISTER_POSTEXEC);
- if (execute_abstract_command(target, command) != ERROR_OK)
- goto restore_s0;
-
- uint64_t value;
- if (register_read(target, &value, GDB_REGNO_S0) != ERROR_OK)
- goto restore_s0;
- buf_set_u64(buffer, 0, 8 * size, value);
- log_memory_access(address, value, size, true);
- result = ERROR_OK;
-
-restore_s0:
- if (riscv_set_register(target, GDB_REGNO_S0, s0) != ERROR_OK)
- result = ERROR_FAIL;
-
-restore_mstatus:
- if (mstatus != mstatus_old)
- if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
- result = ERROR_FAIL;
+ uint32_t cmderr;
+ if (execute_abstract_command(target, command, &cmderr) != ERROR_OK)
+ return ERROR_FAIL;
- return result;
+ return read_word_from_s1(target, access, 0);
}
/**
@@ -3417,15 +4476,16 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment)
{
if (riscv_xlen(target) < size * 8) {
- LOG_ERROR("XLEN (%d) is too short for %d-bit memory read.",
- riscv_xlen(target), size * 8);
+ LOG_TARGET_ERROR(target, "XLEN (%d) is too short for %"
+ PRIu32 "-bit memory read.", riscv_xlen(target), size * 8);
return ERROR_FAIL;
}
- int result = ERROR_OK;
+ LOG_TARGET_DEBUG(target, "reading %" PRIu32 " elements of %" PRIu32
+ " bytes from 0x%" TARGET_PRIxADDR, count, size, address);
- LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count,
- size, address);
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
select_dmi(target);
@@ -3434,98 +4494,26 @@ static int read_memory_progbuf(struct target *target, target_addr_t address,
if (execute_fence(target) != ERROR_OK)
return ERROR_FAIL;
- if (count == 1)
- return read_memory_progbuf_one(target, address, size, buffer);
-
uint64_t mstatus = 0;
uint64_t mstatus_old = 0;
if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
return ERROR_FAIL;
- /* s0 holds the next address to read from
- * s1 holds the next data value read
- * s2 is a counter in case increment is 0
- */
- uint64_t s0, s1, s2;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
- return ERROR_FAIL;
- if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
- return ERROR_FAIL;
- if (increment == 0 && register_read(target, &s2, GDB_REGNO_S2) != ERROR_OK)
- return ERROR_FAIL;
-
- /* Write the program (load, increment) */
- struct riscv_program program;
- riscv_program_init(&program, target);
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
-
- switch (size) {
- case 1:
- riscv_program_lbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 2:
- riscv_program_lhr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 4:
- riscv_program_lwr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 8:
- riscv_program_ldr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- default:
- LOG_ERROR("Unsupported size: %d", size);
- return ERROR_FAIL;
- }
-
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
- if (increment == 0)
- riscv_program_addi(&program, GDB_REGNO_S2, GDB_REGNO_S2, 1);
- else
- riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, increment);
+ const bool mprven = riscv_enable_virtual && get_field(mstatus, MSTATUS_MPRV);
+ const struct memory_access_info access = {
+ .target_address = address,
+ .increment = increment,
+ .buffer_address = buffer,
+ .element_size = size,
+ };
+ int result = (count == 1) ?
+ read_memory_progbuf_inner_one(target, access, mprven) :
+ read_memory_progbuf_inner(target, access, count, mprven);
- if (riscv_program_ebreak(&program) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_program_write(&program) != ERROR_OK)
+ if (mstatus != mstatus_old &&
+ register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old) != ERROR_OK)
return ERROR_FAIL;
- result = read_memory_progbuf_inner(target, address, size, count, buffer, increment);
-
- if (result != ERROR_OK) {
- /* The full read did not succeed, so we will try to read each word individually. */
- /* This will not be fast, but reading outside actual memory is a special case anyway. */
- /* It will make the toolchain happier, especially Eclipse Memory View as it reads ahead. */
- target_addr_t address_i = address;
- uint32_t count_i = 1;
- uint8_t *buffer_i = buffer;
-
- for (uint32_t i = 0; i < count; i++, address_i += increment, buffer_i += size) {
- /* TODO: This is much slower than it needs to be because we end up
- * writing the address to read for every word we read. */
- result = read_memory_progbuf_inner(target, address_i, size, count_i, buffer_i, increment);
-
- /* The read of a single word failed, so we will just return 0 for that instead */
- if (result != ERROR_OK) {
- LOG_DEBUG("error reading single word of %d bytes from 0x%" TARGET_PRIxADDR,
- size, address_i);
-
- buf_set_u64(buffer_i, 0, 8 * size, 0);
- }
- }
- result = ERROR_OK;
- }
-
- riscv_set_register(target, GDB_REGNO_S0, s0);
- riscv_set_register(target, GDB_REGNO_S1, s1);
- if (increment == 0)
- riscv_set_register(target, GDB_REGNO_S2, s2);
-
- /* Restore MSTATUS */
- if (mstatus != mstatus_old)
- if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
- return ERROR_FAIL;
-
return result;
}
@@ -3536,7 +4524,7 @@ static int read_memory(struct target *target, target_addr_t address,
return ERROR_OK;
if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
- LOG_ERROR("BUG: Unsupported size for memory read: %d", size);
+ LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory read: %d", size);
return ERROR_FAIL;
}
@@ -3588,8 +4576,8 @@ static int read_memory(struct target *target, target_addr_t address,
return ret;
}
- LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address);
- LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+ LOG_TARGET_ERROR(target, "Failed to read memory (addr=0x%" PRIx64 ")", address);
+ LOG_TARGET_ERROR(target, " progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
return ret;
}
@@ -3597,9 +4585,9 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
/*1) write sbaddress: for singlewrite and autoincrement, we need to write the address once*/
- LOG_DEBUG("System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
+ LOG_TARGET_DEBUG(target, "System Bus Access: size: %d\tcount:%d\tstart address: 0x%08"
TARGET_PRIxADDR, size, count, address);
- dmi_write(target, DM_SBADDRESS0, address);
+ dm_write(target, DM_SBADDRESS0, address);
int64_t value = 0;
int64_t access = 0;
riscv_addr_t offset = 0;
@@ -3612,10 +4600,10 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
access = 0;
access = set_field(access, DM_SBCS_SBACCESS, size/2);
- dmi_write(target, DM_SBCS, access);
- LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access);
- LOG_DEBUG("\r\nwrite_memory:SAB: ONE OFF: value 0x%08" PRIx64, value);
- dmi_write(target, DM_SBDATA0, value);
+ dm_write(target, DM_SBCS, access);
+ LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access);
+ LOG_TARGET_DEBUG(target, " write_memory:SAB: ONE OFF: value 0x%08" PRIx64, value);
+ dm_write(target, DM_SBDATA0, value);
return ERROR_OK;
}
@@ -3624,8 +4612,8 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
access = 0;
access = set_field(access, DM_SBCS_SBACCESS, size/2);
access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 1);
- LOG_DEBUG("\r\naccess: 0x%08" PRIx64, access);
- dmi_write(target, DM_SBCS, access);
+ LOG_TARGET_DEBUG(target, " access: 0x%08" PRIx64, access);
+ dm_write(target, DM_SBCS, access);
/*2)set the value according to the size required and write*/
for (riscv_addr_t i = 0; i < count; ++i) {
@@ -3635,13 +4623,13 @@ static int write_memory_bus_v0(struct target *target, target_addr_t address,
t_buffer = buffer + offset;
value = buf_get_u64(t_buffer, 0, 8 * size);
- LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x"
+ LOG_TARGET_DEBUG(target, "SAB:autoincrement: expected address: 0x%08x value: 0x%08x"
PRIx64, (uint32_t)t_addr, (uint32_t)value);
- dmi_write(target, DM_SBDATA0, value);
+ dm_write(target, DM_SBDATA0, value);
}
/*reset the autoincrement when finished (something weird is happening if this is not done at the end*/
access = set_field(access, DM_SBCS_SBAUTOINCREMENT, 0);
- dmi_write(target, DM_SBCS, access);
+ dm_write(target, DM_SBCS, access);
return ERROR_OK;
}
@@ -3652,7 +4640,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
RISCV013_INFO(info);
uint32_t sbcs = sb_sbaccess(size);
sbcs = set_field(sbcs, DM_SBCS_SBAUTOINCREMENT, 1);
- dmi_write(target, DM_SBCS, sbcs);
+ dm_write(target, DM_SBCS, sbcs);
target_addr_t next_address = address;
target_addr_t end_address = address + count * size;
@@ -3661,13 +4649,10 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
sb_write_address(target, next_address, true);
while (next_address < end_address) {
- LOG_DEBUG("transferring burst starting at address 0x%" TARGET_PRIxADDR,
+ LOG_TARGET_DEBUG(target, "Transferring burst starting at address 0x%" TARGET_PRIxADDR,
next_address);
- struct riscv_batch *batch = riscv_batch_alloc(
- target,
- 32,
- info->dmi_busy_delay + info->bus_master_write_delay);
+ struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE);
if (!batch)
return ERROR_FAIL;
@@ -3677,40 +4662,48 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
if (riscv_batch_available_scans(batch) < (size + 3) / 4)
break;
- if (size > 12)
- riscv_batch_add_dmi_write(batch, DM_SBDATA3,
- ((uint32_t) p[12]) |
- (((uint32_t) p[13]) << 8) |
- (((uint32_t) p[14]) << 16) |
- (((uint32_t) p[15]) << 24));
-
- if (size > 8)
- riscv_batch_add_dmi_write(batch, DM_SBDATA2,
- ((uint32_t) p[8]) |
- (((uint32_t) p[9]) << 8) |
- (((uint32_t) p[10]) << 16) |
- (((uint32_t) p[11]) << 24));
- if (size > 4)
- riscv_batch_add_dmi_write(batch, DM_SBDATA1,
- ((uint32_t) p[4]) |
- (((uint32_t) p[5]) << 8) |
- (((uint32_t) p[6]) << 16) |
- (((uint32_t) p[7]) << 24));
- uint32_t value = p[0];
+ uint32_t sbvalue[4] = { 0 };
+ if (size > 12) {
+ sbvalue[3] = ((uint32_t)p[12]) |
+ (((uint32_t)p[13]) << 8) |
+ (((uint32_t)p[14]) << 16) |
+ (((uint32_t)p[15]) << 24);
+ riscv_batch_add_dm_write(batch, DM_SBDATA3, sbvalue[3], false);
+ }
+
+ if (size > 8) {
+ sbvalue[2] = ((uint32_t)p[8]) |
+ (((uint32_t)p[9]) << 8) |
+ (((uint32_t)p[10]) << 16) |
+ (((uint32_t)p[11]) << 24);
+ riscv_batch_add_dm_write(batch, DM_SBDATA2, sbvalue[2], false);
+ }
+ if (size > 4) {
+ sbvalue[1] = ((uint32_t)p[4]) |
+ (((uint32_t)p[5]) << 8) |
+ (((uint32_t)p[6]) << 16) |
+ (((uint32_t)p[7]) << 24);
+ riscv_batch_add_dm_write(batch, DM_SBDATA1, sbvalue[1], false);
+ }
+
+ sbvalue[0] = p[0];
if (size > 2) {
- value |= ((uint32_t) p[2]) << 16;
- value |= ((uint32_t) p[3]) << 24;
+ sbvalue[0] |= ((uint32_t)p[2]) << 16;
+ sbvalue[0] |= ((uint32_t)p[3]) << 24;
}
if (size > 1)
- value |= ((uint32_t) p[1]) << 8;
- riscv_batch_add_dmi_write(batch, DM_SBDATA0, value);
+ sbvalue[0] |= ((uint32_t)p[1]) << 8;
+
+ riscv_batch_add_dm_write(batch, DM_SBDATA0, sbvalue[0], false);
+
+ log_memory_access(address + i * size, sbvalue, size, false);
- log_memory_access(address + i * size, value, size, false);
next_address += size;
}
/* Execute the batch of writes */
- result = batch_run(target, batch);
+ result = batch_run(target, batch,
+ info->dmi_busy_delay + info->bus_master_write_delay);
riscv_batch_free(batch);
if (result != ERROR_OK)
return result;
@@ -3718,30 +4711,30 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
/* Read sbcs value.
* At the same time, detect if DMI busy has occurred during the batch write. */
bool dmi_busy_encountered;
- if (dmi_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ,
+ if (dm_op(target, &sbcs, &dmi_busy_encountered, DMI_OP_READ,
DM_SBCS, 0, false, true) != ERROR_OK)
return ERROR_FAIL;
if (dmi_busy_encountered)
- LOG_DEBUG("DMI busy encountered during system bus write.");
+ LOG_TARGET_DEBUG(target, "DMI busy encountered during system bus write.");
/* Wait until sbbusy goes low */
time_t start = time(NULL);
while (get_field(sbcs, DM_SBCS_SBBUSY)) {
if (time(NULL) - start > riscv_command_timeout_sec) {
- LOG_ERROR("Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
+ LOG_TARGET_ERROR(target, "Timed out after %ds waiting for sbbusy to go low (sbcs=0x%x). "
"Increase the timeout with riscv set_command_timeout_sec.",
riscv_command_timeout_sec, sbcs);
return ERROR_FAIL;
}
- if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK)
+ if (dm_read(target, &sbcs, DM_SBCS) != ERROR_OK)
return ERROR_FAIL;
}
if (get_field(sbcs, DM_SBCS_SBBUSYERROR)) {
/* We wrote while the target was busy. */
- LOG_DEBUG("Sbbusyerror encountered during system bus write.");
+ LOG_TARGET_DEBUG(target, "Sbbusyerror encountered during system bus write.");
/* Clear the sticky error flag. */
- dmi_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR);
+ dm_write(target, DM_SBCS, sbcs | DM_SBCS_SBBUSYERROR);
/* Slow down before trying again. */
info->bus_master_write_delay += info->bus_master_write_delay / 10 + 1;
}
@@ -3752,7 +4745,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
next_address = sb_read_address(target);
if (next_address < address) {
/* This should never happen, probably buggy hardware. */
- LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+ LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR
" - buggy sbautoincrement in hw?", next_address);
/* Fail the whole operation. */
return ERROR_FAIL;
@@ -3768,16 +4761,16 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
* (unless sbautoincrement in the HW is buggy).
*/
target_addr_t sbaddress = sb_read_address(target);
- LOG_DEBUG("System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")",
+ LOG_TARGET_DEBUG(target, "System bus access failed with sberror=%u (sbaddress=0x%" TARGET_PRIxADDR ")",
sberror, sbaddress);
if (sbaddress < address) {
/* This should never happen, probably buggy hardware.
* Make a note to the user not to trust the sbaddress value. */
- LOG_DEBUG("unexpected sbaddress=0x%" TARGET_PRIxADDR
+ LOG_TARGET_DEBUG(target, "unexpected sbaddress=0x%" TARGET_PRIxADDR
" - buggy sbautoincrement in hw?", next_address);
}
/* Clear the sticky error flag */
- dmi_write(target, DM_SBCS, DM_SBCS_SBERROR);
+ dm_write(target, DM_SBCS, DM_SBCS_SBERROR);
/* Fail the whole operation */
return ERROR_FAIL;
}
@@ -3786,187 +4779,284 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address,
return ERROR_OK;
}
-static int write_memory_progbuf(struct target *target, target_addr_t address,
- uint32_t size, uint32_t count, const uint8_t *buffer)
+/**
+ * This function is used to start the memory-writing pipeline.
+ * As part of the process, the function writes the first item and waits for completion,
+ * so forward progress is ensured.
+ * The pipeline looks like this:
+ * debugger -> dm_data[0:1] -> s1 -> memory
+ * Prior to calling it, the program buffer should contain the appropriate
+ * program.
+ * This function sets DM_ABSTRACTAUTO_AUTOEXECDATA to trigger second stage of the
+ * pipeline (dm_data[0:1] -> s1) whenever dm_data is written.
+ */
+static int write_memory_progbuf_startup(struct target *target, target_addr_t *address_p,
+ const uint8_t *buffer, uint32_t size)
{
- RISCV013_INFO(info);
+ /* TODO: There is potential to gain some performance if the operations below are
+ * executed inside the first DMI batch (not separately). */
+ if (register_write_direct(target, GDB_REGNO_S0, *address_p) != ERROR_OK)
+ return ERROR_FAIL;
- if (riscv_xlen(target) < size * 8) {
- LOG_ERROR("XLEN (%d) is too short for %d-bit memory write.",
- riscv_xlen(target), size * 8);
+ /* Write the first item to data0 [, data1] */
+ assert(size <= 8);
+ const uint64_t value = buf_get_u64(buffer, 0, 8 * size);
+ if (write_abstract_arg(target, /*index*/ 0, value, size > 4 ? 64 : 32)
+ != ERROR_OK)
return ERROR_FAIL;
- }
- LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address);
+ /* Write and execute command that moves the value from data0 [, data1]
+ * into S1 and executes program buffer. */
+ uint32_t command = access_register_command(target,
+ GDB_REGNO_S1, riscv_xlen(target),
+ AC_ACCESS_REGISTER_POSTEXEC |
+ AC_ACCESS_REGISTER_TRANSFER |
+ AC_ACCESS_REGISTER_WRITE);
- select_dmi(target);
+ uint32_t cmderr;
+ if (execute_abstract_command(target, command, &cmderr) != ERROR_OK)
+ return ERROR_FAIL;
- uint64_t mstatus = 0;
- uint64_t mstatus_old = 0;
- if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
+ log_memory_access64(*address_p, value, size, /*is_read*/ false);
+
+ /* The execution of the command succeeded, which means:
+ * - write of the first item to memory succeeded
+ * - address on the target (S0) was incremented
+ */
+ *address_p += size;
+
+ /* TODO: Setting abstractauto.autoexecdata is not necessary for a write
+ * of one element. */
+ return dm_write(target, DM_ABSTRACTAUTO,
+ 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+}
+
+/**
+ * This function reverts the changes made by `write_memory_progbuf_startup()`
+ */
+static int write_memory_progbuf_teardown(struct target *target)
+{
+ return dm_write(target, DM_ABSTRACTAUTO, 0);
+}
+
+/**
+ * This function attempts to restore the pipeline after a busy on abstract
+ * access or a DMI busy by reading the content of s0 -- the address of the
+ * failed write.
+ */
+static int write_memory_progbuf_handle_busy(struct target *target,
+ target_addr_t *address_p, uint32_t size, const uint8_t *buffer)
+{
+ riscv013_clear_abstract_error(target);
+ increase_ac_busy_delay(target);
+
+ if (write_memory_progbuf_teardown(target) != ERROR_OK)
return ERROR_FAIL;
- /* s0 holds the next address to write to
- * s1 holds the next data value to write
+ target_addr_t address_on_target;
+ if (register_read_direct(target, &address_on_target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ const uint8_t * const curr_buff = buffer + (address_on_target - *address_p);
+ LOG_TARGET_DEBUG(target, "Restarting from 0x%" TARGET_PRIxADDR, *address_p);
+ *address_p = address_on_target;
+ /* This restores the pipeline and ensures one item gets reliably written */
+ return write_memory_progbuf_startup(target, address_p, curr_buff, size);
+}
+
+/**
+ * This function fills the batch with DMI writes (but does not execute the batch).
+ * It returns the next address -- the address that will be the start of the next batch.
+ */
+static target_addr_t write_memory_progbuf_fill_batch(struct riscv_batch *batch,
+ target_addr_t start_address, target_addr_t end_address, uint32_t size,
+ const uint8_t *buffer)
+{
+ assert(size <= 8);
+ const unsigned int writes_per_element = size > 4 ? 2 : 1;
+ const size_t batch_capacity = riscv_batch_available_scans(batch) / writes_per_element;
+ /* This is safe even for the edge case when writing at the very top of
+ * the 64-bit address space (in which case end_address overflows to 0).
*/
+ const target_addr_t batch_end_address = start_address +
+ MIN((target_addr_t)batch_capacity * size,
+ end_address - start_address);
+ for (target_addr_t address = start_address; address != batch_end_address;
+ address += size, buffer += size) {
+ assert(size <= 8);
+ const uint64_t value = buf_get_u64(buffer, 0, 8 * size);
+ log_memory_access64(address, value, size, /*is_read*/ false);
+ if (writes_per_element == 2)
+ riscv_batch_add_dm_write(batch, DM_DATA1,
+ (uint32_t)(value >> 32), false);
+ riscv_batch_add_dm_write(batch, DM_DATA0, (uint32_t)value, false);
+ }
+ return batch_end_address;
+}
- int result = ERROR_OK;
- uint64_t s0, s1;
- if (register_read(target, &s0, GDB_REGNO_S0) != ERROR_OK)
+/**
+ * This function runs the batch of writes and updates address_p with the
+ * address of the next write.
+ */
+static int write_memory_progbuf_run_batch(struct target *target, struct riscv_batch *batch,
+ target_addr_t *address_p, target_addr_t end_address, uint32_t size,
+ const uint8_t *buffer)
+{
+ RISCV013_INFO(info);
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
return ERROR_FAIL;
- if (register_read(target, &s1, GDB_REGNO_S1) != ERROR_OK)
+
+ /* Abstract commands are executed while running the batch. */
+ dm->abstract_cmd_maybe_busy = true;
+ if (batch_run(target, batch, info->dmi_busy_delay + info->ac_busy_delay) != ERROR_OK)
return ERROR_FAIL;
- /* Write the program (store, increment) */
- struct riscv_program program;
- riscv_program_init(&program, target);
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrsi(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
+ /* Note that if the scan resulted in a Busy DMI response, it
+ * is this call to wait_for_idle() that will cause the dmi_busy_delay
+ * to be incremented if necessary. */
+ uint32_t abstractcs;
- switch (size) {
- case 1:
- riscv_program_sbr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 2:
- riscv_program_shr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 4:
- riscv_program_swr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- case 8:
- riscv_program_sdr(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0);
- break;
- default:
- LOG_ERROR("write_memory_progbuf(): Unsupported size: %d", size);
- result = ERROR_FAIL;
- goto error;
+ if (wait_for_idle(target, &abstractcs) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint32_t cmderr = get_field32(abstractcs, DM_ABSTRACTCS_CMDERR);
+ const bool dmi_busy_encountered = riscv_batch_was_batch_busy(batch);
+ if (cmderr == CMDERR_NONE && !dmi_busy_encountered) {
+ LOG_TARGET_DEBUG(target, "Successfully written memory block M[0x%" TARGET_PRIxADDR
+ ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address);
+ *address_p = end_address;
+ return ERROR_OK;
+ } else if (cmderr == CMDERR_BUSY || dmi_busy_encountered) {
+ if (cmderr == CMDERR_BUSY)
+ LOG_TARGET_DEBUG(target, "Encountered abstract command busy response while writing block M[0x%"
+ TARGET_PRIxADDR ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address);
+ if (dmi_busy_encountered)
+ LOG_TARGET_DEBUG(target, "Encountered DMI busy response while writing block M[0x%"
+ TARGET_PRIxADDR ".. 0x%" TARGET_PRIxADDR ")", *address_p, end_address);
+ /* TODO: If dmi busy is encountered, the address of the last
+ * successful write can be deduced by analysing the batch.
+ */
+ return write_memory_progbuf_handle_busy(target, address_p, size, buffer);
}
+ LOG_TARGET_ERROR(target, "Error when writing memory, abstractcs=0x%" PRIx32,
+ abstractcs);
+ riscv013_clear_abstract_error(target);
+ return ERROR_FAIL;
+}
- if (riscv_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV))
- riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR);
- riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size);
+static int write_memory_progbuf_try_to_write(struct target *target,
+ target_addr_t *address_p, target_addr_t end_address, uint32_t size,
+ const uint8_t *buffer)
+{
+ struct riscv_batch * const batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE);
+ if (!batch)
+ return ERROR_FAIL;
- result = riscv_program_ebreak(&program);
- if (result != ERROR_OK)
- goto error;
- riscv_program_write(&program);
+ const target_addr_t batch_end_addr = write_memory_progbuf_fill_batch(batch,
+ *address_p, end_address, size, buffer);
- riscv_addr_t cur_addr = address;
- riscv_addr_t fin_addr = address + (count * size);
- bool setup_needed = true;
- LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr);
- while (cur_addr < fin_addr) {
- LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64,
- cur_addr);
+ int result = write_memory_progbuf_run_batch(target, batch, address_p,
+ batch_end_addr, size, buffer);
+ riscv_batch_free(batch);
+ return result;
+}
- struct riscv_batch *batch = riscv_batch_alloc(
- target,
- 32,
- info->dmi_busy_delay + info->ac_busy_delay);
- if (!batch)
- goto error;
+static int riscv_program_store_mprv(struct riscv_program *p, enum gdb_regno d,
+ enum gdb_regno b, int offset, unsigned int size, bool mprven)
+{
+ if (mprven && riscv_program_csrrsi(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN,
+ GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
- /* To write another word, we put it in S1 and execute the program. */
- unsigned start = (cur_addr - address) / size;
- for (unsigned i = start; i < count; ++i) {
- unsigned offset = size*i;
- const uint8_t *t_buffer = buffer + offset;
+ if (riscv_program_store(p, d, b, offset, size) != ERROR_OK)
+ return ERROR_FAIL;
- uint64_t value = buf_get_u64(t_buffer, 0, 8 * size);
+ if (mprven && riscv_program_csrrci(p, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN,
+ GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
- log_memory_access(address + offset, value, size, false);
- cur_addr += size;
+ return ERROR_OK;
+}
- if (setup_needed) {
- result = register_write_direct(target, GDB_REGNO_S0,
- address + offset);
- if (result != ERROR_OK) {
- riscv_batch_free(batch);
- goto error;
- }
+static int write_memory_progbuf_fill_progbuf(struct target *target,
+ uint32_t size, bool mprven)
+{
+ if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK)
+ return ERROR_FAIL;
- /* Write value. */
- if (size > 4)
- dmi_write(target, DM_DATA1, value >> 32);
- dmi_write(target, DM_DATA0, value);
-
- /* Write and execute command that moves value into S1 and
- * executes program buffer. */
- uint32_t command = access_register_command(target,
- GDB_REGNO_S1, riscv_xlen(target),
- AC_ACCESS_REGISTER_POSTEXEC |
- AC_ACCESS_REGISTER_TRANSFER |
- AC_ACCESS_REGISTER_WRITE);
- result = execute_abstract_command(target, command);
- if (result != ERROR_OK) {
- riscv_batch_free(batch);
- goto error;
- }
+ struct riscv_program program;
- /* Turn on autoexec */
- dmi_write(target, DM_ABSTRACTAUTO,
- 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
+ riscv_program_init(&program, target);
+ if (riscv_program_store_mprv(&program, GDB_REGNO_S1, GDB_REGNO_S0, 0, size,
+ mprven) != ERROR_OK)
+ return ERROR_FAIL;
- setup_needed = false;
- } else {
- if (size > 4)
- riscv_batch_add_dmi_write(batch, DM_DATA1, value >> 32);
- riscv_batch_add_dmi_write(batch, DM_DATA0, value);
- if (riscv_batch_full(batch))
- break;
- }
- }
+ if (riscv_program_addi(&program, GDB_REGNO_S0, GDB_REGNO_S0, size) != ERROR_OK)
+ return ERROR_FAIL;
- result = batch_run(target, batch);
- riscv_batch_free(batch);
- if (result != ERROR_OK)
- goto error;
+ if (riscv_program_ebreak(&program) != ERROR_OK)
+ return ERROR_FAIL;
- /* Note that if the scan resulted in a Busy DMI response, it
- * is this read to abstractcs that will cause the dmi_busy_delay
- * to be incremented if necessary. */
+ return riscv_program_write(&program);
+}
- uint32_t abstractcs;
- bool dmi_busy_encountered;
- result = dmi_op(target, &abstractcs, &dmi_busy_encountered,
- DMI_OP_READ, DM_ABSTRACTCS, 0, false, true);
- if (result != ERROR_OK)
- goto error;
- while (get_field(abstractcs, DM_ABSTRACTCS_BUSY))
- if (dmi_read(target, &abstractcs, DM_ABSTRACTCS) != ERROR_OK)
- return ERROR_FAIL;
- info->cmderr = get_field(abstractcs, DM_ABSTRACTCS_CMDERR);
- if (info->cmderr == CMDERR_NONE && !dmi_busy_encountered) {
- LOG_DEBUG("successful (partial?) memory write");
- } else if (info->cmderr == CMDERR_BUSY || dmi_busy_encountered) {
- if (info->cmderr == CMDERR_BUSY)
- LOG_DEBUG("Memory write resulted in abstract command busy response.");
- else if (dmi_busy_encountered)
- LOG_DEBUG("Memory write resulted in DMI busy response.");
- riscv013_clear_abstract_error(target);
- increase_ac_busy_delay(target);
-
- dmi_write(target, DM_ABSTRACTAUTO, 0);
- result = register_read_direct(target, &cur_addr, GDB_REGNO_S0);
- if (result != ERROR_OK)
- goto error;
- setup_needed = true;
- } else {
- LOG_ERROR("error when writing memory, abstractcs=0x%08lx", (long)abstractcs);
- riscv013_clear_abstract_error(target);
- result = ERROR_FAIL;
- goto error;
+static int write_memory_progbuf_inner(struct target *target, target_addr_t start_addr,
+ uint32_t size, uint32_t count, const uint8_t *buffer, bool mprven)
+{
+ if (write_memory_progbuf_fill_progbuf(target, size,
+ mprven) != ERROR_OK)
+ return ERROR_FAIL;
+
+ target_addr_t addr_on_target = start_addr;
+ if (write_memory_progbuf_startup(target, &addr_on_target, buffer, size) != ERROR_OK)
+ return ERROR_FAIL;
+
+ const target_addr_t end_addr = start_addr + (target_addr_t)size * count;
+
+ for (target_addr_t next_addr_on_target = addr_on_target; addr_on_target != end_addr;
+ addr_on_target = next_addr_on_target) {
+ const uint8_t * const curr_buff = buffer + (addr_on_target - start_addr);
+ if (write_memory_progbuf_try_to_write(target, &next_addr_on_target,
+ end_addr, size, curr_buff) != ERROR_OK) {
+ write_memory_progbuf_teardown(target);
+ return ERROR_FAIL;
}
+ /* write_memory_progbuf_try_to_write() ensures that at least one item
+ * gets successfully written even when busy condition is encountered.
+ * These assertions shuld hold when next_address_on_target overflows. */
+ assert(next_addr_on_target - addr_on_target > 0);
+ assert(next_addr_on_target - start_addr <= (target_addr_t)size * count);
}
-error:
- dmi_write(target, DM_ABSTRACTAUTO, 0);
+ return write_memory_progbuf_teardown(target);
+}
- if (register_write_direct(target, GDB_REGNO_S1, s1) != ERROR_OK)
+static int write_memory_progbuf(struct target *target, target_addr_t address,
+ uint32_t size, uint32_t count, const uint8_t *buffer)
+{
+ if (riscv_xlen(target) < size * 8) {
+ LOG_TARGET_ERROR(target, "XLEN (%u) is too short for %" PRIu32 "-bit memory write.",
+ riscv_xlen(target), size * 8);
return ERROR_FAIL;
- if (register_write_direct(target, GDB_REGNO_S0, s0) != ERROR_OK)
+ }
+
+ LOG_TARGET_DEBUG(target, "writing %" PRIu32 " words of %" PRIu32
+ " bytes to 0x%" TARGET_PRIxADDR, count, size, address);
+
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t mstatus = 0;
+ uint64_t mstatus_old = 0;
+ if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK)
return ERROR_FAIL;
+ const bool mprven = riscv_enable_virtual && get_field(mstatus, MSTATUS_MPRV);
+
+ int result = write_memory_progbuf_inner(target, address, size, count, buffer, mprven);
+
/* Restore MSTATUS */
if (mstatus != mstatus_old)
if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old))
@@ -3982,7 +5072,7 @@ static int write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) {
- LOG_ERROR("BUG: Unsupported size for memory write: %d", size);
+ LOG_TARGET_ERROR(target, "BUG: Unsupported size for memory write: %d", size);
return ERROR_FAIL;
}
@@ -4034,8 +5124,8 @@ static int write_memory(struct target *target, target_addr_t address,
return ret;
}
- LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address);
- LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
+ LOG_TARGET_ERROR(target, "Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address);
+ LOG_TARGET_ERROR(target, " progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result);
return ret;
}
@@ -4065,137 +5155,134 @@ struct target_type riscv013_target = {
/*** 0.13-specific implementations of various RISC-V helper functions. ***/
static int riscv013_get_register(struct target *target,
- riscv_reg_t *value, int rid)
+ riscv_reg_t *value, enum gdb_regno rid)
{
- LOG_DEBUG("[%s] reading register %s", target_name(target),
- gdb_regno_name(rid));
-
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
- int result = ERROR_OK;
- if (rid == GDB_REGNO_PC) {
- /* TODO: move this into riscv.c. */
- result = register_read(target, value, GDB_REGNO_DPC);
- LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value);
- } else if (rid == GDB_REGNO_PRIV) {
+ /* It would be beneficial to move this redirection to the
+ * version-independent section, but there is a conflict:
+ * `dcsr[5]` is `dcsr.v` in current spec, but it is `dcsr.debugint` in 0.11.
+ */
+ if (rid == GDB_REGNO_PRIV) {
uint64_t dcsr;
- /* TODO: move this into riscv.c. */
- result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
*value = set_field(0, VIRT_PRIV_V, get_field(dcsr, CSR_DCSR_V));
*value = set_field(*value, VIRT_PRIV_PRV, get_field(dcsr, CSR_DCSR_PRV));
- } else {
- result = register_read(target, value, rid);
- if (result != ERROR_OK)
- *value = -1;
+ return ERROR_OK;
}
- return result;
-}
+ LOG_TARGET_DEBUG(target, "reading register %s", gdb_regno_name(target, rid));
-static int riscv013_set_register(struct target *target, int rid, uint64_t value)
-{
- riscv013_select_current_hart(target);
- LOG_DEBUG("[%d] writing 0x%" PRIx64 " to register %s",
- target->coreid, value, gdb_regno_name(rid));
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
- if (rid <= GDB_REGNO_XPR31) {
- return register_write_direct(target, rid, value);
- } else if (rid == GDB_REGNO_PC) {
- LOG_DEBUG("[%d] writing PC to DPC: 0x%" PRIx64, target->coreid, value);
- register_write_direct(target, GDB_REGNO_DPC, value);
- uint64_t actual_value;
- register_read_direct(target, &actual_value, GDB_REGNO_DPC);
- LOG_DEBUG("[%d] actual DPC written: 0x%016" PRIx64, target->coreid, actual_value);
- if (value != actual_value) {
- LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back "
- "value (0x%" PRIx64 ")", value, actual_value);
- return ERROR_FAIL;
- }
- } else if (rid == GDB_REGNO_PRIV) {
- uint64_t dcsr;
- register_read(target, &dcsr, GDB_REGNO_DCSR);
- dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
- dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
- return register_write_direct(target, GDB_REGNO_DCSR, dcsr);
- } else {
- return register_write_direct(target, rid, value);
+ if (register_read_direct(target, value, rid) != ERROR_OK) {
+ *value = -1;
+ return ERROR_FAIL;
}
return ERROR_OK;
}
-static int riscv013_select_current_hart(struct target *target)
+static int riscv013_set_register(struct target *target, enum gdb_regno rid,
+ riscv_reg_t value)
{
- RISCV_INFO(r);
+ LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s",
+ value, gdb_regno_name(target, rid));
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return register_write_direct(target, rid, value);
+}
+
+static int dm013_select_hart(struct target *target, int hart_index)
+{
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
- if (r->current_hartid == dm->current_hartid)
+ if (hart_index == dm->current_hartid)
return ERROR_OK;
- uint32_t dmcontrol;
- /* TODO: can't we just "dmcontrol = DMI_DMACTIVE"? */
- if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
+ /* `hartsel` should not be changed if `abstractcs.busy` is set. */
+ int result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+
+ uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index);
+ if (dm_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) {
+ /* Who knows what the state is? */
+ dm->current_hartid = HART_INDEX_UNKNOWN;
return ERROR_FAIL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
- int result = dmi_write(target, DM_DMCONTROL, dmcontrol);
- dm->current_hartid = r->current_hartid;
- return result;
+ }
+ dm->current_hartid = hart_index;
+ return ERROR_OK;
}
/* Select all harts that were prepped and that are selectable, clearing the
* prepped flag on the harts that actually were selected. */
-static int select_prepped_harts(struct target *target, bool *use_hasel)
+static int select_prepped_harts(struct target *target)
{
+ RISCV_INFO(r);
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
if (!dm->hasel_supported) {
- RISCV_INFO(r);
r->prepped = false;
- *use_hasel = false;
- return ERROR_OK;
+ return dm013_select_target(target);
}
assert(dm->hart_count);
unsigned hawindow_count = (dm->hart_count + 31) / 32;
- uint32_t hawindow[hawindow_count];
-
- memset(hawindow, 0, sizeof(uint32_t) * hawindow_count);
+ uint32_t *hawindow = calloc(hawindow_count, sizeof(uint32_t));
+ if (!hawindow)
+ return ERROR_FAIL;
target_list_t *entry;
unsigned total_selected = 0;
+ unsigned int selected_index = 0;
list_for_each_entry(entry, &dm->target_list, list) {
struct target *t = entry->target;
- struct riscv_info *r = riscv_info(t);
- riscv013_info_t *info = get_info(t);
- unsigned index = info->index;
- LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, r->prepped);
- r->selected = r->prepped;
- if (r->prepped) {
+ struct riscv_info *info = riscv_info(t);
+ riscv013_info_t *info_013 = get_info(t);
+ unsigned int index = info_013->index;
+ LOG_TARGET_DEBUG(target, "index=%d, prepped=%d", index, info->prepped);
+ if (info->prepped) {
+ info_013->selected = true;
hawindow[index / 32] |= 1 << (index % 32);
- r->prepped = false;
+ info->prepped = false;
total_selected++;
+ selected_index = index;
}
- index++;
}
- /* Don't use hasel if we only need to talk to one hart. */
- if (total_selected <= 1) {
- *use_hasel = false;
- return ERROR_OK;
+ if (total_selected == 0) {
+ LOG_TARGET_ERROR(target, "No harts were prepped!");
+ free(hawindow);
+ return ERROR_FAIL;
+ } else if (total_selected == 1) {
+ /* Don't use hasel if we only need to talk to one hart. */
+ free(hawindow);
+ return dm013_select_hart(target, selected_index);
+ }
+
+ if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) {
+ free(hawindow);
+ return ERROR_FAIL;
}
for (unsigned i = 0; i < hawindow_count; i++) {
- if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK)
+ if (dm_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) {
+ free(hawindow);
return ERROR_FAIL;
- if (dmi_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK)
+ }
+ if (dm_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) {
+ free(hawindow);
return ERROR_FAIL;
+ }
}
- *use_hasel = true;
+ free(hawindow);
return ERROR_OK;
}
@@ -4206,72 +5293,107 @@ static int riscv013_halt_prep(struct target *target)
static int riscv013_halt_go(struct target *target)
{
- bool use_hasel = false;
- if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
return ERROR_FAIL;
- RISCV_INFO(r);
- LOG_DEBUG("halting hart %d", r->current_hartid);
+ if (select_prepped_harts(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ LOG_TARGET_DEBUG(target, "halting hart");
+
+ /* `haltreq` should not be issued if `abstractcs.busy` is set. */
+ int result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
/* Issue the halt command, and then wait for the current hart to halt. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ;
- if (use_hasel)
- dmcontrol |= DM_DMCONTROL_HASEL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
- dmi_write(target, DM_DMCONTROL, dmcontrol);
- for (size_t i = 0; i < 256; ++i)
- if (riscv_is_halted(target))
- break;
-
- if (!riscv_is_halted(target)) {
- uint32_t dmstatus;
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
+ dm_write(target, DM_DMCONTROL, dmcontrol);
+ uint32_t dmstatus;
+ for (size_t i = 0; i < 256; ++i) {
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
- if (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
+ /* When no harts are running, there's no point in continuing this loop. */
+ if (!get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))
+ break;
+ }
+
+ /* We declare success if no harts are running. One or more of them may be
+ * unavailable, though. */
+
+ if ((get_field(dmstatus, DM_DMSTATUS_ANYRUNNING))) {
+ if (dm_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK)
return ERROR_FAIL;
- LOG_ERROR("unable to halt hart %d", r->current_hartid);
- LOG_ERROR(" dmcontrol=0x%08x", dmcontrol);
- LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ LOG_TARGET_ERROR(target, "Unable to halt. dmcontrol=0x%08x, dmstatus=0x%08x",
+ dmcontrol, dmstatus);
return ERROR_FAIL;
}
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HALTREQ, 0);
- dmi_write(target, DM_DMCONTROL, dmcontrol);
+ dm_write(target, DM_DMCONTROL, dmcontrol);
- if (use_hasel) {
+ if (dm->current_hartid == HART_INDEX_MULTIPLE) {
target_list_t *entry;
- dm013_info_t *dm = get_dm(target);
- if (!dm)
- return ERROR_FAIL;
list_for_each_entry(entry, &dm->target_list, list) {
struct target *t = entry->target;
- t->state = TARGET_HALTED;
- if (t->debug_reason == DBG_REASON_NOTHALTED)
- t->debug_reason = DBG_REASON_DBGRQ;
+ uint32_t t_dmstatus;
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED) ||
+ get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ /* All harts are either halted or unavailable. No
+ * need to read dmstatus for each hart. */
+ t_dmstatus = dmstatus;
+ } else {
+ /* Only some harts were halted/unavailable. Read
+ * dmstatus for this one to see what its status
+ * is. */
+ if (dm013_select_target(target) != ERROR_OK)
+ return ERROR_FAIL;
+ if (dm_read(target, &t_dmstatus, DM_DMSTATUS) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ /* Set state for the current target based on its dmstatus. */
+ if (get_field(t_dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ t->state = TARGET_HALTED;
+ if (t->debug_reason == DBG_REASON_NOTHALTED)
+ t->debug_reason = DBG_REASON_DBGRQ;
+ } else if (get_field(t_dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ t->state = TARGET_UNAVAILABLE;
+ }
+ }
+
+ } else {
+ /* Set state for the current target based on its dmstatus. */
+ if (get_field(dmstatus, DM_DMSTATUS_ALLHALTED)) {
+ target->state = TARGET_HALTED;
+ if (target->debug_reason == DBG_REASON_NOTHALTED)
+ target->debug_reason = DBG_REASON_DBGRQ;
+ } else if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL)) {
+ target->state = TARGET_UNAVAILABLE;
}
}
- /* The "else" case is handled in halt_go(). */
return ERROR_OK;
}
static int riscv013_resume_go(struct target *target)
{
- bool use_hasel = false;
- if (select_prepped_harts(target, &use_hasel) != ERROR_OK)
+ if (select_prepped_harts(target) != ERROR_OK)
return ERROR_FAIL;
- return riscv013_step_or_resume_current_hart(target, false, use_hasel);
+ return riscv013_step_or_resume_current_hart(target, false);
}
static int riscv013_step_current_hart(struct target *target)
{
- return riscv013_step_or_resume_current_hart(target, true, false);
+ return riscv013_step_or_resume_current_hart(target, true);
}
static int riscv013_resume_prep(struct target *target)
{
+ assert(target->state == TARGET_HALTED);
return riscv013_on_step_or_resume(target, false);
}
@@ -4280,95 +5402,78 @@ static int riscv013_on_step(struct target *target)
return riscv013_on_step_or_resume(target, true);
}
-static int riscv013_on_halt(struct target *target)
-{
- return ERROR_OK;
-}
-
-static bool riscv013_is_halted(struct target *target)
-{
- uint32_t dmstatus;
- if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
- return false;
- if (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL))
- LOG_ERROR("Hart %d is unavailable.", riscv_current_hartid(target));
- if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT))
- LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target));
- if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) {
- int hartid = riscv_current_hartid(target);
- LOG_INFO("Hart %d unexpectedly reset!", hartid);
- /* TODO: Can we make this more obvious to eg. a gdb user? */
- uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE |
- DM_DMCONTROL_ACKHAVERESET;
- dmcontrol = set_hartsel(dmcontrol, hartid);
- /* If we had been halted when we reset, request another halt. If we
- * ended up running out of reset, then the user will (hopefully) get a
- * message that a reset happened, that the target is running, and then
- * that it is halted again once the request goes through.
- */
- if (target->state == TARGET_HALTED)
- dmcontrol |= DM_DMCONTROL_HALTREQ;
- dmi_write(target, DM_DMCONTROL, dmcontrol);
- }
- return get_field(dmstatus, DM_DMSTATUS_ALLHALTED);
-}
-
static enum riscv_halt_reason riscv013_halt_reason(struct target *target)
{
riscv_reg_t dcsr;
- int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
+ int result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR);
if (result != ERROR_OK)
return RISCV_HALT_UNKNOWN;
- LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
+ LOG_TARGET_DEBUG(target, "dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
switch (get_field(dcsr, CSR_DCSR_CAUSE)) {
- case CSR_DCSR_CAUSE_SWBP:
- return RISCV_HALT_BREAKPOINT;
+ case CSR_DCSR_CAUSE_EBREAK:
+ return RISCV_HALT_EBREAK;
case CSR_DCSR_CAUSE_TRIGGER:
/* We could get here before triggers are enumerated if a trigger was
* already set when we connected. Force enumeration now, which has the
* side effect of clearing any triggers we did not set. */
riscv_enumerate_triggers(target);
- LOG_DEBUG("{%d} halted because of trigger", target->coreid);
+ LOG_TARGET_DEBUG(target, "halted because of trigger");
return RISCV_HALT_TRIGGER;
case CSR_DCSR_CAUSE_STEP:
return RISCV_HALT_SINGLESTEP;
- case CSR_DCSR_CAUSE_DEBUGINT:
- case CSR_DCSR_CAUSE_HALT:
+ case CSR_DCSR_CAUSE_HALTREQ:
+ case CSR_DCSR_CAUSE_RESETHALTREQ:
return RISCV_HALT_INTERRUPT;
case CSR_DCSR_CAUSE_GROUP:
return RISCV_HALT_GROUP;
}
- LOG_ERROR("Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
- LOG_ERROR(" dcsr=0x%016lx", (long)dcsr);
+ LOG_TARGET_ERROR(target, "Unknown DCSR cause field: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE));
+ LOG_TARGET_ERROR(target, " dcsr=0x%" PRIx32, (uint32_t)dcsr);
return RISCV_HALT_UNKNOWN;
}
-int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data)
+static int riscv013_write_progbuf(struct target *target, unsigned int index, riscv_insn_t data)
{
dm013_info_t *dm = get_dm(target);
if (!dm)
return ERROR_FAIL;
if (dm->progbuf_cache[index] != data) {
- if (dmi_write(target, DM_PROGBUF0 + index, data) != ERROR_OK)
+ if (dm_write(target, DM_PROGBUF0 + index, data) != ERROR_OK)
return ERROR_FAIL;
dm->progbuf_cache[index] = data;
} else {
- LOG_DEBUG("cache hit for 0x%" PRIx32 " @%d", data, index);
+ LOG_TARGET_DEBUG(target, "Cache hit for 0x%" PRIx32 " @%d", data, index);
}
return ERROR_OK;
}
-riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index)
+static riscv_insn_t riscv013_read_progbuf(struct target *target, unsigned int index)
{
uint32_t value;
- dmi_read(target, &value, DM_PROGBUF0 + index);
- return value;
+ if (dm_read(target, &value, DM_PROGBUF0 + index) == ERROR_OK)
+ return value;
+ else
+ return 0;
+}
+
+static int riscv013_invalidate_cached_progbuf(struct target *target)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm) {
+ LOG_TARGET_DEBUG(target, "No DM is specified for the target");
+ return ERROR_FAIL;
+ }
+
+ LOG_TARGET_DEBUG(target, "Invalidating progbuf cache");
+ for (unsigned int i = 0; i < 15; i++)
+ dm->progbuf_cache[i] = 0;
+ return ERROR_OK;
}
-int riscv013_execute_debug_buffer(struct target *target)
+static int riscv013_execute_progbuf(struct target *target, uint32_t *cmderr)
{
uint32_t run_program = 0;
run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2);
@@ -4376,10 +5481,10 @@ int riscv013_execute_debug_buffer(struct target *target)
run_program = set_field(run_program, AC_ACCESS_REGISTER_TRANSFER, 0);
run_program = set_field(run_program, AC_ACCESS_REGISTER_REGNO, 0x1000);
- return execute_abstract_command(target, run_program);
+ return execute_abstract_command(target, run_program, cmderr);
}
-void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+static void riscv013_fill_dmi_write(struct target *target, char *buf, uint64_t a, uint32_t d)
{
RISCV013_INFO(info);
buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_WRITE);
@@ -4387,7 +5492,7 @@ void riscv013_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64
buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
}
-void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a)
+static void riscv013_fill_dmi_read(struct target *target, char *buf, uint64_t a)
{
RISCV013_INFO(info);
buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_READ);
@@ -4395,7 +5500,7 @@ void riscv013_fill_dmi_read_u64(struct target *target, char *buf, int a)
buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, a);
}
-void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
+static void riscv013_fill_dmi_nop(struct target *target, char *buf)
{
RISCV013_INFO(info);
buf_set_u64((unsigned char *)buf, DTM_DMI_OP_OFFSET, DTM_DMI_OP_LENGTH, DMI_OP_NOP);
@@ -4403,15 +5508,36 @@ void riscv013_fill_dmi_nop_u64(struct target *target, char *buf)
buf_set_u64((unsigned char *)buf, DTM_DMI_ADDRESS_OFFSET, info->abits, 0);
}
-int riscv013_dmi_write_u64_bits(struct target *target)
+static int riscv013_get_dmi_scan_length(struct target *target)
{
RISCV013_INFO(info);
return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH;
}
+void riscv013_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return;
+ riscv013_fill_dmi_write(target, buf, a + dm->base, d);
+}
+
+void riscv013_fill_dm_read(struct target *target, char *buf, uint64_t a)
+{
+ dm013_info_t *dm = get_dm(target);
+ if (!dm)
+ return;
+ riscv013_fill_dmi_read(target, buf, a + dm->base);
+}
+
+void riscv013_fill_dm_nop(struct target *target, char *buf)
+{
+ riscv013_fill_dmi_nop(target, buf);
+}
+
static int maybe_execute_fence_i(struct target *target)
{
- if (has_sufficient_progbuf(target, 3))
+ if (has_sufficient_progbuf(target, 2))
return execute_fence(target);
return ERROR_OK;
}
@@ -4422,36 +5548,36 @@ static int riscv013_on_step_or_resume(struct target *target, bool step)
if (maybe_execute_fence_i(target) != ERROR_OK)
return ERROR_FAIL;
- /* We want to twiddle some bits in the debug CSR so debugging works. */
- riscv_reg_t dcsr;
- int result = register_read(target, &dcsr, GDB_REGNO_DCSR);
- if (result != ERROR_OK)
- return result;
- dcsr = set_field(dcsr, CSR_DCSR_STEP, step);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKM, riscv_ebreakm);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKS, riscv_ebreaks);
- dcsr = set_field(dcsr, CSR_DCSR_EBREAKU, riscv_ebreaku);
- return riscv_set_register(target, GDB_REGNO_DCSR, dcsr);
+ if (set_dcsr_ebreak(target, step) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_OK;
}
static int riscv013_step_or_resume_current_hart(struct target *target,
- bool step, bool use_hasel)
+ bool step)
{
- RISCV_INFO(r);
- LOG_DEBUG("resuming hart %d (for step?=%d)", r->current_hartid, step);
- if (!riscv_is_halted(target)) {
- LOG_ERROR("Hart %d is not halted!", r->current_hartid);
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Hart is not halted!");
return ERROR_FAIL;
}
+ LOG_TARGET_DEBUG(target, "resuming (for step?=%d)", step);
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ dm013_info_t *dm = get_dm(target);
/* Issue the resume command, and then wait for the current hart to resume. */
uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_RESUMEREQ;
- if (use_hasel)
- dmcontrol |= DM_DMCONTROL_HASEL;
- dmcontrol = set_hartsel(dmcontrol, r->current_hartid);
- dmi_write(target, DM_DMCONTROL, dmcontrol);
+ dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid);
+ /* `resumereq` should not be issued if `abstractcs.busy` is set. */
+ int result = wait_for_idle_if_needed(target);
+ if (result != ERROR_OK)
+ return result;
+ dm_write(target, DM_DMCONTROL, dmcontrol);
- dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0);
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0);
uint32_t dmstatus;
@@ -4459,24 +5585,26 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
usleep(10);
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
+ if (get_field(dmstatus, DM_DMSTATUS_ALLUNAVAIL))
+ return ERROR_FAIL;
if (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0)
continue;
if (step && get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0)
continue;
- dmi_write(target, DM_DMCONTROL, dmcontrol);
+ dm_write(target, DM_DMCONTROL, dmcontrol);
return ERROR_OK;
}
- dmi_write(target, DM_DMCONTROL, dmcontrol);
+ dm_write(target, DM_DMCONTROL, dmcontrol);
- LOG_ERROR("unable to resume hart %d", r->current_hartid);
+ LOG_TARGET_ERROR(target, "unable to resume");
if (dmstatus_read(target, &dmstatus, true) != ERROR_OK)
return ERROR_FAIL;
- LOG_ERROR(" dmstatus =0x%08x", dmstatus);
+ LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus);
if (step) {
- LOG_ERROR(" was stepping, halting");
+ LOG_TARGET_ERROR(target, " was stepping, halting");
riscv_halt(target);
return ERROR_OK;
}
@@ -4484,24 +5612,12 @@ static int riscv013_step_or_resume_current_hart(struct target *target,
return ERROR_FAIL;
}
-void riscv013_clear_abstract_error(struct target *target)
+static int riscv013_clear_abstract_error(struct target *target)
{
- /* Wait for busy to go away. */
- time_t start = time(NULL);
uint32_t abstractcs;
- dmi_read(target, &abstractcs, DM_ABSTRACTCS);
- while (get_field(abstractcs, DM_ABSTRACTCS_BUSY)) {
- dmi_read(target, &abstractcs, DM_ABSTRACTCS);
-
- if (time(NULL) - start > riscv_command_timeout_sec) {
- LOG_ERROR("abstractcs.busy is not going low after %d seconds "
- "(abstractcs=0x%x). The target is either really slow or "
- "broken. You could increase the timeout with riscv "
- "set_command_timeout_sec.",
- riscv_command_timeout_sec, abstractcs);
- break;
- }
- }
- /* Clear the error status. */
- dmi_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
+ int result = wait_for_idle(target, &abstractcs);
+ /* Clear the error status, even if busy is still set. */
+ if (dm_write(target, DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR) != ERROR_OK)
+ result = ERROR_FAIL;
+ return result;
}
diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c
index 9cd4922..e7a2fe3 100644
--- a/src/target/riscv/riscv.c
+++ b/src/target/riscv/riscv.c
@@ -17,97 +17,22 @@
#include "jtag/jtag.h"
#include "target/register.h"
#include "target/breakpoints.h"
+#include "helper/base64.h"
+#include "helper/time_support.h"
#include "riscv.h"
+#include "program.h"
#include "gdb_regs.h"
#include "rtos/rtos.h"
#include "debug_defines.h"
#include <helper/bits.h>
-
-#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
-#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
-
-/* Constants for legacy SiFive hardware breakpoints. */
-#define CSR_BPCONTROL_X (1<<0)
-#define CSR_BPCONTROL_W (1<<1)
-#define CSR_BPCONTROL_R (1<<2)
-#define CSR_BPCONTROL_U (1<<3)
-#define CSR_BPCONTROL_S (1<<4)
-#define CSR_BPCONTROL_H (1<<5)
-#define CSR_BPCONTROL_M (1<<6)
-#define CSR_BPCONTROL_BPMATCH (0xf<<7)
-#define CSR_BPCONTROL_BPACTION (0xff<<11)
-
-#define DEBUG_ROM_START 0x800
-#define DEBUG_ROM_RESUME (DEBUG_ROM_START + 4)
-#define DEBUG_ROM_EXCEPTION (DEBUG_ROM_START + 8)
-#define DEBUG_RAM_START 0x400
-
-#define SETHALTNOT 0x10c
+#include "field_helpers.h"
/*** JTAG registers. ***/
#define DTMCONTROL 0x10
-#define DTMCONTROL_DBUS_RESET (1<<16)
-#define DTMCONTROL_IDLE (7<<10)
-#define DTMCONTROL_ADDRBITS (0xf<<4)
#define DTMCONTROL_VERSION (0xf)
#define DBUS 0x11
-#define DBUS_OP_START 0
-#define DBUS_OP_SIZE 2
-typedef enum {
- DBUS_OP_NOP = 0,
- DBUS_OP_READ = 1,
- DBUS_OP_WRITE = 2
-} dbus_op_t;
-typedef enum {
- DBUS_STATUS_SUCCESS = 0,
- DBUS_STATUS_FAILED = 2,
- DBUS_STATUS_BUSY = 3
-} dbus_status_t;
-#define DBUS_DATA_START 2
-#define DBUS_DATA_SIZE 34
-#define DBUS_ADDRESS_START 36
-
-typedef enum slot {
- SLOT0,
- SLOT1,
- SLOT_LAST,
-} slot_t;
-
-/*** Debug Bus registers. ***/
-
-#define DMCONTROL 0x10
-#define DMCONTROL_INTERRUPT (((uint64_t)1)<<33)
-#define DMCONTROL_HALTNOT (((uint64_t)1)<<32)
-#define DMCONTROL_BUSERROR (7<<19)
-#define DMCONTROL_SERIAL (3<<16)
-#define DMCONTROL_AUTOINCREMENT (1<<15)
-#define DMCONTROL_ACCESS (7<<12)
-#define DMCONTROL_HARTID (0x3ff<<2)
-#define DMCONTROL_NDRESET (1<<1)
-#define DMCONTROL_FULLRESET 1
-
-#define DMINFO 0x11
-#define DMINFO_ABUSSIZE (0x7fU<<25)
-#define DMINFO_SERIALCOUNT (0xf<<21)
-#define DMINFO_ACCESS128 (1<<20)
-#define DMINFO_ACCESS64 (1<<19)
-#define DMINFO_ACCESS32 (1<<18)
-#define DMINFO_ACCESS16 (1<<17)
-#define DMINFO_ACCESS8 (1<<16)
-#define DMINFO_DRAMSIZE (0x3f<<10)
-#define DMINFO_AUTHENTICATED (1<<5)
-#define DMINFO_AUTHBUSY (1<<4)
-#define DMINFO_AUTHTYPE (3<<2)
-#define DMINFO_VERSION 3
-
-/*** Info about the core being debugged. ***/
-
-#define DBUS_ADDRESS_UNKNOWN 0xffff
-
-#define MAX_HWBPS 16
-#define DRAM_CACHE_SIZE 16
static uint8_t ir_dtmcontrol[4] = {DTMCONTROL};
struct scan_field select_dtmcontrol = {
@@ -127,6 +52,7 @@ struct scan_field select_idcode = {
static bscan_tunnel_type_t bscan_tunnel_type;
int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */
+static int bscan_tunnel_ir_id; /* IR ID of the JTAG TAP to access the tunnel. Valid when not 0 */
static const uint8_t bscan_zero[4] = {0};
static const uint8_t bscan_one[4] = {1};
@@ -195,10 +121,21 @@ struct trigger {
uint32_t length;
uint64_t mask;
uint64_t value;
- bool read, write, execute;
+ bool is_read, is_write, is_execute;
int unique_id;
};
+struct tdata2_cache {
+ struct list_head elem_tdata2;
+ riscv_reg_t tdata2;
+};
+
+struct tdata1_cache {
+ riscv_reg_t tdata1;
+ struct list_head tdata2_cache_head;
+ struct list_head elem_tdata1;
+};
+
/* Wall-clock timeout for a command/access. Settable via RISC-V Target commands.*/
int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
@@ -206,9 +143,6 @@ int riscv_command_timeout_sec = DEFAULT_COMMAND_TIMEOUT_SEC;
int riscv_reset_timeout_sec = DEFAULT_RESET_TIMEOUT_SEC;
static bool riscv_enable_virt2phys = true;
-bool riscv_ebreakm = true;
-bool riscv_ebreaks = true;
-bool riscv_ebreaku = true;
bool riscv_enable_virtual;
@@ -230,6 +164,19 @@ static const virt2phys_info_t sv32 = {
.pa_ppn_mask = {0x3ff, 0xfff},
};
+static const virt2phys_info_t sv32x4 = {
+ .name = "Sv32x4",
+ .va_bits = 34,
+ .level = 2,
+ .pte_shift = 2,
+ .vpn_shift = {12, 22},
+ .vpn_mask = {0x3ff, 0xfff},
+ .pte_ppn_shift = {10, 20},
+ .pte_ppn_mask = {0x3ff, 0xfff},
+ .pa_ppn_shift = {12, 22},
+ .pa_ppn_mask = {0x3ff, 0xfff},
+};
+
static const virt2phys_info_t sv39 = {
.name = "Sv39",
.va_bits = 39,
@@ -243,6 +190,19 @@ static const virt2phys_info_t sv39 = {
.pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
};
+static const virt2phys_info_t sv39x4 = {
+ .name = "Sv39x4",
+ .va_bits = 41,
+ .level = 3,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30},
+ .vpn_mask = {0x1ff, 0x1ff, 0x7ff},
+ .pte_ppn_shift = {10, 19, 28},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+ .pa_ppn_shift = {12, 21, 30},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x3ffffff},
+};
+
static const virt2phys_info_t sv48 = {
.name = "Sv48",
.va_bits = 48,
@@ -256,7 +216,46 @@ static const virt2phys_info_t sv48 = {
.pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
};
-static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid);
+static const virt2phys_info_t sv48x4 = {
+ .name = "Sv48x4",
+ .va_bits = 50,
+ .level = 4,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30, 39},
+ .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x7ff},
+ .pte_ppn_shift = {10, 19, 28, 37},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
+ .pa_ppn_shift = {12, 21, 30, 39},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ffff},
+};
+
+static const virt2phys_info_t sv57 = {
+ .name = "Sv57",
+ .va_bits = 57,
+ .level = 5,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30, 39, 48},
+ .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x1ff},
+ .pte_ppn_shift = {10, 19, 28, 37, 46},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff},
+ .pa_ppn_shift = {12, 21, 30, 39, 48},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff},
+};
+
+static const virt2phys_info_t sv57x4 = {
+ .name = "Sv57x4",
+ .va_bits = 59,
+ .level = 5,
+ .pte_shift = 3,
+ .vpn_shift = {12, 21, 30, 39, 48},
+ .vpn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0x7ff},
+ .pte_ppn_shift = {10, 19, 28, 37, 46},
+ .pte_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff},
+ .pa_ppn_shift = {12, 21, 30, 39, 48},
+ .pa_ppn_mask = {0x1ff, 0x1ff, 0x1ff, 0x1ff, 0xff},
+};
+
+static enum riscv_halt_reason riscv_halt_reason(struct target *target);
static void riscv_info_init(struct target *target, struct riscv_info *r);
static void riscv_invalidate_register_cache(struct target *target);
static int riscv_step_rtos_hart(struct target *target);
@@ -290,7 +289,7 @@ void select_dmi_via_bscan(struct target *target)
bscan_tunnel_nested_tap_select_dmi, TAP_IDLE);
}
-uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
+int dtmcontrol_scan_via_bscan(struct target *target, uint32_t out, uint32_t *in_ptr)
{
/* On BSCAN TAP: Select IR=USER4, issue tunneled IR scan via BSCAN TAP's DR */
uint8_t tunneled_ir_width[4] = {bscan_tunnel_ir_width};
@@ -371,18 +370,19 @@ uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out)
uint32_t in = buf_get_u32(in_value, 1, 32);
LOG_DEBUG("DTMCS: 0x%x -> 0x%x", out, in);
- return in;
+ if (in_ptr)
+ *in_ptr = in;
+ return ERROR_OK;
}
-static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
+static int dtmcontrol_scan(struct target *target, uint32_t out, uint32_t *in_ptr)
{
struct scan_field field;
uint8_t in_value[4];
uint8_t out_value[4] = { 0 };
if (bscan_tunnel_ir_width != 0)
- return dtmcontrol_scan_via_bscan(target, out);
-
+ return dtmcontrol_scan_via_bscan(target, out, in_ptr);
buf_set_u32(out_value, 0, 32, out);
@@ -398,41 +398,46 @@ static uint32_t dtmcontrol_scan(struct target *target, uint32_t out)
int retval = jtag_execute_queue();
if (retval != ERROR_OK) {
- LOG_ERROR("failed jtag scan: %d", retval);
+ LOG_TARGET_ERROR(target, "dtmcontrol scan failed, error code = %d", retval);
return retval;
}
uint32_t in = buf_get_u32(field.in_value, 0, 32);
LOG_DEBUG("DTMCONTROL: 0x%x -> 0x%x", out, in);
- return in;
+ if (in_ptr)
+ *in_ptr = in;
+ return ERROR_OK;
}
static struct target_type *get_target_type(struct target *target)
{
if (!target->arch_info) {
- LOG_ERROR("Target has not been initialized");
+ LOG_TARGET_ERROR(target, "Target has not been initialized.");
return NULL;
}
RISCV_INFO(info);
switch (info->dtm_version) {
- case 0:
+ case DTM_DTMCS_VERSION_0_11:
return &riscv011_target;
- case 1:
+ case DTM_DTMCS_VERSION_1_0:
return &riscv013_target;
default:
- LOG_ERROR("Unsupported DTM version: %d", info->dtm_version);
+ /* TODO: once we have proper support for non-examined targets
+ * we should have an assert here */
+ LOG_TARGET_ERROR(target, "Unsupported DTM version: %d",
+ info->dtm_version);
return NULL;
}
}
static int riscv_create_target(struct target *target, Jim_Interp *interp)
{
- LOG_DEBUG("riscv_create_target()");
+ LOG_TARGET_DEBUG(target, "riscv_create_target()");
target->arch_info = calloc(1, sizeof(struct riscv_info));
if (!target->arch_info) {
- LOG_ERROR("Failed to allocate RISC-V target structure.");
+ LOG_TARGET_ERROR(target, "Failed to allocate RISC-V target structure.");
return ERROR_FAIL;
}
riscv_info_init(target, target->arch_info);
@@ -442,17 +447,22 @@ static int riscv_create_target(struct target *target, Jim_Interp *interp)
static int riscv_init_target(struct command_context *cmd_ctx,
struct target *target)
{
- LOG_DEBUG("riscv_init_target()");
+ LOG_TARGET_DEBUG(target, "riscv_init_target()");
RISCV_INFO(info);
info->cmd_ctx = cmd_ctx;
+ info->reset_delays_wait = -1;
select_dtmcontrol.num_bits = target->tap->ir_length;
select_dbus.num_bits = target->tap->ir_length;
select_idcode.num_bits = target->tap->ir_length;
if (bscan_tunnel_ir_width != 0) {
- assert(target->tap->ir_length >= 6);
- uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+ uint32_t ir_user4_raw = bscan_tunnel_ir_id;
+ /* Provide a default value which target some Xilinx FPGA USER4 IR */
+ if (ir_user4_raw == 0) {
+ assert(target->tap->ir_length >= 6);
+ ir_user4_raw = 0x23 << (target->tap->ir_length - 6);
+ }
h_u32_to_le(ir_user4, ir_user4_raw);
select_user4.num_bits = target->tap->ir_length;
bscan_tunneled_ir_width[0] = bscan_tunnel_ir_width;
@@ -469,39 +479,84 @@ static int riscv_init_target(struct command_context *cmd_ctx,
return ERROR_OK;
}
+static void free_reg_names(struct target *target);
+
static void riscv_free_registers(struct target *target)
{
+ free_reg_names(target);
/* Free the shared structure use for most registers. */
- if (target->reg_cache) {
- if (target->reg_cache->reg_list) {
- free(target->reg_cache->reg_list[0].arch_info);
- /* Free the ones we allocated separately. */
- for (unsigned i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
- free(target->reg_cache->reg_list[i].arch_info);
- for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
- free(target->reg_cache->reg_list[i].value);
- free(target->reg_cache->reg_list);
+ if (!target->reg_cache)
+ return;
+ if (target->reg_cache->reg_list) {
+ for (unsigned int i = GDB_REGNO_COUNT; i < target->reg_cache->num_regs; i++)
+ free(target->reg_cache->reg_list[i].arch_info);
+ for (unsigned int i = 0; i < target->reg_cache->num_regs; i++)
+ free(target->reg_cache->reg_list[i].value);
+ free(target->reg_cache->reg_list);
+ }
+ free(target->reg_cache);
+ target->reg_cache = NULL;
+}
+
+static void free_custom_register_names(struct target *target)
+{
+ RISCV_INFO(info);
+
+ if (!info->custom_register_names.reg_names)
+ return;
+
+ for (unsigned int i = 0; i < info->custom_register_names.num_entries; i++)
+ free(info->custom_register_names.reg_names[i]);
+ free(info->custom_register_names.reg_names);
+ info->custom_register_names.reg_names = NULL;
+}
+
+static void free_wp_triggers_cache(struct target *target)
+{
+ RISCV_INFO(r);
+
+ for (unsigned int i = 0; i < r->trigger_count; ++i) {
+ struct tdata1_cache *elem_1, *tmp_1;
+ list_for_each_entry_safe(elem_1, tmp_1, &r->wp_triggers_negative_cache[i], elem_tdata1) {
+ struct tdata2_cache *elem_2, *tmp_2;
+ list_for_each_entry_safe(elem_2, tmp_2, &elem_1->tdata2_cache_head, elem_tdata2) {
+ list_del(&elem_2->elem_tdata2);
+ free(elem_2);
+ }
+ list_del(&elem_1->elem_tdata1);
+ free(elem_1);
}
- free(target->reg_cache);
}
+ free(r->wp_triggers_negative_cache);
}
static void riscv_deinit_target(struct target *target)
{
- LOG_DEBUG("riscv_deinit_target()");
+ LOG_TARGET_DEBUG(target, "riscv_deinit_target()");
struct riscv_info *info = target->arch_info;
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ LOG_TARGET_ERROR(target, "Could not identify target type.");
+
+ if (riscv_flush_registers(target) != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to flush registers. Ignoring this error.");
if (tt && info && info->version_specific)
tt->deinit_target(target);
riscv_free_registers(target);
+ free_wp_triggers_cache(target);
if (!info)
return;
range_list_t *entry, *tmp;
+ list_for_each_entry_safe(entry, tmp, &info->hide_csr, list) {
+ free(entry->name);
+ free(entry);
+ }
+
list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) {
free(entry->name);
free(entry);
@@ -512,7 +567,6 @@ static void riscv_deinit_target(struct target *target)
free(entry);
}
- free(info->reg_names);
free(target->arch_info);
target->arch_info = NULL;
@@ -524,16 +578,121 @@ static void trigger_from_breakpoint(struct trigger *trigger,
trigger->address = breakpoint->address;
trigger->length = breakpoint->length;
trigger->mask = ~0LL;
- trigger->read = false;
- trigger->write = false;
- trigger->execute = true;
+ trigger->is_read = false;
+ trigger->is_write = false;
+ trigger->is_execute = true;
/* unique_id is unique across both breakpoints and watchpoints. */
trigger->unique_id = breakpoint->unique_id;
}
-static int maybe_add_trigger_t1(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static bool can_use_napot_match(struct trigger *trigger)
+{
+ riscv_reg_t addr = trigger->address;
+ riscv_reg_t size = trigger->length;
+ bool size_power_of_2 = (size & (size - 1)) == 0;
+ bool addr_aligned = (addr & (size - 1)) == 0;
+ return size > 1 && size_power_of_2 && addr_aligned;
+}
+
+/* Find the next free trigger of the given type, without talking to the target. */
+static int find_next_free_trigger(struct target *target, int type, bool chained,
+ unsigned int *idx)
{
+ assert(idx);
+ RISCV_INFO(r);
+
+ unsigned int num_found = 0;
+ unsigned int num_required = chained ? 2 : 1;
+
+ for (unsigned int i = *idx; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == -1) {
+ if (r->trigger_tinfo[i] & (1 << type)) {
+ num_found++;
+ if (num_required == num_found) {
+ /* Found num_required consecutive free triggers - success, done. */
+ *idx = i - (num_required - 1);
+ LOG_TARGET_DEBUG(target,
+ "%d trigger(s) of type %d found on index %u, "
+ "chained == %s",
+ num_required, type, *idx,
+ chained ? "true" : "false");
+ return ERROR_OK;
+ }
+ /* Found a trigger but need more consecutive ones */
+ continue;
+ }
+ }
+ /* Trigger already occupied or incompatible type.
+ * Reset the counter of found consecutive triggers */
+ num_found = 0;
+ }
+
+ return ERROR_FAIL;
+}
+
+static int find_first_trigger_by_id(struct target *target, int unique_id)
+{
+ RISCV_INFO(r);
+
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == unique_id)
+ return i;
+ }
+ return -1;
+}
+
+static int set_trigger(struct target *target, unsigned int idx, riscv_reg_t tdata1, riscv_reg_t tdata2,
+ riscv_reg_t tdata1_ignore_mask)
+{
+ riscv_reg_t tdata1_rb, tdata2_rb;
+ // Select which trigger to use
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, idx) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Disable the trigger by writing 0 to it
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Set trigger data for tdata2 (and tdata3 if it was supported)
+ if (riscv_set_register(target, GDB_REGNO_TDATA2, tdata2) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Set trigger data for tdata1
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ // Read back tdata1, tdata2, (tdata3), and check if the configuration is supported
+ if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_get_register(target, &tdata2_rb, GDB_REGNO_TDATA2) != ERROR_OK)
+ return ERROR_FAIL;
+ bool tdata1_config_denied = (tdata1 & ~tdata1_ignore_mask) != (tdata1_rb & ~tdata1_ignore_mask);
+ bool tdata2_config_denied = tdata2 != tdata2_rb;
+ if (tdata1_config_denied || tdata2_config_denied) {
+ LOG_TARGET_DEBUG(target, "Trigger %u doesn't support what we need.", idx);
+
+ if (tdata1_config_denied)
+ LOG_TARGET_DEBUG(target,
+ "After writing 0x%" PRIx64 " to tdata1 it contains 0x%" PRIx64 "; tdata1_ignore_mask=0x%" PRIx64,
+ tdata1, tdata1_rb, tdata1_ignore_mask);
+
+ if (tdata2_config_denied)
+ LOG_TARGET_DEBUG(target,
+ "wrote 0x%" PRIx64 " to tdata2 but read back 0x%" PRIx64,
+ tdata2, tdata2_rb);
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
+ return ERROR_FAIL;
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ return ERROR_OK;
+}
+
+static int maybe_add_trigger_t1(struct target *target, struct trigger *trigger)
+{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
+
RISCV_INFO(r);
const uint32_t bpcontrol_x = 1<<0;
@@ -546,204 +705,566 @@ static int maybe_add_trigger_t1(struct target *target,
const uint32_t bpcontrol_bpmatch = 0xf << 7;
const uint32_t bpcontrol_bpaction = 0xff << 11;
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_LEGACY, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
if (tdata1 & (bpcontrol_r | bpcontrol_w | bpcontrol_x)) {
/* Trigger is already in use, presumably by user code. */
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
- tdata1 = set_field(tdata1, bpcontrol_r, trigger->read);
- tdata1 = set_field(tdata1, bpcontrol_w, trigger->write);
- tdata1 = set_field(tdata1, bpcontrol_x, trigger->execute);
- tdata1 = set_field(tdata1, bpcontrol_u,
- !!(r->misa & BIT('U' - 'A')));
- tdata1 = set_field(tdata1, bpcontrol_s,
- !!(r->misa & BIT('S' - 'A')));
- tdata1 = set_field(tdata1, bpcontrol_h,
- !!(r->misa & BIT('H' - 'A')));
- tdata1 |= bpcontrol_m;
- tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, bpcontrol_r, trigger->is_read);
+ tdata1 = set_field(tdata1, bpcontrol_w, trigger->is_write);
+ tdata1 = set_field(tdata1, bpcontrol_x, trigger->is_execute);
+ tdata1 = set_field(tdata1, bpcontrol_u, !!(r->misa & BIT('U' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_s, !!(r->misa & BIT('S' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_h, !!(r->misa & BIT('H' - 'A')));
+ tdata1 = set_field(tdata1, bpcontrol_m, 1);
tdata1 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */
+ tdata1 = set_field(tdata1, bpcontrol_bpmatch, 0); /* exact match */
+ tdata2 = trigger->address;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ return ERROR_OK;
+}
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
+struct trigger_request_info {
+ riscv_reg_t tdata1;
+ riscv_reg_t tdata2;
+ riscv_reg_t tdata1_ignore_mask;
+};
- riscv_reg_t tdata1_rb;
- if (riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1) != ERROR_OK)
- return ERROR_FAIL;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+static void log_trigger_request_info(struct trigger_request_info trig_info)
+{
+ LOG_DEBUG("tdata1=%" PRIx64 ", tdata2=%" PRIx64 ", tdata1_ignore_mask=%" PRIx64,
+ trig_info.tdata1, trig_info.tdata2, trig_info.tdata1_ignore_mask);
+};
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+static struct tdata1_cache *tdata1_cache_alloc(struct list_head *tdata1_cache_head, riscv_reg_t tdata1)
+{
+ struct tdata1_cache *elem = (struct tdata1_cache *)calloc(1, sizeof(struct tdata1_cache));
+ elem->tdata1 = tdata1;
+ INIT_LIST_HEAD(&elem->tdata2_cache_head);
+ list_add_tail(&elem->elem_tdata1, tdata1_cache_head);
+ return elem;
+}
+
+static void tdata2_cache_alloc(struct list_head *tdata2_cache_head, riscv_reg_t tdata2)
+{
+ struct tdata2_cache * const elem = calloc(1, sizeof(struct tdata2_cache));
+ elem->tdata2 = tdata2;
+ list_add(&elem->elem_tdata2, tdata2_cache_head);
+}
+
+struct tdata2_cache *tdata2_cache_search(struct list_head *tdata2_cache_head, riscv_reg_t find_tdata2)
+{
+ struct tdata2_cache *elem_2;
+ list_for_each_entry(elem_2, tdata2_cache_head, elem_tdata2) {
+ if (elem_2->tdata2 == find_tdata2)
+ return elem_2;
}
+ return NULL;
+}
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+struct tdata1_cache *tdata1_cache_search(struct list_head *tdata1_cache_head, riscv_reg_t find_tdata1)
+{
+ struct tdata1_cache *elem_1;
+ list_for_each_entry(elem_1, tdata1_cache_head, elem_tdata1) {
+ if (elem_1->tdata1 == find_tdata1)
+ return elem_1;
+ }
+ return NULL;
+}
- return ERROR_OK;
+static void create_wp_trigger_cache(struct target *target)
+{
+ RISCV_INFO(r);
+
+ r->wp_triggers_negative_cache = (struct list_head *)calloc(r->trigger_count,
+ sizeof(struct list_head));
+ for (unsigned int i = 0; i < r->trigger_count; ++i)
+ INIT_LIST_HEAD(&r->wp_triggers_negative_cache[i]);
}
-static int maybe_add_trigger_t2(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static void wp_triggers_cache_add(struct target *target, unsigned int idx, riscv_reg_t tdata1,
+ riscv_reg_t tdata2, int error_code)
{
RISCV_INFO(r);
- /* tselect is already set */
- if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) {
- /* Trigger is already in use, presumably by user code. */
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ struct tdata1_cache *tdata1_cache = tdata1_cache_search(&r->wp_triggers_negative_cache[idx], tdata1);
+ if (!tdata1_cache) {
+ tdata1_cache = tdata1_cache_alloc(&r->wp_triggers_negative_cache[idx], tdata1);
+ } else {
+ struct tdata2_cache *tdata2_cache = tdata2_cache_search(&tdata1_cache->tdata2_cache_head, tdata2);
+ if (tdata2_cache) {
+ list_move(&tdata2_cache->elem_tdata2, &tdata1_cache->tdata2_cache_head);
+ return;
+ }
}
+ tdata2_cache_alloc(&tdata1_cache->tdata2_cache_head, tdata2);
+}
- /* address/data match trigger */
- tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
- tdata1 = set_field(tdata1, MCONTROL_ACTION,
- MCONTROL_ACTION_DEBUG_MODE);
- tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL);
- tdata1 |= MCONTROL_M;
- if (r->misa & (1 << ('S' - 'A')))
- tdata1 |= MCONTROL_S;
- if (r->misa & (1 << ('U' - 'A')))
- tdata1 |= MCONTROL_U;
-
- if (trigger->execute)
- tdata1 |= MCONTROL_EXECUTE;
- if (trigger->read)
- tdata1 |= MCONTROL_LOAD;
- if (trigger->write)
- tdata1 |= MCONTROL_STORE;
-
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
-
- uint64_t tdata1_rb;
- int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+static bool wp_triggers_cache_search(struct target *target, unsigned int idx,
+ riscv_reg_t tdata1, riscv_reg_t tdata2)
+{
+ RISCV_INFO(r);
+
+ struct tdata1_cache *tdata1_cache = tdata1_cache_search(&r->wp_triggers_negative_cache[idx], tdata1);
+ if (!tdata1_cache)
+ return false;
+ struct tdata2_cache *tdata2_cache = tdata2_cache_search(&tdata1_cache->tdata2_cache_head, tdata2);
+ if (!tdata2_cache)
+ return false;
+ assert(tdata1_cache->tdata1 == tdata1 && tdata2_cache->tdata2 == tdata2);
+ return true;
+}
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+static int try_use_trigger_and_cache_result(struct target *target, unsigned int idx, riscv_reg_t tdata1,
+ riscv_reg_t tdata2, riscv_reg_t tdata1_ignore_mask)
+{
+ if (wp_triggers_cache_search(target, idx, tdata1, tdata2))
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+ int ret = set_trigger(target, idx, tdata1, tdata2, tdata1_ignore_mask);
- return ERROR_OK;
+ /* Add these values to the cache to remember that they are not supported. */
+ if (ret == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ wp_triggers_cache_add(target, idx, tdata1, tdata2, ret);
+ return ret;
}
-static int maybe_add_trigger_t6(struct target *target,
- struct trigger *trigger, uint64_t tdata1)
+static int try_setup_single_match_trigger(struct target *target,
+ struct trigger *trigger, struct trigger_request_info trig_info)
{
+ LOG_TARGET_DEBUG(target, "trying to set up a match trigger");
+ log_trigger_request_info(trig_info);
+
+ int trigger_type =
+ get_field(trig_info.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target)));
+ int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
RISCV_INFO(r);
- /* tselect is already set */
- if (tdata1 & (CSR_MCONTROL6_EXECUTE | CSR_MCONTROL6_STORE | CSR_MCONTROL6_LOAD)) {
- /* Trigger is already in use, presumably by user code. */
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ /* Find the first trigger, supporting required tdata1 value */
+ for (unsigned int idx = 0;
+ find_next_free_trigger(target, trigger_type, false, &idx) == ERROR_OK;
+ ++idx) {
+ ret = try_use_trigger_and_cache_result(target, idx, trig_info.tdata1, trig_info.tdata2,
+ trig_info.tdata1_ignore_mask);
+
+ if (ret == ERROR_OK) {
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ return ERROR_OK;
+ }
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
}
+ return ret;
+}
- /* address/data match trigger */
- tdata1 |= MCONTROL_DMODE(riscv_xlen(target));
- tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION,
- MCONTROL_ACTION_DEBUG_MODE);
- tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, MCONTROL_MATCH_EQUAL);
- tdata1 |= CSR_MCONTROL6_M;
- if (r->misa & (1 << ('H' - 'A')))
- tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU;
- if (r->misa & (1 << ('S' - 'A')))
- tdata1 |= CSR_MCONTROL6_S;
- if (r->misa & (1 << ('U' - 'A')))
- tdata1 |= CSR_MCONTROL6_U;
-
- if (trigger->execute)
- tdata1 |= CSR_MCONTROL6_EXECUTE;
- if (trigger->read)
- tdata1 |= CSR_MCONTROL6_LOAD;
- if (trigger->write)
- tdata1 |= CSR_MCONTROL6_STORE;
-
- riscv_set_register(target, GDB_REGNO_TDATA1, tdata1);
-
- uint64_t tdata1_rb;
- int result = riscv_get_register(target, &tdata1_rb, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- LOG_DEBUG("tdata1=0x%" PRIx64, tdata1_rb);
+static int try_setup_chained_match_triggers(struct target *target,
+ struct trigger *trigger, struct trigger_request_info t1,
+ struct trigger_request_info t2)
+{
+ LOG_TARGET_DEBUG(target, "trying to set up a chain of match triggers");
+ log_trigger_request_info(t1);
+ log_trigger_request_info(t2);
+ int trigger_type =
+ get_field(t1.tdata1, CSR_MCONTROL_TYPE(riscv_xlen(target)));
+ int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ RISCV_INFO(r);
- if (tdata1 != tdata1_rb) {
- LOG_DEBUG("Trigger doesn't support what we need; After writing 0x%"
- PRIx64 " to tdata1 it contains 0x%" PRIx64,
- tdata1, tdata1_rb);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
+ /* Find the first 2 consecutive triggers, supporting required tdata1 values */
+ for (unsigned int idx = 0;
+ find_next_free_trigger(target, trigger_type, true, &idx) == ERROR_OK;
+ ++idx) {
+ ret = try_use_trigger_and_cache_result(target, idx, t1.tdata1, t1.tdata2,
+ t1.tdata1_ignore_mask);
+
+ if (ret == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ continue;
+ else if (ret != ERROR_OK)
+ return ret;
- riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address);
+ ret = try_use_trigger_and_cache_result(target, idx + 1, t2.tdata1, t2.tdata2,
+ t2.tdata1_ignore_mask);
- return ERROR_OK;
+ if (ret == ERROR_OK) {
+ r->trigger_unique_id[idx] = trigger->unique_id;
+ r->trigger_unique_id[idx + 1] = trigger->unique_id;
+ return ERROR_OK;
+ }
+ /* Undo the setting of the previous trigger */
+ int ret_undo = set_trigger(target, idx, 0, 0, 0);
+ if (ret_undo != ERROR_OK)
+ return ret_undo;
+
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+ }
+ return ret;
}
-static int add_trigger(struct target *target, struct trigger *trigger)
+struct match_triggers_tdata1_fields {
+ riscv_reg_t common;
+ struct {
+ /* Other values are available for this field,
+ * but currently only `any` is needed.
+ */
+ riscv_reg_t any;
+ } size;
+ struct {
+ riscv_reg_t enable;
+ riscv_reg_t disable;
+ } chain;
+ struct {
+ riscv_reg_t napot;
+ riscv_reg_t lt;
+ riscv_reg_t ge;
+ riscv_reg_t eq;
+ } match;
+ riscv_reg_t tdata1_ignore_mask;
+};
+
+static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t2(struct target *target,
+ struct trigger *trigger)
{
RISCV_INFO(r);
- if (riscv_enumerate_triggers(target) != ERROR_OK)
- return ERROR_FAIL;
+ struct match_triggers_tdata1_fields result = {
+ .common =
+ field_value(CSR_MCONTROL_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL) |
+ field_value(CSR_MCONTROL_DMODE(riscv_xlen(target)), 1) |
+ field_value(CSR_MCONTROL_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) |
+ field_value(CSR_MCONTROL_M, 1) |
+ field_value(CSR_MCONTROL_S, !!(r->misa & BIT('S' - 'A'))) |
+ field_value(CSR_MCONTROL_U, !!(r->misa & BIT('U' - 'A'))) |
+ field_value(CSR_MCONTROL_EXECUTE, trigger->is_execute) |
+ field_value(CSR_MCONTROL_LOAD, trigger->is_read) |
+ field_value(CSR_MCONTROL_STORE, trigger->is_write),
+ .size = {
+ .any =
+ field_value(CSR_MCONTROL_SIZELO, CSR_MCONTROL_SIZELO_ANY & 3) |
+ field_value(CSR_MCONTROL_SIZEHI, (CSR_MCONTROL_SIZELO_ANY >> 2) & 3)
+ },
+ .chain = {
+ .enable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_ENABLED),
+ .disable = field_value(CSR_MCONTROL_CHAIN, CSR_MCONTROL_CHAIN_DISABLED)
+ },
+ .match = {
+ .napot = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_NAPOT),
+ .lt = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_LT),
+ .ge = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_GE),
+ .eq = field_value(CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_EQUAL)
+ },
+ .tdata1_ignore_mask = CSR_MCONTROL_MASKMAX(riscv_xlen(target))
+ };
+ return result;
+}
- riscv_reg_t tselect;
- if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
- return ERROR_FAIL;
+static struct match_triggers_tdata1_fields fill_match_triggers_tdata1_fields_t6(struct target *target,
+ struct trigger *trigger)
+{
+ bool misa_s = riscv_supports_extension(target, 'S');
+ bool misa_u = riscv_supports_extension(target, 'U');
+ bool misa_h = riscv_supports_extension(target, 'H');
+
+ struct match_triggers_tdata1_fields result = {
+ .common =
+ field_value(CSR_MCONTROL6_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_MCONTROL6) |
+ field_value(CSR_MCONTROL6_DMODE(riscv_xlen(target)), 1) |
+ field_value(CSR_MCONTROL6_ACTION, CSR_MCONTROL_ACTION_DEBUG_MODE) |
+ field_value(CSR_MCONTROL6_M, 1) |
+ field_value(CSR_MCONTROL6_S, misa_s) |
+ field_value(CSR_MCONTROL6_U, misa_u) |
+ field_value(CSR_MCONTROL6_VS, misa_h && misa_s) |
+ field_value(CSR_MCONTROL6_VU, misa_h && misa_u) |
+ field_value(CSR_MCONTROL6_EXECUTE, trigger->is_execute) |
+ field_value(CSR_MCONTROL6_LOAD, trigger->is_read) |
+ field_value(CSR_MCONTROL6_STORE, trigger->is_write),
+ .size = {
+ .any = field_value(CSR_MCONTROL6_SIZE, CSR_MCONTROL6_SIZE_ANY)
+ },
+ .chain = {
+ .enable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_ENABLED),
+ .disable = field_value(CSR_MCONTROL6_CHAIN, CSR_MCONTROL6_CHAIN_DISABLED)
+ },
+ .match = {
+ .napot = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_NAPOT),
+ .lt = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_LT),
+ .ge = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_GE),
+ .eq = field_value(CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_EQUAL)
+ },
+ .tdata1_ignore_mask = 0
+ };
+ return result;
+}
- unsigned int i;
- for (i = 0; i < r->trigger_count; i++) {
- if (r->trigger_unique_id[i] != -1)
- continue;
+static int maybe_add_trigger_t2_t6_for_wp(struct target *target,
+ struct trigger *trigger, struct match_triggers_tdata1_fields fields)
+{
+ RISCV_INFO(r);
+ int ret = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+ if (trigger->length > 0) {
+ /* Setting a load/store trigger ("watchpoint") on a range of addresses */
+ if (can_use_napot_match(trigger)) {
+ if (r->wp_allow_napot_trigger) {
+ LOG_TARGET_DEBUG(target, "trying to setup NAPOT match trigger");
+ struct trigger_request_info napot = {
+ .tdata1 = fields.common | fields.size.any |
+ fields.chain.disable | fields.match.napot,
+ .tdata2 = trigger->address | ((trigger->length - 1) >> 1),
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_single_match_trigger(target, trigger, napot);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+ } else {
+ LOG_TARGET_DEBUG(target, "NAPOT match triggers are disabled for watchpoints. "
+ "Use 'riscv set_enable_trigger_feature napot wp' to enable it.");
+ }
+ }
- riscv_set_register(target, GDB_REGNO_TSELECT, i);
+ if (r->wp_allow_ge_lt_trigger) {
+ LOG_TARGET_DEBUG(target, "trying to setup GE+LT chained match trigger pair");
+ struct trigger_request_info ge_1 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.enable |
+ fields.match.ge,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ struct trigger_request_info lt_2 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.lt,
+ .tdata2 = trigger->address + trigger->length,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_chained_match_triggers(target, trigger, ge_1, lt_2);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+
+ LOG_TARGET_DEBUG(target, "trying to setup LT+GE chained match trigger pair");
+ struct trigger_request_info lt_1 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.enable |
+ fields.match.lt,
+ .tdata2 = trigger->address + trigger->length,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ struct trigger_request_info ge_2 = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.ge,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_chained_match_triggers(target, trigger, lt_1, ge_2);
+ if (ret != ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ return ret;
+ } else {
+ LOG_TARGET_DEBUG(target, "LT+GE chained match triggers are disabled for watchpoints. "
+ "Use 'riscv set_enable_trigger_feature ge_lt wp' to enable it.");
+ }
+ }
- uint64_t tdata1;
- int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
+ if (r->wp_allow_equality_match_trigger) {
+ LOG_TARGET_DEBUG(target, "trying to setup equality match trigger");
+ struct trigger_request_info eq = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.eq,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
+ ret = try_setup_single_match_trigger(target, trigger, eq);
+ if (ret != ERROR_OK)
+ return ret;
+ } else {
+ LOG_TARGET_DEBUG(target, "equality match triggers are disabled for watchpoints. "
+ "Use 'riscv set_enable_trigger_feature eq wp' to enable it.");
+ }
+
+ if (ret == ERROR_OK && trigger->length > 1) {
+ LOG_TARGET_DEBUG(target, "Trigger will match accesses at address 0x%" TARGET_PRIxADDR
+ ", but may not match accesses at addresses in the inclusive range from 0x%"
+ TARGET_PRIxADDR " to 0x%" TARGET_PRIxADDR ".", trigger->address,
+ trigger->address + 1, trigger->address + trigger->length - 1);
+ RISCV_INFO(info);
+ if (!info->range_trigger_fallback_encountered)
+ /* This message is displayed only once per target to avoid
+ * overwhelming the user with such messages on resume.
+ */
+ LOG_TARGET_WARNING(target,
+ "Could not set a trigger that will match a whole address range. "
+ "As a fallback, this trigger (and maybe others) will only match "
+ "against the first address of the range.");
+ info->range_trigger_fallback_encountered = true;
+ }
- switch (type) {
- case 1:
- result = maybe_add_trigger_t1(target, trigger, tdata1);
- break;
- case 2:
- result = maybe_add_trigger_t2(target, trigger, tdata1);
- break;
- case 6:
- result = maybe_add_trigger_t6(target, trigger, tdata1);
- break;
- default:
- LOG_DEBUG("trigger %d has unknown type %d", i, type);
- continue;
- }
+ return ret;
+}
- if (result != ERROR_OK)
- continue;
+static int maybe_add_trigger_t2_t6_for_bp(struct target *target,
+ struct trigger *trigger, struct match_triggers_tdata1_fields fields)
+{
+ LOG_TARGET_DEBUG(target, "trying to setup equality match trigger");
+ struct trigger_request_info eq = {
+ .tdata1 = fields.common | fields.size.any | fields.chain.disable |
+ fields.match.eq,
+ .tdata2 = trigger->address,
+ .tdata1_ignore_mask = fields.tdata1_ignore_mask
+ };
- LOG_DEBUG("[%d] Using trigger %d (type %d) for bp %d", target->coreid,
- i, type, trigger->unique_id);
- r->trigger_unique_id[i] = trigger->unique_id;
- break;
+ return try_setup_single_match_trigger(target, trigger, eq);
+}
+
+static int maybe_add_trigger_t2_t6(struct target *target,
+ struct trigger *trigger, struct match_triggers_tdata1_fields fields)
+{
+ if (trigger->is_execute) {
+ assert(!trigger->is_read && !trigger->is_write);
+ return maybe_add_trigger_t2_t6_for_bp(target, trigger, fields);
}
- riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
+ assert(trigger->is_read || trigger->is_write);
+ return maybe_add_trigger_t2_t6_for_wp(target, trigger, fields);
+}
- if (i >= r->trigger_count) {
- LOG_ERROR("Couldn't find an available hardware trigger.");
- return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
- }
+static int maybe_add_trigger_t3(struct target *target, bool vs, bool vu,
+ bool m, bool s, bool u, bool pending, unsigned int count,
+ int unique_id)
+{
+ int ret;
+ riscv_reg_t tdata1;
+ RISCV_INFO(r);
+
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ICOUNT_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ICOUNT);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_ACTION, CSR_ICOUNT_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_PENDING, pending);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_M, m);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_S, s);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_U, u);
+ tdata1 = set_field(tdata1, CSR_ICOUNT_COUNT, count);
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ICOUNT, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, 0, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
+ return ERROR_OK;
+}
+
+static int maybe_add_trigger_t4(struct target *target, bool vs, bool vu,
+ bool nmi, bool m, bool s, bool u, riscv_reg_t interrupts,
+ int unique_id)
+{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
+
+ RISCV_INFO(r);
+
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ITRIGGER);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_ACTION, CSR_ITRIGGER_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_NMI, nmi);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_M, m);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_S, s);
+ tdata1 = set_field(tdata1, CSR_ITRIGGER_U, u);
+
+ tdata2 = interrupts;
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ITRIGGER, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
return ERROR_OK;
}
+static int maybe_add_trigger_t5(struct target *target, bool vs, bool vu,
+ bool m, bool s, bool u, riscv_reg_t exception_codes,
+ int unique_id)
+{
+ int ret;
+ riscv_reg_t tdata1, tdata2;
+
+ RISCV_INFO(r);
+
+ tdata1 = 0;
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_TYPE(riscv_xlen(target)), CSR_TDATA1_TYPE_ETRIGGER);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_DMODE(riscv_xlen(target)), 1);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_ACTION, CSR_ETRIGGER_ACTION_DEBUG_MODE);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_VS, vs);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_VU, vu);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_M, m);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_S, s);
+ tdata1 = set_field(tdata1, CSR_ETRIGGER_U, u);
+
+ tdata2 = exception_codes;
+
+ unsigned int idx = 0;
+ ret = find_next_free_trigger(target, CSR_TDATA1_TYPE_ETRIGGER, false, &idx);
+ if (ret != ERROR_OK)
+ return ret;
+ ret = set_trigger(target, idx, tdata1, tdata2, 0);
+ if (ret != ERROR_OK)
+ return ret;
+ r->trigger_unique_id[idx] = unique_id;
+ return ERROR_OK;
+}
+
+static int add_trigger(struct target *target, struct trigger *trigger)
+{
+ int ret;
+ riscv_reg_t tselect;
+
+ ret = riscv_enumerate_triggers(target);
+ if (ret != ERROR_OK)
+ return ret;
+
+ ret = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ if (ret != ERROR_OK)
+ return ret;
+
+ do {
+ ret = maybe_add_trigger_t1(target, trigger);
+ if (ret == ERROR_OK)
+ break;
+ ret = maybe_add_trigger_t2_t6(target, trigger,
+ fill_match_triggers_tdata1_fields_t2(target, trigger));
+ if (ret == ERROR_OK)
+ break;
+ ret = maybe_add_trigger_t2_t6(target, trigger,
+ fill_match_triggers_tdata1_fields_t6(target, trigger));
+ if (ret == ERROR_OK)
+ break;
+ } while (0);
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK &&
+ ret == ERROR_OK)
+ return ERROR_FAIL;
+
+ return ret;
+}
+
/**
* Write one memory item of given "size". Use memory access of given "access_size".
* Utilize read-modify-write, if needed.
@@ -864,24 +1385,25 @@ int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_
static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
- LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, breakpoint->address);
+ LOG_TARGET_DEBUG(target, "@0x%" TARGET_PRIxADDR, breakpoint->address);
assert(breakpoint);
if (breakpoint->type == BKPT_SOFT) {
/** @todo check RVC for size/alignment */
if (!(breakpoint->length == 4 || breakpoint->length == 2)) {
- LOG_ERROR("Invalid breakpoint length %d", breakpoint->length);
+ LOG_TARGET_ERROR(target, "Invalid breakpoint length %d", breakpoint->length);
return ERROR_FAIL;
}
if (0 != (breakpoint->address % 2)) {
- LOG_ERROR("Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, breakpoint->address);
+ LOG_TARGET_ERROR(target, "Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR,
+ breakpoint->address);
return ERROR_FAIL;
}
/* Read the original instruction. */
if (riscv_read_by_any_size(
target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
- LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR,
+ LOG_TARGET_ERROR(target, "Failed to read original instruction at 0x%" TARGET_PRIxADDR,
breakpoint->address);
return ERROR_FAIL;
}
@@ -890,10 +1412,11 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp
buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c());
/* Write the ebreak instruction. */
if (riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) {
- LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%"
+ LOG_TARGET_ERROR(target, "Failed to write %d-byte breakpoint instruction at 0x%"
TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
}
+ breakpoint->is_set = true;
} else if (breakpoint->type == BKPT_HARD) {
struct trigger trigger;
@@ -901,43 +1424,46 @@ static int riscv_add_breakpoint(struct target *target, struct breakpoint *breakp
int const result = add_trigger(target, &trigger);
if (result != ERROR_OK)
return result;
+
+ int trigger_idx = find_first_trigger_by_id(target, breakpoint->unique_id);
+ breakpoint_hw_set(breakpoint, trigger_idx);
} else {
- LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
-
- breakpoint->is_set = true;
return ERROR_OK;
}
-static int remove_trigger(struct target *target, struct trigger *trigger)
+static int remove_trigger(struct target *target, int unique_id)
{
RISCV_INFO(r);
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
- unsigned int i;
- for (i = 0; i < r->trigger_count; i++) {
- if (r->trigger_unique_id[i] == trigger->unique_id)
- break;
- }
- if (i >= r->trigger_count) {
- LOG_ERROR("Couldn't find the hardware resources used by hardware "
- "trigger.");
- return ERROR_FAIL;
- }
- LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i,
- trigger->unique_id);
-
riscv_reg_t tselect;
int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
if (result != ERROR_OK)
return result;
- riscv_set_register(target, GDB_REGNO_TSELECT, i);
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+
+ bool done = false;
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == unique_id) {
+ riscv_set_register(target, GDB_REGNO_TSELECT, i);
+ riscv_set_register(target, GDB_REGNO_TDATA1, 0);
+ r->trigger_unique_id[i] = -1;
+ LOG_TARGET_DEBUG(target, "Stop using resource %d for bp %d",
+ i, unique_id);
+ done = true;
+ }
+ }
+ if (!done) {
+ LOG_TARGET_ERROR(target,
+ "Couldn't find the hardware resources used by hardware trigger.");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
- r->trigger_unique_id[i] = -1;
return ERROR_OK;
}
@@ -949,7 +1475,7 @@ static int riscv_remove_breakpoint(struct target *target,
/* Write the original instruction. */
if (riscv_write_by_any_size(
target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) {
- LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at "
+ LOG_TARGET_ERROR(target, "Failed to restore instruction for %d-byte breakpoint at "
"0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address);
return ERROR_FAIL;
}
@@ -957,12 +1483,12 @@ static int riscv_remove_breakpoint(struct target *target,
} else if (breakpoint->type == BKPT_HARD) {
struct trigger trigger;
trigger_from_breakpoint(&trigger, breakpoint);
- int result = remove_trigger(target, &trigger);
+ int result = remove_trigger(target, trigger.unique_id);
if (result != ERROR_OK)
return result;
} else {
- LOG_INFO("OpenOCD only supports hardware and software breakpoints.");
+ LOG_TARGET_INFO(target, "OpenOCD only supports hardware and software breakpoints.");
return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
}
@@ -978,22 +1504,29 @@ static void trigger_from_watchpoint(struct trigger *trigger,
trigger->length = watchpoint->length;
trigger->mask = watchpoint->mask;
trigger->value = watchpoint->value;
- trigger->read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
- trigger->write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
- trigger->execute = false;
+ trigger->is_read = (watchpoint->rw == WPT_READ || watchpoint->rw == WPT_ACCESS);
+ trigger->is_write = (watchpoint->rw == WPT_WRITE || watchpoint->rw == WPT_ACCESS);
+ trigger->is_execute = false;
/* unique_id is unique across both breakpoints and watchpoints. */
trigger->unique_id = watchpoint->unique_id;
}
int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
{
+ if (watchpoint->mask != WATCHPOINT_IGNORE_DATA_VALUE_MASK) {
+ LOG_TARGET_ERROR(target, "Watchpoints on data values are not implemented");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint);
int result = add_trigger(target, &trigger);
if (result != ERROR_OK)
return result;
- watchpoint->is_set = true;
+
+ int trigger_idx = find_first_trigger_by_id(target, watchpoint->unique_id);
+ watchpoint_set(watchpoint, trigger_idx);
return ERROR_OK;
}
@@ -1001,12 +1534,12 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint)
int riscv_remove_watchpoint(struct target *target,
struct watchpoint *watchpoint)
{
- LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address);
+ LOG_TARGET_DEBUG(target, "Removing watchpoint @0x%" TARGET_PRIxADDR, watchpoint->address);
struct trigger trigger;
trigger_from_watchpoint(&trigger, watchpoint);
- int result = remove_trigger(target, &trigger);
+ int result = remove_trigger(target, trigger.unique_id);
if (result != ERROR_OK)
return result;
watchpoint->is_set = false;
@@ -1014,6 +1547,510 @@ int riscv_remove_watchpoint(struct target *target,
return ERROR_OK;
}
+/**
+ * Look at the trigger hit bits to find out which trigger is the reason we're
+ * halted. Sets *unique_id to the unique ID of that trigger. If *unique_id is
+ * ~0, no match was found.
+ */
+static int riscv_hit_trigger_hit_bit(struct target *target, uint32_t *unique_id)
+{
+ RISCV_INFO(r);
+
+ riscv_reg_t tselect;
+ if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = ~0;
+ for (unsigned int i = 0; i < r->trigger_count; i++) {
+ if (r->trigger_unique_id[i] == -1)
+ continue;
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, i) != ERROR_OK)
+ return ERROR_FAIL;
+
+ uint64_t tdata1;
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+ int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
+
+ uint64_t hit_mask = 0;
+ switch (type) {
+ case CSR_TDATA1_TYPE_LEGACY:
+ /* Doesn't support hit bit. */
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL:
+ hit_mask = CSR_MCONTROL_HIT;
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL6:
+ hit_mask = CSR_MCONTROL6_HIT0 | CSR_MCONTROL6_HIT1;
+ break;
+ case CSR_TDATA1_TYPE_ICOUNT:
+ hit_mask = CSR_ICOUNT_HIT;
+ break;
+ case CSR_TDATA1_TYPE_ITRIGGER:
+ hit_mask = CSR_ITRIGGER_HIT(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_ETRIGGER:
+ hit_mask = CSR_ETRIGGER_HIT(riscv_xlen(target));
+ break;
+ default:
+ LOG_TARGET_DEBUG(target, "Trigger %d has unknown type %d", i, type);
+ continue;
+ }
+
+ /* Note: If we ever use chained triggers, then this logic needs
+ * to be changed to ignore triggers that are not the last one in
+ * the chain. */
+ if (tdata1 & hit_mask) {
+ LOG_TARGET_DEBUG(target, "Trigger %d (unique_id=%d) has hit bit set.", i, r->trigger_unique_id[i]);
+ if (riscv_set_register(target, GDB_REGNO_TDATA1, tdata1 & ~hit_mask) != ERROR_OK)
+ return ERROR_FAIL;
+
+ *unique_id = r->trigger_unique_id[i];
+ break;
+ }
+ }
+
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK)
+ return ERROR_FAIL;
+
+ return ERROR_OK;
+}
+
+/**
+ * These functions are needed to extract individual bits (for offset)
+ * from the instruction
+ */
+// c.lwsp rd_n0 c_uimm8sphi c_uimm8splo - offset[5] offset[4:2|7:6]
+static uint16_t get_offset_clwsp(riscv_insn_t instruction)
+{
+ uint16_t offset_4to2and7to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM8SPLO);
+ uint16_t offset_4to2_bits = offset_4to2and7to6_bits >> 2;
+ uint16_t offset_7to6_bits = offset_4to2and7to6_bits & 0x3;
+ uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM8SPHI);
+ return (offset_4to2_bits << 2) + (offset_5_bit << 5)
+ + (offset_7to6_bits << 6);
+}
+
+// c.ldsp rd_n0 c_uimm9sphi c_uimm9splo - offset[5] offset[4:3|8:6]
+static uint16_t get_offset_cldsp(riscv_insn_t instruction)
+{
+ uint16_t offset_4to3and8to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM9SPLO);
+ uint16_t offset_4to3_bits = offset_4to3and8to6_bits >> 3;
+ uint16_t offset_8to6_bits = offset_4to3and8to6_bits & 0x7;
+ uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM9SPHI);
+ return (offset_4to3_bits << 3) + (offset_5_bit << 5)
+ + (offset_8to6_bits << 6);
+}
+
+// c.swsp c_rs2 c_uimm8sp_s - offset[5:2|7:6]
+static uint16_t get_offset_cswsp(riscv_insn_t instruction)
+{
+ uint16_t offset_5to2and7to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM8SP_S);
+ uint16_t offset_5to2_bits = offset_5to2and7to6_bits >> 2;
+ uint16_t offset_7to6_bits = offset_5to2and7to6_bits & 0x3;
+ return (offset_5to2_bits << 2) + (offset_7to6_bits << 6);
+}
+
+// c.sdsp c_rs2 c_uimm9sp_s - offset[5:3|8:6]
+static uint16_t get_offset_csdsp(riscv_insn_t instruction)
+{
+ uint16_t offset_5to3and8to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM9SP_S);
+ uint16_t offset_5to3_bits = offset_5to3and8to6_bits >> 3;
+ uint16_t offset_8to6_bits = offset_5to3and8to6_bits & 0x7;
+ return (offset_5to3_bits << 3) + (offset_8to6_bits << 6);
+}
+
+// c.lw rd_p rs1_p c_uimm7lo c_uimm7hi - offset[2|6] offset[5:3]
+static uint16_t get_offset_clw(riscv_insn_t instruction)
+{
+ uint16_t offset_2and6_bits = get_field32(instruction, INSN_FIELD_C_UIMM7LO);
+ uint16_t offset_2_bit = offset_2and6_bits >> 1;
+ uint16_t offset_6_bit = offset_2and6_bits & 0x1;
+ uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM7HI);
+ return (offset_2_bit << 2) + (offset_5to3_bits << 3) + (offset_6_bit << 6);
+}
+
+// c.ld rd_p rs1_p c_uimm8lo c_uimm8hi - offset[7:6] offset[5:3]
+static uint16_t get_offset_cld(riscv_insn_t instruction)
+{
+ uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM8LO);
+ uint16_t offset_5to3_bits = get_field32(instruction, INSN_FIELD_C_UIMM8HI);
+ return (offset_5to3_bits << 3) + (offset_7to6_bits << 6);
+}
+
+// c.lq rd_p rs1_p c_uimm9lo c_uimm9hi - offset[7:6] offset[5|4|8]
+static uint16_t get_offset_clq(riscv_insn_t instruction)
+{
+ uint16_t offset_7to6_bits = get_field32(instruction, INSN_FIELD_C_UIMM9LO);
+ uint16_t offset_5to4and8_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM9HI);
+ uint16_t offset_5to4_bits = offset_5to4and8_bits >> 1;
+ uint16_t offset_8_bit = offset_5to4and8_bits & 0x1;
+ return (offset_5to4_bits << 4) + (offset_7to6_bits << 6)
+ + (offset_8_bit << 8);
+}
+
+// c.lqsp rd_n0 c_uimm10sphi c_uimm10splo - offset[5] offset[4|9:6]
+static uint16_t get_offset_clqsp(riscv_insn_t instruction)
+{
+ uint16_t offset_4and9to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM10SPLO);
+ uint16_t offset_4_bit = offset_4and9to6_bits >> 4;
+ uint16_t offset_9to6_bits = offset_4and9to6_bits & 0xf;
+ uint16_t offset_5_bit = get_field32(instruction, INSN_FIELD_C_UIMM10SPHI);
+ return (offset_4_bit << 4) + (offset_5_bit << 5) + (offset_9to6_bits << 6);
+}
+
+// c.sqsp c_rs2 c_uimm10sp_s - offset[5:4|9:6]
+static uint16_t get_offset_csqsp(riscv_insn_t instruction)
+{
+ uint16_t offset_5to4and9to6_bits =
+ get_field32(instruction, INSN_FIELD_C_UIMM10SP_S);
+ uint16_t offset_5to4_biits = offset_5to4and9to6_bits >> 4;
+ uint16_t offset_9to6_bits = offset_5to4and9to6_bits & 0xf;
+ return (offset_5to4_biits << 4) + (offset_9to6_bits << 6);
+}
+
+/**
+ * Decode rs1' register num for RVC.
+ * See "Table: Registers specified by the three-bit rs1′, rs2′, and rd′ fields
+ * of the CIW, CL, CS, CA, and CB formats" in "The RISC-V Instruction Set Manual
+ * Volume I: Unprivileged ISA".
+ * */
+static uint32_t get_rs1_c(riscv_insn_t instruction)
+{
+ return GDB_REGNO_S0 + get_field32(instruction, INSN_FIELD_C_SREG1);
+}
+
+static uint32_t get_opcode(const riscv_insn_t instruction)
+{
+ // opcode is first 7 bits of the instruction
+ uint32_t opcode = instruction & INSN_FIELD_OPCODE;
+ if ((instruction & 0x03) < 0x03) { // opcode size RVC
+ // RVC MASK_C = 0xe003 for load/store instructions
+ opcode = instruction & MASK_C_LD;
+ }
+ return opcode;
+}
+
+static int get_loadstore_membase_regno(struct target *target,
+ const riscv_insn_t instruction, int *regid)
+{
+ uint32_t opcode = get_opcode(instruction);
+ int rs;
+
+ switch (opcode) {
+ case MATCH_LB:
+ case MATCH_FLH & ~INSN_FIELD_FUNCT3:
+ case MATCH_SB:
+ case MATCH_FSH & ~INSN_FIELD_FUNCT3:
+ rs = get_field32(instruction, INSN_FIELD_RS1);
+ break;
+
+ case MATCH_C_LWSP:
+ case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP:
+ case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128
+ case MATCH_C_SWSP:
+ case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP:
+ case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128
+ rs = GDB_REGNO_SP;
+ break;
+
+ case MATCH_C_LW:
+ case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64
+ case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128
+ case MATCH_C_SW:
+ case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64
+ case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128
+ rs = get_rs1_c(instruction);
+ break;
+
+ default:
+ LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or"
+ " store", instruction);
+ return ERROR_FAIL;
+ }
+ *regid = rs;
+ return ERROR_OK;
+}
+
+static int get_loadstore_memoffset(struct target *target,
+ const riscv_insn_t instruction, int16_t *memoffset)
+{
+ uint32_t opcode = get_opcode(instruction);
+ int16_t offset;
+
+ switch (opcode) {
+ case MATCH_LB:
+ case MATCH_FLH & ~INSN_FIELD_FUNCT3:
+ case MATCH_SB:
+ case MATCH_FSH & ~INSN_FIELD_FUNCT3:
+ if (opcode == MATCH_SB || opcode == (MATCH_FSH & ~INSN_FIELD_FUNCT3)) {
+ offset = get_field32(instruction, INSN_FIELD_IMM12LO) |
+ (get_field32(instruction, INSN_FIELD_IMM12HI) << 5);
+ } else if (opcode == MATCH_LB ||
+ opcode == (MATCH_FLH & ~INSN_FIELD_FUNCT3)) {
+ offset = get_field32(instruction, INSN_FIELD_IMM12);
+ } else {
+ assert(false);
+ }
+ /* sign extend 12-bit imm to 16-bits */
+ if (offset & (1 << 11))
+ offset |= 0xf000;
+ break;
+
+ case MATCH_C_LWSP:
+ offset = get_offset_clwsp(instruction);
+ break;
+
+ case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP:
+ if (riscv_xlen(target) > 32) { // MATCH_C_LDSP
+ offset = get_offset_cldsp(instruction);
+ } else { // MATCH_C_FLWSP
+ offset = get_offset_clwsp(instruction);
+ }
+ break;
+
+ case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128
+ if (riscv_xlen(target) == 128) { // MATCH_C_LQSP
+ offset = get_offset_clqsp(instruction);
+ } else { // MATCH_C_FLDSP
+ offset = get_offset_cldsp(instruction);
+ }
+ break;
+
+ case MATCH_C_SWSP:
+ offset = get_offset_cswsp(instruction);
+ break;
+
+ case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP:
+ if (riscv_xlen(target) > 32) { // MATCH_C_SDSP
+ offset = get_offset_csdsp(instruction);
+ } else { // MATCH_C_FSWSP
+ offset = get_offset_cswsp(instruction);
+ }
+ break;
+
+ case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128
+ if (riscv_xlen(target) == 128) { // MATCH_C_SQSP
+ offset = get_offset_csqsp(instruction);
+ } else { // MATCH_C_FSDSP
+ offset = get_offset_csdsp(instruction); // same as C.SDSP
+ }
+ break;
+
+ case MATCH_C_LW:
+ offset = get_offset_clw(instruction);
+ break;
+
+ case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64
+ if (riscv_xlen(target) > 32) { // MATCH_C_LD
+ offset = get_offset_cld(instruction);
+ } else { // MATCH_C_FLW
+ offset = get_offset_clw(instruction); // same as C.FLW
+ }
+ break;
+
+ case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128
+ if (riscv_xlen(target) == 128) { // MATCH_C_LQ
+ offset = get_offset_clq(instruction);
+ } else { // MATCH_C_FLD
+ offset = get_offset_cld(instruction); // same as C.LD
+ }
+ break;
+
+ case MATCH_C_SW:
+ offset = get_offset_clw(instruction); // same as C.LW
+ break;
+
+ case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64
+ if (riscv_xlen(target) > 32) { // MATCH_C_SD
+ offset = get_offset_cld(instruction); // same as C.LD
+ } else { // MATCH_C_FSW
+ offset = get_offset_clw(instruction); // same as C.LW
+ }
+ break;
+
+ case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128
+ if (riscv_xlen(target) == 128) { // MATCH_C_SQ
+ offset = get_offset_clq(instruction); // same as C.LQ
+ } else { // MATCH_C_FSD
+ offset = get_offset_cld(instruction); // same as C.LD
+ }
+ break;
+
+ default:
+ LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or"
+ " store", instruction);
+ return ERROR_FAIL;
+ }
+ *memoffset = offset;
+ return ERROR_OK;
+}
+
+static int verify_loadstore(struct target *target,
+ const riscv_insn_t instruction, bool *is_read)
+{
+ uint32_t opcode = get_opcode(instruction);
+ bool misa_f = riscv_supports_extension(target, 'F');
+ bool misa_d = riscv_supports_extension(target, 'D');
+ enum watchpoint_rw rw;
+
+ switch (opcode) {
+ case MATCH_LB:
+ case MATCH_FLH & ~INSN_FIELD_FUNCT3:
+ rw = WPT_READ;
+ break;
+
+ case MATCH_SB:
+ case MATCH_FSH & ~INSN_FIELD_FUNCT3:
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_LWSP:
+ if (get_field32(instruction, INSN_FIELD_RD) == 0) {
+ LOG_TARGET_DEBUG(target,
+ "The code points with rd = x0 are reserved for C.LWSP");
+ return ERROR_FAIL;
+ }
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_LDSP: // if xlen >= 64 or MATCH_C_FLWSP:
+ if (riscv_xlen(target) > 32) { // MATCH_C_LDSP
+ if (get_field32(instruction, INSN_FIELD_RD) == 0) {
+ LOG_TARGET_DEBUG(target,
+ "The code points with rd = x0 are reserved for C.LDSP");
+ return ERROR_FAIL;
+ }
+ } else { // MATCH_C_FLWSP
+ if (!misa_f) {
+ LOG_TARGET_DEBUG(target, "Matched C.FLWSP but target doesn\'t "
+ "have the \"F\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_FLDSP: // or MATCH_C_LQSP if xlen == 128
+ if (riscv_xlen(target) == 128) { // MATCH_C_LQSP
+ if (get_field32(instruction, INSN_FIELD_RD) == 0) {
+ LOG_TARGET_DEBUG(target,
+ "The code points with rd = x0 are reserved for C.LQSP");
+ return ERROR_FAIL;
+ }
+ } else { // MATCH_C_FLDSP
+ if (!misa_d) {
+ LOG_TARGET_DEBUG(target, "Matched C.FLDSP but target doesn\'t "
+ "have the \"D\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_SWSP:
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_SDSP: // if xlen >= 64 or MATCH_C_FSWSP:
+ if (riscv_xlen(target) == 32) { // MATCH_C_FSWSP
+ if (!misa_f) {
+ LOG_TARGET_DEBUG(target, "Matched C.FSWSP but target doesn\'t "
+ "have the \"F\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_FSDSP: // or MATCH_C_SQSP if xlen == 128
+ if (riscv_xlen(target) != 128) { // MATCH_C_SQSP
+ if (!misa_d) {
+ LOG_TARGET_DEBUG(target, "Matched C.FSDSP but target doesn\'t "
+ "have the \"D\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_LW:
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_FLW: // or MATCH_C_LD if xlen >= 64
+ if (riscv_xlen(target) == 32) { // MATCH_C_FLW
+ if (!misa_f) {
+ LOG_TARGET_DEBUG(target, "Matched C.FLW but target doesn\'t "
+ "have the \"F\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_FLD: // or MATCH_C_LQ if xlen == 128
+ if (riscv_xlen(target) != 128) { // MATCH_C_FLD
+ if (!misa_d) {
+ LOG_TARGET_DEBUG(target, "Matched C.FLD but target doesn\'t "
+ "have the \"D\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_READ;
+ break;
+
+ case MATCH_C_SW:
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_FSW: // or MATCH_C_SD if xlen >= 64
+ if (riscv_xlen(target) == 32) { // MATCH_C_FSW
+ if (!misa_f) {
+ LOG_TARGET_DEBUG(target, "Matched C.FSW but target doesn\'t "
+ "have the \"F\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_WRITE;
+ break;
+
+ case MATCH_C_FSD: // or MATCH_C_SQ if xlen == 128
+ if (riscv_xlen(target) != 128) { // MATCH_C_FSD
+ if (!misa_d) {
+ LOG_TARGET_DEBUG(target, "Matched C.FSD but target doesn\'t "
+ "have the \"D\" extension");
+ return ERROR_FAIL;
+ }
+ }
+ rw = WPT_WRITE;
+ break;
+
+ default:
+ LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is not a RV32I or \"C\" load or"
+ " store", instruction);
+ return ERROR_FAIL;
+ }
+
+ if (rw == WPT_WRITE) {
+ *is_read = false;
+ LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is store instruction",
+ instruction);
+ } else {
+ *is_read = true;
+ LOG_TARGET_DEBUG(target, "0x%" PRIx32 " is load instruction",
+ instruction);
+ }
+ return ERROR_OK;
+}
+
/* Sets *hit_watchpoint to the first watchpoint identified as causing the
* current halt.
*
@@ -1022,67 +2059,74 @@ int riscv_remove_watchpoint(struct target *target,
* and new value. */
static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoint)
{
- struct watchpoint *wp = target->watchpoints;
+ RISCV_INFO(r);
+
+ LOG_TARGET_DEBUG(target, "Hit Watchpoint");
- LOG_DEBUG("Current hartid = %d", riscv_current_hartid(target));
+ /* If we identified which trigger caused the halt earlier, then just use
+ * that. */
+ for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) {
+ if (wp->unique_id == r->trigger_hit) {
+ *hit_watchpoint = wp;
+ return ERROR_OK;
+ }
+ }
- /*TODO instead of disassembling the instruction that we think caused the
- * trigger, check the hit bit of each watchpoint first. The hit bit is
- * simpler and more reliable to check but as it is optional and relatively
- * new, not all hardware will implement it */
riscv_reg_t dpc;
- riscv_get_register(target, &dpc, GDB_REGNO_DPC);
+ if (riscv_get_register(target, &dpc, GDB_REGNO_DPC) != ERROR_OK)
+ return ERROR_FAIL;
const uint8_t length = 4;
- LOG_DEBUG("dpc is 0x%" PRIx64, dpc);
+ LOG_TARGET_DEBUG(target, "dpc is 0x%" PRIx64, dpc);
/* fetch the instruction at dpc */
uint8_t buffer[length];
if (target_read_buffer(target, dpc, length, buffer) != ERROR_OK) {
- LOG_ERROR("Failed to read instruction at dpc 0x%" PRIx64, dpc);
+ LOG_TARGET_ERROR(target, "Failed to read instruction at dpc 0x%" PRIx64,
+ dpc);
return ERROR_FAIL;
}
- uint32_t instruction = 0;
+ riscv_insn_t instruction = 0;
for (int i = 0; i < length; i++) {
- LOG_DEBUG("Next byte is %x", buffer[i]);
+ LOG_TARGET_DEBUG(target, "Next byte is %x", buffer[i]);
instruction += (buffer[i] << 8 * i);
}
- LOG_DEBUG("Full instruction is %x", instruction);
+ LOG_TARGET_DEBUG(target, "Full instruction is %x", instruction);
- /* find out which memory address is accessed by the instruction at dpc */
- /* opcode is first 7 bits of the instruction */
- uint8_t opcode = instruction & 0x7F;
- uint32_t rs1;
- int16_t imm;
- riscv_reg_t mem_addr;
+ int rs;
+ target_addr_t mem_addr;
+ int16_t memoffset;
+
+ if (get_loadstore_membase_regno(target, instruction, &rs) != ERROR_OK)
+ return ERROR_FAIL;
+ if (riscv_get_register(target, &mem_addr, rs) != ERROR_OK)
+ return ERROR_FAIL;
+ if (get_loadstore_memoffset(target, instruction, &memoffset) != ERROR_OK)
+ return ERROR_FAIL;
- if (opcode == MATCH_LB || opcode == MATCH_SB) {
- rs1 = (instruction & 0xf8000) >> 15;
- riscv_get_register(target, &mem_addr, rs1);
+ mem_addr += memoffset;
+ bool is_load;
- if (opcode == MATCH_SB) {
- LOG_DEBUG("%x is store instruction", instruction);
- imm = ((instruction & 0xf80) >> 7) | ((instruction & 0xfe000000) >> 20);
- } else {
- LOG_DEBUG("%x is load instruction", instruction);
- imm = (instruction & 0xfff00000) >> 20;
- }
- /* sign extend 12-bit imm to 16-bits */
- if (imm & (1 << 11))
- imm |= 0xf000;
- mem_addr += imm;
- LOG_DEBUG("memory address=0x%" PRIx64, mem_addr);
- } else {
- LOG_DEBUG("%x is not a RV32I load or store", instruction);
+ if (verify_loadstore(target, instruction, &is_load) != ERROR_OK)
return ERROR_FAIL;
- }
+ struct watchpoint *wp = target->watchpoints;
while (wp) {
- /*TODO support length/mask */
- if (wp->address == mem_addr) {
+ /* TODO support mask and check read/write/access */
+ /* TODO check for intersection of the access range and watchpoint range
+ Recommended matching:
+ if (intersects(mem_addr, mem_addr + ref_size, wp->address, wp->address + wp->length))
+ */
+ if (mem_addr >= wp->address &&
+ mem_addr < (wp->address + wp->length)) {
*hit_watchpoint = wp;
- LOG_DEBUG("Hit address=%" TARGET_PRIxADDR, wp->address);
+ LOG_TARGET_DEBUG(target, "WP hit found: %s 0x%" TARGET_PRIxADDR
+ " covered by %s wp at address 0x%" TARGET_PRIxADDR,
+ is_load ? "Load from" : "Store to", mem_addr,
+ (wp->rw == WPT_READ ?
+ "read" : (wp->rw == WPT_WRITE ? "write" : "access")),
+ wp->address);
return ERROR_OK;
}
wp = wp->next;
@@ -1093,87 +2137,186 @@ static int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_w
*
* OpenOCD will behave as if this function had never been implemented i.e.
* report the halt to GDB with no address information. */
+ LOG_TARGET_DEBUG(target, "No watchpoint found that would cover %s 0x%"
+ TARGET_PRIxADDR, is_load ? "load from" : "store to", mem_addr);
return ERROR_FAIL;
}
-
static int oldriscv_step(struct target *target, int current, uint32_t address,
int handle_breakpoints)
{
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
return tt->step(target, current, address, handle_breakpoints);
}
-static int old_or_new_riscv_step(struct target *target, int current,
- target_addr_t address, int handle_breakpoints)
+static int riscv_openocd_step_impl(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int handle_callbacks);
+
+static int old_or_new_riscv_step_impl(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int handle_callbacks)
{
RISCV_INFO(r);
- LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
- if (!r->is_halted)
+ LOG_TARGET_DEBUG(target, "handle_breakpoints=%d", handle_breakpoints);
+ if (!r->get_hart_state)
return oldriscv_step(target, current, address, handle_breakpoints);
else
- return riscv_openocd_step(target, current, address, handle_breakpoints);
+ return riscv_openocd_step_impl(target, current, address, handle_breakpoints,
+ handle_callbacks);
}
+static int old_or_new_riscv_step(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints)
+{
+ return old_or_new_riscv_step_impl(target, current, address,
+ handle_breakpoints, true /* handle callbacks*/);
+}
static int riscv_examine(struct target *target)
{
- LOG_DEBUG("riscv_examine()");
+ LOG_TARGET_DEBUG(target, "Starting examination");
if (target_was_examined(target)) {
- LOG_DEBUG("Target was already examined.");
+ LOG_TARGET_DEBUG(target, "Target was already examined.");
return ERROR_OK;
}
/* Don't need to select dbus, since the first thing we do is read dtmcontrol. */
RISCV_INFO(info);
- uint32_t dtmcontrol = dtmcontrol_scan(target, 0);
- LOG_DEBUG("dtmcontrol=0x%x", dtmcontrol);
+ uint32_t dtmcontrol;
+ if (dtmcontrol_scan(target, 0, &dtmcontrol) != ERROR_OK || dtmcontrol == 0) {
+ LOG_TARGET_ERROR(target, "Could not read dtmcontrol. Check JTAG connectivity/board power.");
+ return ERROR_FAIL;
+ }
+ LOG_TARGET_DEBUG(target, "dtmcontrol=0x%x", dtmcontrol);
info->dtm_version = get_field(dtmcontrol, DTMCONTROL_VERSION);
- LOG_DEBUG(" version=0x%x", info->dtm_version);
+ LOG_TARGET_DEBUG(target, "version=0x%x", info->dtm_version);
+ int examine_status = ERROR_FAIL;
struct target_type *tt = get_target_type(target);
if (!tt)
- return ERROR_FAIL;
+ goto examine_fail;
- int result = tt->init_target(info->cmd_ctx, target);
- if (result != ERROR_OK)
- return result;
+ examine_status = tt->init_target(info->cmd_ctx, target);
+ if (examine_status != ERROR_OK)
+ goto examine_fail;
- return tt->examine(target);
+ examine_status = tt->examine(target);
+ if (examine_status != ERROR_OK)
+ goto examine_fail;
+
+ return ERROR_OK;
+
+examine_fail:
+ info->dtm_version = DTM_DTMCS_VERSION_UNKNOWN;
+ return examine_status;
}
static int oldriscv_poll(struct target *target)
{
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
return tt->poll(target);
}
static int old_or_new_riscv_poll(struct target *target)
{
RISCV_INFO(r);
- if (!r->is_halted)
+ if (!r->get_hart_state)
return oldriscv_poll(target);
else
return riscv_openocd_poll(target);
}
-int riscv_select_current_hart(struct target *target)
+static struct reg *get_reg_cache_entry(const struct target *target,
+ uint32_t number)
{
- return riscv_set_current_hartid(target, target->coreid);
+ assert(target->reg_cache);
+ assert(target->reg_cache->reg_list);
+ assert(number < target->reg_cache->num_regs);
+ return &target->reg_cache->reg_list[number];
+}
+
+int riscv_flush_registers(struct target *target)
+{
+ RISCV_INFO(r);
+
+ if (!target->reg_cache)
+ return ERROR_OK;
+
+ LOG_TARGET_DEBUG(target, "Flushing register cache");
+
+ /* Writing non-GPR registers may require progbuf execution, and some GPRs
+ * may become dirty in the process (e.g. S0, S1). For that reason, flush
+ * registers in reverse order, so that GPRs are flushed last.
+ */
+ for (unsigned int number = target->reg_cache->num_regs; number-- > 0; ) {
+ struct reg *reg = get_reg_cache_entry(target, number);
+ if (reg->valid && reg->dirty) {
+ riscv_reg_t value = buf_get_u64(reg->value, 0, reg->size);
+
+ LOG_TARGET_DEBUG(target, "%s is dirty; write back 0x%" PRIx64,
+ reg->name, value);
+ if (r->set_register(target, number, value) != ERROR_OK)
+ return ERROR_FAIL;
+ reg->dirty = false;
+ }
+ }
+ LOG_TARGET_DEBUG(target, "Flush of register cache completed");
+ return ERROR_OK;
+}
+
+/**
+ * Set OpenOCD's generic debug reason from the RISC-V halt reason.
+ */
+static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
+{
+ RISCV_INFO(r);
+ r->trigger_hit = -1;
+ switch (halt_reason) {
+ case RISCV_HALT_EBREAK:
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case RISCV_HALT_TRIGGER:
+ if (riscv_hit_trigger_hit_bit(target, &r->trigger_hit) != ERROR_OK)
+ return ERROR_FAIL;
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ /* Check if we hit a hardware breakpoint. */
+ for (struct breakpoint *bp = target->breakpoints; bp; bp = bp->next) {
+ if (bp->unique_id == r->trigger_hit)
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ }
+ break;
+ case RISCV_HALT_INTERRUPT:
+ case RISCV_HALT_GROUP:
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case RISCV_HALT_SINGLESTEP:
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ break;
+ case RISCV_HALT_UNKNOWN:
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ break;
+ case RISCV_HALT_ERROR:
+ return ERROR_FAIL;
+ }
+ LOG_TARGET_DEBUG(target, "debug_reason=%d", target->debug_reason);
+
+ return ERROR_OK;
}
static int halt_prep(struct target *target)
{
RISCV_INFO(r);
- LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target),
- target->debug_reason);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- LOG_DEBUG("[%s] Hart is already halted (reason=%d).",
- target_name(target), target->debug_reason);
+ LOG_TARGET_DEBUG(target, "prep hart, debug_reason=%d", target->debug_reason);
+ r->prepped = false;
+ if (target->state == TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target, "Hart is already halted.");
+ } else if (target->state == TARGET_UNAVAILABLE) {
+ LOG_TARGET_DEBUG(target, "Hart is unavailable.");
} else {
if (r->halt_prep(target) != ERROR_OK)
return ERROR_FAIL;
@@ -1187,16 +2330,23 @@ static int riscv_halt_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
+ enum riscv_hart_state state;
+ if (riscv_get_hart_state(target, &state) != ERROR_OK)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- LOG_DEBUG("[%s] Hart is already halted.", target_name(target));
+ if (state == RISCV_STATE_HALTED) {
+ LOG_TARGET_DEBUG(target, "Hart is already halted.");
+ if (target->state != TARGET_HALTED) {
+ target->state = TARGET_HALTED;
+ enum riscv_halt_reason halt_reason = riscv_halt_reason(target);
+ if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ return ERROR_FAIL;
+ }
} else {
if (r->halt_go(target) != ERROR_OK)
return ERROR_FAIL;
- }
- riscv_invalidate_register_cache(target);
+ riscv_invalidate_register_cache(target);
+ }
return ERROR_OK;
}
@@ -1205,13 +2355,14 @@ static int halt_go(struct target *target)
{
RISCV_INFO(r);
int result;
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
result = tt->halt(target);
} else {
result = riscv_halt_go_all_harts(target);
}
- target->state = TARGET_HALTED;
if (target->debug_reason == DBG_REASON_NOTHALTED)
target->debug_reason = DBG_REASON_DBGRQ;
@@ -1227,12 +2378,14 @@ int riscv_halt(struct target *target)
{
RISCV_INFO(r);
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
return tt->halt(target);
}
- LOG_DEBUG("[%d] halting all harts", target->coreid);
+ LOG_TARGET_DEBUG(target, "halting all harts");
int result = ERROR_OK;
if (target->smp) {
@@ -1272,38 +2425,21 @@ int riscv_halt(struct target *target)
static int riscv_assert_reset(struct target *target)
{
- LOG_DEBUG("[%d]", target->coreid);
+ LOG_TARGET_DEBUG(target, "");
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
riscv_invalidate_register_cache(target);
return tt->assert_reset(target);
}
static int riscv_deassert_reset(struct target *target)
{
- LOG_DEBUG("[%d]", target->coreid);
+ LOG_TARGET_DEBUG(target, "");
struct target_type *tt = get_target_type(target);
- return tt->deassert_reset(target);
-}
-
-static int riscv_resume_prep_all_harts(struct target *target)
-{
- RISCV_INFO(r);
-
- LOG_DEBUG("[%s] prep hart", target_name(target));
- if (riscv_select_current_hart(target) != ERROR_OK)
+ if (!tt)
return ERROR_FAIL;
- if (riscv_is_halted(target)) {
- if (r->resume_prep(target) != ERROR_OK)
- return ERROR_FAIL;
- } else {
- LOG_DEBUG("[%s] hart requested resume, but was already resumed",
- target_name(target));
- }
-
- LOG_DEBUG("[%s] mark as prepped", target_name(target));
- r->prepped = true;
-
- return ERROR_OK;
+ return tt->deassert_reset(target);
}
/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */
@@ -1311,7 +2447,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
{
RISCV_INFO(r);
- LOG_DEBUG("deal with triggers");
+ LOG_TARGET_DEBUG(target, "Disabling triggers.");
if (riscv_enumerate_triggers(target) != ERROR_OK)
return ERROR_FAIL;
@@ -1327,7 +2463,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
riscv_reg_t tdata1;
if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
return ERROR_FAIL;
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) {
+ if (tdata1 & CSR_TDATA1_DMODE(riscv_xlen(target))) {
state[t] = tdata1;
if (riscv_set_register(target, GDB_REGNO_TDATA1, 0) != ERROR_OK)
return ERROR_FAIL;
@@ -1341,7 +2477,7 @@ static int disable_triggers(struct target *target, riscv_reg_t *state)
struct watchpoint *watchpoint = target->watchpoints;
int i = 0;
while (watchpoint) {
- LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set);
+ LOG_TARGET_DEBUG(target, "Watchpoint %d: set=%d", i, watchpoint->is_set);
state[i] = watchpoint->is_set;
if (watchpoint->is_set) {
if (riscv_remove_watchpoint(target, watchpoint) != ERROR_OK)
@@ -1379,7 +2515,7 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
struct watchpoint *watchpoint = target->watchpoints;
int i = 0;
while (watchpoint) {
- LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]);
+ LOG_TARGET_DEBUG(target, "Watchpoint %d: cleared=%" PRId64, i, state[i]);
if (state[i]) {
if (riscv_add_watchpoint(target, watchpoint) != ERROR_OK)
return ERROR_FAIL;
@@ -1398,33 +2534,27 @@ static int enable_triggers(struct target *target, riscv_reg_t *state)
static int resume_prep(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
RISCV_INFO(r);
- LOG_DEBUG("[%d]", target->coreid);
- if (!current)
- riscv_set_register(target, GDB_REGNO_PC, address);
-
- if (target->debug_reason == DBG_REASON_WATCHPOINT) {
- /* To be able to run off a trigger, disable all the triggers, step, and
- * then resume as usual. */
- riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
-
- if (disable_triggers(target, trigger_state) != ERROR_OK)
- return ERROR_FAIL;
-
- if (old_or_new_riscv_step(target, true, 0, false) != ERROR_OK)
- return ERROR_FAIL;
+ if (!current && riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ return ERROR_FAIL;
- if (enable_triggers(target, trigger_state) != ERROR_OK)
+ if (handle_breakpoints) {
+ /* To be able to run off a trigger, we perform a step operation and then
+ * resume. If handle_breakpoints is true then step temporarily disables
+ * pending breakpoints so we can safely perform the step. */
+ if (old_or_new_riscv_step_impl(target, current, address, handle_breakpoints,
+ false /* callbacks are not called */) != ERROR_OK)
return ERROR_FAIL;
}
- if (r->is_halted) {
- if (riscv_resume_prep_all_harts(target) != ERROR_OK)
+ if (r->get_hart_state) {
+ if (r->resume_prep(target) != ERROR_OK)
return ERROR_FAIL;
}
- LOG_DEBUG("[%d] mark as prepped", target->coreid);
+ LOG_TARGET_DEBUG(target, "Mark as prepped.");
r->prepped = true;
return ERROR_OK;
@@ -1437,10 +2567,13 @@ static int resume_prep(struct target *target, int current,
static int resume_go(struct target *target, int current,
target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
RISCV_INFO(r);
int result;
- if (!r->is_halted) {
+ if (!r->get_hart_state) {
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
result = tt->resume(target, current, address, handle_breakpoints,
debug_execution);
} else {
@@ -1450,13 +2583,15 @@ static int resume_go(struct target *target, int current,
return result;
}
-static int resume_finish(struct target *target)
+static int resume_finish(struct target *target, int debug_execution)
{
+ assert(target->state == TARGET_HALTED);
register_cache_invalidate(target->reg_cache);
- target->state = TARGET_RUNNING;
+ target->state = debug_execution ? TARGET_DEBUG_RUNNING : TARGET_RUNNING;
target->debug_reason = DBG_REASON_NOTHALTED;
- return target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
+ return target_call_event_callbacks(target,
+ debug_execution ? TARGET_EVENT_DEBUG_RESUMED : TARGET_EVENT_RESUMED);
}
/**
@@ -1471,159 +2606,230 @@ static int riscv_resume(
int debug_execution,
bool single_hart)
{
- LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints);
int result = ERROR_OK;
- if (target->smp && !single_hart) {
- struct target_list *tlist;
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- if (resume_prep(t, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- }
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- struct riscv_info *i = riscv_info(t);
- if (i->prepped) {
- if (resume_go(t, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- }
- }
+ struct list_head *targets;
- foreach_smp_target_direction(resume_order == RO_NORMAL,
- tlist, target->smp_targets) {
- struct target *t = tlist->target;
- if (resume_finish(t) != ERROR_OK)
- return ERROR_FAIL;
- }
+ LIST_HEAD(single_target_list);
+ struct target_list single_target_entry = {
+ .lh = {NULL, NULL},
+ .target = target
+ };
+ if (target->smp && !single_hart) {
+ targets = target->smp_targets;
} else {
- if (resume_prep(target, current, address, handle_breakpoints,
- debug_execution) != ERROR_OK)
- result = ERROR_FAIL;
- if (resume_go(target, current, address, handle_breakpoints,
+ /* Make a list that just contains a single target, so we can
+ * share code below. */
+ list_add(&single_target_entry.lh, &single_target_list);
+ targets = &single_target_list;
+ }
+
+ LOG_TARGET_DEBUG(target, "current=%s, address=0x%"
+ TARGET_PRIxADDR ", handle_breakpoints=%s, debug_exec=%s",
+ current ? "true" : "false",
+ address,
+ handle_breakpoints ? "true" : "false",
+ debug_execution ? "true" : "false");
+
+ struct target_list *tlist;
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ LOG_TARGET_DEBUG(t, "target->state=%s", target_state_name(t));
+ if (t->state != TARGET_HALTED)
+ LOG_TARGET_DEBUG(t, "skipping this target: target not halted");
+ else if (resume_prep(t, current, address, handle_breakpoints,
debug_execution) != ERROR_OK)
result = ERROR_FAIL;
- if (resume_finish(target) != ERROR_OK)
- return ERROR_FAIL;
+ }
+
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ struct riscv_info *i = riscv_info(t);
+ if (i->prepped) {
+ if (resume_go(t, current, address, handle_breakpoints,
+ debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
+ }
+
+ foreach_smp_target_direction(resume_order == RO_NORMAL, tlist, targets) {
+ struct target *t = tlist->target;
+ if (t->state == TARGET_HALTED) {
+ if (resume_finish(t, debug_execution) != ERROR_OK)
+ result = ERROR_FAIL;
+ }
}
return result;
}
-static int riscv_target_resume(struct target *target, int current, target_addr_t address,
- int handle_breakpoints, int debug_execution)
+static int riscv_target_resume(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int debug_execution)
{
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Not halted.");
+ return ERROR_TARGET_NOT_HALTED;
+ }
return riscv_resume(target, current, address, handle_breakpoints,
debug_execution, false);
}
+static int riscv_effective_privilege_mode(struct target *target, int *v_mode, int *effective_mode)
+{
+ riscv_reg_t priv;
+ if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read priv register.");
+ return ERROR_FAIL;
+ }
+ *v_mode = get_field(priv, VIRT_PRIV_V);
+
+ riscv_reg_t mstatus;
+ if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read mstatus register.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(mstatus, MSTATUS_MPRV))
+ *effective_mode = get_field(mstatus, MSTATUS_MPP);
+ else
+ *effective_mode = get_field(priv, VIRT_PRIV_PRV);
+
+ LOG_TARGET_DEBUG(target, "Effective mode=%d; v=%d", *effective_mode, *v_mode);
+
+ return ERROR_OK;
+}
+
static int riscv_mmu(struct target *target, int *enabled)
{
- if (!riscv_enable_virt2phys) {
- *enabled = 0;
+ *enabled = 0;
+
+ if (!riscv_enable_virt2phys)
return ERROR_OK;
- }
/* Don't use MMU in explicit or effective M (machine) mode */
riscv_reg_t priv;
if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
- LOG_ERROR("Failed to read priv register.");
+ LOG_TARGET_ERROR(target, "Failed to read priv register.");
return ERROR_FAIL;
}
- riscv_reg_t mstatus;
- if (riscv_get_register(target, &mstatus, GDB_REGNO_MSTATUS) != ERROR_OK) {
- LOG_ERROR("Failed to read mstatus register.");
+ int effective_mode;
+ int v_mode;
+ if (riscv_effective_privilege_mode(target, &v_mode, &effective_mode) != ERROR_OK)
return ERROR_FAIL;
+
+ unsigned int xlen = riscv_xlen(target);
+
+ if (v_mode) {
+ /* vsatp and hgatp registers are considered active for the
+ * purposes of the address-translation algorithm unless the
+ * effective privilege mode is U and hstatus.HU=0. */
+ if (effective_mode == PRV_U) {
+ riscv_reg_t hstatus;
+ if (riscv_get_register(target, &hstatus, GDB_REGNO_HSTATUS) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read hstatus register.");
+ return ERROR_FAIL;
+ }
+
+ if (get_field(hstatus, HSTATUS_HU) == 0)
+ /* In hypervisor mode regular satp translation
+ * doesn't happen. */
+ return ERROR_OK;
+
+ }
+
+ riscv_reg_t vsatp;
+ if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read vsatp register; priv=0x%" PRIx64,
+ priv);
+ return ERROR_FAIL;
+ }
+ /* vsatp is identical to satp, so we can use the satp macros. */
+ if (RISCV_SATP_MODE(xlen) != SATP_MODE_OFF) {
+ LOG_TARGET_DEBUG(target, "VS-stage translation is enabled.");
+ *enabled = 1;
+ return ERROR_OK;
+ }
+
+ riscv_reg_t hgatp;
+ if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read hgatp register; priv=0x%" PRIx64,
+ priv);
+ return ERROR_FAIL;
+ }
+ if (RISCV_HGATP_MODE(xlen) != HGATP_MODE_OFF) {
+ LOG_TARGET_DEBUG(target, "G-stage address translation is enabled.");
+ *enabled = 1;
+ } else {
+ LOG_TARGET_DEBUG(target, "No V-mode address translation enabled.");
+ }
+
+ return ERROR_OK;
}
- if ((get_field(mstatus, MSTATUS_MPRV) ? get_field(mstatus, MSTATUS_MPP) : priv) == PRV_M) {
- LOG_DEBUG("SATP/MMU ignored in Machine mode (mstatus=0x%" PRIx64 ").", mstatus);
- *enabled = 0;
+ /* Don't use MMU in explicit or effective M (machine) mode */
+ if (effective_mode == PRV_M) {
+ LOG_TARGET_DEBUG(target, "SATP/MMU ignored in Machine mode.");
return ERROR_OK;
}
riscv_reg_t satp;
if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) {
- LOG_DEBUG("Couldn't read SATP.");
+ LOG_TARGET_DEBUG(target, "Couldn't read SATP.");
/* If we can't read SATP, then there must not be an MMU. */
- *enabled = 0;
return ERROR_OK;
}
- if (get_field(satp, RISCV_SATP_MODE(riscv_xlen(target))) == SATP_MODE_OFF) {
- LOG_DEBUG("MMU is disabled.");
- *enabled = 0;
+ if (get_field(satp, RISCV_SATP_MODE(xlen)) == SATP_MODE_OFF) {
+ LOG_TARGET_DEBUG(target, "MMU is disabled.");
} else {
- LOG_DEBUG("MMU is enabled.");
+ LOG_TARGET_DEBUG(target, "MMU is enabled.");
*enabled = 1;
}
return ERROR_OK;
}
+/* Translate address from virtual to physical, using info and ppn.
+ * If extra_info is non-NULL, then translate page table accesses for the primary
+ * translation using extra_info and extra_ppn. */
static int riscv_address_translate(struct target *target,
+ const virt2phys_info_t *info, target_addr_t ppn,
+ const virt2phys_info_t *extra_info, target_addr_t extra_ppn,
target_addr_t virtual, target_addr_t *physical)
{
RISCV_INFO(r);
- riscv_reg_t satp_value;
- int mode;
- uint64_t ppn_value;
- target_addr_t table_address;
- const virt2phys_info_t *info;
- uint64_t pte = 0;
- int i;
+ unsigned int xlen = riscv_xlen(target);
- int result = riscv_get_register(target, &satp_value, GDB_REGNO_SATP);
- if (result != ERROR_OK)
- return result;
-
- unsigned xlen = riscv_xlen(target);
- mode = get_field(satp_value, RISCV_SATP_MODE(xlen));
- switch (mode) {
- case SATP_MODE_SV32:
- info = &sv32;
- break;
- case SATP_MODE_SV39:
- info = &sv39;
- break;
- case SATP_MODE_SV48:
- info = &sv48;
- break;
- case SATP_MODE_OFF:
- LOG_ERROR("No translation or protection." \
- " (satp: 0x%" PRIx64 ")", satp_value);
- return ERROR_FAIL;
- default:
- LOG_ERROR("The translation mode is not supported." \
- " (satp: 0x%" PRIx64 ")", satp_value);
- return ERROR_FAIL;
- }
- LOG_DEBUG("virtual=0x%" TARGET_PRIxADDR "; mode=%s", virtual, info->name);
+ LOG_TARGET_DEBUG(target, "mode=%s; ppn=0x%" TARGET_PRIxADDR "; virtual=0x%" TARGET_PRIxADDR,
+ info->name, ppn, virtual);
/* verify bits xlen-1:va_bits-1 are all equal */
assert(xlen >= info->va_bits);
target_addr_t mask = ((target_addr_t)1 << (xlen - (info->va_bits - 1))) - 1;
target_addr_t masked_msbs = (virtual >> (info->va_bits - 1)) & mask;
if (masked_msbs != 0 && masked_msbs != mask) {
- LOG_ERROR("Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended "
+ LOG_TARGET_ERROR(target, "Virtual address 0x%" TARGET_PRIxADDR " is not sign-extended "
"for %s mode.", virtual, info->name);
return ERROR_FAIL;
}
- ppn_value = get_field(satp_value, RISCV_SATP_PPN(xlen));
- table_address = ppn_value << RISCV_PGSHIFT;
- i = info->level - 1;
+ uint64_t pte = 0;
+ target_addr_t table_address = ppn << RISCV_PGSHIFT;
+ int i = info->level - 1;
while (i >= 0) {
uint64_t vpn = virtual >> info->vpn_shift[i];
vpn &= info->vpn_mask[i];
- target_addr_t pte_address = table_address +
- (vpn << info->pte_shift);
+ target_addr_t pte_address = table_address + (vpn << info->pte_shift);
+
+ if (extra_info) {
+ /* Perform extra stage translation. */
+ if (riscv_address_translate(target, extra_info, extra_ppn,
+ NULL, 0, pte_address, &pte_address) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
uint8_t buffer[8];
assert(info->pte_shift <= 3);
int retval = r->read_memory(target, pte_address,
@@ -1636,24 +2842,27 @@ static int riscv_address_translate(struct target *target,
else
pte = buf_get_u64(buffer, 0, 64);
- LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i,
+ LOG_TARGET_DEBUG(target, "i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i,
pte_address, pte);
- if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W)))
+ if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+ LOG_TARGET_ERROR(target, "invalid PTE @0x%" TARGET_PRIxADDR ": 0x%" PRIx64
+ "; mode=%s; i=%d", pte_address, pte, info->name, i);
return ERROR_FAIL;
+ }
- if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */
+ if ((pte & PTE_R) || (pte & PTE_W) || (pte & PTE_X)) /* Found leaf PTE. */
break;
i--;
if (i < 0)
break;
- ppn_value = pte >> PTE_PPN_SHIFT;
- table_address = ppn_value << RISCV_PGSHIFT;
+ ppn = pte >> PTE_PPN_SHIFT;
+ table_address = ppn << RISCV_PGSHIFT;
}
if (i < 0) {
- LOG_ERROR("Couldn't find the PTE.");
+ LOG_TARGET_ERROR(target, "Couldn't find the PTE.");
return ERROR_FAIL;
}
@@ -1661,15 +2870,125 @@ static int riscv_address_translate(struct target *target,
*physical = virtual & (((target_addr_t)1 << info->va_bits) - 1);
while (i < info->level) {
- ppn_value = pte >> info->pte_ppn_shift[i];
- ppn_value &= info->pte_ppn_mask[i];
+ ppn = pte >> info->pte_ppn_shift[i];
+ ppn &= info->pte_ppn_mask[i];
*physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) <<
info->pa_ppn_shift[i]);
- *physical |= (ppn_value << info->pa_ppn_shift[i]);
+ *physical |= (ppn << info->pa_ppn_shift[i]);
i++;
}
- LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual,
- *physical);
+ LOG_TARGET_DEBUG(target, "mode=%s; 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR,
+ info->name, virtual, *physical);
+ return ERROR_OK;
+}
+
+/* Virtual to physical translation for hypervisor mode. */
+static int riscv_virt2phys_v(struct target *target, target_addr_t virtual, target_addr_t *physical)
+{
+ riscv_reg_t vsatp;
+ if (riscv_get_register(target, &vsatp, GDB_REGNO_VSATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read vsatp register.");
+ return ERROR_FAIL;
+ }
+ /* vsatp is identical to satp, so we can use the satp macros. */
+ unsigned int xlen = riscv_xlen(target);
+ int vsatp_mode = get_field(vsatp, RISCV_SATP_MODE(xlen));
+ LOG_TARGET_DEBUG(target, "VS-stage translation mode: %d", vsatp_mode);
+ riscv_reg_t hgatp;
+ if (riscv_get_register(target, &hgatp, GDB_REGNO_HGATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read hgatp register.");
+ return ERROR_FAIL;
+ }
+ int hgatp_mode = get_field(hgatp, RISCV_HGATP_MODE(xlen));
+ LOG_TARGET_DEBUG(target, "G-stage translation mode: %d", hgatp_mode);
+
+ const virt2phys_info_t *vsatp_info;
+ /* VS-stage address translation. */
+ switch (vsatp_mode) {
+ case SATP_MODE_SV32:
+ vsatp_info = &sv32;
+ break;
+ case SATP_MODE_SV39:
+ vsatp_info = &sv39;
+ break;
+ case SATP_MODE_SV48:
+ vsatp_info = &sv48;
+ break;
+ case SATP_MODE_SV57:
+ vsatp_info = &sv57;
+ break;
+ case SATP_MODE_OFF:
+ vsatp_info = NULL;
+ LOG_TARGET_DEBUG(target, "vsatp mode is %d. No VS-stage translation. (vsatp: 0x%" PRIx64 ")",
+ vsatp_mode, vsatp);
+ break;
+ default:
+ LOG_TARGET_ERROR(target,
+ "vsatp mode %d is not supported. (vsatp: 0x%" PRIx64 ")",
+ vsatp_mode, vsatp);
+ return ERROR_FAIL;
+ }
+
+ const virt2phys_info_t *hgatp_info;
+ /* G-stage address translation. */
+ switch (hgatp_mode) {
+ case HGATP_MODE_SV32X4:
+ hgatp_info = &sv32x4;
+ break;
+ case HGATP_MODE_SV39X4:
+ hgatp_info = &sv39x4;
+ break;
+ case HGATP_MODE_SV48X4:
+ hgatp_info = &sv48x4;
+ break;
+ case HGATP_MODE_SV57X4:
+ hgatp_info = &sv57x4;
+ break;
+ case HGATP_MODE_OFF:
+ hgatp_info = NULL;
+ LOG_TARGET_DEBUG(target, "hgatp mode is %d. No G-stage translation. (hgatp: 0x%" PRIx64 ")",
+ hgatp_mode, hgatp);
+ break;
+ default:
+ LOG_TARGET_ERROR(target,
+ "hgatp mode %d is not supported. (hgatp: 0x%" PRIx64 ")",
+ hgatp_mode, hgatp);
+ return ERROR_FAIL;
+ }
+
+ /* For any virtual memory access, the original virtual address is
+ * converted in the first stage by VS-level address translation,
+ * as controlled by the vsatp register, into a guest physical
+ * address. */
+ target_addr_t guest_physical;
+ if (vsatp_info) {
+ /* When V=1, memory accesses that would normally bypass
+ * address translation are subject to G- stage address
+ * translation alone. This includes memory accesses made
+ * in support of VS-stage address translation, such as
+ * reads and writes of VS-level page tables. */
+
+ if (riscv_address_translate(target,
+ vsatp_info, get_field(vsatp, RISCV_SATP_PPN(xlen)),
+ hgatp_info, get_field(hgatp, RISCV_SATP_PPN(xlen)),
+ virtual, &guest_physical) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ guest_physical = virtual;
+ }
+
+ /* The guest physical address is then converted in the second
+ * stage by guest physical address translation, as controlled by
+ * the hgatp register, into a supervisor physical address. */
+ if (hgatp_info) {
+ if (riscv_address_translate(target,
+ hgatp_info, get_field(hgatp, RISCV_HGATP_PPN(xlen)),
+ NULL, 0,
+ guest_physical, physical) != ERROR_OK)
+ return ERROR_FAIL;
+ } else {
+ *physical = guest_physical;
+ }
return ERROR_OK;
}
@@ -1677,23 +2996,65 @@ static int riscv_address_translate(struct target *target,
static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_addr_t *physical)
{
int enabled;
- if (riscv_mmu(target, &enabled) == ERROR_OK) {
- if (!enabled)
- return ERROR_FAIL;
+ if (riscv_mmu(target, &enabled) != ERROR_OK)
+ return ERROR_FAIL;
+ if (!enabled) {
+ *physical = virtual;
+ LOG_TARGET_DEBUG(target, "MMU is disabled. 0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, *physical);
+ return ERROR_OK;
+ }
- if (riscv_address_translate(target, virtual, physical) == ERROR_OK)
- return ERROR_OK;
+ riscv_reg_t priv;
+ if (riscv_get_register(target, &priv, GDB_REGNO_PRIV) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read priv register.");
+ return ERROR_FAIL;
}
- return ERROR_FAIL;
+ if (priv & VIRT_PRIV_V)
+ return riscv_virt2phys_v(target, virtual, physical);
+
+ riscv_reg_t satp_value;
+ if (riscv_get_register(target, &satp_value, GDB_REGNO_SATP) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Failed to read SATP register.");
+ return ERROR_FAIL;
+ }
+
+ unsigned int xlen = riscv_xlen(target);
+ int satp_mode = get_field(satp_value, RISCV_SATP_MODE(xlen));
+ const virt2phys_info_t *satp_info;
+ switch (satp_mode) {
+ case SATP_MODE_SV32:
+ satp_info = &sv32;
+ break;
+ case SATP_MODE_SV39:
+ satp_info = &sv39;
+ break;
+ case SATP_MODE_SV48:
+ satp_info = &sv48;
+ break;
+ case SATP_MODE_SV57:
+ satp_info = &sv57;
+ break;
+ case SATP_MODE_OFF:
+ LOG_TARGET_ERROR(target, "No translation or protection."
+ " (satp: 0x%" PRIx64 ")", satp_value);
+ return ERROR_FAIL;
+ default:
+ LOG_TARGET_ERROR(target, "The translation mode is not supported."
+ " (satp: 0x%" PRIx64 ")", satp_value);
+ return ERROR_FAIL;
+ }
+
+ return riscv_address_translate(target,
+ satp_info, get_field(satp_value, RISCV_SATP_PPN(xlen)),
+ NULL, 0,
+ virtual, physical);
}
static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
return r->read_memory(target, phys_address, size, count, buffer, size);
}
@@ -1701,27 +3062,27 @@ static int riscv_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
if (count == 0) {
- LOG_WARNING("0-length read from 0x%" TARGET_PRIxADDR, address);
+ LOG_TARGET_WARNING(target, "0-length read from 0x%" TARGET_PRIxADDR, address);
return ERROR_OK;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
target_addr_t physical_addr;
- if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
- address = physical_addr;
+ int result = target->type->virt2phys(target, address, &physical_addr);
+ if (result != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Address translation failed.");
+ return result;
+ }
RISCV_INFO(r);
- return r->read_memory(target, address, size, count, buffer, size);
+ return r->read_memory(target, physical_addr, size, count, buffer, size);
}
static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
return tt->write_memory(target, phys_address, size, count, buffer);
}
@@ -1729,19 +3090,21 @@ static int riscv_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
if (count == 0) {
- LOG_WARNING("0-length write to 0x%" TARGET_PRIxADDR, address);
+ LOG_TARGET_WARNING(target, "0-length write to 0x%" TARGET_PRIxADDR, address);
return ERROR_OK;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
target_addr_t physical_addr;
- if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK)
- address = physical_addr;
+ int result = target->type->virt2phys(target, address, &physical_addr);
+ if (result != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Address translation failed.");
+ return result;
+ }
struct target_type *tt = get_target_type(target);
- return tt->write_memory(target, address, size, count, buffer);
+ if (!tt)
+ return ERROR_FAIL;
+ return tt->write_memory(target, physical_addr, size, count, buffer);
}
static const char *riscv_get_gdb_arch(const struct target *target)
@@ -1752,26 +3115,21 @@ static const char *riscv_get_gdb_arch(const struct target *target)
case 64:
return "riscv:rv64";
}
- LOG_ERROR("Unsupported xlen: %d", riscv_xlen(target));
+ LOG_TARGET_ERROR(target, "Unsupported xlen: %d", riscv_xlen(target));
return NULL;
}
static int riscv_get_gdb_reg_list_internal(struct target *target,
struct reg **reg_list[], int *reg_list_size,
- enum target_register_class reg_class, bool read)
+ enum target_register_class reg_class, bool is_read)
{
- RISCV_INFO(r);
- LOG_DEBUG("[%s] {%d} reg_class=%d, read=%d",
- target_name(target), r->current_hartid, reg_class, read);
+ LOG_TARGET_DEBUG(target, "reg_class=%d, read=%d", reg_class, is_read);
if (!target->reg_cache) {
- LOG_ERROR("Target not initialized. Return ERROR_FAIL.");
+ LOG_TARGET_ERROR(target, "Target not initialized. Return ERROR_FAIL.");
return ERROR_FAIL;
}
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
switch (reg_class) {
case REG_CLASS_GENERAL:
*reg_list_size = 33;
@@ -1780,7 +3138,7 @@ static int riscv_get_gdb_reg_list_internal(struct target *target,
*reg_list_size = target->reg_cache->num_regs;
break;
default:
- LOG_ERROR("Unsupported reg_class: %d", reg_class);
+ LOG_TARGET_ERROR(target, "Unsupported reg_class: %d", reg_class);
return ERROR_FAIL;
}
@@ -1792,7 +3150,7 @@ static int riscv_get_gdb_reg_list_internal(struct target *target,
assert(!target->reg_cache->reg_list[i].valid ||
target->reg_cache->reg_list[i].size > 0);
(*reg_list)[i] = &target->reg_cache->reg_list[i];
- if (read &&
+ if (is_read &&
target->reg_cache->reg_list[i].exist &&
!target->reg_cache->reg_list[i].valid) {
if (target->reg_cache->reg_list[i].type->get(
@@ -1822,7 +3180,17 @@ static int riscv_get_gdb_reg_list(struct target *target,
static int riscv_arch_state(struct target *target)
{
+ assert(target->state == TARGET_HALTED);
+ const bool semihosting_active = target->semihosting &&
+ target->semihosting->is_active;
+ LOG_USER("%s halted due to %s.%s",
+ target_name(target),
+ debug_reason_name(target),
+ semihosting_active ? " Semihosting is active." : "");
struct target_type *tt = get_target_type(target);
+ if (!tt)
+ return ERROR_FAIL;
+ assert(tt->arch_state);
return tt->arch_state(target);
}
@@ -1834,40 +3202,48 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
{
RISCV_INFO(info);
- if (num_mem_params > 0) {
- LOG_ERROR("Memory parameters are not supported for RISC-V algorithms.");
- return ERROR_FAIL;
- }
-
if (target->state != TARGET_HALTED) {
LOG_TARGET_ERROR(target, "not halted (run target algo)");
return ERROR_TARGET_NOT_HALTED;
}
+ /* Write memory parameters to the target memory */
+ for (int i = 0; i < num_mem_params; i++) {
+ if (mem_params[i].direction == PARAM_OUT ||
+ mem_params[i].direction == PARAM_IN_OUT) {
+ int retval = target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value);
+ if (retval != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't write input mem param into the memory, addr=0x%" TARGET_PRIxADDR
+ " size=0x%" PRIx32, mem_params[i].address, mem_params[i].size);
+ return retval;
+ }
+ }
+ }
+
/* Save registers */
struct reg *reg_pc = register_get_by_name(target->reg_cache, "pc", true);
if (!reg_pc || reg_pc->type->get(reg_pc) != ERROR_OK)
return ERROR_FAIL;
uint64_t saved_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
- LOG_DEBUG("saved_pc=0x%" PRIx64, saved_pc);
+ LOG_TARGET_DEBUG(target, "saved_pc=0x%" PRIx64, saved_pc);
uint64_t saved_regs[32];
for (int i = 0; i < num_reg_params; i++) {
- LOG_DEBUG("save %s", reg_params[i].reg_name);
+ LOG_TARGET_DEBUG(target, "save %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
if (!r) {
- LOG_ERROR("Couldn't find register named '%s'", reg_params[i].reg_name);
+ LOG_TARGET_ERROR(target, "Couldn't find register named '%s'", reg_params[i].reg_name);
return ERROR_FAIL;
}
if (r->size != reg_params[i].size) {
- LOG_ERROR("Register %s is %d bits instead of %d bits.",
+ LOG_TARGET_ERROR(target, "Register %s is %d bits instead of %d bits.",
reg_params[i].reg_name, r->size, reg_params[i].size);
return ERROR_FAIL;
}
if (r->number > GDB_REGNO_XPR31) {
- LOG_ERROR("Only GPRs can be use as argument registers.");
+ LOG_TARGET_ERROR(target, "Only GPRs can be use as argument registers.");
return ERROR_FAIL;
}
@@ -1881,38 +3257,23 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
}
}
-
/* Disable Interrupts before attempting to run the algorithm. */
uint64_t current_mstatus;
- uint8_t mstatus_bytes[8] = { 0 };
-
- LOG_DEBUG("Disabling Interrupts");
- struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
- "mstatus", true);
- if (!reg_mstatus) {
- LOG_ERROR("Couldn't find mstatus!");
+ uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+ if (riscv_interrupts_disable(target, irq_disabled_mask, &current_mstatus) != ERROR_OK)
return ERROR_FAIL;
- }
-
- reg_mstatus->type->get(reg_mstatus);
- current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
- uint64_t ie_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
- buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
- ie_mask, 0));
-
- reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
/* Run algorithm */
- LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point);
- if (riscv_resume(target, 0, entry_point, 0, 0, true) != ERROR_OK)
+ LOG_TARGET_DEBUG(target, "Resume at 0x%" TARGET_PRIxADDR, entry_point);
+ if (riscv_resume(target, 0, entry_point, 0, 1, true) != ERROR_OK)
return ERROR_FAIL;
int64_t start = timeval_ms();
while (target->state != TARGET_HALTED) {
- LOG_DEBUG("poll()");
+ LOG_TARGET_DEBUG(target, "poll()");
int64_t now = timeval_ms();
if (now - start > timeout_ms) {
- LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start);
+ LOG_TARGET_ERROR(target, "Algorithm timed out after %" PRId64 " ms.", now - start);
riscv_halt(target);
old_or_new_riscv_poll(target);
enum gdb_regno regnums[] = {
@@ -1932,7 +3293,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
riscv_reg_t reg_value;
if (riscv_get_register(target, &reg_value, regno) != ERROR_OK)
break;
- LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value);
+
+ LOG_TARGET_ERROR(target, "%s = 0x%" PRIx64, gdb_regno_name(target, regno), reg_value);
}
return ERROR_TARGET_TIMEOUT;
}
@@ -1942,23 +3304,22 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
return result;
}
- /* The current hart id might have been changed in poll(). */
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
+ /* TODO: The current hart id might have been changed in poll(). */
+ /* if (riscv_select_current_hart(target) != ERROR_OK)
+ return ERROR_FAIL; */
if (reg_pc->type->get(reg_pc) != ERROR_OK)
return ERROR_FAIL;
uint64_t final_pc = buf_get_u64(reg_pc->value, 0, reg_pc->size);
if (exit_point && final_pc != exit_point) {
- LOG_ERROR("PC ended up at 0x%" PRIx64 " instead of 0x%"
+ LOG_TARGET_ERROR(target, "PC ended up at 0x%" PRIx64 " instead of 0x%"
TARGET_PRIxADDR, final_pc, exit_point);
return ERROR_FAIL;
}
/* Restore Interrupts */
- LOG_DEBUG("Restoring Interrupts");
- buf_set_u64(mstatus_bytes, 0, info->xlen, current_mstatus);
- reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+ if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK)
+ return ERROR_FAIL;
/* Restore registers */
uint8_t buf[8] = { 0 };
@@ -1971,20 +3332,35 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params,
reg_params[i].direction == PARAM_IN_OUT) {
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
if (r->type->get(r) != ERROR_OK) {
- LOG_ERROR("get(%s) failed", r->name);
+ LOG_TARGET_ERROR(target, "get(%s) failed", r->name);
return ERROR_FAIL;
}
buf_cpy(r->value, reg_params[i].value, reg_params[i].size);
}
- LOG_DEBUG("restore %s", reg_params[i].reg_name);
+ LOG_TARGET_DEBUG(target, "restore %s", reg_params[i].reg_name);
struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false);
buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]);
if (r->type->set(r, buf) != ERROR_OK) {
- LOG_ERROR("set(%s) failed", r->name);
+ LOG_TARGET_ERROR(target, "set(%s) failed", r->name);
return ERROR_FAIL;
}
}
+ /* Read memory parameters from the target memory */
+ for (int i = 0; i < num_mem_params; i++) {
+ if (mem_params[i].direction == PARAM_IN ||
+ mem_params[i].direction == PARAM_IN_OUT) {
+ int retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size,
+ mem_params[i].value);
+ if (retval != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "Couldn't read output mem param from the memory, "
+ "addr=0x%" TARGET_PRIxADDR " size=0x%" PRIx32,
+ mem_params[i].address, mem_params[i].size);
+ return retval;
+ }
+ }
+ }
+
return ERROR_OK;
}
@@ -1996,7 +3372,7 @@ static int riscv_checksum_memory(struct target *target,
struct reg_param reg_params[2];
int retval;
- LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count);
+ LOG_TARGET_DEBUG(target, "address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count);
static const uint8_t riscv32_crc_code[] = {
#include "../../../contrib/loaders/checksum/riscv32_crc.inc"
@@ -2040,7 +3416,7 @@ static int riscv_checksum_memory(struct target *target,
retval = target_write_buffer(target, crc_algorithm->address, crc_code_size,
crc_code);
if (retval != ERROR_OK) {
- LOG_ERROR("Failed to write code to " TARGET_ADDR_FMT ": %d",
+ LOG_TARGET_ERROR(target, "Failed to write code to " TARGET_ADDR_FMT ": %d",
crc_algorithm->address, retval);
target_free_working_area(target, crc_algorithm);
return retval;
@@ -2062,74 +3438,153 @@ static int riscv_checksum_memory(struct target *target,
if (retval == ERROR_OK)
*checksum = buf_get_u32(reg_params[0].value, 0, 32);
else
- LOG_ERROR("error executing RISC-V CRC algorithm");
+ LOG_TARGET_ERROR(target, "Error executing RISC-V CRC algorithm.");
destroy_reg_param(&reg_params[0]);
destroy_reg_param(&reg_params[1]);
target_free_working_area(target, crc_algorithm);
- LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval);
+ LOG_TARGET_DEBUG(target, "checksum=0x%" PRIx32 ", result=%d", *checksum, retval);
return retval;
}
/*** OpenOCD Helper Functions ***/
-enum riscv_poll_hart {
- RPH_NO_CHANGE,
- RPH_DISCOVERED_HALTED,
- RPH_DISCOVERED_RUNNING,
- RPH_ERROR
+enum riscv_next_action {
+ RPH_NONE,
+ RPH_RESUME,
+ RPH_REMAIN_HALTED
};
-static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid)
+static int riscv_poll_hart(struct target *target, enum riscv_next_action *next_action)
{
RISCV_INFO(r);
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
- return RPH_ERROR;
- LOG_DEBUG("polling hart %d, target->state=%d", hartid, target->state);
-
- /* If OpenOCD thinks we're running but this hart is halted then it's time
- * to raise an event. */
- bool halted = riscv_is_halted(target);
- if (target->state != TARGET_HALTED && halted) {
- LOG_DEBUG(" triggered a halt");
- r->on_halt(target);
- return RPH_DISCOVERED_HALTED;
- } else if (target->state != TARGET_RUNNING && !halted) {
- LOG_DEBUG(" triggered running");
- target->state = TARGET_RUNNING;
- target->debug_reason = DBG_REASON_NOTHALTED;
- return RPH_DISCOVERED_RUNNING;
- }
+ LOG_TARGET_DEBUG(target, "polling, target->state=%d", target->state);
- return RPH_NO_CHANGE;
-}
+ *next_action = RPH_NONE;
-static int set_debug_reason(struct target *target, enum riscv_halt_reason halt_reason)
-{
- switch (halt_reason) {
- case RISCV_HALT_BREAKPOINT:
- target->debug_reason = DBG_REASON_BREAKPOINT;
+ enum riscv_hart_state previous_riscv_state = 0;
+ enum target_state previous_target_state = target->state;
+ switch (target->state) {
+ case TARGET_UNKNOWN:
+ /* Special case, handled further down. */
+ previous_riscv_state = RISCV_STATE_UNAVAILABLE; /* Need to assign something. */
break;
- case RISCV_HALT_TRIGGER:
- target->debug_reason = DBG_REASON_WATCHPOINT;
+ case TARGET_RUNNING:
+ previous_riscv_state = RISCV_STATE_RUNNING;
break;
- case RISCV_HALT_INTERRUPT:
- case RISCV_HALT_GROUP:
- target->debug_reason = DBG_REASON_DBGRQ;
+ case TARGET_HALTED:
+ previous_riscv_state = RISCV_STATE_HALTED;
break;
- case RISCV_HALT_SINGLESTEP:
- target->debug_reason = DBG_REASON_SINGLESTEP;
+ case TARGET_RESET:
+ previous_riscv_state = RISCV_STATE_HALTED;
break;
- case RISCV_HALT_UNKNOWN:
- target->debug_reason = DBG_REASON_UNDEFINED;
+ case TARGET_DEBUG_RUNNING:
+ previous_riscv_state = RISCV_STATE_RUNNING;
break;
- case RISCV_HALT_ERROR:
+ case TARGET_UNAVAILABLE:
+ previous_riscv_state = RISCV_STATE_UNAVAILABLE;
+ break;
+ }
+
+ /* If OpenOCD thinks we're running but this hart is halted then it's time
+ * to raise an event. */
+ enum riscv_hart_state state;
+ if (riscv_get_hart_state(target, &state) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (state == RISCV_STATE_NON_EXISTENT) {
+ LOG_TARGET_ERROR(target, "Hart is non-existent!");
+ return ERROR_FAIL;
+ }
+
+ if (state == RISCV_STATE_HALTED && timeval_ms() - r->last_activity > 100) {
+ /* If we've been idle for a while, flush the register cache. Just in case
+ * OpenOCD is going to be disconnected without shutting down cleanly. */
+ if (riscv_flush_registers(target) != ERROR_OK)
return ERROR_FAIL;
}
- LOG_DEBUG("[%s] debug_reason=%d", target_name(target), target->debug_reason);
+
+ if (target->state == TARGET_UNKNOWN || state != previous_riscv_state) {
+ switch (state) {
+ case RISCV_STATE_HALTED:
+ if (previous_riscv_state == RISCV_STATE_UNAVAILABLE)
+ LOG_TARGET_INFO(target, "became available (halted)");
+
+ LOG_TARGET_DEBUG(target, " triggered a halt; previous_target_state=%d",
+ previous_target_state);
+ target->state = TARGET_HALTED;
+ enum riscv_halt_reason halt_reason = riscv_halt_reason(target);
+ if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (halt_reason == RISCV_HALT_EBREAK) {
+ int retval;
+ /* Detect if this EBREAK is a semihosting request. If so, handle it. */
+ switch (riscv_semihosting(target, &retval)) {
+ case SEMIHOSTING_NONE:
+ break;
+ case SEMIHOSTING_WAITING:
+ /* This hart should remain halted. */
+ *next_action = RPH_REMAIN_HALTED;
+ break;
+ case SEMIHOSTING_HANDLED:
+ /* This hart should be resumed, along with any other
+ * harts that halted due to haltgroups. */
+ *next_action = RPH_RESUME;
+ return ERROR_OK;
+ case SEMIHOSTING_ERROR:
+ return retval;
+ }
+ }
+
+ if (r->handle_became_halted &&
+ r->handle_became_halted(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+
+ /* We shouldn't do the callbacks yet. What if
+ * there are multiple harts that halted at the
+ * same time? We need to set debug reason on each
+ * of them before calling a callback, which is
+ * going to figure out the "current thread". */
+
+ r->halted_needs_event_callback = true;
+ if (previous_target_state == TARGET_DEBUG_RUNNING)
+ r->halted_callback_event = TARGET_EVENT_DEBUG_HALTED;
+ else
+ r->halted_callback_event = TARGET_EVENT_HALTED;
+ break;
+
+ case RISCV_STATE_RUNNING:
+ if (previous_riscv_state == RISCV_STATE_UNAVAILABLE)
+ LOG_TARGET_INFO(target, "became available (running)");
+
+ LOG_TARGET_DEBUG(target, " triggered running");
+ target->state = TARGET_RUNNING;
+ target->debug_reason = DBG_REASON_NOTHALTED;
+ if (r->handle_became_running &&
+ r->handle_became_running(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+
+ case RISCV_STATE_UNAVAILABLE:
+ LOG_TARGET_DEBUG(target, " became unavailable");
+ LOG_TARGET_INFO(target, "became unavailable.");
+ target->state = TARGET_UNAVAILABLE;
+ if (r->handle_became_unavailable &&
+ r->handle_became_unavailable(target, previous_riscv_state) != ERROR_OK)
+ return ERROR_FAIL;
+ break;
+
+ case RISCV_STATE_NON_EXISTENT:
+ LOG_TARGET_ERROR(target, "Hart is non-existent!");
+ target->state = TARGET_UNAVAILABLE;
+ break;
+ }
+ }
+
return ERROR_OK;
}
@@ -2140,7 +3595,7 @@ static int sample_memory(struct target *target)
if (!r->sample_buf.buf || !r->sample_config.enabled)
return ERROR_OK;
- LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size);
+ LOG_TARGET_DEBUG(target, "buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size);
uint64_t start = timeval_ms();
riscv_sample_buf_maybe_add_timestamp(target, true);
@@ -2174,7 +3629,7 @@ static int sample_memory(struct target *target)
exit:
riscv_sample_buf_maybe_add_timestamp(target, false);
if (result != ERROR_OK) {
- LOG_INFO("Turning off memory sampling because it failed.");
+ LOG_TARGET_INFO(target, "Turning off memory sampling because it failed.");
r->sample_config.enabled = false;
}
return result;
@@ -2183,159 +3638,207 @@ exit:
/*** OpenOCD Interface ***/
int riscv_openocd_poll(struct target *target)
{
- LOG_DEBUG("polling all harts");
- int halted_hart = -1;
+ LOG_TARGET_DEBUG(target, "Polling all harts.");
- if (target->smp) {
- unsigned should_remain_halted = 0;
- unsigned should_resume = 0;
- struct target_list *list;
- foreach_smp_target(list, target->smp_targets) {
- struct target *t = list->target;
- struct riscv_info *r = riscv_info(t);
- enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid);
- switch (out) {
- case RPH_NO_CHANGE:
- break;
- case RPH_DISCOVERED_RUNNING:
- t->state = TARGET_RUNNING;
- t->debug_reason = DBG_REASON_NOTHALTED;
- break;
- case RPH_DISCOVERED_HALTED:
- t->state = TARGET_HALTED;
- enum riscv_halt_reason halt_reason =
- riscv_halt_reason(t, r->current_hartid);
- if (set_debug_reason(t, halt_reason) != ERROR_OK)
- return ERROR_FAIL;
+ struct list_head *targets;
- if (halt_reason == RISCV_HALT_BREAKPOINT) {
- int retval;
- switch (riscv_semihosting(t, &retval)) {
- case SEMIHOSTING_NONE:
- case SEMIHOSTING_WAITING:
- /* This hart should remain halted. */
- should_remain_halted++;
- break;
- case SEMIHOSTING_HANDLED:
- /* This hart should be resumed, along with any other
- * harts that halted due to haltgroups. */
- should_resume++;
- break;
- case SEMIHOSTING_ERROR:
- return retval;
- }
- } else if (halt_reason != RISCV_HALT_GROUP) {
- should_remain_halted++;
- }
- break;
+ LIST_HEAD(single_target_list);
+ struct target_list single_target_entry = {
+ .lh = {NULL, NULL},
+ .target = target
+ };
- case RPH_ERROR:
- return ERROR_FAIL;
- }
- }
+ if (target->smp) {
+ targets = target->smp_targets;
+ } else {
+ /* Make a list that just contains a single target, so we can
+ * share code below. */
+ list_add(&single_target_entry.lh, &single_target_list);
+ targets = &single_target_list;
+ }
+
+ unsigned int should_remain_halted = 0;
+ unsigned int should_resume = 0;
+ unsigned int halted = 0;
+ unsigned int running = 0;
+ struct target_list *entry;
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+
+ /* Clear here just in case there were errors and we never got to
+ * check this flag further down. */
+ info->halted_needs_event_callback = false;
+
+ if (!target_was_examined(t))
+ continue;
- LOG_DEBUG("should_remain_halted=%d, should_resume=%d",
- should_remain_halted, should_resume);
- if (should_remain_halted && should_resume) {
- LOG_WARNING("%d harts should remain halted, and %d should resume.",
- should_remain_halted, should_resume);
- }
- if (should_remain_halted) {
- LOG_DEBUG("halt all");
- riscv_halt(target);
- } else if (should_resume) {
- LOG_DEBUG("resume all");
- riscv_resume(target, true, 0, 0, 0, false);
- }
+ enum riscv_next_action next_action;
+ if (riscv_poll_hart(t, &next_action) != ERROR_OK)
+ return ERROR_FAIL;
- /* Sample memory if any target is running. */
- foreach_smp_target(list, target->smp_targets) {
- struct target *t = list->target;
- if (t->state == TARGET_RUNNING) {
- sample_memory(target);
+ switch (next_action) {
+ case RPH_NONE:
+ if (t->state == TARGET_HALTED)
+ halted++;
+ if (t->state == TARGET_RUNNING ||
+ t->state == TARGET_DEBUG_RUNNING)
+ running++;
+ break;
+ case RPH_REMAIN_HALTED:
+ should_remain_halted++;
+ break;
+ case RPH_RESUME:
+ should_resume++;
break;
- }
}
+ }
- return ERROR_OK;
-
+ LOG_TARGET_DEBUG(target, "should_remain_halted=%d, should_resume=%d",
+ should_remain_halted, should_resume);
+ if (should_remain_halted && should_resume) {
+ LOG_TARGET_WARNING(target, "%d harts should remain halted, and %d should resume.",
+ should_remain_halted, should_resume);
+ }
+ if (should_remain_halted) {
+ LOG_TARGET_DEBUG(target, "halt all; should_remain_halted=%d",
+ should_remain_halted);
+ riscv_halt(target);
+ } else if (should_resume) {
+ LOG_TARGET_DEBUG(target, "resume all");
+ riscv_resume(target, true, 0, 0, 0, false);
+ } else if (halted && running) {
+ LOG_TARGET_DEBUG(target, "halt all; halted=%d",
+ halted);
+ riscv_halt(target);
} else {
- enum riscv_poll_hart out = riscv_poll_hart(target,
- riscv_current_hartid(target));
- if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) {
- if (target->state == TARGET_RUNNING)
- sample_memory(target);
- return ERROR_OK;
- } else if (out == RPH_ERROR) {
- return ERROR_FAIL;
+ /* For targets that were discovered to be halted, call the
+ * appropriate callback. */
+ foreach_smp_target(entry, targets)
+ {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+ if (info->halted_needs_event_callback) {
+ target_call_event_callbacks(t, info->halted_callback_event);
+ info->halted_needs_event_callback = false;
+ }
}
+ }
- halted_hart = riscv_current_hartid(target);
- LOG_DEBUG(" hart %d halted", halted_hart);
-
- enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart);
- if (set_debug_reason(target, halt_reason) != ERROR_OK)
+ /* Call tick() for every hart. What happens in tick() is opaque to this
+ * layer. The reason it's outside the previous loop is that at this point
+ * the state of every hart has settled, so any side effects happening in
+ * tick() won't affect the delicate poll() code. */
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ struct riscv_info *info = riscv_info(t);
+ if (info->tick && info->tick(t) != ERROR_OK)
return ERROR_FAIL;
- target->state = TARGET_HALTED;
}
- if (target->debug_reason == DBG_REASON_BREAKPOINT) {
- int retval;
- switch (riscv_semihosting(target, &retval)) {
- case SEMIHOSTING_NONE:
- case SEMIHOSTING_WAITING:
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
- break;
- case SEMIHOSTING_HANDLED:
- if (riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK)
- return ERROR_FAIL;
- break;
- case SEMIHOSTING_ERROR:
- return retval;
+ /* Sample memory if any target is running. */
+ foreach_smp_target(entry, targets) {
+ struct target *t = entry->target;
+ if (t->state == TARGET_RUNNING) {
+ sample_memory(target);
+ break;
}
- } else {
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
}
return ERROR_OK;
}
-int riscv_openocd_step(struct target *target, int current,
- target_addr_t address, int handle_breakpoints)
+static int riscv_openocd_step_impl(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints, int handle_callbacks)
{
- LOG_DEBUG("stepping rtos hart");
+ LOG_TARGET_DEBUG(target, "stepping hart");
+
+ if (!current) {
+ if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK)
+ return ERROR_FAIL;
+ }
- if (!current)
- riscv_set_register(target, GDB_REGNO_PC, address);
+ struct breakpoint *breakpoint = NULL;
+ /* the front-end may request us not to handle breakpoints */
+ if (handle_breakpoints) {
+ if (current) {
+ if (riscv_get_register(target, &address, GDB_REGNO_PC) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+ breakpoint = breakpoint_find(target, address);
+ if (breakpoint && (riscv_remove_breakpoint(target, breakpoint) != ERROR_OK))
+ return ERROR_FAIL;
+ }
riscv_reg_t trigger_state[RISCV_MAX_HWBPS] = {0};
if (disable_triggers(target, trigger_state) != ERROR_OK)
return ERROR_FAIL;
- int out = riscv_step_rtos_hart(target);
- if (out != ERROR_OK) {
- LOG_ERROR("unable to step rtos hart");
- return out;
+ bool success = true;
+ uint64_t current_mstatus;
+ RISCV_INFO(info);
+
+ if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) {
+ /* Disable Interrupts before stepping. */
+ uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE;
+ if (riscv_interrupts_disable(target, irq_disabled_mask,
+ &current_mstatus) != ERROR_OK) {
+ success = false;
+ LOG_TARGET_ERROR(target, "Unable to disable interrupts.");
+ goto _exit;
+ }
+ }
+
+ if (riscv_step_rtos_hart(target) != ERROR_OK) {
+ success = false;
+ LOG_TARGET_ERROR(target, "Unable to step rtos hart.");
}
register_cache_invalidate(target->reg_cache);
- if (enable_triggers(target, trigger_state) != ERROR_OK)
- return ERROR_FAIL;
+ if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY)
+ if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) {
+ success = false;
+ LOG_TARGET_ERROR(target, "Unable to restore interrupts.");
+ }
+
+_exit:
+ if (enable_triggers(target, trigger_state) != ERROR_OK) {
+ success = false;
+ LOG_TARGET_ERROR(target, "Unable to enable triggers.");
+ }
+
+ if (breakpoint && (riscv_add_breakpoint(target, breakpoint) != ERROR_OK)) {
+ success = false;
+ LOG_TARGET_ERROR(target, "Unable to restore the disabled breakpoint.");
+ }
+
+ if (success) {
+ target->state = TARGET_RUNNING;
+ if (handle_callbacks)
+ target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
- target->state = TARGET_RUNNING;
- target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
- target->state = TARGET_HALTED;
- target->debug_reason = DBG_REASON_SINGLESTEP;
- target_call_event_callbacks(target, TARGET_EVENT_HALTED);
- return out;
+ target->state = TARGET_HALTED;
+ target->debug_reason = DBG_REASON_SINGLESTEP;
+ if (handle_callbacks)
+ target_call_event_callbacks(target, TARGET_EVENT_HALTED);
+ }
+
+ return success ? ERROR_OK : ERROR_FAIL;
+}
+
+int riscv_openocd_step(struct target *target, int current,
+ target_addr_t address, int handle_breakpoints)
+{
+ return riscv_openocd_step_impl(target, current, address, handle_breakpoints,
+ true /* handle_callbacks */);
}
/* Command Handlers */
COMMAND_HANDLER(riscv_set_command_timeout_sec)
{
if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ LOG_ERROR("Command takes exactly 1 parameter.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
int timeout = atoi(CMD_ARGV[0]);
@@ -2352,7 +3855,7 @@ COMMAND_HANDLER(riscv_set_command_timeout_sec)
COMMAND_HANDLER(riscv_set_reset_timeout_sec)
{
if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ LOG_ERROR("Command takes exactly 1 parameter.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
int timeout = atoi(CMD_ARGV[0]);
@@ -2555,7 +4058,7 @@ static int parse_ranges(struct list_head *ranges, const char *tcl_arg, const cha
COMMAND_HANDLER(riscv_set_expose_csrs)
{
if (CMD_ARGC == 0) {
- LOG_ERROR("Command expects parameters");
+ LOG_ERROR("Command expects parameters.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
@@ -2575,7 +4078,7 @@ COMMAND_HANDLER(riscv_set_expose_csrs)
COMMAND_HANDLER(riscv_set_expose_custom)
{
if (CMD_ARGC == 0) {
- LOG_ERROR("Command expects parameters");
+ LOG_ERROR("Command expects parameters.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
@@ -2592,6 +4095,26 @@ COMMAND_HANDLER(riscv_set_expose_custom)
return ret;
}
+COMMAND_HANDLER(riscv_hide_csrs)
+{
+ if (CMD_ARGC == 0) {
+ LOG_ERROR("Command expects parameters");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+ int ret = ERROR_OK;
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ ret = parse_ranges(&info->hide_csr, CMD_ARGV[i], "csr", 0xfff);
+ if (ret != ERROR_OK)
+ break;
+ }
+
+ return ret;
+}
+
COMMAND_HANDLER(riscv_authdata_read)
{
unsigned int index = 0;
@@ -2600,7 +4123,7 @@ COMMAND_HANDLER(riscv_authdata_read)
} else if (CMD_ARGC == 1) {
COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index);
} else {
- LOG_ERROR("Command takes at most one parameter");
+ LOG_ERROR("Command takes at most one parameter.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
@@ -2612,7 +4135,7 @@ COMMAND_HANDLER(riscv_authdata_read)
RISCV_INFO(r);
if (!r) {
- LOG_ERROR("riscv_info is NULL!");
+ LOG_TARGET_ERROR(target, "riscv_info is NULL!");
return ERROR_FAIL;
}
@@ -2623,7 +4146,7 @@ COMMAND_HANDLER(riscv_authdata_read)
command_print_sameline(CMD, "0x%08" PRIx32, value);
return ERROR_OK;
} else {
- LOG_ERROR("authdata_read is not implemented for this target.");
+ LOG_TARGET_ERROR(target, "authdata_read is not implemented for this target.");
return ERROR_FAIL;
}
}
@@ -2647,76 +4170,153 @@ COMMAND_HANDLER(riscv_authdata_write)
RISCV_INFO(r);
if (!r->authdata_write) {
- LOG_ERROR("authdata_write is not implemented for this target.");
+ LOG_TARGET_ERROR(target, "authdata_write is not implemented for this target.");
return ERROR_FAIL;
}
return r->authdata_write(target, value, index);
}
-COMMAND_HANDLER(riscv_dmi_read)
+uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes 1 parameter");
- return ERROR_COMMAND_SYNTAX_ERROR;
- }
+ assert(target);
+ RISCV_INFO(r);
+ if (!r || !r->get_dmi_address)
+ return dm_address;
+ return r->get_dmi_address(target, dm_address);
+}
- struct target *target = get_current_target(CMD_CTX);
+static int riscv_dmi_read(struct target *target, uint32_t *value, uint32_t address)
+{
if (!target) {
LOG_ERROR("target is NULL!");
return ERROR_FAIL;
}
-
RISCV_INFO(r);
if (!r) {
- LOG_ERROR("riscv_info is NULL!");
+ LOG_TARGET_ERROR(target, "riscv_info is NULL!");
+ return ERROR_FAIL;
+ }
+ if (!r->dmi_read) {
+ LOG_TARGET_ERROR(target, "dmi_read is not implemented.");
return ERROR_FAIL;
}
+ return r->dmi_read(target, value, address);
+}
- if (r->dmi_read) {
- uint32_t address, value;
- COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
- if (r->dmi_read(target, &value, address) != ERROR_OK)
- return ERROR_FAIL;
- command_print(CMD, "0x%" PRIx32, value);
- return ERROR_OK;
- } else {
- LOG_ERROR("dmi_read is not implemented for this target.");
+static int riscv_dmi_write(struct target *target, uint32_t dmi_address, uint32_t value)
+{
+ if (!target) {
+ LOG_ERROR("target is NULL!");
return ERROR_FAIL;
}
+ RISCV_INFO(r);
+ if (!r) {
+ LOG_TARGET_ERROR(target, "riscv_info is NULL!");
+ return ERROR_FAIL;
+ }
+ if (!r->dmi_write) {
+ LOG_TARGET_ERROR(target, "dmi_write is not implemented.");
+ return ERROR_FAIL;
+ }
+ const int result = r->dmi_write(target, dmi_address, value);
+ /* Invalidate our cached progbuf copy:
+ * - if the user tinkered directly with a progbuf register
+ * - if debug module was reset, in which case progbuf registers
+ * may not retain their value.
+ * FIXME: If there are multiple DMs on a single TAP, it is possible to
+ * clobber progbuf or reset the DM of another target.
+ */
+ const bool progbuf_touched =
+ (dmi_address >= riscv_get_dmi_address(target, DM_PROGBUF0) &&
+ dmi_address <= riscv_get_dmi_address(target, DM_PROGBUF15));
+ const bool dm_deactivated =
+ (dmi_address == riscv_get_dmi_address(target, DM_DMCONTROL) &&
+ (value & DM_DMCONTROL_DMACTIVE) == 0);
+ if (progbuf_touched || dm_deactivated) {
+ if (r->invalidate_cached_progbuf) {
+ /* Here the return value of invalidate_cached_progbuf()
+ * is ignored. It is okay to do so for now, since the
+ * only case an error is returned is a failure to
+ * assign a DM to the target, which would have already
+ * caused an error during dmi_write().
+ * FIXME: invalidate_cached_progbuf() should be void.
+ */
+ r->invalidate_cached_progbuf(target);
+ } else {
+ LOG_TARGET_DEBUG(target,
+ "invalidate_cached_progbuf() is not implemented.");
+ }
+ }
+ return result;
}
+COMMAND_HANDLER(handle_riscv_dmi_read)
+{
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
-COMMAND_HANDLER(riscv_dmi_write)
+ uint32_t dmi_address;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmi_address);
+
+ struct target * const target = get_current_target(CMD_CTX);
+ uint32_t value;
+ const int result = riscv_dmi_read(target, &value, dmi_address);
+ if (result == ERROR_OK)
+ command_print(CMD, "0x%" PRIx32, value);
+ return result;
+}
+
+COMMAND_HANDLER(handle_riscv_dmi_write)
{
- if (CMD_ARGC != 2) {
- LOG_ERROR("Command takes exactly 2 arguments");
+ if (CMD_ARGC != 2)
return ERROR_COMMAND_SYNTAX_ERROR;
- }
- struct target *target = get_current_target(CMD_CTX);
- RISCV_INFO(r);
+ uint32_t dmi_address, value;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dmi_address);
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
+
+ struct target * const target = get_current_target(CMD_CTX);
+ return riscv_dmi_write(target, dmi_address, value);
+}
+
+COMMAND_HANDLER(handle_riscv_dm_read)
+{
+ if (CMD_ARGC != 1)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ uint32_t dm_address;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dm_address);
+
+ struct target * const target = get_current_target(CMD_CTX);
+ uint32_t value;
+ const int result = riscv_dmi_read(target, &value,
+ riscv_get_dmi_address(target, dm_address));
+ if (result == ERROR_OK)
+ command_print(CMD, "0x%" PRIx32, value);
+ return result;
+}
+
+COMMAND_HANDLER(handle_riscv_dm_write)
+{
+ if (CMD_ARGC != 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
- uint32_t address, value;
- COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
+ uint32_t dm_address, value;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], dm_address);
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
- if (r->dmi_write) {
- return r->dmi_write(target, address, value);
- } else {
- LOG_ERROR("dmi_write is not implemented for this target.");
- return ERROR_FAIL;
- }
+ struct target * const target = get_current_target(CMD_CTX);
+ return riscv_dmi_write(target, riscv_get_dmi_address(target, dm_address),
+ value);
}
COMMAND_HANDLER(riscv_reset_delays)
{
int wait = 0;
- if (CMD_ARGC > 1) {
- LOG_ERROR("Command takes at most one argument");
+ if (CMD_ARGC > 1)
return ERROR_COMMAND_SYNTAX_ERROR;
- }
if (CMD_ARGC == 1)
COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], wait);
@@ -2794,6 +4394,48 @@ COMMAND_HANDLER(riscv_use_bscan_tunnel)
return ERROR_OK;
}
+COMMAND_HANDLER(riscv_set_bscan_tunnel_ir)
+{
+ int ir_id = 0;
+
+ if (CMD_ARGC > 1) {
+ LOG_ERROR("Command takes at most one arguments");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ir_id);
+ }
+
+ LOG_INFO("Bscan tunnel IR 0x%x selected", ir_id);
+
+ bscan_tunnel_ir_id = ir_id;
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_maskisr)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(info);
+
+ static const struct jim_nvp nvp_maskisr_modes[] = {
+ { .name = "off", .value = RISCV_ISRMASK_OFF },
+ { .name = "steponly", .value = RISCV_ISRMASK_STEPONLY },
+ { .name = NULL, .value = -1 },
+ };
+ const struct jim_nvp *n;
+
+ if (CMD_ARGC > 0) {
+ n = jim_nvp_name2value_simple(nvp_maskisr_modes, CMD_ARGV[0]);
+ if (!n->name)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ info->isrmask_mode = n->value;
+ } else {
+ n = jim_nvp_value2name_simple(nvp_maskisr_modes, info->isrmask_mode);
+ command_print(CMD, "riscv interrupt mask %s", n->name);
+ }
+
+ return ERROR_OK;
+}
+
COMMAND_HANDLER(riscv_set_enable_virt2phys)
{
if (CMD_ARGC != 1) {
@@ -2806,43 +4448,462 @@ COMMAND_HANDLER(riscv_set_enable_virt2phys)
COMMAND_HANDLER(riscv_set_ebreakm)
{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 0) {
+ command_print(CMD, "riscv_ebreakm enabled: %s", r->riscv_ebreakm ? "on" : "off");
+ return ERROR_OK;
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreakm);
+ return ERROR_OK;
+ }
+
+ LOG_ERROR("Command takes 0 or 1 parameters");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+COMMAND_HANDLER(riscv_set_ebreaks)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 0) {
+ command_print(CMD, "riscv_ebreaks enabled: %s", r->riscv_ebreaks ? "on" : "off");
+ return ERROR_OK;
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreaks);
+ return ERROR_OK;
+ }
+
+ LOG_ERROR("Command takes 0 or 1 parameters");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+COMMAND_HANDLER(riscv_set_ebreaku)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 0) {
+ command_print(CMD, "riscv_ebreaku enabled: %s", r->riscv_ebreaku ? "on" : "off");
+ return ERROR_OK;
+ } else if (CMD_ARGC == 1) {
+ COMMAND_PARSE_ON_OFF(CMD_ARGV[0], r->riscv_ebreaku);
+ return ERROR_OK;
+ }
+
+ LOG_ERROR("Command takes 0 or 1 parameters");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+}
+
+COMMAND_HELPER(riscv_clear_trigger, int trigger_id, const char *name)
+{
+ struct target *target = get_current_target(CMD_CTX);
if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ LOG_ERROR("clear command takes no extra arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (find_first_trigger_by_id(target, trigger_id) < 0) {
+ LOG_TARGET_ERROR(target, "No %s is set. Nothing to clear.", name);
+ return ERROR_FAIL;
+ }
+ return remove_trigger(target, trigger_id);
+}
+
+COMMAND_HANDLER(riscv_itrigger)
+{
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ITRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ITRIGGER;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ITRIGGER_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An itrigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool nmi = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ riscv_reg_t interrupts = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "nmi"))
+ nmi = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], interrupts);
+ }
+ if (!nmi && interrupts == 0) {
+ LOG_ERROR("Doesn't make sense to set itrigger with "
+ "mie_bits=0 and without nmi.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set itrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t4(target, vs, vu, nmi, m, s, u, interrupts, ITRIGGER_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested itrigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ITRIGGER_UNIQUE_ID, "itrigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
- COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm);
return ERROR_OK;
}
-COMMAND_HANDLER(riscv_set_ebreaks)
+COMMAND_HANDLER(riscv_icount)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ICOUNT_UNIQUE_ID = -CSR_TDATA1_TYPE_ICOUNT;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ICOUNT_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An icount trigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ bool pending = false;
+ unsigned int count = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "pending"))
+ pending = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(uint, CMD_ARGV[i], count);
+ }
+ if (count == 0) {
+ LOG_ERROR("Doesn't make sense to set icount trigger with "
+ "count=0.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set itrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t3(target, vs, vu, m, s, u, pending, count, ICOUNT_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested icount trigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ICOUNT_UNIQUE_ID, "icount trigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
- COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks);
return ERROR_OK;
}
-COMMAND_HANDLER(riscv_set_ebreaku)
+COMMAND_HANDLER(riscv_etrigger)
{
- if (CMD_ARGC != 1) {
- LOG_ERROR("Command takes exactly 1 parameter");
+ if (CMD_ARGC < 1) {
+ LOG_ERROR("Command takes at least 1 parameter");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+ const int ETRIGGER_UNIQUE_ID = -CSR_TDATA1_TYPE_ETRIGGER;
+
+ if (riscv_enumerate_triggers(target) != ERROR_OK)
+ return ERROR_FAIL;
+
+ if (!strcmp(CMD_ARGV[0], "set")) {
+ if (find_first_trigger_by_id(target, ETRIGGER_UNIQUE_ID) >= 0) {
+ LOG_TARGET_ERROR(target, "An etrigger is already set, and OpenOCD "
+ "doesn't support setting more than one at a time.");
+ return ERROR_FAIL;
+ }
+ bool vs = false;
+ bool vu = false;
+ bool m = false;
+ bool s = false;
+ bool u = false;
+ riscv_reg_t exception_codes = 0;
+
+ for (unsigned int i = 1; i < CMD_ARGC; i++) {
+ if (!strcmp(CMD_ARGV[i], "vs"))
+ vs = true;
+ else if (!strcmp(CMD_ARGV[i], "vu"))
+ vu = true;
+ else if (!strcmp(CMD_ARGV[i], "m"))
+ m = true;
+ else if (!strcmp(CMD_ARGV[i], "s"))
+ s = true;
+ else if (!strcmp(CMD_ARGV[i], "u"))
+ u = true;
+ else
+ COMMAND_PARSE_NUMBER(u64, CMD_ARGV[i], exception_codes);
+ }
+ if (exception_codes == 0) {
+ LOG_ERROR("Doesn't make sense to set etrigger with "
+ "exception_codes=0.");
+ return ERROR_FAIL;
+ } else if (!vs && !vu && !m && !s && !u) {
+ LOG_ERROR("Doesn't make sense to set etrigger without at "
+ "least one of vs, vu, m, s, or u.");
+ return ERROR_FAIL;
+ }
+ int result = maybe_add_trigger_t5(target, vs, vu, m, s, u, exception_codes, ETRIGGER_UNIQUE_ID);
+ if (result != ERROR_OK)
+ LOG_TARGET_ERROR(target, "Failed to set requested etrigger.");
+ return result;
+
+ } else if (!strcmp(CMD_ARGV[0], "clear")) {
+ return riscv_clear_trigger(CMD, ETRIGGER_UNIQUE_ID, "etrigger");
+
+ } else {
+ LOG_ERROR("First argument must be either 'set' or 'clear'.");
return ERROR_COMMAND_SYNTAX_ERROR;
}
- COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaku);
return ERROR_OK;
}
-COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
- unsigned int value)
+COMMAND_HANDLER(handle_repeat_read)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC < 2) {
+ LOG_ERROR("Command requires at least count and address arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ if (CMD_ARGC > 3) {
+ LOG_ERROR("Command takes at most 3 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ uint32_t count;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], count);
+ target_addr_t address;
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[1], address);
+ uint32_t size = 4;
+ if (CMD_ARGC > 2)
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], size);
+
+ if (count == 0)
+ return ERROR_OK;
+
+ uint8_t *buffer = malloc(size * count);
+ if (!buffer) {
+ LOG_ERROR("malloc failed");
+ return ERROR_FAIL;
+ }
+ int result = r->read_memory(target, address, size, count, buffer, 0);
+ if (result == ERROR_OK) {
+ target_handle_md_output(cmd, target, address, size, count, buffer,
+ false);
+ }
+ free(buffer);
+ return result;
+}
+
+COMMAND_HANDLER(handle_memory_sample_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 0) {
+ command_print(CMD, "Memory sample configuration for %s:", target_name(target));
+ for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) {
+ if (r->sample_config.bucket[i].enabled) {
+ command_print(CMD, "bucket %d; address=0x%" TARGET_PRIxADDR "; size=%d", i,
+ r->sample_config.bucket[i].address,
+ r->sample_config.bucket[i].size_bytes);
+ } else {
+ command_print(CMD, "bucket %d; disabled", i);
+ }
+ }
+ return ERROR_OK;
+ }
+
+ if (CMD_ARGC < 2) {
+ LOG_ERROR("Command requires at least bucket and address arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ uint32_t bucket;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], bucket);
+ if (bucket > ARRAY_SIZE(r->sample_config.bucket)) {
+ LOG_TARGET_ERROR(target, "Max bucket number is %zd.", ARRAY_SIZE(r->sample_config.bucket));
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+
+ if (!strcmp(CMD_ARGV[1], "clear")) {
+ r->sample_config.bucket[bucket].enabled = false;
+ } else {
+ COMMAND_PARSE_ADDRESS(CMD_ARGV[1], r->sample_config.bucket[bucket].address);
+
+ if (CMD_ARGC > 2) {
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], r->sample_config.bucket[bucket].size_bytes);
+ if (r->sample_config.bucket[bucket].size_bytes != 4 &&
+ r->sample_config.bucket[bucket].size_bytes != 8) {
+ LOG_TARGET_ERROR(target, "Only 4-byte and 8-byte sizes are supported.");
+ return ERROR_COMMAND_ARGUMENT_INVALID;
+ }
+ } else {
+ r->sample_config.bucket[bucket].size_bytes = 4;
+ }
+
+ r->sample_config.bucket[bucket].enabled = true;
+ }
+
+ if (!r->sample_buf.buf) {
+ r->sample_buf.size = 1024 * 1024;
+ r->sample_buf.buf = malloc(r->sample_buf.size);
+ }
+
+ /* Clear the buffer when the configuration is changed. */
+ r->sample_buf.used = 0;
+
+ r->sample_config.enabled = true;
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(handle_dump_sample_buf_command)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC > 1) {
+ LOG_ERROR("Command takes at most 1 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ bool base64 = false;
+ if (CMD_ARGC > 0) {
+ if (!strcmp(CMD_ARGV[0], "base64")) {
+ base64 = true;
+ } else {
+ LOG_ERROR("Unknown argument: %s", CMD_ARGV[0]);
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ }
+
+ int result = ERROR_OK;
+ if (base64) {
+ unsigned char *encoded = base64_encode(r->sample_buf.buf,
+ r->sample_buf.used, NULL);
+ if (!encoded) {
+ LOG_TARGET_ERROR(target, "Failed base64 encode!");
+ result = ERROR_FAIL;
+ goto error;
+ }
+ command_print(CMD, "%s", encoded);
+ free(encoded);
+ } else {
+ unsigned int i = 0;
+ while (i < r->sample_buf.used) {
+ uint8_t command = r->sample_buf.buf[i++];
+ if (command == RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE) {
+ uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "timestamp before: %u", timestamp);
+ } else if (command == RISCV_SAMPLE_BUF_TIMESTAMP_AFTER) {
+ uint32_t timestamp = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "timestamp after: %u", timestamp);
+ } else if (command < ARRAY_SIZE(r->sample_config.bucket)) {
+ command_print_sameline(CMD, "0x%" TARGET_PRIxADDR ": ",
+ r->sample_config.bucket[command].address);
+ if (r->sample_config.bucket[command].size_bytes == 4) {
+ uint32_t value = buf_get_u32(r->sample_buf.buf + i, 0, 32);
+ i += 4;
+ command_print(CMD, "0x%08" PRIx32, value);
+ } else if (r->sample_config.bucket[command].size_bytes == 8) {
+ uint64_t value = buf_get_u64(r->sample_buf.buf + i, 0, 64);
+ i += 8;
+ command_print(CMD, "0x%016" PRIx64, value);
+ } else {
+ LOG_TARGET_ERROR(target, "Found invalid size in bucket %d: %d", command,
+ r->sample_config.bucket[command].size_bytes);
+ result = ERROR_FAIL;
+ goto error;
+ }
+ } else {
+ LOG_TARGET_ERROR(target, "Found invalid command byte in sample buf: 0x%2x at offset 0x%x",
+ command, i - 1);
+ result = ERROR_FAIL;
+ goto error;
+ }
+ }
+ }
+
+error:
+ /* Clear the sample buffer even when there was an error. */
+ r->sample_buf.used = 0;
+ return result;
+}
+
+static COMMAND_HELPER(riscv_print_info_line_if_available, const char *section,
+ const char *key, unsigned int value, bool is_available)
{
char full_key[80];
snprintf(full_key, sizeof(full_key), "%s.%s", section, key);
- command_print(CMD, "%-21s %3d", full_key, value);
+ if (is_available)
+ command_print(CMD, "%-21s %3d", full_key, value);
+ else
+ command_print(CMD, "%-21s unavailable", full_key);
return 0;
}
+COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
+ unsigned int value)
+{
+ return CALL_COMMAND_HANDLER(riscv_print_info_line_if_available, section,
+ key, value, /*is_available*/ true);
+}
+
COMMAND_HANDLER(handle_info)
{
struct target *target = get_current_target(CMD_CTX);
@@ -2851,25 +4912,143 @@ COMMAND_HANDLER(handle_info)
/* This output format can be fed directly into TCL's "array set". */
riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target));
- riscv_enumerate_triggers(target);
- riscv_print_info_line(CMD, "hart", "trigger_count",
- r->trigger_count);
+ const bool trigger_count_available =
+ riscv_enumerate_triggers(target) == ERROR_OK;
+ riscv_print_info_line_if_available(CMD, "hart", "trigger_count",
+ r->trigger_count, trigger_count_available);
if (r->print_info)
return CALL_COMMAND_HANDLER(r->print_info, target);
return 0;
}
+COMMAND_HANDLER(riscv_exec_progbuf)
+{
+ if (CMD_ARGC < 1 || CMD_ARGC > 16) {
+ LOG_ERROR("Command 'exec_progbuf' takes 1 to 16 arguments.");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ struct target *target = get_current_target(CMD_CTX);
+
+ RISCV_INFO(r);
+ if (r->dtm_version != DTM_DTMCS_VERSION_1_0) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer is "
+ "only supported on v0.13 or v1.0 targets.");
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Can't execute "
+ "program buffer, target not halted.");
+ return ERROR_FAIL;
+ }
+
+ if (riscv_progbuf_size(target) == 0) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer not implemented "
+ "in the target.");
+ return ERROR_FAIL;
+ }
+
+ struct riscv_program prog;
+ riscv_program_init(&prog, target);
+
+ for (unsigned int i = 0; i < CMD_ARGC; i++) {
+ riscv_insn_t instr;
+ COMMAND_PARSE_NUMBER(u32, CMD_ARGV[i], instr);
+ if (riscv_program_insert(&prog, instr) != ERROR_OK)
+ return ERROR_FAIL;
+ }
+
+ if (riscv_flush_registers(target) != ERROR_OK)
+ return ERROR_FAIL;
+ int error = riscv_program_exec(&prog, target);
+ riscv_invalidate_register_cache(target);
+
+ if (error != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "exec_progbuf: Program buffer execution failed.");
+ return ERROR_FAIL;
+ }
+
+ LOG_TARGET_DEBUG(target, "exec_progbuf: Program buffer execution successful.");
+
+ return ERROR_OK;
+}
+
+COMMAND_HANDLER(riscv_set_enable_trigger_feature)
+{
+ struct target *target = get_current_target(CMD_CTX);
+ RISCV_INFO(r);
+
+ if (CMD_ARGC == 2) {
+ bool enable_for_wp = true;
+
+ if (!strcmp(CMD_ARGV[1], "wp"))
+ enable_for_wp = true;
+ else if (!strcmp(CMD_ARGV[1], "none"))
+ enable_for_wp = false;
+ else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ if (!strcmp(CMD_ARGV[0], "all")) {
+ r->wp_allow_equality_match_trigger = enable_for_wp;
+ r->wp_allow_napot_trigger = enable_for_wp;
+ r->wp_allow_ge_lt_trigger = enable_for_wp;
+ } else if (!strcmp(CMD_ARGV[0], "eq")) {
+ r->wp_allow_equality_match_trigger = enable_for_wp;
+ } else if (!strcmp(CMD_ARGV[0], "napot")) {
+ r->wp_allow_napot_trigger = enable_for_wp;
+ } else if (!strcmp(CMD_ARGV[0], "ge_lt")) {
+ r->wp_allow_ge_lt_trigger = enable_for_wp;
+ } else {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+ } else if (CMD_ARGC != 0) {
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ command_print(CMD, "Triggers feature configuration:\n"
+ "Equality match trigger: for wp (%s)\n"
+ "NAPOT trigger: for wp (%s)\n"
+ "ge-lt chained triggers: for wp (%s)",
+ r->wp_allow_equality_match_trigger ? "enabled" : "disabled",
+ r->wp_allow_napot_trigger ? "enabled" : "disabled",
+ r->wp_allow_ge_lt_trigger ? "enabled" : "disabled");
+
+ return ERROR_OK;
+}
+
static const struct command_registration riscv_exec_command_handlers[] = {
{
+ .name = "dump_sample_buf",
+ .handler = handle_dump_sample_buf_command,
+ .mode = COMMAND_ANY,
+ .usage = "[base64]",
+ .help = "Print the contents of the sample buffer, and clear the buffer."
+ },
+ {
.name = "info",
.handler = handle_info,
- .mode = COMMAND_EXEC,
+ .mode = COMMAND_ANY,
.usage = "",
.help = "Displays some information OpenOCD detected about the target."
},
{
+ .name = "memory_sample",
+ .handler = handle_memory_sample_command,
+ .mode = COMMAND_ANY,
+ .usage = "bucket address|clear [size=4]",
+ .help = "Causes OpenOCD to frequently read size bytes at the given address."
+ },
+ {
+ .name = "repeat_read",
+ .handler = handle_repeat_read,
+ .mode = COMMAND_ANY,
+ .usage = "count address [size=4]",
+ .help = "Repeatedly read the value at address."
+ },
+ {
.name = "set_command_timeout_sec",
.handler = riscv_set_command_timeout_sec,
.mode = COMMAND_ANY,
@@ -2919,6 +5098,16 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"etc. This must be executed before `init`."
},
{
+ .name = "hide_csrs",
+ .handler = riscv_hide_csrs,
+ .mode = COMMAND_CONFIG,
+ .usage = "{n0|n-m0}[,n1|n-m1]......",
+ .help = "Configure a list of inclusive ranges for CSRs to hide from gdb. "
+ "Hidden registers are still available, but are not listed in "
+ "gdb target description and `reg` command output. "
+ "This must be executed before `init`."
+ },
+ {
.name = "authdata_read",
.handler = riscv_authdata_read,
.usage = "[index]",
@@ -2936,17 +5125,34 @@ static const struct command_registration riscv_exec_command_handlers[] = {
},
{
.name = "dmi_read",
- .handler = riscv_dmi_read,
+ .handler = handle_riscv_dmi_read,
.mode = COMMAND_ANY,
.usage = "address",
- .help = "Perform a 32-bit DMI read at address, returning the value."
+ .help = "Read and return 32-bit value from the given address on the "
+ "RISC-V DMI bus."
},
{
.name = "dmi_write",
- .handler = riscv_dmi_write,
+ .handler = handle_riscv_dmi_write,
.mode = COMMAND_ANY,
.usage = "address value",
- .help = "Perform a 32-bit DMI write of value at address."
+ .help = "Write a 32-bit value to the given address on the RISC-V DMI bus."
+ },
+ {
+ .name = "dm_read",
+ .handler = handle_riscv_dm_read,
+ .mode = COMMAND_ANY,
+ .usage = "reg_address",
+ .help = "Read and return 32-bit value from a debug module's register "
+ "at reg_address."
+ },
+ {
+ .name = "dm_write",
+ .handler = handle_riscv_dm_write,
+ .mode = COMMAND_ANY,
+ .usage = "reg_address value",
+ .help = "Write a 32-bit value to the debug module's register at "
+ "reg_address."
},
{
.name = "reset_delays",
@@ -2986,6 +5192,22 @@ static const struct command_registration riscv_exec_command_handlers[] = {
"1: DATA_REGISTER}"
},
{
+ .name = "set_bscan_tunnel_ir",
+ .handler = riscv_set_bscan_tunnel_ir,
+ .mode = COMMAND_ANY,
+ .usage = "value",
+ .help = "Specify the JTAG TAP IR used to access the bscan tunnel. "
+ "By default it is 0x23 << (ir_length - 6), which map some "
+ "Xilinx FPGA (IR USER4)"
+ },
+ {
+ .name = "set_maskisr",
+ .handler = riscv_set_maskisr,
+ .mode = COMMAND_EXEC,
+ .help = "mask riscv interrupts",
+ .usage = "['off'|'steponly']",
+ },
+ {
.name = "set_enable_virt2phys",
.handler = riscv_set_enable_virt2phys,
.mode = COMMAND_ANY,
@@ -2997,7 +5219,7 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.name = "set_ebreakm",
.handler = riscv_set_ebreakm,
.mode = COMMAND_ANY,
- .usage = "on|off",
+ .usage = "[on|off]",
.help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions "
"don't trap to OpenOCD. Defaults to on."
},
@@ -3005,7 +5227,7 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.name = "set_ebreaks",
.handler = riscv_set_ebreaks,
.mode = COMMAND_ANY,
- .usage = "on|off",
+ .usage = "[on|off]",
.help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions "
"don't trap to OpenOCD. Defaults to on."
},
@@ -3013,10 +5235,46 @@ static const struct command_registration riscv_exec_command_handlers[] = {
.name = "set_ebreaku",
.handler = riscv_set_ebreaku,
.mode = COMMAND_ANY,
- .usage = "on|off",
+ .usage = "[on|off]",
.help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions "
"don't trap to OpenOCD. Defaults to on."
},
+ {
+ .name = "etrigger",
+ .handler = riscv_etrigger,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [m] [s] [u] <exception_codes>|clear",
+ .help = "Set or clear a single exception trigger."
+ },
+ {
+ .name = "icount",
+ .handler = riscv_icount,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [m] [s] [u] [pending] <count>|clear",
+ .help = "Set or clear a single instruction count trigger."
+ },
+ {
+ .name = "itrigger",
+ .handler = riscv_itrigger,
+ .mode = COMMAND_EXEC,
+ .usage = "set [vs] [vu] [nmi] [m] [s] [u] <mie_bits>|clear",
+ .help = "Set or clear a single interrupt trigger."
+ },
+ {
+ .name = "exec_progbuf",
+ .handler = riscv_exec_progbuf,
+ .mode = COMMAND_EXEC,
+ .usage = "instr1 [instr2 [... instr16]]",
+ .help = "Execute a sequence of 32-bit instructions using the program buffer. "
+ "The final ebreak instruction is added automatically, if needed."
+ },
+ {
+ .name = "set_enable_trigger_feature",
+ .handler = riscv_set_enable_trigger_feature,
+ .mode = COMMAND_ANY,
+ .usage = "[('eq'|'napot'|'ge_lt'|'all') ('wp'|'none')]",
+ .help = "Control whether OpenOCD is allowed to use certain RISC-V trigger features for watchpoints."
+ },
COMMAND_REGISTRATION_DONE
};
@@ -3049,10 +5307,13 @@ static const struct command_registration riscv_command_handlers[] = {
.usage = "",
.chain = semihosting_common_handlers
},
+ {
+ .chain = smp_command_handlers
+ },
COMMAND_REGISTRATION_DONE
};
-static unsigned riscv_xlen_nonconst(struct target *target)
+static unsigned int riscv_xlen_nonconst(struct target *target)
{
return riscv_xlen(target);
}
@@ -3123,14 +5384,15 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
r->common_magic = RISCV_COMMON_MAGIC;
- r->dtm_version = 1;
- r->current_hartid = target->coreid;
+ r->dtm_version = DTM_DTMCS_VERSION_UNKNOWN;
r->version_specific = NULL;
memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id));
r->xlen = -1;
+ r->isrmask_mode = RISCV_ISRMASK_OFF;
+
r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF;
r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS;
r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT;
@@ -3141,54 +5403,101 @@ static void riscv_info_init(struct target *target, struct riscv_info *r)
INIT_LIST_HEAD(&r->expose_csr);
INIT_LIST_HEAD(&r->expose_custom);
+ INIT_LIST_HEAD(&r->hide_csr);
+
+ r->vsew64_supported = YNM_MAYBE;
+
+ r->riscv_ebreakm = true;
+ r->riscv_ebreaks = true;
+ r->riscv_ebreaku = true;
+
+ r->wp_allow_equality_match_trigger = true;
+ r->wp_allow_ge_lt_trigger = true;
+ r->wp_allow_napot_trigger = true;
}
static int riscv_resume_go_all_harts(struct target *target)
{
RISCV_INFO(r);
- LOG_DEBUG("[%s] resuming hart", target_name(target));
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- if (riscv_is_halted(target)) {
+ LOG_TARGET_DEBUG(target, "Resuming hart, state=%d.", target->state);
+ if (target->state == TARGET_HALTED) {
if (r->resume_go(target) != ERROR_OK)
return ERROR_FAIL;
} else {
- LOG_DEBUG("[%s] hart requested resume, but was already resumed",
- target_name(target));
+ LOG_TARGET_DEBUG(target, "Hart requested resume, but was already resumed.");
}
riscv_invalidate_register_cache(target);
return ERROR_OK;
}
-/* Steps the hart that's currently selected in the RTOS, or if there is no RTOS
- * then the only hart. */
+int riscv_interrupts_disable(struct target *target, uint64_t irq_mask, uint64_t *old_mstatus)
+{
+ LOG_TARGET_DEBUG(target, "Disabling interrupts.");
+ struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+ "mstatus", true);
+ if (!reg_mstatus) {
+ LOG_TARGET_ERROR(target, "Couldn't find mstatus!");
+ return ERROR_FAIL;
+ }
+
+ int retval = reg_mstatus->type->get(reg_mstatus);
+ if (retval != ERROR_OK)
+ return retval;
+
+ RISCV_INFO(info);
+ uint8_t mstatus_bytes[8] = { 0 };
+ uint64_t current_mstatus = buf_get_u64(reg_mstatus->value, 0, reg_mstatus->size);
+ buf_set_u64(mstatus_bytes, 0, info->xlen, set_field(current_mstatus,
+ irq_mask, 0));
+
+ retval = reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+ if (retval != ERROR_OK)
+ return retval;
+
+ if (old_mstatus)
+ *old_mstatus = current_mstatus;
+
+ return ERROR_OK;
+}
+
+int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus)
+{
+ LOG_TARGET_DEBUG(target, "Restoring interrupts.");
+ struct reg *reg_mstatus = register_get_by_name(target->reg_cache,
+ "mstatus", true);
+ if (!reg_mstatus) {
+ LOG_TARGET_ERROR(target, "Couldn't find mstatus!");
+ return ERROR_FAIL;
+ }
+
+ RISCV_INFO(info);
+ uint8_t mstatus_bytes[8];
+ buf_set_u64(mstatus_bytes, 0, info->xlen, old_mstatus);
+ return reg_mstatus->type->set(reg_mstatus, mstatus_bytes);
+}
+
static int riscv_step_rtos_hart(struct target *target)
{
RISCV_INFO(r);
- if (riscv_select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
- LOG_DEBUG("[%s] stepping", target_name(target));
+ LOG_TARGET_DEBUG(target, "Stepping.");
- if (!riscv_is_halted(target)) {
- LOG_ERROR("Hart isn't halted before single step!");
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Hart isn't halted before single step!");
return ERROR_FAIL;
}
- riscv_invalidate_register_cache(target);
r->on_step(target);
if (r->step_current_hart(target) != ERROR_OK)
return ERROR_FAIL;
- riscv_invalidate_register_cache(target);
- r->on_halt(target);
- if (!riscv_is_halted(target)) {
- LOG_ERROR("Hart was not halted after single step!");
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Hart was not halted after single step!");
return ERROR_FAIL;
}
return ERROR_OK;
}
-bool riscv_supports_extension(struct target *target, char letter)
+bool riscv_supports_extension(const struct target *target, char letter)
{
RISCV_INFO(r);
unsigned num;
@@ -3207,47 +5516,23 @@ unsigned riscv_xlen(const struct target *target)
return r->xlen;
}
-int riscv_set_current_hartid(struct target *target, int hartid)
+unsigned int riscv_vlenb(const struct target *target)
{
RISCV_INFO(r);
- if (!r->select_current_hart)
- return ERROR_OK;
-
- int previous_hartid = riscv_current_hartid(target);
- r->current_hartid = hartid;
- LOG_DEBUG("setting hartid to %d, was %d", hartid, previous_hartid);
- if (r->select_current_hart(target) != ERROR_OK)
- return ERROR_FAIL;
-
- return ERROR_OK;
+ return r->vlenb;
}
-/* Invalidates the register cache. */
static void riscv_invalidate_register_cache(struct target *target)
{
- LOG_DEBUG("[%d]", target->coreid);
- register_cache_invalidate(target->reg_cache);
- for (size_t i = 0; i < target->reg_cache->num_regs; ++i) {
- struct reg *reg = &target->reg_cache->reg_list[i];
- reg->valid = false;
- }
-}
+ /* Do not invalidate the register cache if it is not yet set up
+ * (e.g. when the target failed to get examined). */
+ if (!target->reg_cache)
+ return;
-int riscv_current_hartid(const struct target *target)
-{
- RISCV_INFO(r);
- return r->current_hartid;
+ LOG_TARGET_DEBUG(target, "Invalidating register cache.");
+ register_cache_invalidate(target->reg_cache);
}
-int riscv_count_harts(struct target *target)
-{
- if (!target)
- return 1;
- RISCV_INFO(r);
- if (!r || !r->hart_count)
- return 1;
- return r->hart_count(target);
-}
/**
* If write is true:
@@ -3257,8 +5542,11 @@ int riscv_count_harts(struct target *target)
* return true iff we are guaranteed that the register will read the same
* value in the future as the value we just read.
*/
-static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
+static bool gdb_regno_cacheable(enum gdb_regno regno, bool is_write)
{
+ if (regno == GDB_REGNO_ZERO)
+ return !is_write;
+
/* GPRs, FPRs, vector registers are just normal data stores. */
if (regno <= GDB_REGNO_XPR31 ||
(regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
@@ -3269,8 +5557,6 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
* CSRs. */
switch (regno) {
case GDB_REGNO_DPC:
- return true;
-
case GDB_REGNO_VSTART:
case GDB_REGNO_VXSAT:
case GDB_REGNO_VXRM:
@@ -3288,150 +5574,342 @@ static bool gdb_regno_cacheable(enum gdb_regno regno, bool write)
* WARL registers might not contain the value we just wrote, but
* these ones won't spontaneously change their value either. *
*/
- return !write;
+ return !is_write;
case GDB_REGNO_TSELECT: /* I think this should be above, but then it doesn't work. */
case GDB_REGNO_TDATA1: /* Changes value when tselect is changed. */
- case GDB_REGNO_TDATA2: /* Changse value when tselect is changed. */
+ case GDB_REGNO_TDATA2: /* Changes value when tselect is changed. */
default:
return false;
}
}
/**
- * This function is called when the debug user wants to change the value of a
- * register. The new value may be cached, and may not be written until the hart
- * is resumed. */
-int riscv_set_register(struct target *target, enum gdb_regno regid, riscv_reg_t value)
+ * This function is used internally by functions that change register values.
+ * If `write_through` is true, it is ensured that the value of the target's
+ * register is set to be equal to the `value` argument. The cached value is
+ * updated if the register is cacheable.
+ */
+static int riscv_set_or_write_register(struct target *target,
+ enum gdb_regno regid, riscv_reg_t value, bool write_through)
{
RISCV_INFO(r);
- LOG_DEBUG("[%s] %s <- %" PRIx64, target_name(target), gdb_regno_name(regid), value);
assert(r->set_register);
keep_alive();
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 && value == 0 &&
- riscv_supports_extension(target, 'E'))
- return ERROR_OK;
+ if (regid == GDB_REGNO_PC) {
+ return riscv_set_or_write_register(target, GDB_REGNO_DPC, value, write_through);
+ } else if (regid == GDB_REGNO_PRIV) {
+ riscv_reg_t dcsr;
+
+ if (riscv_get_register(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK)
+ return ERROR_FAIL;
+ dcsr = set_field(dcsr, CSR_DCSR_PRV, get_field(value, VIRT_PRIV_PRV));
+ dcsr = set_field(dcsr, CSR_DCSR_V, get_field(value, VIRT_PRIV_V));
+ return riscv_set_or_write_register(target, GDB_REGNO_DCSR, dcsr, write_through);
+ }
+
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ LOG_TARGET_DEBUG(target,
+ "No cache, writing to target: %s <- 0x%" PRIx64,
+ gdb_regno_name(target, regid), value);
+ return r->set_register(target, regid, value);
+ }
+
+ struct reg *reg = get_reg_cache_entry(target, regid);
+
+ if (!reg->exist) {
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
+ return ERROR_FAIL;
+ }
+
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_DEBUG(target,
+ "Target not halted, writing to target: %s <- 0x%" PRIx64,
+ reg->name, value);
+ return r->set_register(target, regid, value);
+ }
+
+ const bool need_to_write = !reg->valid || reg->dirty ||
+ value != buf_get_u64(reg->value, 0, reg->size);
+ const bool cacheable = gdb_regno_cacheable(regid, need_to_write);
+
+ if (!cacheable || (write_through && need_to_write)) {
+ LOG_TARGET_DEBUG(target,
+ "Writing to target: %s <- 0x%" PRIx64 " (cacheable=%s, valid=%s, dirty=%s)",
+ reg->name, value, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ if (r->set_register(target, regid, value) != ERROR_OK)
+ return ERROR_FAIL;
+
+ reg->dirty = false;
+ } else {
+ reg->dirty = need_to_write;
+ }
- struct reg *reg = &target->reg_cache->reg_list[regid];
buf_set_u64(reg->value, 0, reg->size, value);
+ reg->valid = cacheable;
- int result = r->set_register(target, regid, value);
- if (result == ERROR_OK)
- reg->valid = gdb_regno_cacheable(regid, true);
- else
- reg->valid = false;
- LOG_DEBUG("[%s] wrote 0x%" PRIx64 " to %s valid=%d",
- target_name(target), value, reg->name, reg->valid);
- return result;
+ LOG_TARGET_DEBUG(target,
+ "Wrote 0x%" PRIx64 " to %s (cacheable=%s, valid=%s, dirty=%s)",
+ value, reg->name, cacheable ? "true" : "false",
+ reg->valid ? "true" : "false",
+ reg->dirty ? "true" : "false");
+ return ERROR_OK;
+}
+
+/**
+ * This function is used to change the value of a register. The new value may
+ * be cached, and may not be written until the hart is resumed.
+ */
+int riscv_set_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ false);
+}
+
+/**
+ * This function is used to change the value of a register. The new value may
+ * be cached, but it will be written to hart immediately.
+ */
+int riscv_write_register(struct target *target, enum gdb_regno regid,
+ riscv_reg_t value)
+{
+ return riscv_set_or_write_register(target, regid, value,
+ /* write_through */ true);
}
+/**
+ * This function is used to get the value of a register. If possible, the value
+ * in cache will be updated.
+ */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno regid)
{
RISCV_INFO(r);
+ assert(r->get_register);
keep_alive();
- struct reg *reg = &target->reg_cache->reg_list[regid];
+ if (regid == GDB_REGNO_PC)
+ return riscv_get_register(target, value, GDB_REGNO_DPC);
+
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ LOG_TARGET_DEBUG(target, "No cache, reading %s from target",
+ gdb_regno_name(target, regid));
+ return r->get_register(target, value, regid);
+ }
+
+ struct reg *reg = get_reg_cache_entry(target, regid);
if (!reg->exist) {
- LOG_DEBUG("[%s] %s does not exist.",
- target_name(target), gdb_regno_name(regid));
+ LOG_TARGET_DEBUG(target, "Register %s does not exist.", reg->name);
return ERROR_FAIL;
}
- if (reg && reg->valid) {
+ if (reg->valid) {
*value = buf_get_u64(reg->value, 0, reg->size);
- LOG_DEBUG("[%s] %s: %" PRIx64 " (cached)", target_name(target),
- gdb_regno_name(regid), *value);
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64 " (cached)", reg->name,
+ *value);
return ERROR_OK;
}
- /* TODO: Hack to deal with gdb that thinks these registers still exist. */
- if (regid > GDB_REGNO_XPR15 && regid <= GDB_REGNO_XPR31 &&
- riscv_supports_extension(target, 'E')) {
- *value = 0;
+ LOG_TARGET_DEBUG(target, "Reading %s from target", reg->name);
+ if (r->get_register(target, value, regid) != ERROR_OK)
+ return ERROR_FAIL;
+
+ buf_set_u64(reg->value, 0, reg->size, *value);
+ reg->valid = gdb_regno_cacheable(regid, /* is write? */ false) &&
+ target->state == TARGET_HALTED;
+ reg->dirty = false;
+
+ LOG_TARGET_DEBUG(target, "Read %s: 0x%" PRIx64, reg->name, *value);
+ return ERROR_OK;
+}
+
+/**
+ * This function is used to save the value of a register in cache. The register
+ * is marked as dirty, and writeback is delayed for as long as possible.
+ */
+int riscv_save_register(struct target *target, enum gdb_regno regid)
+{
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Can't save register %s on a hart that is not halted.",
+ gdb_regno_name(target, regid));
+ return ERROR_FAIL;
+ }
+ assert(gdb_regno_cacheable(regid, /* is write? */ false) &&
+ "Only cacheable registers can be saved.");
+
+ RISCV_INFO(r);
+ riscv_reg_t value;
+ if (!target->reg_cache) {
+ assert(!target_was_examined(target));
+ /* To create register cache it is needed to examine the target first,
+ * therefore during examine, any changed register needs to be saved
+ * and restored manually.
+ */
return ERROR_OK;
}
- int result = r->get_register(target, value, regid);
+ struct reg *reg = get_reg_cache_entry(target, regid);
- if (result == ERROR_OK)
- reg->valid = gdb_regno_cacheable(regid, false);
+ LOG_TARGET_DEBUG(target, "Saving %s", reg->name);
+ if (riscv_get_register(target, &value, regid) != ERROR_OK)
+ return ERROR_FAIL;
- LOG_DEBUG("[%s] %s: %" PRIx64, target_name(target),
- gdb_regno_name(regid), *value);
- return result;
+ assert(reg->valid &&
+ "The register is cacheable, so the cache entry must be valid now.");
+ /* Mark the register dirty. We assume that this function is called
+ * because the caller is about to mess with the underlying value of the
+ * register. */
+ reg->dirty = true;
+
+ r->last_activity = timeval_ms();
+
+ return ERROR_OK;
}
-bool riscv_is_halted(struct target *target)
+int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state)
{
RISCV_INFO(r);
- assert(r->is_halted);
- return r->is_halted(target);
+ assert(r->get_hart_state);
+ return r->get_hart_state(target, state);
}
-static enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid)
+static enum riscv_halt_reason riscv_halt_reason(struct target *target)
{
RISCV_INFO(r);
- if (riscv_set_current_hartid(target, hartid) != ERROR_OK)
- return RISCV_HALT_ERROR;
- if (!riscv_is_halted(target)) {
- LOG_ERROR("Hart is not halted!");
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Hart is not halted!");
return RISCV_HALT_UNKNOWN;
}
return r->halt_reason(target);
}
-size_t riscv_debug_buffer_size(struct target *target)
+size_t riscv_progbuf_size(struct target *target)
{
RISCV_INFO(r);
- return r->debug_buffer_size;
+ return r->progbuf_size;
}
-int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn)
+int riscv_write_progbuf(struct target *target, int index, riscv_insn_t insn)
{
RISCV_INFO(r);
- r->write_debug_buffer(target, index, insn);
+ r->write_progbuf(target, index, insn);
return ERROR_OK;
}
-riscv_insn_t riscv_read_debug_buffer(struct target *target, int index)
+riscv_insn_t riscv_read_progbuf(struct target *target, int index)
{
RISCV_INFO(r);
- return r->read_debug_buffer(target, index);
+ return r->read_progbuf(target, index);
}
-int riscv_execute_debug_buffer(struct target *target)
+int riscv_execute_progbuf(struct target *target, uint32_t *cmderr)
{
RISCV_INFO(r);
- return r->execute_debug_buffer(target);
+ return r->execute_progbuf(target, cmderr);
}
-void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d)
+void riscv_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d)
{
RISCV_INFO(r);
- r->fill_dmi_write_u64(target, buf, a, d);
+ r->fill_dm_write(target, buf, a, d);
}
-void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a)
+void riscv_fill_dm_read(struct target *target, char *buf, uint64_t a)
{
RISCV_INFO(r);
- r->fill_dmi_read_u64(target, buf, a);
+ r->fill_dm_read(target, buf, a);
}
-void riscv_fill_dmi_nop_u64(struct target *target, char *buf)
+void riscv_fill_dm_nop(struct target *target, char *buf)
{
RISCV_INFO(r);
- r->fill_dmi_nop_u64(target, buf);
+ r->fill_dm_nop(target, buf);
}
-int riscv_dmi_write_u64_bits(struct target *target)
+int riscv_get_dmi_scan_length(struct target *target)
{
RISCV_INFO(r);
- return r->dmi_write_u64_bits(target);
+ return r->get_dmi_scan_length(target);
+}
+
+static int check_if_trigger_exists(struct target *target, unsigned int index)
+{
+ /* If we can't write tselect, then this hart does not support triggers. */
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, index) != ERROR_OK)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ riscv_reg_t tselect_rb;
+ if (riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT) != ERROR_OK)
+ return ERROR_FAIL;
+ /* Mask off the top bit, which is used as tdrmode in legacy RISC-V Debug Spec
+ * (old revisions of v0.11 spec). */
+ tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
+ if (tselect_rb != index)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ return ERROR_OK;
+}
+
+/**
+ * This function reads `tinfo` or `tdata1`, when reading `tinfo` fails,
+ * to determine trigger types supported by a trigger.
+ * It is assumed that the trigger is already selected via writing `tselect`.
+ */
+static int get_trigger_types(struct target *target, unsigned int *trigger_tinfo,
+ riscv_reg_t tdata1)
+{
+ assert(trigger_tinfo);
+ riscv_reg_t tinfo;
+ if (riscv_get_register(target, &tinfo, GDB_REGNO_TINFO) == ERROR_OK) {
+ /* tinfo.INFO == 1: trigger doesn’t exist
+ * tinfo == 0 or tinfo.INFO != 1 and tinfo LSB is set: invalid tinfo */
+ if (tinfo == 0 || tinfo & 0x1)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ *trigger_tinfo = tinfo;
+ return ERROR_OK;
+ }
+ const unsigned int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)));
+ if (type == 0)
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ *trigger_tinfo = 1 << type;
+ return ERROR_OK;
+}
+
+static int disable_trigger_if_dmode(struct target *target, riscv_reg_t tdata1)
+{
+ bool dmode_is_set = false;
+ switch (get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target)))) {
+ case CSR_TDATA1_TYPE_LEGACY:
+ /* On these older cores we don't support software using
+ * triggers. */
+ dmode_is_set = true;
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL:
+ dmode_is_set = tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_MCONTROL6:
+ dmode_is_set = tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_ICOUNT:
+ dmode_is_set = tdata1 & CSR_ICOUNT_DMODE(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_ITRIGGER:
+ dmode_is_set = tdata1 & CSR_ITRIGGER_DMODE(riscv_xlen(target));
+ break;
+ case CSR_TDATA1_TYPE_ETRIGGER:
+ dmode_is_set = tdata1 & CSR_ETRIGGER_DMODE(riscv_xlen(target));
+ break;
+ }
+ if (!dmode_is_set)
+ /* Nothing to do */
+ return ERROR_OK;
+ return riscv_set_register(target, GDB_REGNO_TDATA1, 0);
}
/**
@@ -3440,7 +5918,7 @@ int riscv_dmi_write_u64_bits(struct target *target)
* something.
* Disable any hardware triggers that have dmode set. We can't have set them
* ourselves. Maybe they're left over from some killed debug session.
- * */
+ */
int riscv_enumerate_triggers(struct target *target)
{
RISCV_INFO(r);
@@ -3448,264 +5926,254 @@ int riscv_enumerate_triggers(struct target *target)
if (r->triggers_enumerated)
return ERROR_OK;
- r->triggers_enumerated = true; /* At the very least we tried. */
+ if (target->state != TARGET_HALTED) {
+ LOG_TARGET_ERROR(target, "Unable to enumerate triggers: target not halted.");
+ return ERROR_FAIL;
+ }
- riscv_reg_t tselect;
- int result = riscv_get_register(target, &tselect, GDB_REGNO_TSELECT);
+ riscv_reg_t orig_tselect;
+ int result = riscv_get_register(target, &orig_tselect, GDB_REGNO_TSELECT);
/* If tselect is not readable, the trigger module is likely not
- * implemented. There are no triggers to enumerate then and no error
- * should be thrown. */
+ * implemented. */
if (result != ERROR_OK) {
- LOG_DEBUG("[%s] Cannot access tselect register. "
- "Assuming that triggers are not implemented.", target_name(target));
+ LOG_TARGET_INFO(target, "Cannot access tselect register. "
+ "Assuming that triggers are not implemented.");
+ r->triggers_enumerated = true;
r->trigger_count = 0;
return ERROR_OK;
}
- for (unsigned int t = 0; t < RISCV_MAX_TRIGGERS; ++t) {
- r->trigger_count = t;
-
- /* If we can't write tselect, then this hart does not support triggers. */
- if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK)
- break;
- uint64_t tselect_rb;
- result = riscv_get_register(target, &tselect_rb, GDB_REGNO_TSELECT);
- if (result != ERROR_OK)
- return result;
- /* Mask off the top bit, which is used as tdrmode in old
- * implementations. */
- tselect_rb &= ~(1ULL << (riscv_xlen(target) - 1));
- if (tselect_rb != t)
+ unsigned int t = 0;
+ for (; t < ARRAY_SIZE(r->trigger_tinfo); ++t) {
+ result = check_if_trigger_exists(target, t);
+ if (result == ERROR_FAIL)
+ return ERROR_FAIL;
+ if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
break;
- uint64_t tdata1;
- result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1);
- if (result != ERROR_OK)
- return result;
- int type = get_field(tdata1, MCONTROL_TYPE(riscv_xlen(target)));
- if (type == 0)
+ riscv_reg_t tdata1;
+ if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK)
+ return ERROR_FAIL;
+
+ result = get_trigger_types(target, &r->trigger_tinfo[t], tdata1);
+ if (result == ERROR_FAIL)
+ return ERROR_FAIL;
+ if (result == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
break;
- switch (type) {
- case 1:
- /* On these older cores we don't support software using
- * triggers. */
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- break;
- case 2:
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- break;
- case 6:
- if (tdata1 & MCONTROL_DMODE(riscv_xlen(target)))
- riscv_set_register(target, GDB_REGNO_TDATA1, 0);
- break;
- }
- }
- riscv_set_register(target, GDB_REGNO_TSELECT, tselect);
+ LOG_TARGET_DEBUG(target, "Trigger %u: supported types (mask) = 0x%08x",
+ t, r->trigger_tinfo[t]);
+
+ if (disable_trigger_if_dmode(target, tdata1) != ERROR_OK)
+ return ERROR_FAIL;
+ }
- LOG_INFO("[%s] Found %d triggers", target_name(target), r->trigger_count);
+ if (riscv_set_register(target, GDB_REGNO_TSELECT, orig_tselect) != ERROR_OK)
+ return ERROR_FAIL;
+ r->triggers_enumerated = true;
+ r->trigger_count = t;
+ LOG_TARGET_INFO(target, "Found %d triggers", r->trigger_count);
+ create_wp_trigger_cache(target);
return ERROR_OK;
}
-const char *gdb_regno_name(enum gdb_regno regno)
+static char *init_reg_name(const char *name)
{
- static char buf[32];
+ const int size_buf = strlen(name) + 1;
- switch (regno) {
- case GDB_REGNO_ZERO:
- return "zero";
- case GDB_REGNO_RA:
- return "ra";
- case GDB_REGNO_SP:
- return "sp";
- case GDB_REGNO_GP:
- return "gp";
- case GDB_REGNO_TP:
- return "tp";
- case GDB_REGNO_T0:
- return "t0";
- case GDB_REGNO_T1:
- return "t1";
- case GDB_REGNO_T2:
- return "t2";
- case GDB_REGNO_S0:
- return "s0";
- case GDB_REGNO_S1:
- return "s1";
- case GDB_REGNO_A0:
- return "a0";
- case GDB_REGNO_A1:
- return "a1";
- case GDB_REGNO_A2:
- return "a2";
- case GDB_REGNO_A3:
- return "a3";
- case GDB_REGNO_A4:
- return "a4";
- case GDB_REGNO_A5:
- return "a5";
- case GDB_REGNO_A6:
- return "a6";
- case GDB_REGNO_A7:
- return "a7";
- case GDB_REGNO_S2:
- return "s2";
- case GDB_REGNO_S3:
- return "s3";
- case GDB_REGNO_S4:
- return "s4";
- case GDB_REGNO_S5:
- return "s5";
- case GDB_REGNO_S6:
- return "s6";
- case GDB_REGNO_S7:
- return "s7";
- case GDB_REGNO_S8:
- return "s8";
- case GDB_REGNO_S9:
- return "s9";
- case GDB_REGNO_S10:
- return "s10";
- case GDB_REGNO_S11:
- return "s11";
- case GDB_REGNO_T3:
- return "t3";
- case GDB_REGNO_T4:
- return "t4";
- case GDB_REGNO_T5:
- return "t5";
- case GDB_REGNO_T6:
- return "t6";
- case GDB_REGNO_PC:
- return "pc";
- case GDB_REGNO_FPR0:
- return "fpr0";
- case GDB_REGNO_FPR31:
- return "fpr31";
- case GDB_REGNO_CSR0:
- return "csr0";
- case GDB_REGNO_TSELECT:
- return "tselect";
- case GDB_REGNO_TDATA1:
- return "tdata1";
- case GDB_REGNO_TDATA2:
- return "tdata2";
- case GDB_REGNO_MISA:
- return "misa";
- case GDB_REGNO_DPC:
- return "dpc";
- case GDB_REGNO_DCSR:
- return "dcsr";
- case GDB_REGNO_DSCRATCH0:
- return "dscratch0";
- case GDB_REGNO_MSTATUS:
- return "mstatus";
- case GDB_REGNO_MEPC:
- return "mepc";
- case GDB_REGNO_MCAUSE:
- return "mcause";
- case GDB_REGNO_PRIV:
- return "priv";
- case GDB_REGNO_SATP:
- return "satp";
- case GDB_REGNO_VTYPE:
- return "vtype";
- case GDB_REGNO_VL:
- return "vl";
- case GDB_REGNO_V0:
- return "v0";
- case GDB_REGNO_V1:
- return "v1";
- case GDB_REGNO_V2:
- return "v2";
- case GDB_REGNO_V3:
- return "v3";
- case GDB_REGNO_V4:
- return "v4";
- case GDB_REGNO_V5:
- return "v5";
- case GDB_REGNO_V6:
- return "v6";
- case GDB_REGNO_V7:
- return "v7";
- case GDB_REGNO_V8:
- return "v8";
- case GDB_REGNO_V9:
- return "v9";
- case GDB_REGNO_V10:
- return "v10";
- case GDB_REGNO_V11:
- return "v11";
- case GDB_REGNO_V12:
- return "v12";
- case GDB_REGNO_V13:
- return "v13";
- case GDB_REGNO_V14:
- return "v14";
- case GDB_REGNO_V15:
- return "v15";
- case GDB_REGNO_V16:
- return "v16";
- case GDB_REGNO_V17:
- return "v17";
- case GDB_REGNO_V18:
- return "v18";
- case GDB_REGNO_V19:
- return "v19";
- case GDB_REGNO_V20:
- return "v20";
- case GDB_REGNO_V21:
- return "v21";
- case GDB_REGNO_V22:
- return "v22";
- case GDB_REGNO_V23:
- return "v23";
- case GDB_REGNO_V24:
- return "v24";
- case GDB_REGNO_V25:
- return "v25";
- case GDB_REGNO_V26:
- return "v26";
- case GDB_REGNO_V27:
- return "v27";
- case GDB_REGNO_V28:
- return "v28";
- case GDB_REGNO_V29:
- return "v29";
- case GDB_REGNO_V30:
- return "v30";
- case GDB_REGNO_V31:
- return "v31";
- default:
- if (regno <= GDB_REGNO_XPR31)
- sprintf(buf, "x%d", regno - GDB_REGNO_ZERO);
- else if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
- sprintf(buf, "csr%d", regno - GDB_REGNO_CSR0);
- else if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
- sprintf(buf, "f%d", regno - GDB_REGNO_FPR0);
- else
- sprintf(buf, "gdb_regno_%d", regno);
- return buf;
+ char * const buf = calloc(size_buf, sizeof(char));
+ if (!buf) {
+ LOG_ERROR("Failed to allocate memory for a register name.");
+ return NULL;
+ }
+ strcpy(buf, name);
+ return buf;
+}
+
+static char *init_reg_name_with_prefix(const char *name_prefix,
+ unsigned int num)
+{
+ const int size_buf = snprintf(NULL, 0, "%s%d", name_prefix, num) + 1;
+
+ char * const buf = calloc(size_buf, sizeof(char));
+ if (!buf) {
+ LOG_ERROR("Failed to allocate memory for a register name.");
+ return NULL;
}
+ int result = snprintf(buf, size_buf, "%s%d", name_prefix, num);
+ assert(result > 0 && result <= (size_buf - 1));
+ return buf;
}
+static const char * const default_reg_names[GDB_REGNO_COUNT] = {
+ [GDB_REGNO_ZERO] = "zero",
+ [GDB_REGNO_RA] = "ra",
+ [GDB_REGNO_SP] = "sp",
+ [GDB_REGNO_GP] = "gp",
+ [GDB_REGNO_TP] = "tp",
+ [GDB_REGNO_T0] = "t0",
+ [GDB_REGNO_T1] = "t1",
+ [GDB_REGNO_T2] = "t2",
+ [GDB_REGNO_FP] = "fp",
+ [GDB_REGNO_S1] = "s1",
+ [GDB_REGNO_A0] = "a0",
+ [GDB_REGNO_A1] = "a1",
+ [GDB_REGNO_A2] = "a2",
+ [GDB_REGNO_A3] = "a3",
+ [GDB_REGNO_A4] = "a4",
+ [GDB_REGNO_A5] = "a5",
+ [GDB_REGNO_A6] = "a6",
+ [GDB_REGNO_A7] = "a7",
+ [GDB_REGNO_S2] = "s2",
+ [GDB_REGNO_S3] = "s3",
+ [GDB_REGNO_S4] = "s4",
+ [GDB_REGNO_S5] = "s5",
+ [GDB_REGNO_S6] = "s6",
+ [GDB_REGNO_S7] = "s7",
+ [GDB_REGNO_S8] = "s8",
+ [GDB_REGNO_S9] = "s9",
+ [GDB_REGNO_S10] = "s10",
+ [GDB_REGNO_S11] = "s11",
+ [GDB_REGNO_T3] = "t3",
+ [GDB_REGNO_T4] = "t4",
+ [GDB_REGNO_T5] = "t5",
+ [GDB_REGNO_T6] = "t6",
+ [GDB_REGNO_PC] = "pc",
+ [GDB_REGNO_CSR0] = "csr0",
+ [GDB_REGNO_PRIV] = "priv",
+ [GDB_REGNO_FT0] = "ft0",
+ [GDB_REGNO_FT1] = "ft1",
+ [GDB_REGNO_FT2] = "ft2",
+ [GDB_REGNO_FT3] = "ft3",
+ [GDB_REGNO_FT4] = "ft4",
+ [GDB_REGNO_FT5] = "ft5",
+ [GDB_REGNO_FT6] = "ft6",
+ [GDB_REGNO_FT7] = "ft7",
+ [GDB_REGNO_FS0] = "fs0",
+ [GDB_REGNO_FS1] = "fs1",
+ [GDB_REGNO_FA0] = "fa0",
+ [GDB_REGNO_FA1] = "fa1",
+ [GDB_REGNO_FA2] = "fa2",
+ [GDB_REGNO_FA3] = "fa3",
+ [GDB_REGNO_FA4] = "fa4",
+ [GDB_REGNO_FA5] = "fa5",
+ [GDB_REGNO_FA6] = "fa6",
+ [GDB_REGNO_FA7] = "fa7",
+ [GDB_REGNO_FS2] = "fs2",
+ [GDB_REGNO_FS3] = "fs3",
+ [GDB_REGNO_FS4] = "fs4",
+ [GDB_REGNO_FS5] = "fs5",
+ [GDB_REGNO_FS6] = "fs6",
+ [GDB_REGNO_FS7] = "fs7",
+ [GDB_REGNO_FS8] = "fs8",
+ [GDB_REGNO_FS9] = "fs9",
+ [GDB_REGNO_FS10] = "fs10",
+ [GDB_REGNO_FS11] = "fs11",
+ [GDB_REGNO_FT8] = "ft8",
+ [GDB_REGNO_FT9] = "ft9",
+ [GDB_REGNO_FT10] = "ft10",
+ [GDB_REGNO_FT11] = "ft11",
+
+ #define DECLARE_CSR(csr_name, number)[(number) + GDB_REGNO_CSR0] = #csr_name,
+ #include "encoding.h"
+ #undef DECLARE_CSR
+};
+
+static void free_reg_names(struct target *target)
+{
+ RISCV_INFO(info);
+
+ if (!info->reg_names)
+ return;
+
+ for (unsigned int i = 0; i < GDB_REGNO_COUNT; ++i)
+ free(info->reg_names[i]);
+ free(info->reg_names);
+ info->reg_names = NULL;
+
+ free_custom_register_names(target);
+}
+
+static void init_custom_csr_names(const struct target *target)
+{
+ RISCV_INFO(info);
+ range_list_t *entry;
+
+ list_for_each_entry(entry, &info->expose_csr, list) {
+ if (!entry->name)
+ continue;
+ assert(entry->low == entry->high);
+ const unsigned int regno = entry->low + GDB_REGNO_CSR0;
+ assert(regno <= GDB_REGNO_CSR4095);
+ if (info->reg_names[regno])
+ return;
+ info->reg_names[regno] = init_reg_name(entry->name);
+ }
+}
+
+const char *gdb_regno_name(const struct target *target, enum gdb_regno regno)
+{
+ RISCV_INFO(info);
+
+ if (regno >= GDB_REGNO_COUNT) {
+ assert(info->custom_register_names.reg_names);
+ assert(regno - GDB_REGNO_COUNT <= info->custom_register_names.num_entries);
+ return info->custom_register_names.reg_names[regno - GDB_REGNO_COUNT];
+ }
+
+ if (!info->reg_names)
+ info->reg_names = calloc(GDB_REGNO_COUNT, sizeof(char *));
+
+ if (info->reg_names[regno])
+ return info->reg_names[regno];
+ if (default_reg_names[regno])
+ return default_reg_names[regno];
+ if (regno <= GDB_REGNO_XPR31) {
+ info->reg_names[regno] = init_reg_name_with_prefix("x", regno - GDB_REGNO_ZERO);
+ return info->reg_names[regno];
+ }
+ if (regno <= GDB_REGNO_V31 && regno >= GDB_REGNO_V0) {
+ info->reg_names[regno] = init_reg_name_with_prefix("v", regno - GDB_REGNO_V0);
+ return info->reg_names[regno];
+ }
+ if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
+ init_custom_csr_names(target);
+ info->reg_names[regno] = init_reg_name_with_prefix("csr", regno - GDB_REGNO_CSR0);
+ return info->reg_names[regno];
+ }
+ assert(!"Encountered uninitialized entry in reg_names table");
+
+ return NULL;
+}
+
+/**
+ * This function is the handler of user's request to read a register.
+ */
static int register_get(struct reg *reg)
{
- riscv_reg_info_t *reg_info = reg->arch_info;
- struct target *target = reg_info->target;
+ struct target *target = ((riscv_reg_info_t *)reg->arch_info)->target;
RISCV_INFO(r);
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E')) {
+ buf_set_u64(reg->value, 0, reg->size, 0);
+ return ERROR_OK;
+ }
+
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (!r->get_register_buf) {
- LOG_ERROR("Reading register %s not supported on this RISC-V target.",
- gdb_regno_name(reg->number));
+ LOG_TARGET_ERROR(target,
+ "Reading register %s not supported on this target.",
+ reg->name);
return ERROR_FAIL;
}
if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK)
return ERROR_FAIL;
+
+ reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ false);
} else {
uint64_t value;
int result = riscv_get_register(target, &value, reg->number);
@@ -3713,33 +6181,32 @@ static int register_get(struct reg *reg)
return result;
buf_set_u64(reg->value, 0, reg->size, value);
}
- reg->valid = gdb_regno_cacheable(reg->number, false);
char *str = buf_to_hex_str(reg->value, reg->size);
- LOG_DEBUG("[%s] read 0x%s from %s (valid=%d)", target_name(target),
- str, reg->name, reg->valid);
+ LOG_TARGET_DEBUG(target, "Read 0x%s from %s (valid=%d).", str, reg->name,
+ reg->valid);
free(str);
return ERROR_OK;
}
+/**
+ * This function is the handler of user's request to write a register.
+ */
static int register_set(struct reg *reg, uint8_t *buf)
{
- riscv_reg_info_t *reg_info = reg->arch_info;
- struct target *target = reg_info->target;
+ struct target *target = ((riscv_reg_info_t *)reg->arch_info)->target;
RISCV_INFO(r);
char *str = buf_to_hex_str(buf, reg->size);
- LOG_DEBUG("[%s] write 0x%s to %s (valid=%d)", target_name(target),
- str, reg->name, reg->valid);
+ LOG_TARGET_DEBUG(target, "Write 0x%s to %s (valid=%d).", str, reg->name,
+ reg->valid);
free(str);
- /* Exit early for writing x0, which on the hardware would be ignored, and we
- * don't want to update our cache. */
- if (reg->number == GDB_REGNO_ZERO)
+ /* TODO: Hack to deal with gdb that thinks these registers still exist. */
+ if (reg->number > GDB_REGNO_XPR15 && reg->number <= GDB_REGNO_XPR31 &&
+ riscv_supports_extension(target, 'E') &&
+ buf_get_u64(buf, 0, reg->size) == 0)
return ERROR_OK;
- memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
- reg->valid = gdb_regno_cacheable(reg->number, true);
-
if (reg->number == GDB_REGNO_TDATA1 ||
reg->number == GDB_REGNO_TDATA2) {
r->manual_hwbp_set = true;
@@ -3753,15 +6220,19 @@ static int register_set(struct reg *reg, uint8_t *buf)
if (reg->number >= GDB_REGNO_V0 && reg->number <= GDB_REGNO_V31) {
if (!r->set_register_buf) {
- LOG_ERROR("Writing register %s not supported on this RISC-V target.",
- gdb_regno_name(reg->number));
+ LOG_TARGET_ERROR(target,
+ "Writing register %s not supported on this target.",
+ reg->name);
return ERROR_FAIL;
}
- if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK)
+ if (r->set_register_buf(target, reg->number, buf) != ERROR_OK)
return ERROR_FAIL;
+
+ memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8));
+ reg->valid = gdb_regno_cacheable(reg->number, /* is write? */ true);
} else {
- uint64_t value = buf_get_u64(buf, 0, reg->size);
+ const riscv_reg_t value = buf_get_u64(buf, 0, reg->size);
if (riscv_set_register(target, reg->number, value) != ERROR_OK)
return ERROR_FAIL;
}
@@ -3774,85 +6245,511 @@ static struct reg_arch_type riscv_reg_arch_type = {
.set = register_set
};
-struct csr_info {
- unsigned number;
- const char *name;
-};
+static int init_custom_register_names(struct list_head *expose_custom,
+ struct reg_name_table *custom_register_names)
+{
+ unsigned int custom_regs_num = 0;
+ if (!list_empty(expose_custom)) {
+ range_list_t *entry;
+ list_for_each_entry(entry, expose_custom, list)
+ custom_regs_num += entry->high - entry->low + 1;
+ }
+
+ if (!custom_regs_num)
+ return ERROR_OK;
+
+ custom_register_names->reg_names = calloc(custom_regs_num, sizeof(char *));
+ if (!custom_register_names->reg_names) {
+ LOG_ERROR("Failed to allocate memory for custom_register_names->reg_names");
+ return ERROR_FAIL;
+ }
+ custom_register_names->num_entries = custom_regs_num;
+ char **reg_names = custom_register_names->reg_names;
+ range_list_t *range;
+ unsigned int next_custom_reg_index = 0;
+ list_for_each_entry(range, expose_custom, list) {
+ for (unsigned int custom_number = range->low; custom_number <= range->high; ++custom_number) {
+ if (range->name)
+ reg_names[next_custom_reg_index] = init_reg_name(range->name);
+ else
+ reg_names[next_custom_reg_index] =
+ init_reg_name_with_prefix("custom", custom_number);
-static int cmp_csr_info(const void *p1, const void *p2)
+ if (!reg_names[next_custom_reg_index])
+ return ERROR_FAIL;
+ ++next_custom_reg_index;
+ }
+ }
+ return ERROR_OK;
+}
+
+static bool is_known_standard_csr(unsigned int csr_num)
{
- return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number);
+ static const bool is_csr_in_buf[GDB_REGNO_CSR4095 - GDB_REGNO_CSR0 + 1] = {
+ #define DECLARE_CSR(csr_name, number)[number] = true,
+ #include "encoding.h"
+ #undef DECLARE_CSR
+ };
+ assert(csr_num < ARRAY_SIZE(is_csr_in_buf));
+
+ return is_csr_in_buf[csr_num];
}
-int riscv_init_registers(struct target *target)
+bool reg_is_initialized(const struct reg *reg)
+{
+ assert(reg);
+ if (!reg->feature) {
+ const struct reg default_reg = {0};
+ assert(!memcmp(&default_reg, reg, sizeof(*reg)));
+ return false;
+ }
+ assert(reg->arch_info);
+ assert(((riscv_reg_info_t *)reg->arch_info)->target);
+ assert((!reg->exist && !reg->value) || (reg->exist && reg->value));
+ assert(reg->valid || !reg->dirty);
+ return true;
+}
+
+static struct reg_feature *gdb_regno_feature(uint32_t regno)
+{
+ if (regno <= GDB_REGNO_XPR31 || regno == GDB_REGNO_PC) {
+ static struct reg_feature feature_cpu = {
+ .name = "org.gnu.gdb.riscv.cpu"
+ };
+ return &feature_cpu;
+ }
+ if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
+ regno == GDB_REGNO_FFLAGS ||
+ regno == GDB_REGNO_FRM ||
+ regno == GDB_REGNO_FCSR) {
+ static struct reg_feature feature_fpu = {
+ .name = "org.gnu.gdb.riscv.fpu"
+ };
+ return &feature_fpu;
+ }
+ if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
+ static struct reg_feature feature_vector = {
+ .name = "org.gnu.gdb.riscv.vector"
+ };
+ return &feature_vector;
+ }
+ if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
+ static struct reg_feature feature_csr = {
+ .name = "org.gnu.gdb.riscv.csr"
+ };
+ return &feature_csr;
+ }
+ if (regno == GDB_REGNO_PRIV) {
+ static struct reg_feature feature_virtual = {
+ .name = "org.gnu.gdb.riscv.virtual"
+ };
+ return &feature_virtual;
+ }
+ assert(regno >= GDB_REGNO_COUNT);
+ static struct reg_feature feature_custom = {
+ .name = "org.gnu.gdb.riscv.custom"
+ };
+ return &feature_custom;
+}
+
+static bool gdb_regno_caller_save(uint32_t regno)
+{
+ return regno <= GDB_REGNO_XPR31 ||
+ regno == GDB_REGNO_PC ||
+ (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31);
+}
+
+static struct reg_data_type *gdb_regno_reg_data_type(const struct target *target,
+ uint32_t regno)
+{
+ if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) {
+ static struct reg_data_type type_ieee_single = {
+ .type = REG_TYPE_IEEE_SINGLE,
+ .id = "ieee_single"
+ };
+ static struct reg_data_type type_ieee_double = {
+ .type = REG_TYPE_IEEE_DOUBLE,
+ .id = "ieee_double"
+ };
+ static struct reg_data_type_union_field single_double_fields[] = {
+ {"float", &type_ieee_single, single_double_fields + 1},
+ {"double", &type_ieee_double, NULL},
+ };
+ static struct reg_data_type_union single_double_union = {
+ .fields = single_double_fields
+ };
+ static struct reg_data_type type_ieee_single_double = {
+ .type = REG_TYPE_ARCH_DEFINED,
+ .id = "FPU_FD",
+ .type_class = REG_TYPE_CLASS_UNION,
+ {.reg_type_union = &single_double_union}
+ };
+ return riscv_supports_extension(target, 'D') ?
+ &type_ieee_single_double :
+ &type_ieee_single;
+ }
+ if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31) {
+ RISCV_INFO(info);
+ return &info->type_vector;
+ }
+ return NULL;
+}
+
+static const char *gdb_regno_group(uint32_t regno)
+{
+ if (regno <= GDB_REGNO_XPR31 ||
+ regno == GDB_REGNO_PC ||
+ regno == GDB_REGNO_PRIV)
+ return "general";
+ if ((regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) ||
+ regno == GDB_REGNO_FFLAGS ||
+ regno == GDB_REGNO_FRM ||
+ regno == GDB_REGNO_FCSR)
+ return "float";
+ if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095)
+ return "csr";
+ if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
+ return "vector";
+ assert(regno >= GDB_REGNO_COUNT);
+ return "custom";
+}
+
+uint32_t gdb_regno_size(const struct target *target, uint32_t regno)
+{
+ if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
+ return riscv_supports_extension(target, 'D') ? 64 : 32;
+ if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
+ return riscv_vlenb(target) * 8;
+ if (regno == GDB_REGNO_PRIV)
+ return 8;
+ if (regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095) {
+ const unsigned int csr_number = regno - GDB_REGNO_CSR0;
+ switch (csr_number) {
+ case CSR_DCSR:
+ case CSR_MVENDORID:
+ case CSR_MCOUNTINHIBIT:
+
+ case CSR_FFLAGS:
+ case CSR_FRM:
+ case CSR_FCSR:
+
+ case CSR_SCOUNTEREN:
+ case CSR_MCOUNTEREN:
+ return 32;
+ }
+ }
+ return riscv_xlen(target);
+}
+
+static bool vlenb_exists(const struct target *target)
+{
+ return riscv_vlenb(target) != 0;
+}
+
+static bool mtopi_exists(const struct target *target)
+{
+ RISCV_INFO(info)
+ /* TODO: The naming is quite unfortunate here. `mtopi_readable` refers
+ * to how the fact that `mtopi` exists was deduced during examine.
+ */
+ return info->mtopi_readable;
+}
+
+static bool mtopei_exists(const struct target *target)
+{
+ RISCV_INFO(info)
+ /* TODO: The naming is quite unfortunate here. `mtopei_readable` refers
+ * to how the fact that `mtopei` exists was deduced during examine.
+ */
+ return info->mtopei_readable;
+}
+
+static bool gdb_regno_exist(const struct target *target, uint32_t regno)
+{
+ if (regno <= GDB_REGNO_XPR15 ||
+ regno == GDB_REGNO_PC ||
+ regno == GDB_REGNO_PRIV)
+ return true;
+ if (regno > GDB_REGNO_XPR15 && regno <= GDB_REGNO_XPR31)
+ return !riscv_supports_extension(target, 'E');
+ if (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31)
+ return riscv_supports_extension(target, 'F');
+ if (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)
+ return vlenb_exists(target);
+ if (regno >= GDB_REGNO_COUNT)
+ return true;
+ assert(regno >= GDB_REGNO_CSR0 && regno <= GDB_REGNO_CSR4095);
+ const unsigned int csr_number = regno - GDB_REGNO_CSR0;
+ switch (csr_number) {
+ case CSR_FFLAGS:
+ case CSR_FRM:
+ case CSR_FCSR:
+ return riscv_supports_extension(target, 'F');
+ case CSR_SCOUNTEREN:
+ case CSR_SSTATUS:
+ case CSR_STVEC:
+ case CSR_SIP:
+ case CSR_SIE:
+ case CSR_SSCRATCH:
+ case CSR_SEPC:
+ case CSR_SCAUSE:
+ case CSR_STVAL:
+ case CSR_SATP:
+ return riscv_supports_extension(target, 'S');
+ case CSR_MEDELEG:
+ case CSR_MIDELEG:
+ /* "In systems with only M-mode, or with both M-mode and
+ * U-mode but without U-mode trap support, the medeleg and
+ * mideleg registers should not exist." */
+ return riscv_supports_extension(target, 'S') ||
+ riscv_supports_extension(target, 'N');
+
+ case CSR_PMPCFG1:
+ case CSR_PMPCFG3:
+ case CSR_CYCLEH:
+ case CSR_TIMEH:
+ case CSR_INSTRETH:
+ case CSR_HPMCOUNTER3H:
+ case CSR_HPMCOUNTER4H:
+ case CSR_HPMCOUNTER5H:
+ case CSR_HPMCOUNTER6H:
+ case CSR_HPMCOUNTER7H:
+ case CSR_HPMCOUNTER8H:
+ case CSR_HPMCOUNTER9H:
+ case CSR_HPMCOUNTER10H:
+ case CSR_HPMCOUNTER11H:
+ case CSR_HPMCOUNTER12H:
+ case CSR_HPMCOUNTER13H:
+ case CSR_HPMCOUNTER14H:
+ case CSR_HPMCOUNTER15H:
+ case CSR_HPMCOUNTER16H:
+ case CSR_HPMCOUNTER17H:
+ case CSR_HPMCOUNTER18H:
+ case CSR_HPMCOUNTER19H:
+ case CSR_HPMCOUNTER20H:
+ case CSR_HPMCOUNTER21H:
+ case CSR_HPMCOUNTER22H:
+ case CSR_HPMCOUNTER23H:
+ case CSR_HPMCOUNTER24H:
+ case CSR_HPMCOUNTER25H:
+ case CSR_HPMCOUNTER26H:
+ case CSR_HPMCOUNTER27H:
+ case CSR_HPMCOUNTER28H:
+ case CSR_HPMCOUNTER29H:
+ case CSR_HPMCOUNTER30H:
+ case CSR_HPMCOUNTER31H:
+ case CSR_MCYCLEH:
+ case CSR_MINSTRETH:
+ case CSR_MHPMCOUNTER4H:
+ case CSR_MHPMCOUNTER5H:
+ case CSR_MHPMCOUNTER6H:
+ case CSR_MHPMCOUNTER7H:
+ case CSR_MHPMCOUNTER8H:
+ case CSR_MHPMCOUNTER9H:
+ case CSR_MHPMCOUNTER10H:
+ case CSR_MHPMCOUNTER11H:
+ case CSR_MHPMCOUNTER12H:
+ case CSR_MHPMCOUNTER13H:
+ case CSR_MHPMCOUNTER14H:
+ case CSR_MHPMCOUNTER15H:
+ case CSR_MHPMCOUNTER16H:
+ case CSR_MHPMCOUNTER17H:
+ case CSR_MHPMCOUNTER18H:
+ case CSR_MHPMCOUNTER19H:
+ case CSR_MHPMCOUNTER20H:
+ case CSR_MHPMCOUNTER21H:
+ case CSR_MHPMCOUNTER22H:
+ case CSR_MHPMCOUNTER23H:
+ case CSR_MHPMCOUNTER24H:
+ case CSR_MHPMCOUNTER25H:
+ case CSR_MHPMCOUNTER26H:
+ case CSR_MHPMCOUNTER27H:
+ case CSR_MHPMCOUNTER28H:
+ case CSR_MHPMCOUNTER29H:
+ case CSR_MHPMCOUNTER30H:
+ case CSR_MHPMCOUNTER31H:
+ return riscv_xlen(target) == 32;
+ case CSR_MCOUNTEREN:
+ return riscv_supports_extension(target, 'U');
+ /* Interrupts M-Mode CSRs. */
+ case CSR_MISELECT:
+ case CSR_MIREG:
+ case CSR_MVIEN:
+ case CSR_MVIP:
+ case CSR_MIEH:
+ case CSR_MIPH:
+ return mtopi_exists(target);
+ case CSR_MIDELEGH:
+ case CSR_MVIENH:
+ case CSR_MVIPH:
+ return mtopi_exists(target) &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'S');
+ /* Interrupts S-Mode CSRs. */
+ case CSR_SISELECT:
+ case CSR_SIREG:
+ case CSR_STOPI:
+ return mtopi_exists(target) &&
+ riscv_supports_extension(target, 'S');
+ case CSR_STOPEI:
+ return mtopei_exists(target) &&
+ riscv_supports_extension(target, 'S');
+ case CSR_SIEH:
+ case CSR_SIPH:
+ return mtopi_exists(target) &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'S');
+ /* Interrupts Hypervisor and VS CSRs. */
+ case CSR_HVIEN:
+ case CSR_HVICTL:
+ case CSR_HVIPRIO1:
+ case CSR_HVIPRIO2:
+ case CSR_VSISELECT:
+ case CSR_VSIREG:
+ case CSR_VSTOPI:
+ return mtopi_exists(target) &&
+ riscv_supports_extension(target, 'H');
+ case CSR_VSTOPEI:
+ return mtopei_exists(target) &&
+ riscv_supports_extension(target, 'H');
+ case CSR_HIDELEGH:
+ case CSR_HVIENH:
+ case CSR_HVIPH:
+ case CSR_HVIPRIO1H:
+ case CSR_HVIPRIO2H:
+ case CSR_VSIEH:
+ case CSR_VSIPH:
+ return mtopi_exists(target) &&
+ riscv_xlen(target) == 32 &&
+ riscv_supports_extension(target, 'H');
+ }
+ return is_known_standard_csr(csr_number);
+}
+
+static unsigned int gdb_regno_custom_number(const struct target *target, uint32_t regno)
+{
+ if (regno < GDB_REGNO_COUNT)
+ return 0;
+
+ RISCV_INFO(info);
+ assert(!list_empty(&info->expose_custom));
+ range_list_t *range;
+ unsigned int regno_start = GDB_REGNO_COUNT;
+ unsigned int start = 0;
+ unsigned int offset = 0;
+ list_for_each_entry(range, &info->expose_custom, list) {
+ start = range->low;
+ assert(regno >= regno_start);
+ offset = regno - regno_start;
+ const unsigned int regs_in_range = range->high - range->low + 1;
+ if (offset < regs_in_range)
+ break;
+ regno_start += regs_in_range;
+ }
+ return start + offset;
+}
+
+static int resize_reg(const struct target *target, uint32_t regno, bool exist,
+ uint32_t size)
+{
+ struct reg *reg = get_reg_cache_entry(target, regno);
+ assert(reg_is_initialized(reg));
+ free(reg->value);
+ reg->size = size;
+ reg->exist = exist;
+ if (reg->exist) {
+ reg->value = malloc(DIV_ROUND_UP(reg->size, 8));
+ if (!reg->value) {
+ LOG_ERROR("Failed to allocate memory.");
+ return ERROR_FAIL;
+ }
+ } else {
+ reg->value = NULL;
+ }
+ assert(reg_is_initialized(reg));
+ return ERROR_OK;
+}
+
+static int set_reg_exist(const struct target *target, uint32_t regno, bool exist)
+{
+ const struct reg *reg = get_reg_cache_entry(target, regno);
+ assert(reg_is_initialized(reg));
+ return resize_reg(target, regno, exist, reg->size);
+}
+
+static int init_reg(struct target *target, uint32_t regno)
+{
+ struct reg * const reg = get_reg_cache_entry(target, regno);
+ if (reg_is_initialized(reg))
+ return ERROR_OK;
+ reg->number = regno;
+ reg->type = &riscv_reg_arch_type;
+ reg->dirty = false;
+ reg->valid = false;
+ reg->hidden = false;
+ reg->name = gdb_regno_name(target, regno);
+ reg->feature = gdb_regno_feature(regno);
+ reg->caller_save = gdb_regno_caller_save(regno);
+ reg->reg_data_type = gdb_regno_reg_data_type(target, regno);
+ reg->group = gdb_regno_group(regno);
+ if (regno < GDB_REGNO_COUNT) {
+ RISCV_INFO(info);
+ reg->arch_info = &info->shared_reg_info;
+ } else {
+ reg->arch_info = calloc(1, sizeof(riscv_reg_info_t));
+ if (!reg->arch_info) {
+ LOG_ERROR("Out of memory.");
+ return ERROR_FAIL;
+ }
+ riscv_reg_info_t * const reg_arch_info = reg->arch_info;
+ reg_arch_info->target = target;
+ reg_arch_info->custom_number = gdb_regno_custom_number(target, regno);
+ }
+ return resize_reg(target, regno, gdb_regno_exist(target, regno),
+ gdb_regno_size(target, regno));
+}
+
+static int riscv_init_reg_cache(struct target *target)
{
RISCV_INFO(info);
riscv_free_registers(target);
target->reg_cache = calloc(1, sizeof(*target->reg_cache));
- if (!target->reg_cache)
+ if (!target->reg_cache) {
+ LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache");
return ERROR_FAIL;
+ }
target->reg_cache->name = "RISC-V Registers";
- target->reg_cache->num_regs = GDB_REGNO_COUNT;
- if (!list_empty(&info->expose_custom)) {
- range_list_t *entry;
- list_for_each_entry(entry, &info->expose_custom, list)
- target->reg_cache->num_regs += entry->high - entry->low + 1;
+ if (init_custom_register_names(&info->expose_custom, &info->custom_register_names) != ERROR_OK) {
+ LOG_TARGET_ERROR(target, "init_custom_register_names failed");
+ return ERROR_FAIL;
}
- LOG_DEBUG("create register cache for %d registers",
+ target->reg_cache->num_regs = GDB_REGNO_COUNT + info->custom_register_names.num_entries;
+ LOG_TARGET_DEBUG(target, "create register cache for %d registers",
target->reg_cache->num_regs);
target->reg_cache->reg_list =
calloc(target->reg_cache->num_regs, sizeof(struct reg));
- if (!target->reg_cache->reg_list)
- return ERROR_FAIL;
-
- const unsigned int max_reg_name_len = 12;
- free(info->reg_names);
- info->reg_names =
- calloc(target->reg_cache->num_regs, max_reg_name_len);
- if (!info->reg_names)
+ if (!target->reg_cache->reg_list) {
+ LOG_TARGET_ERROR(target, "Failed to allocate memory for target->reg_cache->reg_list");
return ERROR_FAIL;
- char *reg_name = info->reg_names;
+ }
+ return ERROR_OK;
+}
- static struct reg_feature feature_cpu = {
- .name = "org.gnu.gdb.riscv.cpu"
- };
- static struct reg_feature feature_fpu = {
- .name = "org.gnu.gdb.riscv.fpu"
- };
- static struct reg_feature feature_csr = {
- .name = "org.gnu.gdb.riscv.csr"
- };
- static struct reg_feature feature_vector = {
- .name = "org.gnu.gdb.riscv.vector"
- };
- static struct reg_feature feature_virtual = {
- .name = "org.gnu.gdb.riscv.virtual"
- };
- static struct reg_feature feature_custom = {
- .name = "org.gnu.gdb.riscv.custom"
- };
+static void init_shared_reg_info(struct target *target)
+{
+ RISCV_INFO(info);
+ info->shared_reg_info.target = target;
+ info->shared_reg_info.custom_number = 0;
+}
- /* These types are built into gdb. */
- static struct reg_data_type type_ieee_single = { .type = REG_TYPE_IEEE_SINGLE, .id = "ieee_single" };
- static struct reg_data_type type_ieee_double = { .type = REG_TYPE_IEEE_DOUBLE, .id = "ieee_double" };
- static struct reg_data_type_union_field single_double_fields[] = {
- {"float", &type_ieee_single, single_double_fields + 1},
- {"double", &type_ieee_double, NULL},
- };
- static struct reg_data_type_union single_double_union = {
- .fields = single_double_fields
- };
- static struct reg_data_type type_ieee_single_double = {
- .type = REG_TYPE_ARCH_DEFINED,
- .id = "FPU_FD",
- .type_class = REG_TYPE_CLASS_UNION,
- { .reg_type_union = &single_double_union }
- };
+static void init_vector_reg_type(const struct target *target)
+{
+ RISCV_INFO(info);
static struct reg_data_type type_uint8 = { .type = REG_TYPE_UINT8, .id = "uint8" };
static struct reg_data_type type_uint16 = { .type = REG_TYPE_UINT16, .id = "uint16" };
static struct reg_data_type type_uint32 = { .type = REG_TYPE_UINT32, .id = "uint32" };
@@ -3875,35 +6772,35 @@ int riscv_init_registers(struct target *target)
*/
info->vector_uint8.type = &type_uint8;
- info->vector_uint8.count = info->vlenb;
+ info->vector_uint8.count = riscv_vlenb(target);
info->type_uint8_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint8_vector.id = "bytes";
info->type_uint8_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint8_vector.reg_type_vector = &info->vector_uint8;
info->vector_uint16.type = &type_uint16;
- info->vector_uint16.count = info->vlenb / 2;
+ info->vector_uint16.count = riscv_vlenb(target) / 2;
info->type_uint16_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint16_vector.id = "shorts";
info->type_uint16_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint16_vector.reg_type_vector = &info->vector_uint16;
info->vector_uint32.type = &type_uint32;
- info->vector_uint32.count = info->vlenb / 4;
+ info->vector_uint32.count = riscv_vlenb(target) / 4;
info->type_uint32_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint32_vector.id = "words";
info->type_uint32_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint32_vector.reg_type_vector = &info->vector_uint32;
info->vector_uint64.type = &type_uint64;
- info->vector_uint64.count = info->vlenb / 8;
+ info->vector_uint64.count = riscv_vlenb(target) / 8;
info->type_uint64_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint64_vector.id = "longs";
info->type_uint64_vector.type_class = REG_TYPE_CLASS_VECTOR;
info->type_uint64_vector.reg_type_vector = &info->vector_uint64;
info->vector_uint128.type = &type_uint128;
- info->vector_uint128.count = info->vlenb / 16;
+ info->vector_uint128.count = riscv_vlenb(target) / 16;
info->type_uint128_vector.type = REG_TYPE_ARCH_DEFINED;
info->type_uint128_vector.id = "quads";
info->type_uint128_vector.type_class = REG_TYPE_CLASS_VECTOR;
@@ -3911,28 +6808,28 @@ int riscv_init_registers(struct target *target)
info->vector_fields[0].name = "b";
info->vector_fields[0].type = &info->type_uint8_vector;
- if (info->vlenb >= 2) {
+ if (riscv_vlenb(target) >= 2) {
info->vector_fields[0].next = info->vector_fields + 1;
info->vector_fields[1].name = "s";
info->vector_fields[1].type = &info->type_uint16_vector;
} else {
info->vector_fields[0].next = NULL;
}
- if (info->vlenb >= 4) {
+ if (riscv_vlenb(target) >= 4) {
info->vector_fields[1].next = info->vector_fields + 2;
info->vector_fields[2].name = "w";
info->vector_fields[2].type = &info->type_uint32_vector;
} else {
info->vector_fields[1].next = NULL;
}
- if (info->vlenb >= 8) {
+ if (riscv_vlenb(target) >= 8) {
info->vector_fields[2].next = info->vector_fields + 3;
info->vector_fields[3].name = "l";
info->vector_fields[3].type = &info->type_uint64_vector;
} else {
info->vector_fields[2].next = NULL;
}
- if (info->vlenb >= 16) {
+ if (riscv_vlenb(target) >= 16) {
info->vector_fields[3].next = info->vector_fields + 4;
info->vector_fields[4].name = "q";
info->vector_fields[4].type = &info->type_uint128_vector;
@@ -3947,472 +6844,81 @@ int riscv_init_registers(struct target *target)
info->type_vector.id = "riscv_vector";
info->type_vector.type_class = REG_TYPE_CLASS_UNION;
info->type_vector.reg_type_union = &info->vector_union;
+}
- struct csr_info csr_info[] = {
-#define DECLARE_CSR(name, number) { number, #name },
-#include "encoding.h"
-#undef DECLARE_CSR
- };
- /* encoding.h does not contain the registers in sorted order. */
- qsort(csr_info, ARRAY_SIZE(csr_info), sizeof(*csr_info), cmp_csr_info);
- unsigned csr_info_index = 0;
-
- int custom_within_range = 0;
-
- riscv_reg_info_t *shared_reg_info = calloc(1, sizeof(riscv_reg_info_t));
- if (!shared_reg_info)
- return ERROR_FAIL;
- shared_reg_info->target = target;
-
- /* When gdb requests register N, gdb_get_register_packet() assumes that this
- * is register at index N in reg_list. So if there are certain registers
- * that don't exist, we need to leave holes in the list (or renumber, but
- * it would be nice not to have yet another set of numbers to translate
- * between). */
- for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) {
- struct reg *r = &target->reg_cache->reg_list[number];
- r->dirty = false;
- r->valid = false;
- r->exist = true;
- r->type = &riscv_reg_arch_type;
- r->arch_info = shared_reg_info;
- r->number = number;
- r->size = riscv_xlen(target);
- /* r->size is set in riscv_invalidate_register_cache, maybe because the
- * target is in theory allowed to change XLEN on us. But I expect a lot
- * of other things to break in that case as well. */
- if (number <= GDB_REGNO_XPR31) {
- r->exist = number <= GDB_REGNO_XPR15 ||
- !riscv_supports_extension(target, 'E');
- /* TODO: For now we fake that all GPRs exist because otherwise gdb
- * doesn't work. */
- r->exist = true;
- r->caller_save = true;
- switch (number) {
- case GDB_REGNO_ZERO:
- r->name = "zero";
- break;
- case GDB_REGNO_RA:
- r->name = "ra";
- break;
- case GDB_REGNO_SP:
- r->name = "sp";
- break;
- case GDB_REGNO_GP:
- r->name = "gp";
- break;
- case GDB_REGNO_TP:
- r->name = "tp";
- break;
- case GDB_REGNO_T0:
- r->name = "t0";
- break;
- case GDB_REGNO_T1:
- r->name = "t1";
- break;
- case GDB_REGNO_T2:
- r->name = "t2";
- break;
- case GDB_REGNO_FP:
- r->name = "fp";
- break;
- case GDB_REGNO_S1:
- r->name = "s1";
- break;
- case GDB_REGNO_A0:
- r->name = "a0";
- break;
- case GDB_REGNO_A1:
- r->name = "a1";
- break;
- case GDB_REGNO_A2:
- r->name = "a2";
- break;
- case GDB_REGNO_A3:
- r->name = "a3";
- break;
- case GDB_REGNO_A4:
- r->name = "a4";
- break;
- case GDB_REGNO_A5:
- r->name = "a5";
- break;
- case GDB_REGNO_A6:
- r->name = "a6";
- break;
- case GDB_REGNO_A7:
- r->name = "a7";
- break;
- case GDB_REGNO_S2:
- r->name = "s2";
- break;
- case GDB_REGNO_S3:
- r->name = "s3";
- break;
- case GDB_REGNO_S4:
- r->name = "s4";
- break;
- case GDB_REGNO_S5:
- r->name = "s5";
- break;
- case GDB_REGNO_S6:
- r->name = "s6";
- break;
- case GDB_REGNO_S7:
- r->name = "s7";
- break;
- case GDB_REGNO_S8:
- r->name = "s8";
- break;
- case GDB_REGNO_S9:
- r->name = "s9";
- break;
- case GDB_REGNO_S10:
- r->name = "s10";
- break;
- case GDB_REGNO_S11:
- r->name = "s11";
- break;
- case GDB_REGNO_T3:
- r->name = "t3";
- break;
- case GDB_REGNO_T4:
- r->name = "t4";
- break;
- case GDB_REGNO_T5:
- r->name = "t5";
- break;
- case GDB_REGNO_T6:
- r->name = "t6";
- break;
- }
- r->group = "general";
- r->feature = &feature_cpu;
- } else if (number == GDB_REGNO_PC) {
- r->caller_save = true;
- sprintf(reg_name, "pc");
- r->group = "general";
- r->feature = &feature_cpu;
- } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) {
- r->caller_save = true;
- if (riscv_supports_extension(target, 'D')) {
- r->size = 64;
- if (riscv_supports_extension(target, 'F'))
- r->reg_data_type = &type_ieee_single_double;
- else
- r->reg_data_type = &type_ieee_double;
- } else if (riscv_supports_extension(target, 'F')) {
- r->reg_data_type = &type_ieee_single;
- r->size = 32;
- } else {
- r->exist = false;
- }
- switch (number) {
- case GDB_REGNO_FT0:
- r->name = "ft0";
- break;
- case GDB_REGNO_FT1:
- r->name = "ft1";
- break;
- case GDB_REGNO_FT2:
- r->name = "ft2";
- break;
- case GDB_REGNO_FT3:
- r->name = "ft3";
- break;
- case GDB_REGNO_FT4:
- r->name = "ft4";
- break;
- case GDB_REGNO_FT5:
- r->name = "ft5";
- break;
- case GDB_REGNO_FT6:
- r->name = "ft6";
- break;
- case GDB_REGNO_FT7:
- r->name = "ft7";
- break;
- case GDB_REGNO_FS0:
- r->name = "fs0";
- break;
- case GDB_REGNO_FS1:
- r->name = "fs1";
- break;
- case GDB_REGNO_FA0:
- r->name = "fa0";
- break;
- case GDB_REGNO_FA1:
- r->name = "fa1";
- break;
- case GDB_REGNO_FA2:
- r->name = "fa2";
- break;
- case GDB_REGNO_FA3:
- r->name = "fa3";
- break;
- case GDB_REGNO_FA4:
- r->name = "fa4";
- break;
- case GDB_REGNO_FA5:
- r->name = "fa5";
- break;
- case GDB_REGNO_FA6:
- r->name = "fa6";
- break;
- case GDB_REGNO_FA7:
- r->name = "fa7";
- break;
- case GDB_REGNO_FS2:
- r->name = "fs2";
- break;
- case GDB_REGNO_FS3:
- r->name = "fs3";
- break;
- case GDB_REGNO_FS4:
- r->name = "fs4";
- break;
- case GDB_REGNO_FS5:
- r->name = "fs5";
- break;
- case GDB_REGNO_FS6:
- r->name = "fs6";
- break;
- case GDB_REGNO_FS7:
- r->name = "fs7";
- break;
- case GDB_REGNO_FS8:
- r->name = "fs8";
- break;
- case GDB_REGNO_FS9:
- r->name = "fs9";
- break;
- case GDB_REGNO_FS10:
- r->name = "fs10";
- break;
- case GDB_REGNO_FS11:
- r->name = "fs11";
- break;
- case GDB_REGNO_FT8:
- r->name = "ft8";
- break;
- case GDB_REGNO_FT9:
- r->name = "ft9";
- break;
- case GDB_REGNO_FT10:
- r->name = "ft10";
- break;
- case GDB_REGNO_FT11:
- r->name = "ft11";
- break;
- }
- r->group = "float";
- r->feature = &feature_fpu;
- } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) {
- r->group = "csr";
- r->feature = &feature_csr;
- unsigned csr_number = number - GDB_REGNO_CSR0;
-
- while (csr_info[csr_info_index].number < csr_number &&
- csr_info_index < ARRAY_SIZE(csr_info) - 1) {
- csr_info_index++;
- }
- if (csr_info[csr_info_index].number == csr_number) {
- r->name = csr_info[csr_info_index].name;
- } else {
- sprintf(reg_name, "csr%d", csr_number);
- /* Assume unnamed registers don't exist, unless we have some
- * configuration that tells us otherwise. That's important
- * because eg. Eclipse crashes if a target has too many
- * registers, and apparently has no way of only showing a
- * subset of registers in any case. */
- r->exist = false;
+static int expose_csrs(const struct target *target)
+{
+ RISCV_INFO(info);
+ range_list_t *entry;
+ list_for_each_entry(entry, &info->expose_csr, list) {
+ assert(entry->low <= entry->high);
+ assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
+ const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
+ for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
+ regno <= last_regno; ++regno) {
+ struct reg * const reg = get_reg_cache_entry(target, regno);
+ const unsigned int csr_number = regno - GDB_REGNO_CSR0;
+ if (reg->exist) {
+ LOG_TARGET_WARNING(target,
+ "Not exposing CSR %d: register already exists.",
+ csr_number);
+ continue;
}
+ if (set_reg_exist(target, regno, /*exist*/ true) != ERROR_OK)
+ return ERROR_FAIL;
+ LOG_TARGET_DEBUG(target, "Exposing additional CSR %d (name=%s)",
+ csr_number, reg->name);
+ }
+ }
+ return ERROR_OK;
+}
- switch (csr_number) {
- case CSR_FFLAGS:
- case CSR_FRM:
- case CSR_FCSR:
- r->exist = riscv_supports_extension(target, 'F');
- r->group = "float";
- r->feature = &feature_fpu;
- break;
- case CSR_SSTATUS:
- case CSR_STVEC:
- case CSR_SIP:
- case CSR_SIE:
- case CSR_SCOUNTEREN:
- case CSR_SSCRATCH:
- case CSR_SEPC:
- case CSR_SCAUSE:
- case CSR_STVAL:
- case CSR_SATP:
- r->exist = riscv_supports_extension(target, 'S');
- break;
- case CSR_MEDELEG:
- case CSR_MIDELEG:
- /* "In systems with only M-mode, or with both M-mode and
- * U-mode but without U-mode trap support, the medeleg and
- * mideleg registers should not exist." */
- r->exist = riscv_supports_extension(target, 'S') ||
- riscv_supports_extension(target, 'N');
- break;
-
- case CSR_PMPCFG1:
- case CSR_PMPCFG3:
- case CSR_CYCLEH:
- case CSR_TIMEH:
- case CSR_INSTRETH:
- case CSR_HPMCOUNTER3H:
- case CSR_HPMCOUNTER4H:
- case CSR_HPMCOUNTER5H:
- case CSR_HPMCOUNTER6H:
- case CSR_HPMCOUNTER7H:
- case CSR_HPMCOUNTER8H:
- case CSR_HPMCOUNTER9H:
- case CSR_HPMCOUNTER10H:
- case CSR_HPMCOUNTER11H:
- case CSR_HPMCOUNTER12H:
- case CSR_HPMCOUNTER13H:
- case CSR_HPMCOUNTER14H:
- case CSR_HPMCOUNTER15H:
- case CSR_HPMCOUNTER16H:
- case CSR_HPMCOUNTER17H:
- case CSR_HPMCOUNTER18H:
- case CSR_HPMCOUNTER19H:
- case CSR_HPMCOUNTER20H:
- case CSR_HPMCOUNTER21H:
- case CSR_HPMCOUNTER22H:
- case CSR_HPMCOUNTER23H:
- case CSR_HPMCOUNTER24H:
- case CSR_HPMCOUNTER25H:
- case CSR_HPMCOUNTER26H:
- case CSR_HPMCOUNTER27H:
- case CSR_HPMCOUNTER28H:
- case CSR_HPMCOUNTER29H:
- case CSR_HPMCOUNTER30H:
- case CSR_HPMCOUNTER31H:
- case CSR_MCYCLEH:
- case CSR_MINSTRETH:
- case CSR_MHPMCOUNTER3H:
- case CSR_MHPMCOUNTER4H:
- case CSR_MHPMCOUNTER5H:
- case CSR_MHPMCOUNTER6H:
- case CSR_MHPMCOUNTER7H:
- case CSR_MHPMCOUNTER8H:
- case CSR_MHPMCOUNTER9H:
- case CSR_MHPMCOUNTER10H:
- case CSR_MHPMCOUNTER11H:
- case CSR_MHPMCOUNTER12H:
- case CSR_MHPMCOUNTER13H:
- case CSR_MHPMCOUNTER14H:
- case CSR_MHPMCOUNTER15H:
- case CSR_MHPMCOUNTER16H:
- case CSR_MHPMCOUNTER17H:
- case CSR_MHPMCOUNTER18H:
- case CSR_MHPMCOUNTER19H:
- case CSR_MHPMCOUNTER20H:
- case CSR_MHPMCOUNTER21H:
- case CSR_MHPMCOUNTER22H:
- case CSR_MHPMCOUNTER23H:
- case CSR_MHPMCOUNTER24H:
- case CSR_MHPMCOUNTER25H:
- case CSR_MHPMCOUNTER26H:
- case CSR_MHPMCOUNTER27H:
- case CSR_MHPMCOUNTER28H:
- case CSR_MHPMCOUNTER29H:
- case CSR_MHPMCOUNTER30H:
- case CSR_MHPMCOUNTER31H:
- r->exist = riscv_xlen(target) == 32;
- break;
-
- case CSR_VSTART:
- case CSR_VXSAT:
- case CSR_VXRM:
- case CSR_VL:
- case CSR_VTYPE:
- case CSR_VLENB:
- r->exist = riscv_supports_extension(target, 'V');
- break;
+static void hide_csrs(const struct target *target)
+{
+ RISCV_INFO(info);
+ range_list_t *entry;
+ list_for_each_entry(entry, &info->hide_csr, list) {
+ assert(entry->high <= GDB_REGNO_CSR4095 - GDB_REGNO_CSR0);
+ const enum gdb_regno last_regno = GDB_REGNO_CSR0 + entry->high;
+ for (enum gdb_regno regno = GDB_REGNO_CSR0 + entry->low;
+ regno <= last_regno; ++regno) {
+ struct reg * const reg = get_reg_cache_entry(target, regno);
+ const unsigned int csr_number = regno - GDB_REGNO_CSR0;
+ if (!reg->exist) {
+ LOG_TARGET_DEBUG(target,
+ "Not hiding CSR %d: register does not exist.",
+ csr_number);
+ continue;
}
+ LOG_TARGET_DEBUG(target, "Hiding CSR %d (name=%s).", csr_number, reg->name);
+ reg->hidden = true;
+ }
+ }
+}
- if (!r->exist && !list_empty(&info->expose_csr)) {
- range_list_t *entry;
- list_for_each_entry(entry, &info->expose_csr, list)
- if ((entry->low <= csr_number) && (csr_number <= entry->high)) {
- if (entry->name) {
- *reg_name = 0;
- r->name = entry->name;
- }
-
- LOG_DEBUG("Exposing additional CSR %d (name=%s)",
- csr_number, entry->name ? entry->name : reg_name);
+int riscv_init_registers(struct target *target)
+{
+ if (riscv_init_reg_cache(target) != ERROR_OK)
+ return ERROR_FAIL;
- r->exist = true;
- break;
- }
- }
+ init_shared_reg_info(target);
- } else if (number == GDB_REGNO_PRIV) {
- sprintf(reg_name, "priv");
- r->group = "general";
- r->feature = &feature_virtual;
- r->size = 8;
-
- } else if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) {
- r->caller_save = false;
- r->exist = riscv_supports_extension(target, 'V') && info->vlenb;
- r->size = info->vlenb * 8;
- sprintf(reg_name, "v%d", number - GDB_REGNO_V0);
- r->group = "vector";
- r->feature = &feature_vector;
- r->reg_data_type = &info->type_vector;
-
- } else if (number >= GDB_REGNO_COUNT) {
- /* Custom registers. */
- assert(!list_empty(&info->expose_custom));
-
- range_list_t *range = list_first_entry(&info->expose_custom, range_list_t, list);
-
- unsigned custom_number = range->low + custom_within_range;
-
- r->group = "custom";
- r->feature = &feature_custom;
- r->arch_info = calloc(1, sizeof(riscv_reg_info_t));
- if (!r->arch_info)
- return ERROR_FAIL;
- ((riscv_reg_info_t *) r->arch_info)->target = target;
- ((riscv_reg_info_t *) r->arch_info)->custom_number = custom_number;
- sprintf(reg_name, "custom%d", custom_number);
+ init_vector_reg_type(target);
- if (range->name) {
- *reg_name = 0;
- r->name = range->name;
- }
+ for (uint32_t reg_num = 0; reg_num < target->reg_cache->num_regs; reg_num++)
+ if (init_reg(target, reg_num) != ERROR_OK)
+ return ERROR_FAIL;
- LOG_DEBUG("Exposing additional custom register %d (name=%s)",
- number, range->name ? range->name : reg_name);
- custom_within_range++;
- if (custom_within_range > range->high - range->low) {
- custom_within_range = 0;
- list_rotate_left(&info->expose_custom);
- }
- }
+ if (expose_csrs(target) != ERROR_OK)
+ return ERROR_FAIL;
- if (reg_name[0]) {
- r->name = reg_name;
- reg_name += strlen(reg_name) + 1;
- assert(reg_name < info->reg_names + target->reg_cache->num_regs *
- max_reg_name_len);
- }
- r->value = calloc(1, DIV_ROUND_UP(r->size, 8));
- }
+ hide_csrs(target);
return ERROR_OK;
}
-
-void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
+void riscv_add_bscan_tunneled_scan(struct target *target, const struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt)
{
jtag_add_ir_scan(target->tap, &select_user4, TAP_IDLE);
diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h
index aba0864..7a95af6 100644
--- a/src/target/riscv/riscv.h
+++ b/src/target/riscv/riscv.h
@@ -12,26 +12,30 @@ struct riscv_program;
#include "target/register.h"
#include "target/semihosting_common.h"
#include <helper/command.h>
+#include <helper/bits.h>
#define RISCV_COMMON_MAGIC 0x52495356U
-/* The register cache is statically allocated. */
-#define RISCV_MAX_HARTS 1024
-#define RISCV_MAX_REGISTERS 5000
+#define RISCV_MAX_HARTS ((int)BIT(20))
#define RISCV_MAX_TRIGGERS 32
#define RISCV_MAX_HWBPS 16
+#define RISCV_MAX_DMS 100
#define DEFAULT_COMMAND_TIMEOUT_SEC 2
#define DEFAULT_RESET_TIMEOUT_SEC 30
#define RISCV_SATP_MODE(xlen) ((xlen) == 32 ? SATP32_MODE : SATP64_MODE)
#define RISCV_SATP_PPN(xlen) ((xlen) == 32 ? SATP32_PPN : SATP64_PPN)
+#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE)
+#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN)
#define RISCV_PGSHIFT 12
-# define PG_MAX_LEVEL 4
+#define PG_MAX_LEVEL 5
#define RISCV_NUM_MEM_ACCESS_METHODS 3
+#define RISCV_BATCH_ALLOC_SIZE 128
+
extern struct target_type riscv011_target;
extern struct target_type riscv013_target;
@@ -42,6 +46,12 @@ typedef uint64_t riscv_reg_t;
typedef uint32_t riscv_insn_t;
typedef uint64_t riscv_addr_t;
+typedef enum {
+ YNM_MAYBE,
+ YNM_YES,
+ YNM_NO
+} yes_no_maybe_t;
+
enum riscv_mem_access_method {
RISCV_MEM_ACCESS_UNSPECIFIED,
RISCV_MEM_ACCESS_PROGBUF,
@@ -51,7 +61,7 @@ enum riscv_mem_access_method {
enum riscv_halt_reason {
RISCV_HALT_INTERRUPT,
- RISCV_HALT_BREAKPOINT,
+ RISCV_HALT_EBREAK,
RISCV_HALT_SINGLESTEP,
RISCV_HALT_TRIGGER,
RISCV_HALT_UNKNOWN,
@@ -59,6 +69,20 @@ enum riscv_halt_reason {
RISCV_HALT_ERROR
};
+enum riscv_isrmasking_mode {
+ /* RISCV_ISRMASK_AUTO, */ /* not supported yet */
+ RISCV_ISRMASK_OFF,
+ /* RISCV_ISRMASK_ON, */ /* not supported yet */
+ RISCV_ISRMASK_STEPONLY,
+};
+
+enum riscv_hart_state {
+ RISCV_STATE_NON_EXISTENT,
+ RISCV_STATE_RUNNING,
+ RISCV_STATE_HALTED,
+ RISCV_STATE_UNAVAILABLE
+};
+
typedef struct {
struct target *target;
unsigned custom_number;
@@ -87,41 +111,62 @@ typedef struct {
char *name;
} range_list_t;
+#define DTM_DTMCS_VERSION_UNKNOWN ((unsigned int)-1)
+
+struct reg_name_table {
+ unsigned int num_entries;
+ char **reg_names;
+};
+
struct riscv_info {
unsigned int common_magic;
- unsigned dtm_version;
+ unsigned int dtm_version;
struct command_context *cmd_ctx;
void *version_specific;
- /* The hart that is currently being debugged. Note that this is
- * different than the hartid that the RTOS is expected to use. This
- * one will change all the time, it's more of a global argument to
- * every function than an actual */
- int current_hartid;
-
- /* Single buffer that contains all register names, instead of calling
- * malloc for each register. Needs to be freed when reg_list is freed. */
- char *reg_names;
+ struct reg_name_table custom_register_names;
+ char **reg_names;
/* It's possible that each core has a different supported ISA set. */
int xlen;
riscv_reg_t misa;
- /* Cached value of vlenb. 0 if vlenb is not readable for some reason. */
+ /* Cached value of vlenb. 0 indicates there is no vector support.
+ * Note that you can have vector support without misa.V set, because
+ * Zve* extensions implement vector registers without setting misa.V. */
unsigned int vlenb;
+ bool mtopi_readable;
+ bool mtopei_readable;
+
/* The number of triggers per hart. */
unsigned int trigger_count;
- /* For each physical trigger, contains -1 if the hwbp is available, or the
- * unique_id of the breakpoint/watchpoint that is using it.
+ /* Data structure to record known unsupported tdata1+tdata2 trigger CSR values.
+ * This is to avoid repetitive attempts to set trigger configurations that are already
+ * known to be unsupported in the HW.
+ * A separate data structure is created for each trigger. */
+ struct list_head *wp_triggers_negative_cache;
+
+ /* record the tinfo of each trigger */
+ unsigned int trigger_tinfo[RISCV_MAX_TRIGGERS];
+
+ /* For each physical trigger contains:
+ * -1: the hwbp is available
+ * -4: The trigger is used by the itrigger command
+ * -5: The trigger is used by the etrigger command
+ * >= 0: unique_id of the breakpoint/watchpoint that is using it.
* Note that in RTOS mode the triggers are the same across all harts the
* target controls, while otherwise only a single hart is controlled. */
int trigger_unique_id[RISCV_MAX_HWBPS];
- /* The number of entries in the debug buffer. */
- int debug_buffer_size;
+ /* The unique id of the trigger that caused the most recent halt. If the
+ * most recent halt was not caused by a trigger, then this is -1. */
+ uint32_t trigger_hit;
+
+ /* The number of entries in the program buffer. */
+ int progbuf_size;
/* This hart contains an implicit ebreak at the end of the program buffer. */
bool impebreak;
@@ -137,21 +182,44 @@ struct riscv_info {
/* This target was selected using hasel. */
bool selected;
+ /* Used by riscv_openocd_poll(). */
+ bool halted_needs_event_callback;
+ enum target_event halted_callback_event;
+
+ enum riscv_isrmasking_mode isrmask_mode;
+
/* Helper functions that target the various RISC-V debug spec
* implementations. */
- int (*get_register)(struct target *target, riscv_reg_t *value, int regid);
- int (*set_register)(struct target *target, int regid, uint64_t value);
- int (*get_register_buf)(struct target *target, uint8_t *buf, int regno);
- int (*set_register_buf)(struct target *target, int regno,
+ int (*get_register)(struct target *target, riscv_reg_t *value,
+ enum gdb_regno regno);
+ int (*set_register)(struct target *target, enum gdb_regno regno,
+ riscv_reg_t value);
+ int (*get_register_buf)(struct target *target, uint8_t *buf,
+ enum gdb_regno regno);
+ int (*set_register_buf)(struct target *target, enum gdb_regno regno,
const uint8_t *buf);
- int (*select_current_hart)(struct target *target);
- bool (*is_halted)(struct target *target);
+ int (*select_target)(struct target *target);
+ int (*get_hart_state)(struct target *target, enum riscv_hart_state *state);
/* Resume this target, as well as every other prepped target that can be
* resumed near-simultaneously. Clear the prepped flag on any target that
* was resumed. */
int (*resume_go)(struct target *target);
int (*step_current_hart)(struct target *target);
- int (*on_halt)(struct target *target);
+
+ /* These get called from riscv_poll_hart(), which is a house of cards
+ * together with openocd_poll(), so be careful not to upset things too
+ * much. */
+ int (*handle_became_halted)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+ int (*handle_became_running)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+ int (*handle_became_unavailable)(struct target *target,
+ enum riscv_hart_state previous_riscv_state);
+
+ /* Called periodically (no guarantees about frequency), while there's
+ * nothing else going on. */
+ int (*tick)(struct target *target);
+
/* Get this target as ready as possible to resume, without actually
* resuming. */
int (*resume_prep)(struct target *target);
@@ -159,14 +227,14 @@ struct riscv_info {
int (*halt_go)(struct target *target);
int (*on_step)(struct target *target);
enum riscv_halt_reason (*halt_reason)(struct target *target);
- int (*write_debug_buffer)(struct target *target, unsigned index,
- riscv_insn_t d);
- riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned index);
- int (*execute_debug_buffer)(struct target *target);
- int (*dmi_write_u64_bits)(struct target *target);
- void (*fill_dmi_write_u64)(struct target *target, char *buf, int a, uint64_t d);
- void (*fill_dmi_read_u64)(struct target *target, char *buf, int a);
- void (*fill_dmi_nop_u64)(struct target *target, char *buf);
+ int (*write_progbuf)(struct target *target, unsigned int index, riscv_insn_t d);
+ riscv_insn_t (*read_progbuf)(struct target *target, unsigned int index);
+ int (*execute_progbuf)(struct target *target, uint32_t *cmderr);
+ int (*invalidate_cached_progbuf)(struct target *target);
+ int (*get_dmi_scan_length)(struct target *target);
+ void (*fill_dm_write)(struct target *target, char *buf, uint64_t a, uint32_t d);
+ void (*fill_dm_read)(struct target *target, char *buf, uint64_t a);
+ void (*fill_dm_nop)(struct target *target, char *buf);
int (*authdata_read)(struct target *target, uint32_t *value, unsigned int index);
int (*authdata_write)(struct target *target, uint32_t value, unsigned int index);
@@ -174,6 +242,12 @@ struct riscv_info {
int (*dmi_read)(struct target *target, uint32_t *value, uint32_t address);
int (*dmi_write)(struct target *target, uint32_t address, uint32_t value);
+ /* Get the DMI address of target's DM's register.
+ * The function should return the passed address
+ * if the target is not assigned a DM yet.
+ */
+ uint32_t (*get_dmi_address)(const struct target *target, uint32_t dm_address);
+
int (*sample_memory)(struct target *target,
struct riscv_sample_buf *buf,
riscv_sample_config_t *config,
@@ -182,12 +256,13 @@ struct riscv_info {
int (*read_memory)(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment);
- /* How many harts are attached to the DM that this target is attached to? */
- int (*hart_count)(struct target *target);
unsigned (*data_bits)(struct target *target);
COMMAND_HELPER((*print_info), struct target *target);
+ /* Storage for arch_info of non-custom registers. */
+ riscv_reg_info_t shared_reg_info;
+
/* Storage for vector register types. */
struct reg_data_type_vector vector_uint8;
struct reg_data_type_vector vector_uint16;
@@ -203,7 +278,7 @@ struct riscv_info {
struct reg_data_type_union vector_union;
struct reg_data_type type_vector;
- /* Set when trigger registers are changed by the user. This indicates we eed
+ /* Set when trigger registers are changed by the user. This indicates we need
* to beware that we may hit a trigger that we didn't realize had been set. */
bool manual_hwbp_set;
@@ -224,8 +299,27 @@ struct riscv_info {
* from range 0xc000 ... 0xffff. */
struct list_head expose_custom;
+ /* The list of registers to mark as "hidden". Hidden registers are available
+ * but do not appear in gdb targets description or reg command output. */
+ struct list_head hide_csr;
+
riscv_sample_config_t sample_config;
struct riscv_sample_buf sample_buf;
+
+ /* Track when we were last asked to do something substantial. */
+ int64_t last_activity;
+
+ yes_no_maybe_t vsew64_supported;
+
+ bool range_trigger_fallback_encountered;
+
+ bool riscv_ebreakm;
+ bool riscv_ebreaks;
+ bool riscv_ebreaku;
+
+ bool wp_allow_equality_match_trigger;
+ bool wp_allow_napot_trigger;
+ bool wp_allow_ge_lt_trigger;
};
COMMAND_HELPER(riscv_print_info_line, const char *section, const char *key,
@@ -240,6 +334,7 @@ typedef struct {
const char *name;
int level;
unsigned va_bits;
+ /* log2(PTESIZE) */
unsigned pte_shift;
unsigned vpn_shift[PG_MAX_LEVEL];
unsigned vpn_mask[PG_MAX_LEVEL];
@@ -256,9 +351,6 @@ extern int riscv_command_timeout_sec;
extern int riscv_reset_timeout_sec;
extern bool riscv_enable_virtual;
-extern bool riscv_ebreakm;
-extern bool riscv_ebreaks;
-extern bool riscv_ebreaku;
/* Everything needs the RISC-V specific info structure, so here's a nice macro
* that provides that. */
@@ -284,7 +376,7 @@ extern uint32_t bscan_tunneled_select_dmi_num_fields;
typedef enum { BSCAN_TUNNEL_NESTED_TAP, BSCAN_TUNNEL_DATA_REGISTER } bscan_tunnel_type_t;
extern int bscan_tunnel_ir_width;
-uint32_t dtmcontrol_scan_via_bscan(struct target *target, uint32_t out);
+int dtmcontrol_scan_via_bscan(struct target *target, uint32_t out, uint32_t *in_ptr);
void select_dmi_via_bscan(struct target *target);
/*** OpenOCD Interface */
@@ -299,52 +391,56 @@ int riscv_openocd_step(
int handle_breakpoints
);
-int riscv_openocd_assert_reset(struct target *target);
-int riscv_openocd_deassert_reset(struct target *target);
-
/*** RISC-V Interface ***/
-bool riscv_supports_extension(struct target *target, char letter);
+bool riscv_supports_extension(const struct target *target, char letter);
/* Returns XLEN for the given (or current) hart. */
unsigned riscv_xlen(const struct target *target);
-int riscv_xlen_of_hart(const struct target *target);
-/* Sets the current hart, which is the hart that will actually be used when
- * issuing debug commands. */
-int riscv_set_current_hartid(struct target *target, int hartid);
-int riscv_select_current_hart(struct target *target);
-int riscv_current_hartid(const struct target *target);
+/* Returns VLENB for the given (or current) hart. */
+unsigned int riscv_vlenb(const struct target *target);
/*** Support functions for the RISC-V 'RTOS', which provides multihart support
* without requiring multiple targets. */
-/* Lists the number of harts in the system, which are assumed to be
- * consecutive and start with mhartid=0. */
-int riscv_count_harts(struct target *target);
-
-/** Set register, updating the cache. */
+/**
+ * Set the register value. For cacheable registers, only the cache is updated
+ * (write-back mode).
+ */
int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
+/**
+ * Set the register value and immediately write it to the target
+ * (write-through mode).
+ */
+int riscv_write_register(struct target *target, enum gdb_regno i, riscv_reg_t v);
/** Get register, from the cache if it's in there. */
int riscv_get_register(struct target *target, riscv_reg_t *value,
enum gdb_regno r);
+/** Read the register into the cache, and mark it dirty so it will be restored
+ * before resuming. */
+int riscv_save_register(struct target *target, enum gdb_regno regid);
+/** Write all dirty registers to the target. */
+int riscv_flush_registers(struct target *target);
/* Checks the state of the current hart -- "is_halted" checks the actual
* on-device register. */
-bool riscv_is_halted(struct target *target);
+int riscv_get_hart_state(struct target *target, enum riscv_hart_state *state);
/* These helper functions let the generic program interface get target-specific
* information. */
-size_t riscv_debug_buffer_size(struct target *target);
+size_t riscv_progbuf_size(struct target *target);
-riscv_insn_t riscv_read_debug_buffer(struct target *target, int index);
-int riscv_write_debug_buffer(struct target *target, int index, riscv_insn_t insn);
-int riscv_execute_debug_buffer(struct target *target);
+riscv_insn_t riscv_read_progbuf(struct target *target, int index);
+int riscv_write_progbuf(struct target *target, int index, riscv_insn_t insn);
+int riscv_execute_progbuf(struct target *target, uint32_t *cmderr);
-void riscv_fill_dmi_nop_u64(struct target *target, char *buf);
-void riscv_fill_dmi_write_u64(struct target *target, char *buf, int a, uint64_t d);
-void riscv_fill_dmi_read_u64(struct target *target, char *buf, int a);
-int riscv_dmi_write_u64_bits(struct target *target);
+void riscv_fill_dm_nop(struct target *target, char *buf);
+void riscv_fill_dm_write(struct target *target, char *buf, uint64_t a, uint32_t d);
+void riscv_fill_dm_read(struct target *target, char *buf, uint64_t a);
+int riscv_get_dmi_scan_length(struct target *target);
+
+uint32_t riscv_get_dmi_address(const struct target *target, uint32_t dm_address);
int riscv_enumerate_triggers(struct target *target);
@@ -358,10 +454,13 @@ void riscv_semihosting_init(struct target *target);
enum semihosting_result riscv_semihosting(struct target *target, int *retval);
-void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *field,
+void riscv_add_bscan_tunneled_scan(struct target *target, const struct scan_field *field,
riscv_bscan_tunneled_scan_context_t *ctxt);
int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
int riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer);
+int riscv_interrupts_disable(struct target *target, uint64_t ie_mask, uint64_t *old_mstatus);
+int riscv_interrupts_restore(struct target *target, uint64_t old_mstatus);
+
#endif
diff --git a/src/target/riscv/riscv_semihosting.c b/src/target/riscv/riscv_semihosting.c
index da237ef..9c708c8 100644
--- a/src/target/riscv/riscv_semihosting.c
+++ b/src/target/riscv/riscv_semihosting.c
@@ -57,12 +57,12 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
{
struct semihosting *semihosting = target->semihosting;
if (!semihosting) {
- LOG_DEBUG(" -> NONE (!semihosting)");
+ LOG_TARGET_DEBUG(target, " -> NONE (!semihosting)");
return SEMIHOSTING_NONE;
}
if (!semihosting->is_active) {
- LOG_DEBUG(" -> NONE (!semihosting->is_active)");
+ LOG_TARGET_DEBUG(target, " -> NONE (!semihosting->is_active)");
return SEMIHOSTING_NONE;
}
@@ -71,33 +71,31 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
if (result != ERROR_OK)
return SEMIHOSTING_ERROR;
- uint8_t tmp_buf[12];
+ /*
+ * The instructions that trigger a semihosting call,
+ * always uncompressed, should look like:
+ */
+ uint32_t magic[] = {
+ 0x01f01013, /* slli zero,zero,0x1f */
+ 0x00100073, /* ebreak */
+ 0x40705013 /* srai zero,zero,0x7 */
+ };
/* Read three uncompressed instructions: The previous, the current one (pointed to by PC) and the next one */
for (int i = 0; i < 3; i++) {
+ uint8_t buf[4];
/* Instruction memories may not support arbitrary read size. Use any size that will work. */
- *retval = riscv_read_by_any_size(target, (pc - 4) + 4 * i, 4, tmp_buf + 4 * i);
+ target_addr_t address = (pc - 4) + 4 * i;
+ *retval = riscv_read_by_any_size(target, address, 4, buf);
if (*retval != ERROR_OK)
return SEMIHOSTING_ERROR;
- }
-
- /*
- * The instructions that trigger a semihosting call,
- * always uncompressed, should look like:
- *
- * 01f01013 slli zero,zero,0x1f
- * 00100073 ebreak
- * 40705013 srai zero,zero,0x7
- */
- uint32_t pre = target_buffer_get_u32(target, tmp_buf);
- uint32_t ebreak = target_buffer_get_u32(target, tmp_buf + 4);
- uint32_t post = target_buffer_get_u32(target, tmp_buf + 8);
- LOG_DEBUG("check %08x %08x %08x from 0x%" PRIx64 "-4", pre, ebreak, post, pc);
-
- if (pre != 0x01f01013 || ebreak != 0x00100073 || post != 0x40705013) {
- /* Not the magic sequence defining semihosting. */
- LOG_DEBUG(" -> NONE (no magic)");
- return SEMIHOSTING_NONE;
+ uint32_t value = target_buffer_get_u32(target, buf);
+ LOG_TARGET_DEBUG(target, "compare 0x%08x from 0x%" PRIx64 " against 0x%08x",
+ value, address, magic[i]);
+ if (value != magic[i]) {
+ LOG_TARGET_DEBUG(target, " -> NONE (no magic)");
+ return SEMIHOSTING_NONE;
+ }
}
/*
@@ -111,13 +109,13 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
result = riscv_get_register(target, &r0, GDB_REGNO_A0);
if (result != ERROR_OK) {
- LOG_DEBUG(" -> ERROR (couldn't read a0)");
+ LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a0)");
return SEMIHOSTING_ERROR;
}
result = riscv_get_register(target, &r1, GDB_REGNO_A1);
if (result != ERROR_OK) {
- LOG_DEBUG(" -> ERROR (couldn't read a1)");
+ LOG_TARGET_ERROR(target, "Could not read semihosting operation code (register a1)");
return SEMIHOSTING_ERROR;
}
@@ -131,12 +129,12 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
*retval = semihosting_common(target);
if (*retval != ERROR_OK) {
- LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op);
+ LOG_TARGET_ERROR(target, "Failed semihosting operation (0x%02X)", semihosting->op);
return SEMIHOSTING_ERROR;
}
} else {
/* Unknown operation number, not a semihosting call. */
- LOG_DEBUG(" -> NONE (unknown operation number)");
+ LOG_TARGET_ERROR(target, "Unknown semihosting operation requested (op = 0x%x)", semihosting->op);
return SEMIHOSTING_NONE;
}
}
@@ -151,11 +149,11 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
* operation to complete.
*/
if (semihosting->is_resumable && !semihosting->hit_fileio) {
- LOG_DEBUG(" -> HANDLED");
+ LOG_TARGET_DEBUG(target, " -> HANDLED");
return SEMIHOSTING_HANDLED;
}
- LOG_DEBUG(" -> WAITING");
+ LOG_TARGET_DEBUG(target, " -> WAITING");
return SEMIHOSTING_WAITING;
}
@@ -168,7 +166,7 @@ enum semihosting_result riscv_semihosting(struct target *target, int *retval)
*/
static int riscv_semihosting_setup(struct target *target, int enable)
{
- LOG_DEBUG("[%s] enable=%d", target_name(target), enable);
+ LOG_TARGET_DEBUG(target, "enable=%d", enable);
struct semihosting *semihosting = target->semihosting;
if (semihosting)
@@ -185,7 +183,7 @@ static int riscv_semihosting_post_result(struct target *target)
return 0;
}
- LOG_DEBUG("0x%" PRIx64, semihosting->result);
+ LOG_TARGET_DEBUG(target, "Result: 0x%" PRIx64, semihosting->result);
riscv_set_register(target, GDB_REGNO_A0, semihosting->result);
return 0;
}
diff --git a/src/target/target.c b/src/target/target.c
index efc1689..5187006 100644
--- a/src/target/target.c
+++ b/src/target/target.c
@@ -110,7 +110,7 @@ static struct target_timer_callback *target_timer_callbacks;
static int64_t target_timer_next_event_value;
static LIST_HEAD(target_reset_callback_list);
static LIST_HEAD(target_trace_callback_list);
-static const int polling_interval = TARGET_DEFAULT_POLLING_INTERVAL;
+static const unsigned int polling_interval = TARGET_DEFAULT_POLLING_INTERVAL;
static LIST_HEAD(empty_smp_targets);
enum nvp_assert {
@@ -212,6 +212,7 @@ static const struct nvp nvp_target_state[] = {
{ .name = "halted", .value = TARGET_HALTED },
{ .name = "reset", .value = TARGET_RESET },
{ .name = "debug-running", .value = TARGET_DEBUG_RUNNING },
+ { .name = "unavailable", .value = TARGET_UNAVAILABLE },
{ .name = NULL, .value = -1 },
};
@@ -684,6 +685,7 @@ int target_examine_one(struct target *target)
return retval;
}
+ LOG_USER("[%s] Target successfully examined.", target_name(target));
target_set_examined(target);
target_call_event_callbacks(target, TARGET_EVENT_EXAMINE_END);
@@ -703,6 +705,12 @@ static int jtag_enable_callback(enum jtag_event event, void *priv)
return target_examine_one(target);
}
+/* When this is true, it's OK to call examine() again in the hopes that this time
+ * it will work. Earlier than that there is probably other initialization that
+ * needs to happen (like scanning the JTAG chain) before examine should be
+ * called. */
+static bool examine_attempted;
+
/* Targets that correctly implement init + examine, i.e.
* no communication with target during init:
*
@@ -713,6 +721,8 @@ int target_examine(void)
int retval = ERROR_OK;
struct target *target;
+ examine_attempted = true;
+
for (target = all_targets; target; target = target->next) {
/* defer examination, but don't skip it */
if (!target->tap->enabled) {
@@ -1200,10 +1210,6 @@ int target_run_read_async_algorithm(struct target *target,
/* Avoid GDB timeouts */
keep_alive();
- if (openocd_is_shutdown_pending()) {
- retval = ERROR_SERVER_INTERRUPTED;
- break;
- }
}
if (retval != ERROR_OK) {
@@ -2967,51 +2973,42 @@ static int handle_target(void *priv)
is_jtag_poll_safe() && target;
target = target->next) {
- if (!target_was_examined(target))
- continue;
-
- if (!target->tap->enabled)
+ /* This function only gets called every polling_interval, so
+ * allow some slack in the time comparison. Otherwise, if we
+ * schedule for now+polling_interval, the next poll won't
+ * actually happen until a polling_interval later. */
+ bool poll_needed = timeval_ms() + polling_interval / 2 >= target->backoff.next_attempt;
+ if (!target->tap->enabled || power_dropout || srst_asserted || !poll_needed)
continue;
- if (target->backoff.times > target->backoff.count) {
- /* do not poll this time as we failed previously */
- target->backoff.count++;
- continue;
+ /* polling may fail silently until the target has been examined */
+ retval = target_poll(target);
+ if (retval == ERROR_OK) {
+ /* Polling succeeded, reset the back-off interval */
+ target->backoff.interval = polling_interval;
+ } else {
+ /* Increase interval between polling up to 5000ms */
+ target->backoff.interval = MAX(polling_interval,
+ MIN(target->backoff.interval * 2 + 1, 5000));
+ /* Do *not* tell gdb the target halted. This might just
+ * be a hiccup. We have no reason to believe the target
+ * is halted, and if it is running while gdb thinks it's
+ * halted things just get unnecessarily confused. gdb
+ * users can hit ^C if the need to interact with the
+ * target. */
}
- target->backoff.count = 0;
+ target->backoff.next_attempt = timeval_ms() + target->backoff.interval;
+ LOG_TARGET_DEBUG(target, "target_poll() -> %d, next attempt in %dms",
+ retval, target->backoff.interval);
- /* only poll target if we've got power and srst isn't asserted */
- if (!power_dropout && !srst_asserted) {
- /* polling may fail silently until the target has been examined */
- retval = target_poll(target);
+ if (retval != ERROR_OK && examine_attempted) {
+ target_reset_examined(target);
+ retval = target_examine_one(target);
if (retval != ERROR_OK) {
- /* 100ms polling interval. Increase interval between polling up to 5000ms */
- if (target->backoff.times * polling_interval < 5000) {
- target->backoff.times *= 2;
- target->backoff.times++;
- }
-
- /* Tell GDB to halt the debugger. This allows the user to
- * run monitor commands to handle the situation.
- */
- target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
- }
- if (target->backoff.times > 0) {
- LOG_USER("Polling target %s failed, trying to reexamine", target_name(target));
- target_reset_examined(target);
- retval = target_examine_one(target);
- /* Target examination could have failed due to unstable connection,
- * but we set the examined flag anyway to repoll it later */
- if (retval != ERROR_OK) {
- target_set_examined(target);
- LOG_USER("Examination failed, GDB will be halted. Polling again in %dms",
- target->backoff.times * polling_interval);
- return retval;
- }
+ LOG_TARGET_DEBUG(target, "Examination failed. Polling again in %dms",
+ target->backoff.interval);
+ return retval;
}
-
- /* Since we succeeded, we reset backoff count */
- target->backoff.times = 0;
}
}
@@ -3053,13 +3050,13 @@ COMMAND_HANDLER(handle_reg_command)
count, reg->name,
reg->size, value,
reg->dirty
- ? " (dirty)"
- : "");
+ ? " (dirty)"
+ : "");
free(value);
} else {
command_print(CMD, "(%i) %s (/%" PRIu32 ")",
- count, reg->name,
- reg->size);
+ count, reg->name,
+ reg->size);
}
}
cache = cache->next;
@@ -3224,11 +3221,8 @@ int target_wait_state(struct target *target, enum target_state state, unsigned i
nvp_value2name(nvp_target_state, state)->name);
}
- if (cur - then > 500) {
+ if (cur - then > 500)
keep_alive();
- if (openocd_is_shutdown_pending())
- return ERROR_SERVER_INTERRUPTED;
- }
if ((cur-then) > ms) {
LOG_ERROR("timed out while waiting for target %s",
@@ -3338,7 +3332,7 @@ COMMAND_HANDLER(handle_step_command)
void target_handle_md_output(struct command_invocation *cmd,
struct target *target, target_addr_t address, unsigned size,
- unsigned count, const uint8_t *buffer)
+ unsigned count, const uint8_t *buffer, bool include_address)
{
const unsigned line_bytecnt = 32;
unsigned line_modulo = line_bytecnt / size;
@@ -3367,7 +3361,7 @@ void target_handle_md_output(struct command_invocation *cmd,
}
for (unsigned i = 0; i < count; i++) {
- if (i % line_modulo == 0) {
+ if (include_address && (i % line_modulo == 0)) {
output_len += snprintf(output + output_len,
sizeof(output) - output_len,
TARGET_ADDR_FMT ": ",
@@ -3451,7 +3445,8 @@ COMMAND_HANDLER(handle_md_command)
struct target *target = get_current_target(CMD_CTX);
int retval = fn(target, address, size, count, buffer);
if (retval == ERROR_OK)
- target_handle_md_output(CMD, target, address, size, count, buffer);
+ target_handle_md_output(CMD, target, address, size, count, buffer,
+ true);
free(buffer);
@@ -3510,11 +3505,6 @@ static int target_fill_mem(struct target *target,
break;
/* avoid GDB timeouts */
keep_alive();
-
- if (openocd_is_shutdown_pending()) {
- retval = ERROR_SERVER_INTERRUPTED;
- break;
- }
}
free(target_buf);
@@ -3857,12 +3847,6 @@ static COMMAND_HELPER(handle_verify_image_command_internal, enum verify_mode ver
}
}
keep_alive();
- if (openocd_is_shutdown_pending()) {
- retval = ERROR_SERVER_INTERRUPTED;
- free(data);
- free(buffer);
- goto done;
- }
}
}
free(data);
diff --git a/src/target/target.h b/src/target/target.h
index d5c0e0e..c74b8c2 100644
--- a/src/target/target.h
+++ b/src/target/target.h
@@ -46,6 +46,8 @@ struct gdb_fileio_info;
* not sure how this is used with all the recent changes)
* TARGET_DEBUG_RUNNING = 4: the target is running, but it is executing code on
* behalf of the debugger (e.g. algorithm for flashing)
+ * TARGET_UNAVAILABLE = 5: The target is unavailable for some reason. It might
+ * be powered down, for instance.
*
* also see: target_state_name();
*/
@@ -56,6 +58,7 @@ enum target_state {
TARGET_HALTED = 2,
TARGET_RESET = 3,
TARGET_DEBUG_RUNNING = 4,
+ TARGET_UNAVAILABLE = 5
};
enum target_reset_mode {
@@ -102,8 +105,8 @@ struct gdb_service {
/* target back off timer */
struct backoff_timer {
- int times;
- int count;
+ int64_t next_attempt;
+ unsigned int interval;
};
/* split target registers into multiple class */
@@ -183,6 +186,9 @@ struct target {
struct rtos *rtos; /* Instance of Real Time Operating System support */
bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto"
* and must be detected when symbols are offered */
+ /* Track when next to poll(). If polling is failing, we don't want to
+ * poll too quickly because we'll just overwhelm the user with error
+ * messages. */
struct backoff_timer backoff;
int smp; /* Unique non-zero number for each SMP group */
struct list_head *smp_targets; /* list all targets in this smp group/cluster
@@ -779,7 +785,7 @@ void target_handle_event(struct target *t, enum target_event e);
void target_handle_md_output(struct command_invocation *cmd,
struct target *target, target_addr_t address, unsigned size,
- unsigned count, const uint8_t *buffer);
+ unsigned count, const uint8_t *buffer, bool include_address);
int target_profiling_default(struct target *target, uint32_t *samples, uint32_t
max_num_samples, uint32_t *num_samples, uint32_t seconds);
diff --git a/tcl/board/esp32c2-ftdi.cfg b/tcl/board/esp32c2-ftdi.cfg
new file mode 100644
index 0000000..bc2b82f
--- /dev/null
+++ b/tcl/board/esp32c2-ftdi.cfg
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C2 connected via ESP-Prog.
+#
+# For example, OpenOCD can be started for ESP32-C2 debugging on
+#
+# openocd -f board/esp32c2-ftdi.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/ftdi/esp32_devkitj_v1.cfg]
+# Source the ESP32-C2 configuration file
+source [find target/esp32c2.cfg]
+
+# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
+# do not relate to OpenOCD trying to read from a memory range without physical
+# memory being present there), you can try lowering this.
+#
+# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
+# if CPU frequency is 160MHz or 240MHz.
+adapter speed 20000
diff --git a/tcl/board/esp32c3-builtin.cfg b/tcl/board/esp32c3-builtin.cfg
new file mode 100644
index 0000000..9e19b1b
--- /dev/null
+++ b/tcl/board/esp32c3-builtin.cfg
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C3 connected via builtin USB-JTAG adapter.
+#
+# For example, OpenOCD can be started for ESP32-C3 debugging on
+#
+# openocd -f board/esp32c3-builtin.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/esp_usb_jtag.cfg]
+# Source the ESP32-C3 configuration file
+source [find target/esp32c3.cfg]
+
+adapter speed 40000
diff --git a/tcl/board/esp32c3-ftdi.cfg b/tcl/board/esp32c3-ftdi.cfg
new file mode 100644
index 0000000..5595374
--- /dev/null
+++ b/tcl/board/esp32c3-ftdi.cfg
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C3 connected via ESP-Prog.
+#
+# For example, OpenOCD can be started for ESP32-C3 debugging on
+#
+# openocd -f board/esp32c3-ftdi.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/ftdi/esp32_devkitj_v1.cfg]
+# Source the ESP32-C3 configuration file
+source [find target/esp32c3.cfg]
+
+# The speed of the JTAG interface, in kHz. If you get DSR/DIR errors (and they
+# do not relate to OpenOCD trying to read from a memory range without physical
+# memory being present there), you can try lowering this.
+#
+# On DevKit-J, this can go as high as 20MHz if CPU frequency is 80MHz, or 26MHz
+# if CPU frequency is 160MHz or 240MHz.
+adapter speed 20000
diff --git a/tcl/board/esp32c6-builtin.cfg b/tcl/board/esp32c6-builtin.cfg
new file mode 100644
index 0000000..abc96b2
--- /dev/null
+++ b/tcl/board/esp32c6-builtin.cfg
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C6 connected via builtin USB-JTAG adapter.
+#
+# For example, OpenOCD can be started for ESP32-C6 debugging on
+#
+# openocd -f board/esp32c6-builtin.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/esp_usb_jtag.cfg]
+# Source the ESP32-C6 configuration file
+source [find target/esp32c6.cfg]
+
+adapter speed 40000
diff --git a/tcl/board/esp32h2-builtin.cfg b/tcl/board/esp32h2-builtin.cfg
new file mode 100644
index 0000000..1ce5961
--- /dev/null
+++ b/tcl/board/esp32h2-builtin.cfg
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Example OpenOCD configuration file for ESP32-C3 connected via builtin USB-JTAG adapter.
+#
+# For example, OpenOCD can be started for ESP32-C3 debugging on
+#
+# openocd -f board/esp32c3-builtin.cfg
+#
+
+# Source the JTAG interface configuration file
+source [find interface/esp_usb_jtag.cfg]
+# Source the ESP32-C3 configuration file
+source [find target/esp32h2.cfg]
+
+adapter speed 40000
diff --git a/tcl/board/sifive-e31arty-cjtag.cfg b/tcl/board/sifive-e31arty-cjtag.cfg
new file mode 100644
index 0000000..58ba23a
--- /dev/null
+++ b/tcl/board/sifive-e31arty-cjtag.cfg
@@ -0,0 +1,23 @@
+#
+# Be sure you include the speed and interface before this file
+# Example:
+# -c "adapter_khz 5000" -f "interface/ftdi/olimex-arm-usb-tiny-h.cfg" -f "board/sifive-e31arty-cjtag.cfg"
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913
+
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+
+flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
+init
+if {[ info exists pulse_srst]} {
+ oscan1_ftdi_set_signal nSRST 0
+ oscan1_ftdi_set_signal nSRST z
+}
+halt
+flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/tcl/board/sifive-e31arty-onboard-ftdi.cfg b/tcl/board/sifive-e31arty-onboard-ftdi.cfg
new file mode 100644
index 0000000..3d40cfa
--- /dev/null
+++ b/tcl/board/sifive-e31arty-onboard-ftdi.cfg
@@ -0,0 +1,25 @@
+#
+# Be sure you include the speed and interface before this file
+# Example:
+# -c "adapter_khz 5000" -f "interface/ftdi/arty-onboard-ftdi.cfg" -f "board/sifive-e31arty-onboard-ftdi.cfg"
+
+set _CHIPNAME riscv
+jtag newtap $_CHIPNAME cpu -irlen 6; # -expected-id 0x0362d093
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+riscv use_bscan_tunnel 5
+
+# Uncomment if hardware has flash
+# flash bank spi0 fespi 0x40000000 0 0 0 $_TARGETNAME.0 0x20004000
+init
+if {[ info exists pulse_srst]} {
+ ftdi_set_signal nSRST 0
+ ftdi_set_signal nSRST z
+}
+halt
+# Uncomment if hardware has flash
+# flash protect 0 64 last off
+echo "Ready for Remote Connections"
diff --git a/tcl/board/sifive-hifive1-revb.cfg b/tcl/board/sifive-hifive1-revb.cfg
index e5fe104..7fcab0c 100644
--- a/tcl/board/sifive-hifive1-revb.cfg
+++ b/tcl/board/sifive-hifive1-revb.cfg
@@ -12,6 +12,9 @@ set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
$_TARGETNAME.0 configure -work-area-phys 0x80000000 -work-area-size 0x4000 -work-area-backup 0
+riscv set_enable_virt2phys off
+riscv set_enable_virtual off
+
flash bank onboard_spi_flash fespi 0x20000000 0 0 0 $_TARGETNAME.0
init
diff --git a/tcl/interface/ftdi/arty-onboard-ftdi.cfg b/tcl/interface/ftdi/arty-onboard-ftdi.cfg
new file mode 100644
index 0000000..0edc615
--- /dev/null
+++ b/tcl/interface/ftdi/arty-onboard-ftdi.cfg
@@ -0,0 +1,7 @@
+interface ftdi
+# ftdi_device_desc "Arty On-board FTDI interface"
+ftdi vid_pid 0x0403 0x6010
+ftdi channel 0
+ftdi layout_init 0x0088 0x008b
+reset_config none
+
diff --git a/tcl/interface/ftdi/digilent-hs2-cjtag.cfg b/tcl/interface/ftdi/digilent-hs2-cjtag.cfg
new file mode 100644
index 0000000..3daf461
--- /dev/null
+++ b/tcl/interface/ftdi/digilent-hs2-cjtag.cfg
@@ -0,0 +1,17 @@
+adapter driver ftdi
+ftdi device_desc "Digilent Adept USB Device"
+ftdi vid_pid 0x0403 0x6014
+
+ftdi channel 0
+ftdi layout_init 0x60e8 0x60eb
+
+reset_config none
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -ndata 0x6000 -oe 0x6000
+
+ftdi layout_signal TMSC_EN -data 0x0020 -oe 0x0020
diff --git a/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
new file mode 100644
index 0000000..6939d00
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-jtag-cjtag.cfg
@@ -0,0 +1,27 @@
+#
+# Olimex ARM JTAG SWD adapter
+# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/
+#
+
+#
+# Olimex ARM-USB-TINY-H
+#
+# http://www.olimex.com/dev/arm-usb-tiny-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H"
+ftdi vid_pid 0x15ba 0x002a
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
new file mode 100644
index 0000000..36ac587
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-ocd-h-cjtag.cfg
@@ -0,0 +1,22 @@
+#
+# Olimex ARM-USB-OCD-H (using cJTAG)
+#
+# http://www.olimex.com/dev/arm-usb-ocd-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-OCD-H"
+ftdi vid_pid 0x15ba 0x002b
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
new file mode 100644
index 0000000..13378b3
--- /dev/null
+++ b/tcl/interface/ftdi/olimex-arm-usb-tiny-h-cjtag.cfg
@@ -0,0 +1,27 @@
+#
+# Olimex ARM JTAG SWD adapter
+# https://www.olimex.com/Products/ARM/JTAG/ARM-JTAG-SWD/
+#
+
+#
+# Olimex ARM-USB-TINY-H (using cJTAG)
+#
+# http://www.olimex.com/dev/arm-usb-tiny-h.html
+#
+
+interface ftdi
+ftdi oscan1_mode on
+ftdi device_desc "Olimex OpenOCD JTAG ARM-USB-TINY-H"
+ftdi vid_pid 0x15ba 0x002a
+
+ftdi layout_init 0x0808 0x0a1b
+ftdi layout_signal nSRST -oe 0x0200
+# oscan1_ftdi_layout_signal nTRST -data 0x0100 -oe 0x0100
+ftdi layout_signal LED -data 0x0800
+
+# These signals are used for cJTAG escape sequence on initialization only
+ftdi layout_signal TCK -data 0x0001
+ftdi layout_signal TDI -data 0x0002
+ftdi layout_signal TDO -input 0x0004
+ftdi layout_signal TMS -data 0x0008
+ftdi layout_signal JTAG_SEL -data 0x0100 -oe 0x0100
diff --git a/tcl/target/1986ве1т.cfg b/tcl/target/1986Be1T.cfg
index a3172cc..a3172cc 100644
--- a/tcl/target/1986ве1т.cfg
+++ b/tcl/target/1986Be1T.cfg
diff --git a/tcl/target/к1879xб1я.cfg b/tcl/target/K1879x61R.cfg
index 8dd330d..8dd330d 100644
--- a/tcl/target/к1879xб1я.cfg
+++ b/tcl/target/K1879x61R.cfg
diff --git a/tcl/target/esp32c2.cfg b/tcl/target/esp32c2.cfg
new file mode 100644
index 0000000..42aeb0a
--- /dev/null
+++ b/tcl/target/esp32c2.cfg
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+# Source the ESP common configuration file.
+source [find target/esp_common.cfg]
+
+# Target specific global variables
+set _CHIPNAME "riscv"
+set _CPUTAPID 0x0000cc25
+set _ESP_ARCH "riscv"
+set _ONLYCPU 1
+set _ESP_SMP_TARGET 0
+set _ESP_SMP_BREAK 0
+set _ESP_EFUSE_MAC_ADDR_REG 0x60008840
+
+# Target specific functions should be implemented for each riscv chips.
+proc riscv_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 WDT
+ mww 0x6001f064 0x50D83AA1
+ mww 0x6001F048 0
+ # RTC WDT
+ mww 0x6000809C 0x50D83AA1
+ mww 0x60008084 0
+ # SWD
+ mww 0x600080A4 0x8F1D312A
+ mww 0x600080A0 0x84B00000
+}
+
+proc riscv_soc_reset { } {
+ global _RISCV_DMCONTROL
+
+ # This procedure does "digital system reset", i.e. resets
+ # all the peripherals except for the RTC block.
+ # It is called from reset-assert-post target event callback,
+ # after assert_reset procedure was called.
+ # Since we need the hart to to execute a write to RTC_CNTL_SW_SYS_RST,
+ # temporarily take it out of reset. Save the dmcontrol state before
+ # doing so.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000001
+ # Trigger the reset
+ mww 0x60008000 0x9c00a000
+ # Workaround for stuck in cpu start during calibration.
+ # By writing zero to TIMG_RTCCALICFG_REG, we are disabling calibration
+ mww 0x6001F068 0
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Disable the watchdogs again
+ riscv_wdt_disable
+
+ # Here debugger reads allresumeack and allhalted bits as set (0x330a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000003
+}
+
+proc riscv_memprot_is_enabled { } {
+ global _RISCV_ABS_CMD _RISCV_ABS_DATA0
+
+ # PMPADDR 0-1 covers entire valid IRAM range and PMPADDR 2-3 covers entire DRAM region
+ # pmpcfg0 holds the configuration for the PMP 0-3 address registers
+
+ # read pmpcfg0 and extract into 8-bit variables.
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203a0
+ set pmpcfg0 [riscv dmi_read $_RISCV_ABS_DATA0]
+
+ set pmp0cfg [expr {($pmpcfg0 >> (8 * 0)) & 0xFF}]
+ set pmp1cfg [expr {($pmpcfg0 >> (8 * 1)) & 0xFF}]
+ set pmp2cfg [expr {($pmpcfg0 >> (8 * 2)) & 0xFF}]
+ set pmp3cfg [expr {($pmpcfg0 >> (8 * 3)) & 0xFF}]
+
+ # read PMPADDR 0-3
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b0
+ set pmpaddr0 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b1
+ set pmpaddr1 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b2
+ set pmpaddr2 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b3
+ set pmpaddr3 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+
+ set IRAM_LOW 0x40380000
+ set IRAM_HIGH 0x403C0000
+ set DRAM_LOW 0x3FCA0000
+ set DRAM_HIGH 0x3FCE0000
+ set PMP_RWX 0x07
+ set PMP_RW 0x03
+
+ # The lock bit remains unset during the execution of the 2nd stage bootloader.
+ # Thus we do not perform a lock bit check for IRAM and DRAM regions.
+
+ # Check OpenOCD can write and execute from IRAM.
+ if {$pmpaddr0 >= $IRAM_LOW && $pmpaddr1 <= $IRAM_HIGH} {
+ if {($pmp0cfg & $PMP_RWX) != 0 || ($pmp1cfg & $PMP_RWX) != $PMP_RWX} {
+ return 1
+ }
+ }
+
+ # Check OpenOCD can read/write entire DRAM region.
+ if {$pmpaddr2 >= $DRAM_LOW && $pmpaddr3 <= $DRAM_HIGH} {
+ if {($pmp2cfg & $PMP_RW) != 0 && ($pmp3cfg & $PMP_RW) != $PMP_RW} {
+ return 1
+ }
+ }
+
+ return 0
+}
+
+create_esp_target $_ESP_ARCH
diff --git a/tcl/target/esp32c3.cfg b/tcl/target/esp32c3.cfg
new file mode 100644
index 0000000..d266ad5
--- /dev/null
+++ b/tcl/target/esp32c3.cfg
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+# Source the ESP common configuration file.
+source [find target/esp_common.cfg]
+
+# Target specific global variables
+set _CHIPNAME "riscv"
+set _CPUTAPID 0x00005c25
+set _ESP_ARCH "riscv"
+set _ONLYCPU 1
+set _ESP_SMP_TARGET 0
+set _ESP_SMP_BREAK 0
+set _ESP_EFUSE_MAC_ADDR_REG 0x60008844
+
+# Target specific functions should be implemented for each riscv chips.
+proc riscv_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 & 1 WDTs
+ mww 0x6001f064 0x50D83AA1
+ mww 0x6001F048 0
+ mww 0x60020064 0x50D83AA1
+ mww 0x60020048 0
+ # RTC WDT
+ mww 0x600080a8 0x50D83AA1
+ mww 0x60008090 0
+ # SWD
+ mww 0x600080b0 0x8F1D312A
+ mww 0x600080ac 0x84B00000
+}
+
+# This is almost identical with the esp32c2_soc_reset.
+# Will be refactored with the other common settings.
+proc riscv_soc_reset { } {
+ global _RISCV_DMCONTROL
+
+ # This procedure does "digital system reset", i.e. resets
+ # all the peripherals except for the RTC block.
+ # It is called from reset-assert-post target event callback,
+ # after assert_reset procedure was called.
+ # Since we need the hart to to execute a write to RTC_CNTL_SW_SYS_RST,
+ # temporarily take it out of reset. Save the dmcontrol state before
+ # doing so.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000001
+ # Trigger the reset
+ mww 0x60008000 0x9c00a000
+ # Workaround for stuck in cpu start during calibration.
+ # By writing zero to TIMG_RTCCALICFG_REG, we are disabling calibration
+ mww 0x6001F068 0
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Disable the watchdogs again
+ riscv_wdt_disable
+
+ # Here debugger reads allresumeack and allhalted bits as set (0x330a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000003
+}
+
+proc riscv_memprot_is_enabled { } {
+ # IRAM0 PMS lock, SENSITIVE_CORE_X_IRAM0_PMS_CONSTRAIN_0_REG
+ if { [get_mmr_bit 0x600C10A8 0] != 0 } {
+ return 1
+ }
+ # DRAM0 PMS lock, SENSITIVE_CORE_X_DRAM0_PMS_CONSTRAIN_0_REG
+ if { [get_mmr_bit 0x600C10C0 0] != 0 } {
+ return 1
+ }
+ return 0
+}
+
+create_esp_target $_ESP_ARCH
diff --git a/tcl/target/esp32c6.cfg b/tcl/target/esp32c6.cfg
new file mode 100644
index 0000000..e1ef10a
--- /dev/null
+++ b/tcl/target/esp32c6.cfg
@@ -0,0 +1,142 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+# Source the ESP common configuration file.
+source [find target/esp_common.cfg]
+
+# Target specific global variables
+set _CHIPNAME "riscv"
+set _CPUTAPID 0x0000dc25
+set _ESP_ARCH "riscv"
+set _ONLYCPU 1
+set _ESP_SMP_TARGET 0
+set _ESP_SMP_BREAK 0
+set _ESP_EFUSE_MAC_ADDR_REG 0x600B0844
+
+# Target specific functions should be implemented for each riscv chips.
+proc riscv_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 & 1 WDTs
+ mww 0x60008064 0x50D83AA1
+ mww 0x60008048 0
+ mww 0x60009064 0x50D83AA1
+ mww 0x60009048 0
+ # LP_WDT_RTC
+ mww 0x600b1c18 0x50D83AA1
+ mww 0x600B1C00 0
+ # LP_WDT_SWD
+ mww 0x600b1c20 0x50D83AA1
+ mww 0x600b1c1c 0x40000000
+}
+
+proc riscv_soc_reset { } {
+ global _RISCV_DMCONTROL _RISCV_SB_CS _RISCV_SB_ADDR0 _RISCV_SB_DATA0
+
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000001
+ riscv dmi_write $_RISCV_SB_CS 0x48000
+ riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034
+ riscv dmi_write $_RISCV_SB_DATA0 0x80000000
+ # clear dmactive to clear sbbusy otherwise debug module gets stuck
+ riscv dmi_write $_RISCV_DMCONTROL 0
+
+ riscv dmi_write $_RISCV_SB_CS 0x48000
+ riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038
+ riscv dmi_write $_RISCV_SB_DATA0 0x10000000
+
+ # clear dmactive to clear sbbusy otherwise debug module gets stuck
+ riscv dmi_write $_RISCV_DMCONTROL 0
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+ # Here debugger reads dmstatus as 0xc03a2
+
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Here debugger reads dmstatus as 0x3a2
+
+ # Disable the watchdogs again
+ riscv_wdt_disable
+
+ # Here debugger reads anyhalted and allhalted bits as set (0x3a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000003
+}
+
+proc riscv_memprot_is_enabled { } {
+ global _RISCV_ABS_CMD _RISCV_ABS_DATA0
+
+ # If IRAM/DRAM split is enabled TOR address match mode is used.
+ # If IRAM/DRAM split is disabled NAPOT mode is used.
+ # In order to determine if the IRAM/DRAM regions are protected against RWX/RW,
+ # it is necessary to first read the mode and then apply the appropriate method for checking.
+ # We can understand the mode reading pmp5cfg in pmpcfg1 register.
+ # If it is none we know that pmp6cfg and pmp7cfg is in TOR mode.
+
+ # Read pmpcfg1 and extract into 8-bit variables.
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203a1
+ set pmpcfg1 [riscv dmi_read $_RISCV_ABS_DATA0]
+
+ set pmp5cfg [expr {($pmpcfg1 >> (8 * 1)) & 0xFF}]
+ set pmp6cfg [expr {($pmpcfg1 >> (8 * 2)) & 0xFF}]
+ set pmp7cfg [expr {($pmpcfg1 >> (8 * 3)) & 0xFF}]
+
+ set IRAM_LOW 0x40800000
+ set IRAM_HIGH 0x40880000
+ set DRAM_LOW 0x40800000
+ set DRAM_HIGH 0x40880000
+ set PMP_RWX 0x07
+ set PMP_RW 0x03
+ set PMP_A [expr {($pmp5cfg >> 3) & 0x03}]
+
+ if {$PMP_A == 0} {
+ # TOR mode used to protect valid address space.
+
+ # Read PMPADDR 5-7
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b5
+ set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b6
+ set pmpaddr6 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b7
+ set pmpaddr7 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+
+ # The lock bit remains unset during the execution of the 2nd stage bootloader.
+ # Thus we do not perform a lock bit check for IRAM and DRAM regions.
+
+ # Check OpenOCD can write and execute from IRAM.
+ if {$pmpaddr5 >= $IRAM_LOW && $pmpaddr6 <= $IRAM_HIGH} {
+ if {($pmp5cfg & $PMP_RWX) != 0 || ($pmp6cfg & $PMP_RWX) != $PMP_RWX} {
+ return 1
+ }
+ }
+
+ # Check OpenOCD can read/write entire DRAM region.
+ if {$pmpaddr7 >= $DRAM_LOW && $pmpaddr7 <= $DRAM_HIGH} {
+ if {($pmp7cfg & $PMP_RW) != $PMP_RW} {
+ return 1
+ }
+ }
+ } elseif {$PMP_A == 3} {
+ # NAPOT mode used to protect valid address space.
+
+ # Read PMPADDR 5
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b5
+ set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0]}]
+
+ # Expected value written to the pmpaddr5
+ set pmpaddr_napot [expr {($IRAM_LOW | (($IRAM_HIGH - $IRAM_LOW - 1) >> 1)) >> 2}]
+ if {($pmpaddr_napot != $pmpaddr5) || ($pmp5cfg & $PMP_RWX) != $PMP_RWX} {
+ return 1
+ }
+ }
+
+ return 0
+}
+
+create_esp_target $_ESP_ARCH
diff --git a/tcl/target/esp32h2.cfg b/tcl/target/esp32h2.cfg
new file mode 100644
index 0000000..45f598f
--- /dev/null
+++ b/tcl/target/esp32h2.cfg
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+# Source the ESP common configuration file.
+source [find target/esp_common.cfg]
+
+# Target specific global variables
+set _CHIPNAME "riscv"
+set _CPUTAPID 0x00010c25
+set _ESP_ARCH "riscv"
+set _ONLYCPU 1
+set _ESP_SMP_TARGET 0
+set _ESP_SMP_BREAK 0
+set _ESP_EFUSE_MAC_ADDR_REG 0x600B0844
+
+# Target specific functions should be implemented for each riscv chips.
+proc riscv_wdt_disable { } {
+ # Halt event can occur during config phase (before "init" is done).
+ # Ignore it since mww commands don't work at that time.
+ if { [string compare [command mode] config] == 0 } {
+ return
+ }
+
+ # Timer Group 0 & 1 WDTs
+ mww 0x60009064 0x50D83AA1
+ mww 0x60009048 0
+ mww 0x6000A064 0x50D83AA1
+ mww 0x6000A048 0
+ # WDT_RTC
+ #mww 0x600b1c18 0x50D83AA1
+ #mww 0x600B1C00 0
+ # WDT_SWD
+ #mww 0x600b1c20 0x8F1D312A
+ #mww 0x600b1c1c 0x84B00000
+}
+
+proc riscv_soc_reset { } {
+ global _RISCV_DMCONTROL _RISCV_SB_CS _RISCV_SB_ADDR0 _RISCV_SB_DATA0
+
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000001
+ riscv dmi_write $_RISCV_SB_CS 0x48000
+ riscv dmi_write $_RISCV_SB_ADDR0 0x600b1034
+ riscv dmi_write $_RISCV_SB_DATA0 0x80000000
+ # clear dmactive to clear sbbusy otherwise debug module gets stuck
+ riscv dmi_write $_RISCV_DMCONTROL 0
+
+ riscv dmi_write $_RISCV_SB_CS 0x48000
+ riscv dmi_write $_RISCV_SB_ADDR0 0x600b1038
+ riscv dmi_write $_RISCV_SB_DATA0 0x10000000
+
+ # clear dmactive to clear sbbusy otherwise debug module gets stuck
+ riscv dmi_write $_RISCV_DMCONTROL 0
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+ # Here debugger reads dmstatus as 0xc03a2
+
+ # Wait for the reset to happen
+ sleep 10
+ poll
+ # Here debugger reads dmstatus as 0x3a2
+
+ # Disable the watchdogs again
+ riscv_wdt_disable
+
+ # Here debugger reads anyhalted and allhalted bits as set (0x3a2)
+ # We will clean allhalted state by resuming the core.
+ riscv dmi_write $_RISCV_DMCONTROL 0x40000001
+
+ # Put the hart back into reset state. Note that we need to keep haltreq set.
+ riscv dmi_write $_RISCV_DMCONTROL 0x80000003
+}
+
+proc riscv_memprot_is_enabled { } {
+ global _RISCV_ABS_CMD _RISCV_ABS_DATA0
+ # If IRAM/DRAM split is enabled, PMPADDR 5-6 will cover valid IRAM region and PMPADDR 7 will cover valid DRAM region
+ # Only TOR mode is used for IRAM and DRAM protections.
+
+ # Read pmpcfg1 and extract into 8-bit variables.
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203a1
+ set pmpcfg1 [riscv dmi_read $_RISCV_ABS_DATA0]
+
+ set pmp5cfg [expr {($pmpcfg1 >> (8 * 1)) & 0xFF}]
+ set pmp6cfg [expr {($pmpcfg1 >> (8 * 2)) & 0xFF}]
+ set pmp7cfg [expr {($pmpcfg1 >> (8 * 3)) & 0xFF}]
+
+ # Read PMPADDR 5-7
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b5
+ set pmpaddr5 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b6
+ set pmpaddr6 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+ riscv dmi_write $_RISCV_ABS_CMD 0x2203b7
+ set pmpaddr7 [expr {[riscv dmi_read $_RISCV_ABS_DATA0] << 2}]
+
+ set IRAM_LOW 0x40800000
+ set IRAM_HIGH 0x40850000
+ set DRAM_LOW 0x40800000
+ set DRAM_HIGH 0x40850000
+
+ set PMP_RWX 0x07
+ set PMP_RW 0x03
+
+ # The lock bit remains unset during the execution of the 2nd stage bootloader.
+ # Thus, we do not perform a lock bit check for IRAM and DRAM regions.
+
+ # Check OpenOCD can write and execute from IRAM.
+ if {$pmpaddr5 >= $IRAM_LOW && $pmpaddr6 <= $IRAM_HIGH} {
+ if {($pmp5cfg & $PMP_RWX) != 0 || ($pmp6cfg & $PMP_RWX) != $PMP_RWX} {
+ return 1
+ }
+ }
+
+ # Check OpenOCD can read/write entire DRAM region.
+ # If IRAM/DRAM split is disabled, pmpaddr7 will be zero, checking only IRAM region is enough.
+ if {$pmpaddr7 != 0 && $pmpaddr7 >= $DRAM_LOW && $pmpaddr7 <= $DRAM_HIGH} {
+ if {($pmp7cfg & $PMP_RW) != $PMP_RW} {
+ return 1
+ }
+ }
+
+ return 0
+}
+
+create_esp_target $_ESP_ARCH
diff --git a/tcl/target/esp_common.cfg b/tcl/target/esp_common.cfg
index ac8cd6a..af2f6ad 100644
--- a/tcl/target/esp_common.cfg
+++ b/tcl/target/esp_common.cfg
@@ -6,6 +6,14 @@ source [find bitsbytes.tcl]
source [find memory.tcl]
source [find mmr_helpers.tcl]
+# Riscv Debug Module Registers which are used around esp configuration files.
+set _RISCV_ABS_DATA0 0x04
+set _RISCV_DMCONTROL 0x10
+set _RISCV_ABS_CMD 0x17
+set _RISCV_SB_CS 0x38
+set _RISCV_SB_ADDR0 0x39
+set _RISCV_SB_DATA0 0x3C
+
# Common ESP chips definitions
# Espressif supports only NuttX in the upstream.
@@ -69,13 +77,12 @@ proc create_esp_target { ARCH } {
set_esp_common_variables
create_esp_jtag
create_openocd_targets
- configure_openocd_events
+ configure_openocd_events $ARCH
if { $ARCH == "xtensa"} {
configure_esp_xtensa_default_settings
} else {
- # riscv targets are not upstreamed yet.
- # they can be found at the official Espressif fork.
+ configure_esp_riscv_default_settings
}
}
@@ -131,7 +138,6 @@ proc configure_event_halted { } {
$_TARGETNAME_0 configure -event halted {
global _ESP_WDT_DISABLE
$_ESP_WDT_DISABLE
- esp halted_event_handler
}
}
@@ -167,12 +173,25 @@ proc configure_event_gdb_attach { } {
}
}
-proc configure_openocd_events { } {
+proc configure_openocd_events { ARCH } {
+ if { $ARCH == "riscv" } {
+ configure_event_halted
+ }
configure_event_examine_end
configure_event_reset_assert_post
configure_event_gdb_attach
}
+proc configure_esp_riscv_default_settings { } {
+ gdb_breakpoint_override hard
+ riscv set_reset_timeout_sec 2
+ riscv set_command_timeout_sec 5
+ riscv set_mem_access sysbus progbuf abstract
+ riscv set_ebreakm on
+ riscv set_ebreaks on
+ riscv set_ebreaku on
+}
+
proc configure_esp_xtensa_default_settings { } {
global _TARGETNAME_0 _ESP_SMP_BREAK _FLASH_VOLTAGE _CHIPNAME
diff --git a/tcl/target/gd32vf103.cfg b/tcl/target/gd32vf103.cfg
index 54a74e8..77fdff7 100644
--- a/tcl/target/gd32vf103.cfg
+++ b/tcl/target/gd32vf103.cfg
@@ -6,15 +6,12 @@
# https://www.gigadevice.com/products/microcontrollers/gd32/risc-v/
#
+adapter speed 1000
source [find mem_helper.tcl]
transport select jtag
-if { [info exists CHIPNAME] } {
- set _CHIPNAME $CHIPNAME
-} else {
- set _CHIPNAME gd32vf103
-}
+reset_config srst_nogate
# The smallest RAM size 6kB (GD32VF103C4/T4/R4)
if { [info exists WORKAREASIZE] } {
@@ -23,10 +20,15 @@ if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE 0x1800
}
+set _CHIPNAME gd32vf103
+# The vendor's configuration expects an ID of 0x1e200a6d, but this one is what
+# I have on my board (Sipeed Longan Nano, GD32VF103CBT6).
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x1000563d
+jtag newtap $_CHIPNAME bs -irlen 5 -expected-id 0x790007a3
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
+$_TARGETNAME riscv set_enable_virt2phys off
proc default_mem_access {} {
riscv set_mem_access progbuf
@@ -39,12 +41,78 @@ $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0x08000000 0 0 0 $_TARGETNAME
-# DBGMCU_CR register cannot be set in examine-end event as the running RISC-V CPU
-# does not allow the debugger to access memory.
-# Stop watchdogs at least before flash programming.
-$_TARGETNAME configure -event reset-init {
- # DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP
- mmw 0xE0042004 0x00000300 0
+# Address 0 is only aliased to main flash when the chip is not running its
+# built-in bootloader. When it is, it's instead aliased to a read only section
+# of flash at 0x1fffb000. However, we can't detect or dynamically switch this,
+# so just pretend it's always aliased to main flash. We need to tell OpenOCD
+# about this alias because otherwise we'll try to use software breakpoints on
+# code in flash, which don't work because flash mappings are read-only.
+flash bank $_CHIPNAME.flashalias virtual 0x0 0 0 0 $_TARGETNAME $_FLASHNAME
+
+# On this chip, ndmreset (the debug module bit that triggers a software reset)
+# doesn't work. So for JTAG connections without an SRST, we need to trigger a
+# reset manually. This is an undocumented reset sequence that's used by the
+# JTAG flashing script in the vendor-supplied GD32VF103 PlatformIO plugin:
+#
+# https://github.com/sipeed/platform-gd32v/commit/f9cbb44819bc05dd2010cc815c32be0486800cc2
+#
+$_TARGETNAME configure -event reset-assert {
+ set dmcontrol 0x10
+ set dmcontrol_dmactive [expr 1 << 0]
+ set dmcontrol_haltreq [expr 1 << 31]
+
+ global _RESETMODE
+ global _TARGETNAME
+
+ # Halt the core so that we can write to memory. We do this first so
+ # that it doesn't clobber our dmcontrol configuration.
+ halt
+
+ # Set haltreq appropriately for the type of reset we're doing. This
+ # replicates what the generic RISC-V reset_assert() function would
+ # do if we weren't overriding it. The $_RESETMODE hack sucks, but
+ # it's the least invasive way to determine whether we need to halt,
+ # and psoc6.cfg already uses the same trick. (reset_deassert(), which
+ # does run, also does this, but at that point it may be too late: the
+ # reset has already been triggered, so there's a race between it and
+ # the haltreq write.)
+ #
+ # If we didn't override the generic handler, we'd actually still have
+ # to do this: the default handler sets ndmreset, which prevents memory
+ # access even though it doesn't actually trigger a reset on this chip.
+ # So we'd need to unset it here, which involves a write to dmcontrol,
+ # Since haltreq is write-only and there's no way to leave it unchanged,
+ # we'd have to figure out its proper value anyway.
+ set val $dmcontrol_dmactive
+ if {$_RESETMODE ne "run"} {
+ set val [expr $val | $dmcontrol_haltreq]
+ }
+ $_TARGETNAME riscv dmi_write $dmcontrol $val
+
+ # Unlock 0xe0042008 so that the next write triggers a reset
+ $_TARGETNAME mww 0xe004200c 0x4b5a6978
+
+ # We need to trigger the reset using abstract memory access, since
+ # progbuf access tries to read a status code out of a core register
+ # after the write happens, which fails when the core is in reset.
+ riscv set_mem_access abstract
+
+ # Go!
+ $_TARGETNAME mww 0xe0042008 0x1
+
+ # Put the memory access mode back to what it was.
+ default_mem_access
+}
+
+# Capture the mode of a given reset so that we can use it later in the
+# reset-assert handler.
+proc init_reset { mode } {
+ global _RESETMODE
+ set _RESETMODE $mode
+
+ if {[using_jtag]} {
+ jtag arp_init-reset
+ }
}
# On this chip, ndmreset (the debug module bit that triggers a software reset)
diff --git a/tools/filter_openocd_log.py b/tools/filter_openocd_log.py
new file mode 100755
index 0000000..666e166
--- /dev/null
+++ b/tools/filter_openocd_log.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+
+# This function is the only OpenOCD-specific part of this script.
+def make_canonical(line):
+ # Remove the line number and time stamp.
+ m = re.match(r"(Debug|Error|Info |User |Warn ): \d+ \d+ (.*)", line)
+ if m:
+ return "%s: - - %s\n" % (m.group(1), m.group(2))
+ else:
+ return line
+
+def buf_startswith(buf, sequence):
+ if len(buf) < len(sequence):
+ return False
+ for i, entry in enumerate(sequence):
+ if entry[1] != buf[i][1]:
+ return False
+ return True
+
+def shorten_buffer(outfd, buf, current_repetition):
+ """Do something to the buffer to make it shorter. If we can't compress
+ anything, then print out the first line and remove it."""
+ length_before = len(buf)
+
+ if current_repetition:
+ while buf_startswith(buf, current_repetition[0]):
+ del buf[:len(current_repetition[0])]
+ current_repetition[1] += 1
+ if len(buf) < length_before:
+ return current_repetition
+ outfd.write("## The following %d lines repeat %d times:\n" % (
+ len(current_repetition[0]), current_repetition[1]))
+ for entry in current_repetition[0]:
+ outfd.write("# %s" % entry[1])
+
+ # Look for repeated sequences...
+ repetitions = []
+ for length in range(1, int(len(buf)/2)):
+ # Is there a repeating sequence of `length` lines?
+ matched_lines = 0
+ for i, entry in enumerate(buf[length:]):
+ if entry[1] == buf[i % length][1]:
+ matched_lines += 1
+ else:
+ break
+ if matched_lines >= length:
+ repetitions.append((matched_lines + length, length))
+
+ if repetitions:
+ repetitions.sort(key=lambda entry: -entry[1])
+ matched_lines, length = repetitions[-1]
+ repeated = int(matched_lines / length)
+ if repeated * length >= 3:
+ sequence = buf[:length]
+ del buf[:repeated * length]
+
+ if matched_lines == length_before:
+ # Could be continued...
+ return [sequence, repeated]
+
+ else:
+ outfd.write("## The following %d lines repeat %d times:\n" %
+ (length, repeated))
+ for entry in sequence:
+ outfd.write("# %s" % entry[1])
+ return None
+
+ if len(buf) >= length_before:
+ line, _ = buf[0]
+ outfd.write(line)
+ buf.pop(0)
+
+ if length_before <= len(buf):
+ print("Buffer:")
+ for entry in buf:
+ print("%r" % entry[0])
+ assert False
+
+ return None
+
+def compress_log(infd, outfd, window):
+ """Compress log by finding repeated runs of lines. Runs in O(lines *
+ window**2), which can probably be improved."""
+ # Contains line, canonical tuples
+ buf = []
+ current_repetition = None
+
+ for line in infd:
+ buf.append((line, make_canonical(line)))
+ if len(buf) > window:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+ while len(buf) > 0:
+ current_repetition = shorten_buffer(outfd, buf, current_repetition)
+
+def main(args):
+ import argparse
+ parser = argparse.ArgumentParser(
+ description='Combine repeated OpenOCD debug output lines. This is '
+ 'very helpful when looking at verbose log files where e.g. target '
+ 'polling is repeated over and over.',
+ epilog='If no files are specified, read standard input.',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('file', nargs='*', help='input file')
+ parser.add_argument('-o', '--output', help='output file', default=sys.stdout)
+ parser.add_argument('-w', '--window', type=int, default=400,
+ help='number of lines to consider when looking for repetitions')
+ args = parser.parse_args(args)
+
+ if args.file:
+ for f in args.file:
+ compress_log(open(f, "r"), args.output, args.window)
+ else:
+ compress_log(sys.stdin, args.output, args.window)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))