diff --git a/.github/workflows/checkpatch.yml b/.github/workflows/checkpatch.yml deleted file mode 100644 index 0c120c0f2..000000000 --- a/.github/workflows/checkpatch.yml +++ /dev/null @@ -1,21 +0,0 @@ -on: pull_request - -name: Check Code Style - -jobs: - check: - runs-on: ubuntu-latest - env: - DL_DIR: ../downloads - BUILD_DIR: ../build - steps: - - name: Checkout Code - uses: actions/checkout@v2 - with: - fetch-depth: 50 - - run: sudo apt-get install patchutils - - name: Run checkpatch - run: | - git diff -U20 HEAD~40 | \ - filterdiff -x "a/src/jtag/drivers/libjaylink/*" -x "a/tools/git2cl/*" | \ - ./tools/scripts/checkpatch.pl --no-signoff - diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml deleted file mode 100644 index 74028d54b..000000000 --- a/.github/workflows/linux-build.yml +++ /dev/null @@ -1,35 +0,0 @@ -on: pull_request - -name: Linux Build - -jobs: - # 32-bit, clang - build32: - runs-on: ubuntu-latest - env: - CFLAGS: -m32 - CC: clang - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - run: sudo apt-get install clang gcc-multilib - - run: ./bootstrap - - run: ./configure --enable-remote-bitbang --enable-jtag_vpi --disable-target64 - - run: make - - run: file src/openocd | grep 32-bit - - - # 64-bit, gcc - build64: - runs-on: ubuntu-latest - env: - CFLAGS: -m64 - CC: gcc - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - run: 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-oscan1 - - run: make - - run: file src/openocd | grep 64-bit diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml deleted file mode 100644 index c53b1944b..000000000 --- a/.github/workflows/snapshot.yml +++ /dev/null @@ -1,82 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -# Copyright (C) 2020 by Tarek BOUCHKATI - -on: pull_request - -name: OpenOCD Snapshot - -jobs: - package: - runs-on: [ubuntu-18.04] - env: - DL_DIR: ../downloads - BUILD_DIR: ../build - steps: - - name: Install needed packages - run: | - 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 - - run: ./bootstrap - - name: Prepare libusb1 - env: - LIBUSB1_VER: 1.0.24 - run: | - mkdir -p $DL_DIR && cd $DL_DIR - wget "https://github.com/libusb/libusb/releases/download/v${LIBUSB1_VER}/libusb-${LIBUSB1_VER}.tar.bz2" - tar -xjf libusb-${LIBUSB1_VER}.tar.bz2 - echo "LIBUSB1_SRC=$PWD/libusb-${LIBUSB1_VER}" >> $GITHUB_ENV - - name: Prepare hidapi - env: - HIDAPI_VER: 0.10.1 - run: | - mkdir -p $DL_DIR && cd $DL_DIR - wget "https://github.com/libusb/hidapi/archive/hidapi-${HIDAPI_VER}.tar.gz" - tar -xzf hidapi-${HIDAPI_VER}.tar.gz - cd hidapi-hidapi-${HIDAPI_VER} - ./bootstrap - echo "HIDAPI_SRC=$PWD" >> $GITHUB_ENV - - name: Prepare capstone - env: - CAPSTONE_VER: 4.0.2 - run: | - mkdir -p $DL_DIR && cd $DL_DIR - CAPSTONE_NAME=${CAPSTONE_VER} - CAPSTONE_FOLDER=capstone-${CAPSTONE_VER} - wget "https://github.com/aquynh/capstone/archive/${CAPSTONE_VER}.tar.gz" - tar -xzf ${CAPSTONE_VER}.tar.gz - echo "CAPSTONE_SRC=$PWD/capstone-${CAPSTONE_VER}" >> $GITHUB_ENV - - name: Package OpenOCD for windows - env: - MAKE_JOBS: 2 - HOST: i686-w64-mingw32 - LIBUSB1_CONFIG: --enable-shared --disable-static - HIDAPI_CONFIG: --enable-shared --disable-static --disable-testgui - CAPSTONE_CONFIG: "CAPSTONE_BUILD_CORE_ONLY=yes CAPSTONE_STATIC=yes CAPSTONE_SHARED=no" - run: | - # check if there is tag pointing at HEAD, otherwise take the HEAD SHA-1 as OPENOCD_TAG - OPENOCD_TAG="`git tag --points-at HEAD`" - [ -z $OPENOCD_TAG ] && OPENOCD_TAG="`git rev-parse --short HEAD`" - # check if there is tag pointing at HEAD, if so the release will have the same name as the tag, - # otherwise it will be named 'latest' - RELEASE_NAME="`git tag --points-at HEAD`" - [ -z $RELEASE_NAME ] && RELEASE_NAME="latest" - [[ $RELEASE_NAME = "latest" ]] && IS_PRE_RELEASE="true" || IS_PRE_RELEASE="false" - # set env and call cross-build.sh - export OPENOCD_TAG=$OPENOCD_TAG - export OPENOCD_SRC=$PWD - export OPENOCD_CONFIG="" - mkdir -p $BUILD_DIR && cd $BUILD_DIR - bash $OPENOCD_SRC/contrib/cross-build.sh $HOST - # add missing dlls - cd $HOST-root/usr - cp `$HOST-gcc --print-file-name=libwinpthread-1.dll` ./bin/ - cp `$HOST-gcc --print-file-name=libgcc_s_sjlj-1.dll` ./bin/ - # prepare the artifact - ARTIFACT="openocd-${OPENOCD_TAG}-${HOST}.tar.gz" - tar -czf $ARTIFACT * - echo "RELEASE_NAME=$RELEASE_NAME" >> $GITHUB_ENV - echo "IS_PRE_RELEASE=$IS_PRE_RELEASE" >> $GITHUB_ENV - echo "ARTIFACT_PATH=$PWD/$ARTIFACT" >> $GITHUB_ENV diff --git a/.gitmodules b/.gitmodules index feb8014b9..f2da17ed7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ +[submodule "tools/git2cl"] + path = tools/git2cl + url = https://git.savannah.nongnu.org/git/git2cl.git [submodule "jimtcl"] path = jimtcl url = https://github.com/msteveb/jimtcl.git -[submodule "tools/git2cl"] - path = tools/git2cl - url = https://repo.or.cz/git2cl.git [submodule "src/jtag/drivers/libjaylink"] path = src/jtag/drivers/libjaylink - url = https://repo.or.cz/libjaylink.git + url = https://gitlab.zapb.de/libjaylink/libjaylink.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d0bc8f261..000000000 --- a/.travis.yml +++ /dev/null @@ -1,76 +0,0 @@ -language: c -dist: trusty - -matrix: - include: - - os: linux - env: - - BUILD=x86_64-linux-gnu - - EXECUTABLE=openocd - addons: - apt: - packages: - - patchutils - compiler: gcc - - - os: linux - env: - - BUILD=i686-linux-gnu - - CFLAGS=-m32 - - EXECUTABLE=openocd - addons: - apt: - packages: - - gcc-multilib patchutils - compiler: gcc - - - os: linux - env: - - BUILD=x86_64-linux-gnu - - EXECUTABLE=openocd - addons: - apt: - packages: - - patchutils - compiler: clang - - - os: linux - env: - - BUILD=i686-linux-gnu - - CFLAGS=-m32 - - CONFIGURE_ARGS="--disable-target64" - - EXECUTABLE=openocd - compiler: clang - addons: - apt: - packages: - - gcc-multilib patchutils - - - os: linux - env: - - BUILD=i686-w64-mingw - - CONFIGURE_ARGS="--build=i686-unknown-linux-gnu --host=i686-w64-mingw32" - - EXECUTABLE=openocd.exe - compiler: i686-w64-mingw32-gcc - addons: - apt: - packages: - - binutils-mingw-w64-i686 gcc-mingw-w64-i686 g++-mingw-w64-i686 patchutils - -script: - # This is here for the signoff check. - # Disabled because when travis does the integration build the last change - # is an automated change, which won't have the Signed-off-by line. - #- ./tools/checkpatch.sh - - # Ideally we'd diff back to where we either branched off OpenOCD or master, - # or riscv. But that's tricky, and the default git clone only gets the last - # 50 changes any case. Most merges won't consist of more than 40 changes, - # so this should work fine most of the time, and be a lot better than not - # checking at all. - - git diff -U20 HEAD~40 | - filterdiff -x "a/src/jtag/drivers/libjaylink/*" -x "a/tools/git2cl/*" - -x "b/src/gnulib/*" | - ./tools/scripts/checkpatch.pl --no-signoff - - - ./bootstrap && ./configure --enable-remote-bitbang --enable-jtag_vpi $CONFIGURE_ARGS && make - - file src/$EXECUTABLE diff --git a/config_subdir.m4 b/config_subdir.m4 index 45a1c6c4f..2be590e44 100644 --- a/config_subdir.m4 +++ b/config_subdir.m4 @@ -7,6 +7,6 @@ AC_DEFUN([AX_CONFIG_SUBDIR_OPTION], AC_CONFIG_SUBDIRS([$1]) m4_ifblank([$2], [rm -f $srcdir/$1/configure.gnu], -[echo -e '#!/bin/sh\nexec "`dirname "'\$'0"`/configure" $2 "'\$'@"' > "$srcdir/$1/configure.gnu" +[echo -e '#!/bin/sh\nexec "`dirname "'\$'0"`/configure" '"$2"' "'\$'@"' > "$srcdir/$1/configure.gnu" ]) ]) diff --git a/configure.ac b/configure.ac index f969680f5..e84bcca74 100644 --- a/configure.ac +++ b/configure.ac @@ -110,10 +110,12 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])]) m4_define([USB1_ADAPTERS], [[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]], - [[ftdi_oscan1], [cJTAG OSCAN1 tunneled thru MPSSE], [FTDI_OSCAN1]], + [[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]], + [[wlinke], [ WLINKE Programmer], [WLINKE]], + [[ch347], [ CH347 Programmer], [CH347]], [[usb_blaster_2], [Altera USB-Blaster II Compatible], [USB_BLASTER_2]], [[ft232r], [Bitbang mode of FT232R based devices], [FT232R]], [[vsllink], [Versaloon-Link JTAG Programmer], [VSLLINK]], @@ -123,8 +125,10 @@ m4_define([USB1_ADAPTERS], [[opendous], [eStick/opendous JTAG Programmer], [OPENDOUS]], [[armjtagew], [Olimex ARM-JTAG-EW Programmer], [ARMJTAGEW]], [[rlink], [Raisonance RLink JTAG Programmer], [RLINK]], - [[usbprog], [USBProg JTAG Programmer], [USBPROG]], - [[aice], [Andes JTAG Programmer], [AICE]]]) + [[usbprog], [USBProg JTAG Programmer], [USBPROG]]]) + +m4_define([DEPRECATED_USB1_ADAPTERS], + [[[aice], [Andes JTAG Programmer (deprecated)], [AICE]]]) m4_define([HIDAPI_ADAPTERS], [[[cmsis_dap], [CMSIS-DAP Compliant Debugger], [CMSIS_DAP_HID]], @@ -257,6 +261,8 @@ AC_ARG_ADAPTERS([ LIBJAYLINK_ADAPTERS ],[auto]) +AC_ARG_ADAPTERS([DEPRECATED_USB1_ADAPTERS],[no]) + AC_ARG_ENABLE([parport], AS_HELP_STRING([--enable-parport], [Enable building the pc parallel port driver]), [build_parport=$enableval], [build_parport=no]) @@ -275,6 +281,16 @@ AC_ARG_ENABLE([jtag_vpi], AS_HELP_STRING([--enable-jtag_vpi], [Enable building support for JTAG VPI]), [build_jtag_vpi=$enableval], [build_jtag_vpi=no]) +AC_ARG_ENABLE([wlinke], + AS_HELP_STRING([--enable-wlinke], [Enable building support for WLINKE]), + [build_wlinke=$enableval], [build_wlinke=no]) +AC_ARG_ENABLE([ch347], + AS_HELP_STRING([--enable-ch347], [Enable building support for CH347]), + [build_ch347=$enableval], [build_ch347=no]) +AC_ARG_ENABLE([vdebug], + AS_HELP_STRING([--enable-vdebug], [Enable building support for Cadence Virtual Debug Interface]), + [build_vdebug=$enableval], [build_vdebug=no]) + AC_ARG_ENABLE([jtag_dpi], AS_HELP_STRING([--enable-jtag_dpi], [Enable building support for JTAG DPI]), [build_jtag_dpi=$enableval], [build_jtag_dpi=no]) @@ -351,6 +367,10 @@ AC_ARG_ENABLE([internal-jimtcl], AS_HELP_STRING([--disable-internal-jimtcl], [Disable building internal jimtcl]), [use_internal_jimtcl=$enableval], [use_internal_jimtcl=yes]) +AC_ARG_ENABLE([jimtcl-maintainer], + AS_HELP_STRING([--enable-jimtcl-maintainer], [Enable maintainer mode when building internal jimtcl]), + [use_internal_jimtcl_maintainer=$enableval], [use_internal_jimtcl_maintainer=no]) + AC_ARG_ENABLE([internal-libjaylink], AS_HELP_STRING([--disable-internal-libjaylink], [Disable building internal libjaylink]), @@ -514,6 +534,24 @@ AS_IF([test "x$build_jtag_vpi" = "xyes"], [ AC_DEFINE([BUILD_JTAG_VPI], [0], [0 if you don't want JTAG VPI.]) ]) +AS_IF([test "x$build_wlinke" = "xyes"], [ + AC_DEFINE([BUILD_WLINKE], [1], [1 if you want WLINKE.]) +], [ + AC_DEFINE([BUILD_WLINKE], [0], [0 if you don't want WLINKE.]) +]) + +AS_IF([test "x$build_ch347" = "xyes"], [ + AC_DEFINE([BUILD_CH347], [1], [1 if you want CH347.]) +], [ + AC_DEFINE([BUILD_CH347], [0], [0 if you don't want CH347.]) +]) + +AS_IF([test "x$build_vdebug" = "xyes"], [ + AC_DEFINE([BUILD_VDEBUG], [1], [1 if you want Cadence vdebug interface.]) +], [ + AC_DEFINE([BUILD_VDEBUG], [0], [0 if you don't want Cadence vdebug interface.]) +]) + AS_IF([test "x$build_jtag_dpi" = "xyes"], [ AC_DEFINE([BUILD_JTAG_DPI], [1], [1 if you want JTAG DPI.]) ], [ @@ -541,7 +579,12 @@ AS_IF([test "x$enable_buspirate" != "xno"], [ AS_IF([test "x$use_internal_jimtcl" = "xyes"], [ AS_IF([test -f "$srcdir/jimtcl/configure.ac"], [ - AX_CONFIG_SUBDIR_OPTION([jimtcl], [--disable-install-jim]) + AS_IF([test "x$use_internal_jimtcl_maintainer" = "xyes"], [ + jimtcl_config_options="--disable-install-jim --maintainer" + ], [ + jimtcl_config_options="--disable-install-jim" + ]) + AX_CONFIG_SUBDIR_OPTION([jimtcl], [$jimtcl_config_options]) ], [ AC_MSG_ERROR([jimtcl not found, run git submodule init and git submodule update.]) ]) @@ -642,6 +685,7 @@ m4_define([PROCESS_ADAPTERS], [ ]) PROCESS_ADAPTERS([USB1_ADAPTERS], ["x$use_libusb1" = "xyes"], [libusb-1.x]) +PROCESS_ADAPTERS([DEPRECATED_USB1_ADAPTERS], ["x$use_libusb1" = "xyes"], [libusb-1.x]) PROCESS_ADAPTERS([HIDAPI_ADAPTERS], ["x$use_hidapi" = "xyes"], [hidapi]) PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libusb1" = "xyes"], [hidapi and libusb-1.x]) PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi]) @@ -689,8 +733,11 @@ AM_CONDITIONAL([AT91RM9200], [test "x$build_at91rm9200" = "xyes"]) AM_CONDITIONAL([BCM2835GPIO], [test "x$build_bcm2835gpio" = "xyes"]) AM_CONDITIONAL([IMX_GPIO], [test "x$build_imx_gpio" = "xyes"]) AM_CONDITIONAL([BITBANG], [test "x$build_bitbang" = "xyes"]) -AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes" -o "x$build_jtag_vpi" = "xyes"]) -AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes" -o "x$build_jtag_dpi" = "xyes"]) +AM_CONDITIONAL([JTAG_VPI], [test "x$build_jtag_vpi" = "xyes"]) +AM_CONDITIONAL([WLINKE], [test "x$build_wlinke" = "xyes" -o "x$build_wlinke" = "xyes"]) +AM_CONDITIONAL([CH347], [test "x$build_ch347" = "xyes" -o "x$build_ch347" = "xyes"]) +AM_CONDITIONAL([VDEBUG], [test "x$build_vdebug" = "xyes"]) +AM_CONDITIONAL([JTAG_DPI], [test "x$build_jtag_dpi" = "xyes"]) AM_CONDITIONAL([USB_BLASTER_DRIVER], [test "x$enable_usb_blaster" != "xno" -o "x$enable_usb_blaster_2" != "xno"]) AM_CONDITIONAL([AMTJTAGACCEL], [test "x$build_amtjtagaccel" = "xyes"]) AM_CONDITIONAL([GW16012], [test "x$build_gw16012" = "xyes"]) @@ -779,6 +826,7 @@ echo echo OpenOCD configuration summary echo -------------------------------------------------- m4_foreach([adapter], [USB1_ADAPTERS, + DEPRECATED_USB1_ADAPTERS, HIDAPI_ADAPTERS, HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS, LIBFTDI_USB1_ADAPTERS, LIBGPIOD_ADAPTERS, diff --git a/contrib/loaders/flash/gd32vf103/Makefile b/contrib/loaders/flash/gd32vf103/Makefile new file mode 100644 index 000000000..2c34e08c0 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/Makefile @@ -0,0 +1,28 @@ +BIN2C = ../../../../src/helper/bin2char.sh + +CROSS_COMPILE ?= riscv-none-embed- + +CC=$(CROSS_COMPILE)gcc +OBJCOPY=$(CROSS_COMPILE)objcopy +OBJDUMP=$(CROSS_COMPILE)objdump + +CFLAGS = -march=rv32i -mabi=ilp32 -static -nostartfiles -nostdlib -Os -g -fPIC + +all: gd32vf103.inc + +.PHONY: clean + +%.elf: %.c + $(CC) $(CFLAGS) $< -o $@ + +%.lst: %.elf + $(OBJDUMP) -S $< > $@ + +%.bin: %.elf + $(OBJCOPY) -Obinary $< $@ + +%.inc: %.bin + $(BIN2C) < $< > $@ + +clean: + -rm -f *.elf *.lst *.bin *.inc diff --git a/contrib/loaders/flash/gd32vf103/gd32vf103.c b/contrib/loaders/flash/gd32vf103/gd32vf103.c new file mode 100644 index 000000000..69225a026 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/gd32vf103.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +#define FLASH_BSY (1 << 0) +#define FLASH_PGERR (1 << 2) +#define FLASH_WRPRTERR (1 << 4) + +void flash_write(volatile uint32_t *flash_sr, + uint32_t hwords_count, + uint16_t *buffer, + uint16_t *target_addr) __attribute__((naked)); + +void flash_write(volatile uint32_t *flash_sr, + uint32_t hwords_count, + uint16_t *buffer, + uint16_t *target_addr) +{ + do { + *target_addr = *buffer++; + + register uint32_t sr; + do { + sr = *flash_sr; + } while (sr & FLASH_BSY); + + if (sr & (FLASH_PGERR | FLASH_WRPRTERR)) + break; + + target_addr++; + } while (--hwords_count); + asm("ebreak"); +} diff --git a/contrib/loaders/flash/gd32vf103/gd32vf103.inc b/contrib/loaders/flash/gd32vf103/gd32vf103.inc new file mode 100644 index 000000000..05eabff21 --- /dev/null +++ b/contrib/loaders/flash/gd32vf103/gd32vf103.inc @@ -0,0 +1,4 @@ +/* Autogenerated with ../../../../src/helper/bin2char.sh */ +0x83,0x57,0x06,0x00,0x13,0x06,0x26,0x00,0x23,0x90,0xf6,0x00,0x83,0x27,0x05,0x00, +0x13,0xf7,0x17,0x00,0xe3,0x1c,0x07,0xfe,0x93,0xf7,0x47,0x01,0x63,0x98,0x07,0x00, +0x93,0x85,0xf5,0xff,0x93,0x86,0x26,0x00,0xe3,0x9c,0x05,0xfc,0x73,0x00,0x10,0x00, diff --git a/contrib/rpc_examples/ocd_rpc_example.py b/contrib/rpc_examples/ocd_rpc_example.py index 3470d848b..e6146f617 100755 --- a/contrib/rpc_examples/ocd_rpc_example.py +++ b/contrib/rpc_examples/ocd_rpc_example.py @@ -95,24 +95,16 @@ def readVariable(self, address): return None if (len(raw) < 2) else strToHex(raw[1]) def readMemory(self, wordLen, address, n): - self.send("array unset output") # better to clear the array before - self.send("mem2array output %d 0x%x %d" % (wordLen, address, n)) - - output = [*map(int, self.send("return $output").split(" "))] - d = dict([tuple(output[i:i + 2]) for i in range(0, len(output), 2)]) - - return [d[k] for k in sorted(d.keys())] + output = self.send("read_memory 0x%x %d %d" % (address, wordLen, n)) + return [*map(lambda x: int(x, 16), output.split(" "))] def writeVariable(self, address, value): assert value is not None self.send("mww 0x%x 0x%x" % (address, value)) - def writeMemory(self, wordLen, address, n, data): - array = " ".join(["%d 0x%x" % (a, b) for a, b in enumerate(data)]) - - self.send("array unset 1986ве1т") # better to clear the array before - self.send("array set 1986ве1т { %s }" % array) - self.send("array2mem 1986ве1т 0x%x %s %d" % (wordLen, address, n)) + def writeMemory(self, wordLen, address, data): + data = "{" + ' '.join(['0x%x' % x for x in data]) + "}" + self.send("write_memory 0x%x %d %s" % (address, wordLen, data)) if __name__ == "__main__": diff --git a/doc/manual/primer/tcl.txt b/doc/manual/primer/tcl.txt index 868a75ba0..eba2f552d 100644 --- a/doc/manual/primer/tcl.txt +++ b/doc/manual/primer/tcl.txt @@ -174,7 +174,7 @@ them. It is similar to this bash statement. EXPORT vn=`date` LINE 2 & 3 - set $vn [expr (1024 * $x)] + set $vn [expr {1024 * $x}] global $vn In line 1, we dynamically created a variable name. Here, we are diff --git a/doc/openocd.texi b/doc/openocd.texi index 04c2affcf..e671b80b5 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -588,6 +588,12 @@ produced, PDF schematics are easily found and it is easy to make. @* A JTAG driver acting as a client for the JTAG VPI server interface. @* Link: @url{http://github.com/fjullien/jtag_vpi} +@item @b{vdebug} +@* A driver for Cadence virtual Debug Interface to emulated or simulated targets. +It implements a client connecting to the vdebug server, which in turn communicates +with the emulated or simulated RTL model through a transactor. The current version +supports only JTAG as a transport, but other virtual transports, like DAP are planned. + @item @b{jtag_dpi} @* A JTAG driver acting as a client for the SystemVerilog Direct Programming Interface (DPI) for JTAG devices. DPI allows OpenOCD to connect to the JTAG @@ -2009,9 +2015,9 @@ proc setc15 @{regs value@} @{ echo [format "set p15 0x%04x, 0x%08x" $regs $value] - arm mcr 15 [expr ($regs>>12)&0x7] \ - [expr ($regs>>0)&0xf] [expr ($regs>>4)&0xf] \ - [expr ($regs>>8)&0x7] $value + arm mcr 15 [expr @{($regs >> 12) & 0x7@}] \ + [expr @{($regs >> 0) & 0xf@}] [expr @{($regs >> 4) & 0xf@}] \ + [expr @{($regs >> 8) & 0x7@}] $value @} @end example @@ -2576,6 +2582,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 }. +@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 @@ -3157,7 +3192,9 @@ the target's supply voltage. The result can be converted to Volts (ignoring the most significant bytes, always zero) @example > set a [st-link cmd 8 0xf7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] -> echo [expr 2*1.2*([lindex $a 4]+256*[lindex $a 5])/([lindex $a 0]+256*[lindex $a 1])] +> set n [expr @{[lindex $a 4] + 256 * [lindex $a 5]@}] +> set d [expr @{[lindex $a 0] + 256 * [lindex $a 1]@}] +> echo [expr @{2 * 1.2 * $n / $d@}] 3.24891518738 @end example @end deffn @@ -3343,6 +3380,41 @@ This value is only used with the standard variant. @end deffn +@deffn {Interface Driver} {vdebug} +Cadence Virtual Debug Interface driver. + +@deffn {Config Command} {vdebug server} host:port +Specifies the host and TCP port number where the vdebug server runs. +@end deffn + +@deffn {Config Command} {vdebug batching} value +Specifies the batching method for the vdebug request. Possible values are +0 for no batching +1 or wr to batch write transactions together (default) +2 or rw to batch both read and write transactions +@end deffn + +@deffn {Config Command} {vdebug polling} min max +Takes two values, representing the polling interval in ms. Lower values mean faster +debugger responsiveness, but lower emulation performance. The minimum should be +around 10, maximum should not exceed 1000, which is the default gdb and keepalive +timeout value. +@end deffn + +@deffn {Config Command} {vdebug bfm_path} path clk_period +Specifies the hierarchical path and input clk period of the vdebug BFM in the design. +The hierarchical path uses Verilog notation top.inst.inst +The clock period must include the unit, for instance 40ns. +@end deffn + +@deffn {Config Command} {vdebug mem_path} path base size +Specifies the hierarchical path to the design memory instance for backdoor access. +Up to 4 memories can be specified. The hierarchical path uses Verilog notation. +The base specifies start address in the design address space, size its size in bytes. +Both values can use hexadecimal notation with prefix 0x. +@end deffn +@end deffn + @deffn {Interface Driver} {jtag_dpi} SystemVerilog Direct Programming Interface (DPI) compatible driver for JTAG devices in emulation. The driver acts as a client for the SystemVerilog @@ -4151,6 +4223,10 @@ option. When vendors put out multiple versions of a chip, or use the same JTAG-level ID for several largely-compatible chips, it may be more practical to ignore the version field than to update config files to handle all of the various chip IDs. The version field is defined as bit 28-31 of the IDCODE. +@item @code{-ignore-bypass} +@*Specify this to ignore the 'bypass' bit of the idcode. Some vendor put +an invalid idcode regarding this bit. Specify this to ignore this bit and +to not consider this tap in bypass mode. @item @code{-ircapture} @var{NUMBER} @*The bit pattern loaded by the TAP into the JTAG shift register on entry to the @sc{ircapture} state, such as 0x01. @@ -4523,13 +4599,13 @@ where the mask bit is 1. The following example sets HPROT3 (cacheable) and leaves the rest of the pattern intact. It configures memory access through DCache on Cortex-M7. @example -set CSW_HPROT3_CACHEABLE [expr 1 << 27] +set CSW_HPROT3_CACHEABLE [expr @{1 << 27@}] samv.dap apcsw $CSW_HPROT3_CACHEABLE $CSW_HPROT3_CACHEABLE @end example Another example clears SPROT bit and leaves the rest of pattern intact: @example -set CSW_SPROT [expr 1 << 30] +set CSW_SPROT [expr @{1 << 30@}] samv.dap apcsw 0 $CSW_SPROT @end example @@ -4667,6 +4743,8 @@ compact Thumb2 instruction set. Supports also ARMv6-M and ARMv8-M cores @item @code{dsp5680xx} -- implements Freescale's 5680x DSP. @item @code{esirisc} -- this is an EnSilica eSi-RISC core. The current implementation supports eSi-32xx cores. +@item @code{esp32c2} -- this is an Espressif SoC with single RISC-V core. +@item @code{esp32c3} -- this is an Espressif SoC with single RISC-V core. @item @code{fa526} -- resembles arm920 (w/o Thumb). @item @code{feroceon} -- resembles arm926. @item @code{hla_target} -- a Cortex-M alternative to work with HL adapters like ST-Link. @@ -4681,9 +4759,9 @@ specified, @xref{gdbportoverride,,option -gdb-port}.), and a fake ARM core will be emulated to comply to GDB remote protocol. @item @code{mips_m4k} -- a MIPS core. @item @code{mips_mips64} -- a MIPS64 core. -@item @code{nds32_v2} -- this is an Andes NDS32 v2 core. -@item @code{nds32_v3} -- this is an Andes NDS32 v3 core. -@item @code{nds32_v3m} -- this is an Andes NDS32 v3m core. +@item @code{nds32_v2} -- this is an Andes NDS32 v2 core (deprecated; would be removed in v0.13.0). +@item @code{nds32_v3} -- this is an Andes NDS32 v3 core (deprecated; would be removed in v0.13.0). +@item @code{nds32_v3m} -- this is an Andes NDS32 v3m core (deprecated; would be removed in v0.13.0). @item @code{or1k} -- this is an OpenRISC 1000 core. The current implementation supports three JTAG TAP cores: @itemize @minus @@ -4933,27 +5011,76 @@ use these to deal with specific reset cases. They are not otherwise documented here. @end deffn -@deffn {Command} {$target_name array2mem} arrayname width address count -@deffnx {Command} {$target_name mem2array} arrayname width address count -These provide an efficient script-oriented interface to memory. -The @code{array2mem} primitive writes bytes, halfwords, words -or double-words; while @code{mem2array} reads them. -In both cases, the TCL side uses an array, and -the target side uses raw memory. +@deffn {Command} {$target_name set_reg} dict +Set register values of the target. -The efficiency comes from enabling the use of -bulk JTAG data transfer operations. -The script orientation comes from working with data -values that are packaged for use by TCL scripts; -@command{mdw} type primitives only print data they retrieve, -and neither store nor return those values. +@itemize +@item @var{dict} ... Tcl dictionary with pairs of register names and values. +@end itemize + +For example, the following command sets the value 0 to the program counter (pc) +register and 0x1000 to the stack pointer (sp) register: + +@example +set_reg @{pc 0 sp 0x1000@} +@end example +@end deffn + +@deffn {Command} {$target_name get_reg} [-force] list +Get register values from the target and return them as Tcl dictionary with pairs +of register names and values. +If option "-force" is set, the register values are read directly from the +target, bypassing any caching. + +@itemize +@item @var{list} ... List of register names +@end itemize + +For example, the following command retrieves the values from the program +counter (pc) and stack pointer (sp) register: + +@example +get_reg @{pc sp@} +@end example +@end deffn + +@deffn {Command} {$target_name write_memory} address width data ['phys'] +This function provides an efficient way to write to the target memory from a Tcl +script. + +@itemize +@item @var{address} ... target memory address +@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64 +@item @var{data} ... Tcl list with the elements to write +@item ['phys'] ... treat the memory address as physical instead of virtual address +@end itemize + +For example, the following command writes two 32 bit words into the target +memory at address 0x20000000: + +@example +write_memory 0x20000000 32 @{0xdeadbeef 0x00230500@} +@end example +@end deffn + +@deffn {Command} {$target_name read_memory} address width count ['phys'] +This function provides an efficient way to read the target memory from a Tcl +script. +A Tcl list containing the requested memory elements is returned by this function. @itemize -@item @var{arrayname} ... is the name of an array variable -@item @var{width} ... is 8/16/32/64 - indicating the memory access size -@item @var{address} ... is the target memory address -@item @var{count} ... is the number of elements to process +@item @var{address} ... target memory address +@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64 +@item @var{count} ... number of elements to read +@item ['phys'] ... treat the memory address as physical instead of virtual address @end itemize + +For example, the following command reads two 32 bit words from the target +memory at address 0x20000000: + +@example +read_memory 0x20000000 32 2 +@end example @end deffn @deffn {Command} {$target_name cget} queryparm @@ -5023,8 +5150,8 @@ When the current target has an MMU which is present and active, Otherwise, or if the optional @var{phys} flag is specified, @var{addr} is interpreted as a physical address. If @var{count} is specified, displays that many units. -(If you want to manipulate the data instead of displaying it, -see the @code{mem2array} primitives.) +(If you want to process the data instead of displaying it, +see the @code{read_memory} primitives.) @end deffn @deffn {Command} {$target_name mwd} [phys] addr doubleword [count] @@ -5179,6 +5306,22 @@ when reset disables PLLs needed to use a fast clock. @* After single-step has completed @item @b{trace-config} @* After target hardware trace configuration was changed +@item @b{semihosting-user-cmd-0x100} +@* The target made a semihosting call with user-defined operation number 0x100 +@item @b{semihosting-user-cmd-0x101} +@* The target made a semihosting call with user-defined operation number 0x101 +@item @b{semihosting-user-cmd-0x102} +@* The target made a semihosting call with user-defined operation number 0x102 +@item @b{semihosting-user-cmd-0x103} +@* The target made a semihosting call with user-defined operation number 0x103 +@item @b{semihosting-user-cmd-0x104} +@* The target made a semihosting call with user-defined operation number 0x104 +@item @b{semihosting-user-cmd-0x105} +@* The target made a semihosting call with user-defined operation number 0x105 +@item @b{semihosting-user-cmd-0x106} +@* The target made a semihosting call with user-defined operation number 0x106 +@item @b{semihosting-user-cmd-0x107} +@* The target made a semihosting call with user-defined operation number 0x107 @end itemize @quotation Note @@ -6351,13 +6494,22 @@ flash bank $_FLASHNAME cc3220sf 0 0 0 0 $_TARGETNAME @end deffn @deffn {Flash Driver} {efm32} -All members of the EFM32 microcontroller family from Energy Micro include -internal flash and use ARM Cortex-M3 cores. The driver automatically recognizes -a number of these chips using the chip identification register, and +All members of the EFM32/EFR32 microcontroller family from Energy Micro (now Silicon Labs) +include internal flash and use Arm Cortex-M3 or Cortex-M4 cores. The driver automatically +recognizes a number of these chips using the chip identification register, and autoconfigures itself. @example flash bank $_FLASHNAME efm32 0 0 0 0 $_TARGETNAME @end example +It supports writing to the user data page, as well as the portion of the lockbits page +past 512 bytes on chips with larger page sizes. The latter is used by the SiLabs +bootloader/AppLoader system for encryption keys. Setting protection on these pages is +currently not supported. +@example +flash bank userdata.flash efm32 0x0FE00000 0 0 0 $_TARGETNAME +flash bank lockbits.flash efm32 0x0FE04000 0 0 0 $_TARGETNAME +@end example + A special feature of efm32 controllers is that it is possible to completely disable the debug interface by writing the correct values to the 'Debug Lock Word'. OpenOCD supports this via the following command: @@ -7172,6 +7324,7 @@ applied to all of them. All members of the STM32F0, STM32F1 and STM32F3 microcontroller families from STMicroelectronics and all members of the GD32F1x0, GD32F3x0 and GD32E23x microcontroller families from GigaDevice include internal flash and use ARM Cortex-M0/M3/M4/M23 cores. +The driver also works with GD32VF103 powered by RISC-V core. The driver automatically recognizes a number of these chips using the chip identification register, and autoconfigures itself. @@ -8419,6 +8572,78 @@ Debug and trace infrastructure: @end example @end deffn +@deffn {Command} {set_reg} dict +Set register values of the target. + +@itemize +@item @var{dict} ... Tcl dictionary with pairs of register names and values. +@end itemize + +For example, the following command sets the value 0 to the program counter (pc) +register and 0x1000 to the stack pointer (sp) register: + +@example +set_reg @{pc 0 sp 0x1000@} +@end example +@end deffn + +@deffn {Command} {get_reg} [-force] list +Get register values from the target and return them as Tcl dictionary with pairs +of register names and values. +If option "-force" is set, the register values are read directly from the +target, bypassing any caching. + +@itemize +@item @var{list} ... List of register names +@end itemize + +For example, the following command retrieves the values from the program +counter (pc) and stack pointer (sp) register: + +@example +get_reg @{pc sp@} +@end example +@end deffn + +@deffn {Command} {write_memory} address width data ['phys'] +This function provides an efficient way to write to the target memory from a Tcl +script. + +@itemize +@item @var{address} ... target memory address +@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64 +@item @var{data} ... Tcl list with the elements to write +@item ['phys'] ... treat the memory address as physical instead of virtual address +@end itemize + +For example, the following command writes two 32 bit words into the target +memory at address 0x20000000: + +@example +write_memory 0x20000000 32 @{0xdeadbeef 0x00230500@} +@end example +@end deffn + +@deffn {Command} {read_memory} address width count ['phys'] +This function provides an efficient way to read the target memory from a Tcl +script. +A Tcl list containing the requested memory elements is returned by this function. + +@itemize +@item @var{address} ... target memory address +@item @var{width} ... memory access bit size, can be 8, 16, 32 or 64 +@item @var{count} ... number of elements to read +@item ['phys'] ... treat the memory address as physical instead of virtual address +@end itemize + +For example, the following command reads two 32 bit words from the target +memory at address 0x20000000: + +@example +read_memory 0x20000000 32 2 +@end example +@end deffn + @deffn {Command} {halt} [ms] @deffnx {Command} {wait_halt} [ms] The @command{halt} command first sends a halt request to the target, @@ -8556,8 +8781,8 @@ When the current target has an MMU which is present and active, Otherwise, or if the optional @var{phys} flag is specified, @var{addr} is interpreted as a physical address. If @var{count} is specified, displays that many units. -(If you want to manipulate the data instead of displaying it, -see the @code{mem2array} primitives.) +(If you want to process the data instead of displaying it, +see the @code{read_memory} primitives.) @end deffn @deffn {Command} {mwd} [phys] addr doubleword [count] @@ -8611,7 +8836,7 @@ In addition the following arguments may be specified: proc load_image_bin @{fname foffset address length @} @{ # Load data from fname filename at foffset offset to # target at address. Load at most length bytes. - load_image $fname [expr $address - $foffset] bin \ + load_image $fname [expr @{$address - $foffset@}] bin \ $address $length @} @end example @@ -9177,6 +9402,17 @@ requests by using a special SVC instruction that is trapped at the Supervisor Call vector by OpenOCD. @end deffn +@deffn {Command} {arm semihosting_redirect} (@option{disable} | @option{tcp} +[@option{debug}|@option{stdio}|@option{all}) +@cindex ARM semihosting +Redirect semihosting messages to a specified TCP port. + +This command redirects debug (READC, WRITEC and WRITE0) and stdio (READ, WRITE) +semihosting operations to the specified TCP port. +The command allows to select which type of operations to redirect (debug, stdio, all (default)). +Note: for stdio operations, only I/O from/to ':tt' file descriptors are redirected. +@end deffn + @deffn {Command} {arm semihosting_cmdline} [@option{enable}|@option{disable}] @cindex ARM semihosting Set the command line to be passed to the debugger. @@ -9226,6 +9462,17 @@ To make the SEMIHOSTING_SYS_EXIT call return normally, enable this option (default: disabled). @end deffn +@deffn {Command} {arm semihosting_read_user_param} +@cindex ARM semihosting +Read parameter of the semihosting call from the target. Usable in +semihosting-user-cmd-0x10* event handlers, returning a string. + +When the target makes semihosting call with operation number from range 0x100- +0x107, an optional string parameter can be passed to the server. This parameter +is valid during the run of the event handlers and is accessible with this +command. +@end deffn + @section ARMv4 and ARMv5 Architecture @cindex ARMv4 @cindex ARMv5 @@ -10409,6 +10656,18 @@ tunneled DR scan consists of: @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 Control dcsr.ebreakm. When on (default), M-mode ebreak instructions trap to OpenOCD. When off, they generate a breakpoint exception handled internally. @@ -10431,7 +10690,7 @@ trivial challenge-response protocol could be implemented as follows in a configuration file, immediately following @command{init}: @example set challenge [riscv authdata_read] -riscv authdata_write [expr $challenge + 1] +riscv authdata_write [expr @{$challenge + 1@}] @end example @deffn {Command} {riscv authdata_read} [index=0] @@ -11494,13 +11753,7 @@ should be passed in to the proc in question. By "low-level", we mean commands that a human would typically not invoke directly. -@itemize @bullet -@item @b{mem2array} <@var{varname}> <@var{width}> <@var{addr}> <@var{nelems}> - -Read memory and return as a Tcl array for script processing -@item @b{array2mem} <@var{varname}> <@var{width}> <@var{addr}> <@var{nelems}> - -Convert a Tcl array to memory locations and write the values +@itemize @item @b{flash banks} <@var{driver}> <@var{base}> <@var{size}> <@var{chip_width}> <@var{bus_width}> <@var{target}> [@option{driver options} ...] Return information about the flash banks @@ -12142,7 +12395,7 @@ it reads a file and executes as a script. @example set x 6 set y 7 - puts [format "The answer: %d" [expr $x * $y]] + puts [format "The answer: %d" [expr @{$x * $y@}]] @end example @enumerate @item The SET command creates 2 variables, X and Y. @@ -12213,13 +12466,13 @@ proc myproc @{ @} @{ @b{Dynamic variable creation} @example # Dynamically create a bunch of variables. -for @{ set x 0 @} @{ $x < 32 @} @{ set x [expr $x + 1]@} @{ +for @{ set x 0 @} @{ $x < 32 @} @{ set x [expr @{$x + 1@}]@} @{ # Create var name set vn [format "BIT%d" $x] # Make it a global global $vn # Set it. - set $vn [expr (1 << $x)] + set $vn [expr @{1 << $x@}] @} @end example @b{Dynamic proc/command creation} diff --git a/git-hooks/pre-push b/git-hooks/pre-push index a7e126655..d7e0aa552 100755 --- a/git-hooks/pre-push +++ b/git-hooks/pre-push @@ -1,3 +1,3 @@ #!/bin/sh -./tools/checkpatch.sh +#./tools/checkpatch.sh diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am old mode 100644 new mode 100755 index a09174448..8f00a9e60 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -66,7 +66,6 @@ NOR_DRIVERS = \ %D%/stm32lx.c \ %D%/stm32l4x.c \ %D%/stm32h7x.c \ - %D%/gd32vf103.c \ %D%/str7x.c \ %D%/str9x.c \ %D%/str9xpec.c \ @@ -76,6 +75,8 @@ NOR_DRIVERS = \ %D%/w600.c \ %D%/xcf.c \ %D%/xmc1xxx.c \ + %D%/wchriscv.c \ + %D%/wcharm.c \ %D%/xmc4xxx.c NORHEADERS = \ diff --git a/src/flash/nor/atsame5.c b/src/flash/nor/atsame5.c index 9ab0e8113..fbf0fb2ed 100644 --- a/src/flash/nor/atsame5.c +++ b/src/flash/nor/atsame5.c @@ -146,6 +146,9 @@ static const struct samd_part same53_parts[] = { { 0x04, "SAME53J20A", 1024, 256 }, { 0x05, "SAME53J19A", 512, 192 }, { 0x06, "SAME53J18A", 256, 128 }, + { 0x55, "LAN9255/ZMX020", 1024, 256 }, + { 0x56, "LAN9255/ZMX019", 512, 192 }, + { 0x57, "LAN9255/ZMX018", 256, 128 }, }; /* Known SAME54 parts. */ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c old mode 100644 new mode 100755 index dc966ed94..b94113a39 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -90,7 +90,8 @@ extern const struct flash_driver w600_flash; extern const struct flash_driver xcf_flash; extern const struct flash_driver xmc1xxx_flash; extern const struct flash_driver xmc4xxx_flash; - +extern const struct flash_driver wch_riscv_flash; +extern const struct flash_driver wch_arm_flash; /** * The list of built-in flash drivers. * @todo Make this dynamically extendable with loadable modules. @@ -120,6 +121,7 @@ static const struct flash_driver * const flash_drivers[] = { &fm3_flash, &fm4_flash, &fespi_flash, + &gd32vf103_flash, &jtagspi_flash, &kinetis_flash, &kinetis_ke_flash, @@ -153,7 +155,6 @@ static const struct flash_driver * const flash_drivers[] = { &stm32lx_flash, &stm32l4x_flash, &stm32h7x_flash, - &gd32vf103_flash, &stmsmi_flash, &stmqspi_flash, &str7x_flash, @@ -166,6 +167,8 @@ static const struct flash_driver * const flash_drivers[] = { &xmc1xxx_flash, &xmc4xxx_flash, &w600_flash, + &wch_riscv_flash, + &wch_arm_flash, NULL, }; diff --git a/src/flash/nor/efm32.c b/src/flash/nor/efm32.c index fb4b09624..2c5a5020e 100644 --- a/src/flash/nor/efm32.c +++ b/src/flash/nor/efm32.c @@ -14,6 +14,9 @@ * Copyright (C) 2014 Nemui Trinomius * * nemuisan_kawausogasuki@live.jp * * * + * Copyright (C) 2021 Doug Brunner * + * doug.a.brunner@gmail.com * + * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * @@ -45,13 +48,16 @@ #define EFM32_FLASH_WDATAREADY_TMO 100 #define EFM32_FLASH_WRITE_TMO 100 +#define EFM32_FLASH_BASE 0 + /* size in bytes, not words; must fit all Gecko devices */ -#define LOCKBITS_PAGE_SZ 512 +#define LOCKWORDS_SZ 512 #define EFM32_MSC_INFO_BASE 0x0fe00000 #define EFM32_MSC_USER_DATA EFM32_MSC_INFO_BASE #define EFM32_MSC_LOCK_BITS (EFM32_MSC_INFO_BASE+0x4000) +#define EFM32_MSC_LOCK_BITS_EXTRA (EFM32_MSC_LOCK_BITS+LOCKWORDS_SZ) #define EFM32_MSC_DEV_INFO (EFM32_MSC_INFO_BASE+0x8000) /* PAGE_SIZE is not present in Zero, Happy and the original Gecko MCU */ @@ -83,6 +89,27 @@ #define EFM32_MSC_REG_LOCK_SERIES1 0x040 #define EFM32_MSC_LOCK_LOCKKEY 0x1b71 +enum efm32_bank_index { + EFM32_BANK_INDEX_MAIN, + EFM32_BANK_INDEX_USER_DATA, + EFM32_BANK_INDEX_LOCK_BITS, + EFM32_N_BANKS +}; + +static int efm32x_get_bank_index(target_addr_t base) +{ + switch (base) { + case EFM32_FLASH_BASE: + return EFM32_BANK_INDEX_MAIN; + case EFM32_MSC_USER_DATA: + return EFM32_BANK_INDEX_USER_DATA; + case EFM32_MSC_LOCK_BITS: + return EFM32_BANK_INDEX_LOCK_BITS; + default: + return ERROR_FAIL; + } +} + struct efm32_family_data { int family_id; const char *name; @@ -98,13 +125,6 @@ struct efm32_family_data { uint32_t msc_regbase; }; -struct efm32x_flash_bank { - bool probed; - uint32_t lb_page[LOCKBITS_PAGE_SZ/4]; - uint32_t reg_base; - uint32_t reg_lock; -}; - struct efm32_info { const struct efm32_family_data *family_data; uint16_t flash_sz_kib; @@ -115,6 +135,15 @@ struct efm32_info { uint16_t page_size; }; +struct efm32x_flash_chip { + struct efm32_info info; + bool probed[EFM32_N_BANKS]; + uint32_t lb_page[LOCKWORDS_SZ/4]; + uint32_t reg_base; + uint32_t reg_lock; + uint32_t refcount; +}; + static const struct efm32_family_data efm32_families[] = { { 16, "EFR32MG1P Mighty", .series = 1 }, { 17, "EFR32MG1B Mighty", .series = 1 }, @@ -175,9 +204,12 @@ static const struct efm32_family_data efm32_families[] = { { 122, "EZR32HG Happy", .series = 0, .page_size = 1024 }, }; +const struct flash_driver efm32_flash; -static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count); +static int efm32x_priv_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t addr, uint32_t count); + +static int efm32x_write_only_lockbits(struct flash_bank *bank); static int efm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_sz) { @@ -207,7 +239,7 @@ static int efm32x_get_prod_rev(struct flash_bank *bank, uint8_t *prev) static int efm32x_read_reg_u32(struct flash_bank *bank, target_addr_t offset, uint32_t *value) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; uint32_t base = efm32x_info->reg_base; return target_read_u32(bank->target, base + offset, value); @@ -216,32 +248,28 @@ static int efm32x_read_reg_u32(struct flash_bank *bank, target_addr_t offset, static int efm32x_write_reg_u32(struct flash_bank *bank, target_addr_t offset, uint32_t value) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; uint32_t base = efm32x_info->reg_base; return target_write_u32(bank->target, base + offset, value); } -static int efm32x_read_info(struct flash_bank *bank, - struct efm32_info *efm32_info) +static int efm32x_read_info(struct flash_bank *bank) { int ret; - uint32_t cpuid = 0; - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + struct efm32_info *efm32_info = &(efm32x_info->info); memset(efm32_info, 0, sizeof(struct efm32_info)); - ret = target_read_u32(bank->target, CPUID, &cpuid); - if (ret != ERROR_OK) - return ret; + const struct cortex_m_common *cortex_m = target_to_cm(bank->target); - if (((cpuid >> 4) & 0xfff) == 0xc23) { - /* Cortex-M3 device */ - } else if (((cpuid >> 4) & 0xfff) == 0xc24) { - /* Cortex-M4 device (WONDER GECKO) */ - } else if (((cpuid >> 4) & 0xfff) == 0xc60) { - /* Cortex-M0+ device */ - } else { + switch (cortex_m->core_info->partno) { + case CORTEX_M3_PARTNO: + case CORTEX_M4_PARTNO: + case CORTEX_M0P_PARTNO: + break; + default: LOG_ERROR("Target is not Cortex-Mx Device"); return ERROR_FAIL; } @@ -327,20 +355,61 @@ static int efm32x_read_info(struct flash_bank *bank, /* flash bank efm32 0 0 */ FLASH_BANK_COMMAND_HANDLER(efm32x_flash_bank_command) { - struct efm32x_flash_bank *efm32x_info; + struct efm32x_flash_chip *efm32x_info = NULL; if (CMD_ARGC < 6) return ERROR_COMMAND_SYNTAX_ERROR; - efm32x_info = malloc(sizeof(struct efm32x_flash_bank)); + int bank_index = efm32x_get_bank_index(bank->base); + if (bank_index < 0) { + LOG_ERROR("Flash bank with base address %" PRIx32 " is not supported", + (uint32_t) bank->base); + return ERROR_FAIL; + } + + /* look for an existing flash structure matching target */ + for (struct flash_bank *bank_iter = flash_bank_list(); bank_iter; bank_iter = bank_iter->next) { + if (bank_iter->driver == &efm32_flash + && bank_iter->target == bank->target + && bank->driver_priv) { + efm32x_info = bank->driver_priv; + break; + } + } + if (!efm32x_info) { + /* target not matched, make a new one */ + efm32x_info = calloc(1, sizeof(struct efm32x_flash_chip)); + + memset(efm32x_info->lb_page, 0xff, LOCKWORDS_SZ); + } + + ++efm32x_info->refcount; bank->driver_priv = efm32x_info; - efm32x_info->probed = false; - memset(efm32x_info->lb_page, 0xff, LOCKBITS_PAGE_SZ); return ERROR_OK; } +/** + * Remove flash structure corresponding to this bank, + * if and only if it's not used by any others + */ +static void efm32x_free_driver_priv(struct flash_bank *bank) +{ + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + + if (efm32x_info) { + /* Use ref count to determine if it can be freed; scanning bank list doesn't work, + * because this function can be called after some banks in the list have been + * already destroyed */ + --efm32x_info->refcount; + if (efm32x_info->refcount == 0) { + free(efm32x_info); + bank->driver_priv = NULL; + } + } +} + /* set or reset given bits in a register */ static int efm32x_set_reg_bits(struct flash_bank *bank, uint32_t reg, uint32_t bitmask, int set) @@ -368,7 +437,7 @@ static int efm32x_set_wren(struct flash_bank *bank, int write_enable) static int efm32x_msc_lock(struct flash_bank *bank, int lock) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; return efm32x_write_reg_u32(bank, efm32x_info->reg_lock, (lock ? 0 : EFM32_MSC_LOCK_LOCKKEY)); } @@ -416,7 +485,6 @@ static int efm32x_erase_page(struct flash_bank *bank, uint32_t addr) */ int ret = 0; uint32_t status = 0; - addr += bank->base; LOG_DEBUG("erasing flash page at 0x%08" PRIx32, addr); ret = efm32x_write_reg_u32(bank, EFM32_MSC_REG_ADDRB, addr); @@ -470,20 +538,28 @@ static int efm32x_erase(struct flash_bank *bank, unsigned int first, } for (unsigned int i = first; i <= last; i++) { - ret = efm32x_erase_page(bank, bank->sectors[i].offset); + ret = efm32x_erase_page(bank, bank->base + bank->sectors[i].offset); if (ret != ERROR_OK) LOG_ERROR("Failed to erase page %d", i); } ret = efm32x_set_wren(bank, 0); efm32x_msc_lock(bank, 1); + if (ret != ERROR_OK) + return ret; + + if (bank->base == EFM32_MSC_LOCK_BITS) { + ret = efm32x_write_only_lockbits(bank); + if (ret != ERROR_OK) + LOG_ERROR("Failed to restore lockbits after erase"); + } return ret; } static int efm32x_read_lock_data(struct flash_bank *bank) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; struct target *target = bank->target; int data_size = 0; uint32_t *ptr = NULL; @@ -557,35 +633,84 @@ static int efm32x_read_lock_data(struct flash_bank *bank) return ERROR_OK; } +static int efm32x_write_only_lockbits(struct flash_bank *bank) +{ + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + return efm32x_priv_write(bank, (uint8_t *)efm32x_info->lb_page, EFM32_MSC_LOCK_BITS, LOCKWORDS_SZ); +} + static int efm32x_write_lock_data(struct flash_bank *bank) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; int ret = 0; + /* Preserve any data written to the high portion of the lockbits page */ + assert(efm32x_info->info.page_size >= LOCKWORDS_SZ); + uint32_t extra_bytes = efm32x_info->info.page_size - LOCKWORDS_SZ; + uint8_t *extra_data = NULL; + if (extra_bytes) { + extra_data = malloc(extra_bytes); + ret = target_read_buffer(bank->target, EFM32_MSC_LOCK_BITS_EXTRA, extra_bytes, extra_data); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read extra contents of LB page"); + free(extra_data); + return ret; + } + } + ret = efm32x_erase_page(bank, EFM32_MSC_LOCK_BITS); if (ret != ERROR_OK) { LOG_ERROR("Failed to erase LB page"); + if (extra_data) + free(extra_data); return ret; } - return efm32x_write(bank, (uint8_t *)efm32x_info->lb_page, EFM32_MSC_LOCK_BITS, - LOCKBITS_PAGE_SZ); + if (extra_data) { + ret = efm32x_priv_write(bank, extra_data, EFM32_MSC_LOCK_BITS_EXTRA, extra_bytes); + free(extra_data); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to restore extra contents of LB page"); + return ret; + } + } + + return efm32x_write_only_lockbits(bank); } static int efm32x_get_page_lock(struct flash_bank *bank, size_t page) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; - uint32_t dw = efm32x_info->lb_page[page >> 5]; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + uint32_t dw = 0; uint32_t mask = 0; - mask = 1 << (page & 0x1f); + switch (bank->base) { + case EFM32_FLASH_BASE: + dw = efm32x_info->lb_page[page >> 5]; + mask = 1 << (page & 0x1f); + break; + case EFM32_MSC_USER_DATA: + dw = efm32x_info->lb_page[126]; + mask = 0x1; + break; + case EFM32_MSC_LOCK_BITS: + dw = efm32x_info->lb_page[126]; + mask = 0x2; + break; + } return (dw & mask) ? 0 : 1; } static int efm32x_set_page_lock(struct flash_bank *bank, size_t page, int set) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + + if (bank->base != EFM32_FLASH_BASE) { + LOG_ERROR("Locking user and lockbits pages is not supported yet"); + return ERROR_FAIL; + } + uint32_t *dw = &efm32x_info->lb_page[page >> 5]; uint32_t mask = 0; @@ -605,11 +730,6 @@ static int efm32x_protect(struct flash_bank *bank, int set, unsigned int first, struct target *target = bank->target; int ret = 0; - if (!set) { - LOG_ERROR("Erase device data to reset page locks"); - return ERROR_FAIL; - } - if (target->state != TARGET_HALTED) { LOG_ERROR("Target not halted"); return ERROR_TARGET_NOT_HALTED; @@ -633,16 +753,15 @@ static int efm32x_protect(struct flash_bank *bank, int set, unsigned int first, } static int efm32x_write_block(struct flash_bank *bank, const uint8_t *buf, - uint32_t offset, uint32_t count) + uint32_t address, uint32_t count) { struct target *target = bank->target; uint32_t buffer_size = 16384; struct working_area *write_algorithm; struct working_area *source; - uint32_t address = bank->base + offset; struct reg_param reg_params[5]; struct armv7m_algorithm armv7m_info; - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; int ret = ERROR_OK; /* see contrib/loaders/flash/efm32.S for src */ @@ -864,8 +983,8 @@ static int efm32x_write_word(struct flash_bank *bank, uint32_t addr, return ERROR_OK; } -static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) +static int efm32x_priv_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t addr, uint32_t count) { struct target *target = bank->target; uint8_t *new_buffer = NULL; @@ -875,9 +994,9 @@ static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, return ERROR_TARGET_NOT_HALTED; } - if (offset & 0x3) { - LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte " - "alignment", offset); + if (addr & 0x3) { + LOG_ERROR("addr 0x%" PRIx32 " breaks required 4-byte " + "alignment", addr); return ERROR_FLASH_DST_BREAKS_ALIGNMENT; } @@ -906,7 +1025,7 @@ static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, goto cleanup; /* try using a block write */ - retval = efm32x_write_block(bank, buffer, offset, words_remaining); + retval = efm32x_write_block(bank, buffer, addr, words_remaining); if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { /* if block write failed (no sufficient working area), @@ -918,13 +1037,13 @@ static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t value; memcpy(&value, buffer, sizeof(uint32_t)); - retval = efm32x_write_word(bank, offset, value); + retval = efm32x_write_word(bank, addr, value); if (retval != ERROR_OK) goto reset_pg_and_lock; words_remaining--; buffer += 4; - offset += 4; + addr += 4; } } @@ -939,63 +1058,77 @@ static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, return retval; } +static int efm32x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + if (bank->base == EFM32_MSC_LOCK_BITS && offset < LOCKWORDS_SZ) { + LOG_ERROR("Cannot write to lock words"); + return ERROR_FAIL; + } + return efm32x_priv_write(bank, buffer, bank->base + offset, count); +} + static int efm32x_probe(struct flash_bank *bank) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; - struct efm32_info efm32_mcu_info; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + struct efm32_info *efm32_mcu_info = &(efm32x_info->info); int ret; - uint32_t base_address = 0x00000000; - efm32x_info->probed = false; - memset(efm32x_info->lb_page, 0xff, LOCKBITS_PAGE_SZ); + int bank_index = efm32x_get_bank_index(bank->base); + assert(bank_index >= 0); + + efm32x_info->probed[bank_index] = false; + memset(efm32x_info->lb_page, 0xff, LOCKWORDS_SZ); - ret = efm32x_read_info(bank, &efm32_mcu_info); + ret = efm32x_read_info(bank); if (ret != ERROR_OK) return ret; LOG_INFO("detected part: %s Gecko, rev %d", - efm32_mcu_info.family_data->name, efm32_mcu_info.prod_rev); - LOG_INFO("flash size = %dkbytes", efm32_mcu_info.flash_sz_kib); - LOG_INFO("flash page size = %dbytes", efm32_mcu_info.page_size); - - assert(efm32_mcu_info.page_size != 0); + efm32_mcu_info->family_data->name, efm32_mcu_info->prod_rev); + LOG_INFO("flash size = %dkbytes", efm32_mcu_info->flash_sz_kib); + LOG_INFO("flash page size = %dbytes", efm32_mcu_info->page_size); - int num_pages = efm32_mcu_info.flash_sz_kib * 1024 / - efm32_mcu_info.page_size; - - assert(num_pages > 0); + assert(efm32_mcu_info->page_size != 0); free(bank->sectors); bank->sectors = NULL; - bank->base = base_address; - bank->size = (num_pages * efm32_mcu_info.page_size); - bank->num_sectors = num_pages; + if (bank->base == EFM32_FLASH_BASE) { + bank->num_sectors = efm32_mcu_info->flash_sz_kib * 1024 / + efm32_mcu_info->page_size; + assert(bank->num_sectors > 0); - ret = efm32x_read_lock_data(bank); - if (ret != ERROR_OK) { - LOG_ERROR("Failed to read LB data"); - return ret; - } - - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - - for (int i = 0; i < num_pages; i++) { - bank->sectors[i].offset = i * efm32_mcu_info.page_size; - bank->sectors[i].size = efm32_mcu_info.page_size; + ret = efm32x_read_lock_data(bank); + if (ret != ERROR_OK) { + LOG_ERROR("Failed to read LB data"); + return ret; + } + } else + bank->num_sectors = 1; + bank->size = bank->num_sectors * efm32_mcu_info->page_size; + bank->sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + + for (uint32_t i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = i * efm32_mcu_info->page_size; + bank->sectors[i].size = efm32_mcu_info->page_size; bank->sectors[i].is_erased = -1; bank->sectors[i].is_protected = 1; } - efm32x_info->probed = true; + efm32x_info->probed[bank_index] = true; return ERROR_OK; } static int efm32x_auto_probe(struct flash_bank *bank) { - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; - if (efm32x_info->probed) + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; + + int bank_index = efm32x_get_bank_index(bank->base); + assert(bank_index >= 0); + + if (efm32x_info->probed[bank_index]) return ERROR_OK; return efm32x_probe(bank); } @@ -1026,16 +1159,17 @@ static int efm32x_protect_check(struct flash_bank *bank) static int get_efm32x_info(struct flash_bank *bank, struct command_invocation *cmd) { - struct efm32_info info; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; int ret; - ret = efm32x_read_info(bank, &info); + ret = efm32x_read_info(bank); if (ret != ERROR_OK) { LOG_ERROR("Failed to read EFM32 info"); return ret; } - command_print_sameline(cmd, "%s Gecko, rev %d", info.family_data->name, info.prod_rev); + command_print_sameline(cmd, "%s Gecko, rev %d", efm32x_info->info.family_data->name, + efm32x_info->info.prod_rev); return ERROR_OK; } @@ -1051,7 +1185,7 @@ COMMAND_HANDLER(efm32x_handle_debuglock_command) if (retval != ERROR_OK) return retval; - struct efm32x_flash_bank *efm32x_info = bank->driver_priv; + struct efm32x_flash_chip *efm32x_info = bank->driver_priv; target = bank->target; @@ -1110,5 +1244,5 @@ const struct flash_driver efm32_flash = { .erase_check = default_flash_blank_check, .protect_check = efm32x_protect_check, .info = get_efm32x_info, - .free_driver_priv = default_flash_free_driver_priv, + .free_driver_priv = efm32x_free_driver_priv, }; diff --git a/src/flash/nor/gd32vf103.c b/src/flash/nor/gd32vf103.c deleted file mode 100644 index 562f41e7b..000000000 --- a/src/flash/nor/gd32vf103.c +++ /dev/null @@ -1,1344 +0,0 @@ -/*************************************************************************** - * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "imp.h" -#include -#include - -/* gd32vf103 register locations */ - -#define FLASH_REG_BASE_B0 0x40022000 -#define FLASH_REG_BASE_B1 0x40022040 - -#define FMC_WS 0x00 -#define FMC_KEY 0x04 -#define FMC_OBKEY 0x08 -#define FMC_STAT 0x0C -#define FMC_CTL 0x10 -#define FMC_ADDR 0x14 -#define FMC_OBSTAT 0x1C - -/* TODO: Check if code using these really should be hard coded to bank 0. - * There are valid cases, on dual flash devices the protection of the - * second bank is done on the bank0 reg's. */ -#define FMC_WS_B0 0x40022000 -#define FMC_KEY_B0 0x40022004 -#define FMC_OBKEY_B0 0x40022008 -#define FMC_STAT_B0 0x4002200C -#define FMC_CTL_B0 0x40022010 -#define FMC_ADDR_B0 0x40022014 -#define FMC_OBSTAT_B0 0x4002201C -#define FMC_WP_B0 0x40022020 - -/* option byte location */ - -#define FMC_OB_RDP 0x1FFFF800 - -/* FMC_CTL register bits */ - -#define FMC_CTL_PG (1 << 0) -#define FMC_CTL_PER (1 << 1) -#define FMC_CTL_MER (1 << 2) -#define FMC_CTL_OBPG (1 << 4) -#define FMC_CTL_OBER (1 << 5) -#define FMC_CTL_START (1 << 6) -#define FMC_CTL_LK (1 << 7) -#define FMC_CTL_OBWEN (1 << 9) - -/* FMC_STAT register bits */ - -#define FMC_STAT_BUSY (1 << 0) -#define FMC_STAT_PGERR (1 << 2) -#define FMC_STAT_WPERR (1 << 4) -#define FMC_STAT_ENDF (1 << 5) - -/* FMC_OBSTAT bit definitions (reading) */ - -#define FMC_OBSTAT_OBERR 0 -#define FMC_OBSTAT_SPC 1 -#define FMC_OBSTAT_WDG_SW 2 -#define FMC_OBSTAT_RST_DSLEEP 3 -#define FMC_OBSTAT_RST_STDBY 4 -#define FMC_OBSTAT_BB 5 /* dual flash bank only */ - -/* register unlock keys */ - -#define UNLOCK_KEY0 0x45670123 -#define UNLOCK_KEY1 0xCDEF89AB - -/* timeout values */ - -#define FLASH_WRITE_TIMEOUT 500 -#define FLASH_ERASE_TIMEOUT 5000 - -struct gd32vf103_options { - uint16_t RDP; - uint16_t user_options; - uint16_t user_data; - uint16_t protection[4]; -}; - -struct gd32vf103_flash_bank { - struct gd32vf103_options option_bytes; - int ppage_size; - int probed; - - bool has_dual_banks; - /* used to access dual flash bank gd32vf103 */ - uint32_t register_base; - uint16_t default_rdp; - int user_data_offset; - int option_offset; - uint32_t user_bank_size; -}; - -static int gd32vf103_mass_erase(struct flash_bank *bank); -static int get_gd32vf103_info(struct flash_bank *bank, struct command_invocation *cmd); -static int gd32vf103_write_block(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count); - -/* flash bank gd32vf103 0 0 - */ -FLASH_BANK_COMMAND_HANDLER(gd32vf103_flash_bank_command) -{ - struct gd32vf103_flash_bank *gd32vf103_info; - - if (CMD_ARGC < 6) - return ERROR_COMMAND_SYNTAX_ERROR; - - gd32vf103_info = malloc(sizeof(struct gd32vf103_flash_bank)); - - bank->driver_priv = gd32vf103_info; - gd32vf103_info->probed = 0; - gd32vf103_info->has_dual_banks = false; - gd32vf103_info->register_base = FLASH_REG_BASE_B0; - gd32vf103_info->user_bank_size = bank->size; - - return ERROR_OK; -} - -static inline int gd32vf103_get_flash_reg(struct flash_bank *bank, uint32_t reg) -{ - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - return reg + gd32vf103_info->register_base; -} - -static inline int gd32vf103_get_flash_status(struct flash_bank *bank, uint32_t *status) -{ - struct target *target = bank->target; - return target_read_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), status); -} - -static int gd32vf103_wait_status_busy(struct flash_bank *bank, int timeout) -{ - struct target *target = bank->target; - uint32_t status; - int retval = ERROR_OK; - - /* wait for busy to clear */ - for (;;) { - retval = gd32vf103_get_flash_status(bank, &status); - if (retval != ERROR_OK) - return retval; - LOG_DEBUG("status: 0x%" PRIx32 "", status); - if ((status & FMC_STAT_BUSY) == 0) - break; - if (timeout-- <= 0) { - LOG_ERROR("timed out waiting for flash"); - return ERROR_FAIL; - } - alive_sleep(1); - } - - if (status & FMC_STAT_WPERR) { - LOG_ERROR("gd32vf103 device protected"); - retval = ERROR_FAIL; - } - - if (status & FMC_STAT_PGERR) { - LOG_ERROR("gd32vf103 device programming failed"); - retval = ERROR_FAIL; - } - - /* Clear but report errors */ - if (status & (FMC_STAT_WPERR | FMC_STAT_PGERR)) { - /* If this operation fails, we ignore it and report the original - * retval - */ - target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), - FMC_STAT_WPERR | FMC_STAT_PGERR); - } - return retval; -} - -static int gd32vf103_check_operation_supported(struct flash_bank *bank) -{ - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - - /* if we have a dual flash bank device then - * we need to perform option byte stuff on bank0 only */ - if (gd32vf103_info->register_base != FLASH_REG_BASE_B0) { - LOG_ERROR("Option Byte Operation's must use bank0"); - return ERROR_FLASH_OPERATION_FAILED; - } - - return ERROR_OK; -} - -static int gd32vf103_read_options(struct flash_bank *bank) -{ - uint32_t optiondata; - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - struct target *target = bank->target; - - gd32vf103_info = bank->driver_priv; - - /* read current option bytes */ - int retval = target_read_u32(target, FMC_OBSTAT_B0, &optiondata); - if (retval != ERROR_OK) - return retval; - - gd32vf103_info->option_bytes.user_options = (optiondata >> gd32vf103_info->option_offset >> 2) & 0xffff; - gd32vf103_info->option_bytes.user_data = (optiondata >> gd32vf103_info->user_data_offset) & 0xffff; - gd32vf103_info->option_bytes.RDP = (optiondata & (1 << FMC_OBSTAT_SPC)) ? 0xFFFF : 0x5AA5; - - if (optiondata & (1 << FMC_OBSTAT_SPC)) - LOG_INFO("Device Security Bit Set"); - - /* each bit refers to a 4bank protection */ - retval = target_read_u32(target, FMC_WP_B0, &optiondata); - if (retval != ERROR_OK) - return retval; - - gd32vf103_info->option_bytes.protection[0] = (uint16_t)optiondata; - gd32vf103_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); - gd32vf103_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); - gd32vf103_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); - - return ERROR_OK; -} - -static int gd32vf103_erase_options(struct flash_bank *bank) -{ - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - struct target *target = bank->target; - - gd32vf103_info = bank->driver_priv; - - /* read current options */ - gd32vf103_read_options(bank); - - /* unlock flash registers */ - int retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - - retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - /* unlock option flash registers */ - retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - /* erase option bytes */ - retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBER | FMC_CTL_OBWEN); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBER | FMC_CTL_START | FMC_CTL_OBWEN); - if (retval != ERROR_OK) - return retval; - - retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); - if (retval != ERROR_OK) - return retval; - - /* clear readout protection and complementary option bytes - * this will also force a device unlock if set */ - gd32vf103_info->option_bytes.RDP = gd32vf103_info->default_rdp; - - return ERROR_OK; -} - -static int gd32vf103_write_options(struct flash_bank *bank) -{ - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - struct target *target = bank->target; - - gd32vf103_info = bank->driver_priv; - - /* unlock flash registers */ - int retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, FMC_KEY_B0, UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - /* unlock option flash registers */ - retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, FMC_OBKEY_B0, UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - /* program option bytes */ - retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_OBPG | FMC_CTL_OBWEN); - if (retval != ERROR_OK) - return retval; - - uint8_t opt_bytes[16]; - - target_buffer_set_u16(target, opt_bytes, gd32vf103_info->option_bytes.RDP); /* SPC */ - target_buffer_set_u16(target, opt_bytes + 2, gd32vf103_info->option_bytes.user_options); /* USER */ - target_buffer_set_u16(target, opt_bytes + 4, gd32vf103_info->option_bytes.user_data & 0xff); /* DATA[7:0] */ - target_buffer_set_u16(target, opt_bytes + 6, (gd32vf103_info->option_bytes.user_data >> 8) & 0xff); /* DATA[15:8] */ - target_buffer_set_u16(target, opt_bytes + 8, gd32vf103_info->option_bytes.protection[0]); /* WP[7:0] */ - target_buffer_set_u16(target, opt_bytes + 10, gd32vf103_info->option_bytes.protection[1]); /* WP[15:8] */ - target_buffer_set_u16(target, opt_bytes + 12, gd32vf103_info->option_bytes.protection[2]); /* WP[23:16] */ - target_buffer_set_u16(target, opt_bytes + 14, gd32vf103_info->option_bytes.protection[3]); /* WP[31:24] */ - - uint32_t offset = FMC_OB_RDP - bank->base; - retval = gd32vf103_write_block(bank, opt_bytes, offset, sizeof(opt_bytes) / 2); - if (retval != ERROR_OK) { - if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - LOG_ERROR("working area required to erase options bytes"); - return retval; - } - - retval = target_write_u32(target, FMC_CTL_B0, FMC_CTL_LK); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; -} - -static int gd32vf103_protect_check(struct flash_bank *bank) -{ - struct target *target = bank->target; - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - - uint32_t protection; - int i; - int num_bits; - int set; - - int retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - retval = target_read_u32(target, FMC_WP_B0, &protection); - if (retval != ERROR_OK) - return retval; - - /* medium density - each protection bit is for 4 * 1K pages - * high density - each protection bit is for 2 * 2K pages */ - num_bits = (bank->num_sectors / gd32vf103_info->ppage_size); - - if (gd32vf103_info->ppage_size == 2) { - /* high density flash/connectivity line protection */ - - set = 1; - - if (protection & (1 << 31)) - set = 0; - - /* bit 31 controls sector 62 - 255 protection for high density - * bit 31 controls sector 62 - 127 protection for connectivity line */ - for (unsigned s = 62; s < bank->num_sectors; s++) - bank->sectors[s].is_protected = set; - - if (bank->num_sectors > 61) - num_bits = 31; - - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (int s = 0; s < gd32vf103_info->ppage_size; s++) - bank->sectors[(i * gd32vf103_info->ppage_size) + s].is_protected = set; - } - } else { - /* low/medium density flash protection */ - for (i = 0; i < num_bits; i++) { - set = 1; - - if (protection & (1 << i)) - set = 0; - - for (int s = 0; s < gd32vf103_info->ppage_size; s++) - bank->sectors[(i * gd32vf103_info->ppage_size) + s].is_protected = set; - } - } - - return ERROR_OK; -} - -static int gd32vf103_erase(struct flash_bank *bank, unsigned first, unsigned last) -{ - struct target *target = bank->target; - uint32_t optiondata; - uint32_t obstat; - - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - gd32vf103_info = bank->driver_priv; - - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - target_read_u32(target, FMC_WP_B0, &optiondata); - target_read_u32(target, FMC_OBSTAT_B0, &obstat); - if ((0xFFFFFFFF != optiondata) || ((obstat & 0x2) != 0)) { - gd32vf103_erase_options(bank); - optiondata = 0xFFFFFFFF; - gd32vf103_info->option_bytes.RDP = 0x5AA5; - gd32vf103_info->option_bytes.protection[0] = (uint16_t)optiondata; - gd32vf103_info->option_bytes.protection[1] = (uint16_t)(optiondata >> 8); - gd32vf103_info->option_bytes.protection[2] = (uint16_t)(optiondata >> 16); - gd32vf103_info->option_bytes.protection[3] = (uint16_t)(optiondata >> 24); - - gd32vf103_write_options(bank); - LOG_INFO(" Unlock flash Sucess !!! Pls Reset Platfrom !!\n"); - return ERROR_FAIL; - } - if ((first == 0) && (last == (bank->num_sectors - 1))) - return gd32vf103_mass_erase(bank); - - /* unlock flash registers */ - int retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - for (unsigned i = first; i <= last; i++) { - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PER); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_ADDR), - bank->base + bank->sectors[i].offset); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, - gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PER | FMC_CTL_START); - if (retval != ERROR_OK) - return retval; - - retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); - if (retval != ERROR_OK) - return retval; - - bank->sectors[i].is_erased = 1; - } - - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; -} - -static int gd32vf103_protect(struct flash_bank *bank, int set, unsigned first, unsigned last) -{ - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - struct target *target = bank->target; - uint16_t prot_reg[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; - int reg, bit; - int status; - uint32_t protection; - - gd32vf103_info = bank->driver_priv; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - int retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - if ((first % gd32vf103_info->ppage_size) != 0) { - LOG_WARNING("aligned start protect sector to a %d sector boundary", - gd32vf103_info->ppage_size); - first = first - (first % gd32vf103_info->ppage_size); - } - if (((last + 1) % gd32vf103_info->ppage_size) != 0) { - LOG_WARNING("aligned end protect sector to a %d sector boundary", - gd32vf103_info->ppage_size); - last++; - last = last - (last % gd32vf103_info->ppage_size); - last--; - } - - /* medium density - each bit refers to a 4bank protection - * high density - each bit refers to a 2bank protection */ - retval = target_read_u32(target, FMC_WP_B0, &protection); - if (retval != ERROR_OK) - return retval; - - prot_reg[0] = (uint16_t)protection; - prot_reg[1] = (uint16_t)(protection >> 8); - prot_reg[2] = (uint16_t)(protection >> 16); - prot_reg[3] = (uint16_t)(protection >> 24); - - if (gd32vf103_info->ppage_size == 2) { - /* high density flash */ - - /* bit 7 controls sector 62 - 255 protection */ - if (last > 61) { - if (set) - prot_reg[3] &= ~(1 << 7); - else - prot_reg[3] |= (1 << 7); - } - - if (first > 61) - first = 62; - if (last > 61) - last = 61; - - for (unsigned i = first; i <= last; i++) { - reg = (i / gd32vf103_info->ppage_size) / 8; - bit = (i / gd32vf103_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } else { - /* medium density flash */ - for (unsigned i = first; i <= last; i++) { - reg = (i / gd32vf103_info->ppage_size) / 8; - bit = (i / gd32vf103_info->ppage_size) - (reg * 8); - - if (set) - prot_reg[reg] &= ~(1 << bit); - else - prot_reg[reg] |= (1 << bit); - } - } - - status = gd32vf103_erase_options(bank); - if (status != ERROR_OK) - return status; - - gd32vf103_info->option_bytes.protection[0] = prot_reg[0]; - gd32vf103_info->option_bytes.protection[1] = prot_reg[1]; - gd32vf103_info->option_bytes.protection[2] = prot_reg[2]; - gd32vf103_info->option_bytes.protection[3] = prot_reg[3]; - - return gd32vf103_write_options(bank); -} - -static int gd32vf103_write_block(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) -{ - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - struct target *target = bank->target; - uint32_t buffer_size = 16384; - struct working_area *write_algorithm; - struct working_area *source; - uint32_t address = bank->base + offset; - struct reg_param reg_params[5]; - int retval = ERROR_OK; - - static const uint8_t gd32vf103_flash_write_code[] = { -#include "../../../contrib/loaders/flash/gd32v/gd32vf103.inc" - }; - - /* flash write code */ - if (target_alloc_working_area(target, sizeof(gd32vf103_flash_write_code), - &write_algorithm) != ERROR_OK) { - LOG_WARNING("no working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - - retval = target_write_buffer(target, write_algorithm->address, - sizeof(gd32vf103_flash_write_code), gd32vf103_flash_write_code); - if (retval != ERROR_OK) { - target_free_working_area(target, write_algorithm); - return retval; - } - - /* memory buffer */ - while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { - buffer_size /= 2; - buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */ - if (buffer_size <= 256) { - /* we already allocated the writing code, but failed to get a - * buffer, free the algorithm */ - target_free_working_area(target, write_algorithm); - - LOG_WARNING("no large enough working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } - } - - init_reg_param(®_params[0], "a0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ - init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (halfword-16bit) */ - init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */ - init_reg_param(®_params[3], "a3", 32, PARAM_OUT); /* buffer end */ - init_reg_param(®_params[4], "a4", 32, PARAM_IN_OUT); /* target address */ - - - uint32_t wp_addr = source->address; - uint32_t rp_addr = source->address + 4; - uint32_t fifo_start_addr = source->address + 8; - uint32_t fifo_end_addr = source->address + source->size; - - uint32_t wp = fifo_start_addr; - uint32_t rp = fifo_start_addr; - uint32_t thisrun_bytes = fifo_end_addr-fifo_start_addr-2; /* (2:block size) */ - - retval = target_write_u32(target, rp_addr, rp); - if (retval != ERROR_OK) - return retval; - - while (count > 0) { - retval = target_read_u32(target, rp_addr, &rp); - if (retval != ERROR_OK) { - LOG_ERROR("failed to get read pointer"); - break; - } - - if (wp != rp) { - LOG_ERROR("Failed to write flash ;; rp = 0x%x ;;; wp = 0x%x", rp, wp); - break; - } - wp = fifo_start_addr; - rp = fifo_start_addr; - retval = target_write_u32(target, rp_addr, rp); - if (retval != ERROR_OK) - break; - /* Limit to the amount of data we actually want to write */ - if (thisrun_bytes > count * 2) - thisrun_bytes = count * 2; - - /* Write data to fifo */ - retval = target_write_buffer(target, wp, thisrun_bytes, buffer); - if (retval != ERROR_OK) - break; - - /* Update counters and wrap write pointer */ - buffer += thisrun_bytes; - count -= thisrun_bytes / 2; - rp = fifo_start_addr; - wp = fifo_start_addr+thisrun_bytes; - - /* Store updated write pointer to target */ - retval = target_write_u32(target, wp_addr, wp); - if (retval != ERROR_OK) - break; - retval = target_write_u32(target, rp_addr, rp); - if (retval != ERROR_OK) - return retval; - - buf_set_u32(reg_params[0].value, 0, 32, gd32vf103_info->register_base); - buf_set_u32(reg_params[1].value, 0, 32, thisrun_bytes/2); - buf_set_u32(reg_params[2].value, 0, 32, source->address); - buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size); - buf_set_u32(reg_params[4].value, 0, 32, address); - - retval = target_run_algorithm(target, 0, NULL, 5, reg_params, - write_algorithm->address, write_algorithm->address+4, - 10000, NULL); - - if (retval != ERROR_OK) { - LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", - write_algorithm->address, retval); - return retval; - } - address += thisrun_bytes; - - } - - - if (retval == ERROR_FLASH_OPERATION_FAILED) { - LOG_ERROR("flash write failed at address 0x%"PRIx32, - buf_get_u32(reg_params[4].value, 0, 32)); - - if (buf_get_u32(reg_params[0].value, 0, 32) & FMC_STAT_PGERR) { - LOG_ERROR("flash memory not erased before writing"); - /* Clear but report errors */ - target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), FMC_STAT_PGERR); - } - - if (buf_get_u32(reg_params[0].value, 0, 32) & FMC_STAT_WPERR) { - LOG_ERROR("flash memory write protected"); - /* Clear but report errors */ - target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_STAT), FMC_STAT_WPERR); - } - } - - target_free_working_area(target, source); - target_free_working_area(target, write_algorithm); - - destroy_reg_param(®_params[0]); - destroy_reg_param(®_params[1]); - destroy_reg_param(®_params[2]); - destroy_reg_param(®_params[3]); - destroy_reg_param(®_params[4]); - - return retval; -} - -static int gd32vf103_write(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) -{ - struct target *target = bank->target; - uint8_t *new_buffer = NULL; - - - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - if (offset & 0x1) { - LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; - } - - /* If there's an odd number of bytes, the data has to be padded. Duplicate - * the buffer and use the normal code path with a single block write since - * it's probably cheaper than to special case the last odd write using - * discrete accesses. */ - if (count & 1) { - new_buffer = malloc(count + 1); - if (new_buffer == NULL) { - LOG_ERROR("odd number of bytes to write and no memory for padding buffer"); - return ERROR_FAIL; - } - LOG_INFO("odd number of bytes to write, padding with 0xff"); - buffer = memcpy(new_buffer, buffer, count); - new_buffer[count++] = 0xff; - } - - uint32_t words_remaining = count / 2; - int retval, retval2; - - /* unlock flash registers */ - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); - if (retval != ERROR_OK) - goto cleanup; - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); - if (retval != ERROR_OK) - goto cleanup; - - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_PG); - if (retval != ERROR_OK) - goto cleanup; - - /* try using a block write */ - retval = gd32vf103_write_block(bank, buffer, offset, words_remaining); - - if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { - /* if block write failed (no sufficient working area), - * we use normal (slow) single halfword accesses */ - LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - - while (words_remaining > 0) { - uint16_t value; - memcpy(&value, buffer, sizeof(uint16_t)); - - retval = target_write_u16(target, bank->base + offset, value); - if (retval != ERROR_OK) - goto reset_pg_and_lock; - - retval = gd32vf103_wait_status_busy(bank, 5); - if (retval != ERROR_OK) - goto reset_pg_and_lock; - - words_remaining--; - buffer += 2; - offset += 2; - } - } - -reset_pg_and_lock: - retval2 = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); - if (retval == ERROR_OK) - retval = retval2; - -cleanup: - if (new_buffer) - free(new_buffer); - - return retval; -} - -static int gd32vf103_get_device_id(struct flash_bank *bank, uint32_t *device_id) -{ - - struct target *target = bank->target; - uint32_t device_id_register = 0xE0042000; - /* read GD32VF103 device id register */ - int retval = target_read_u32(target, device_id_register, device_id); - if (retval != ERROR_OK) - return retval; - - return retval; -} - -static int gd32vf103_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb) -{ - struct target *target = bank->target; - uint32_t flash_size_reg = 0x1FFFF7E0; - - int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb); - if (retval != ERROR_OK) - return retval; - - return retval; -} - -static int gd32vf103_probe(struct flash_bank *bank) -{ - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - int i; - uint16_t flash_size_in_kb; - uint16_t max_flash_size_in_kb; - uint32_t device_id; - int page_size; - uint32_t base_address = 0x08000000; - - gd32vf103_info->probed = 0; - gd32vf103_info->register_base = FLASH_REG_BASE_B0; - gd32vf103_info->user_data_offset = 10; - gd32vf103_info->option_offset = 0; - - /* default factory protection level */ - gd32vf103_info->default_rdp = 0x5AA5; - - /* read gd32vf103 device id register */ - int retval = gd32vf103_get_device_id(bank, &device_id); - if (retval != ERROR_OK) - return retval; - - LOG_INFO("device id = 0x%08" PRIx32 "", device_id); - - /* set page size, protection granularity and max flash size depending on family */ - switch (device_id & 0xfff) { - case 0x410: - case 0x418: /* connectivity line density */ - page_size = 1024; - gd32vf103_info->ppage_size = 4; - max_flash_size_in_kb = 128; - break; - default: - LOG_WARNING("Cannot identify target as a gd32vf103 family."); - return ERROR_FAIL; - } - - /* get flash size from target. */ - retval = gd32vf103_get_flash_size(bank, &flash_size_in_kb); - LOG_INFO("flash_size_in_kb = 0x%08" PRIx32 "", flash_size_in_kb); - /* failed reading flash size or flash size invalid, default to max target family */ - if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) { - LOG_WARNING("gd32vf103 flash size failed, probe inaccurate - assuming %dk flash", - max_flash_size_in_kb); - flash_size_in_kb = max_flash_size_in_kb; - } - - if (gd32vf103_info->has_dual_banks) { - /* split reported size into matching bank */ - if (bank->base != 0x08080000) { - /* bank 0 will be fixed 512k */ - flash_size_in_kb = 512; - } else { - flash_size_in_kb -= 512; - /* bank1 also uses a register offset */ - gd32vf103_info->register_base = FLASH_REG_BASE_B1; - base_address = 0x08080000; - } - } - - /* if the user sets the size manually then ignore the probed value - * this allows us to work around devices that have a invalid flash size register value */ - if (gd32vf103_info->user_bank_size) { - LOG_INFO("ignoring flash probed value, using configured bank size"); - flash_size_in_kb = gd32vf103_info->user_bank_size / 1024; - } - - LOG_INFO("flash size = %dkbytes", flash_size_in_kb); - - /* did we assign flash size? */ - assert(flash_size_in_kb != 0xffff); - - /* calculate numbers of pages */ - int num_pages = flash_size_in_kb * 1024 / page_size; - - /* check that calculation result makes sense */ - assert(num_pages > 0); - - if (bank->sectors) { - free(bank->sectors); - bank->sectors = NULL; - } - - bank->base = base_address; - bank->size = (num_pages * page_size); - bank->num_sectors = num_pages; - bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); - - for (i = 0; i < num_pages; i++) { - bank->sectors[i].offset = i * page_size; - bank->sectors[i].size = page_size; - bank->sectors[i].is_erased = -1; - bank->sectors[i].is_protected = 1; - } - - gd32vf103_info->probed = 1; - - return ERROR_OK; -} - -static int gd32vf103_auto_probe(struct flash_bank *bank) -{ - struct gd32vf103_flash_bank *gd32vf103_info = bank->driver_priv; - if (gd32vf103_info->probed) - return ERROR_OK; - return gd32vf103_probe(bank); -} - -static int get_gd32vf103_info(struct flash_bank *bank, struct command_invocation *cmd) -{ - uint32_t dbgmcu_idcode; - - /* read gd32vf103 device id register */ - int retval = gd32vf103_get_device_id(bank, &dbgmcu_idcode); - if (retval != ERROR_OK) - return retval; - - uint16_t device_id = dbgmcu_idcode & 0xfff; - uint16_t rev_id = dbgmcu_idcode >> 16; - const char *device_str; - const char *rev_str = NULL; - - switch (device_id) { - - case 0x418: - device_str = "gd32vf103 (gdm32501)"; - - switch (rev_id) { - case 0x1000: - rev_str = "A"; - break; - - case 0x1001: - rev_str = "B"; - break; - } - break; - default: - command_print_sameline(cmd, "Cannot identify target as a GD32VF103 x\n"); - return ERROR_FAIL; - } - - if (rev_str != NULL) - command_print_sameline(cmd, "%s - Rev: %s\n", device_str, rev_str); - else - command_print_sameline(cmd, "%s - Rev: unknown (0x%04x)\n", device_str, rev_id); - - return ERROR_OK; -} - -COMMAND_HANDLER(gd32vf103_handle_lock_command) -{ - struct target *target = NULL; - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - gd32vf103_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - if (gd32vf103_erase_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to erase options"); - return ERROR_OK; - } - - /* set readout protection */ - gd32vf103_info->option_bytes.RDP = 0; - - if (gd32vf103_write_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to lock device"); - return ERROR_OK; - } - - command_print(CMD, "gd32vf103 locked"); - - return ERROR_OK; -} - -COMMAND_HANDLER(gd32vf103_handle_unlock_command) -{ - struct target *target = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - if (gd32vf103_erase_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to unlock device"); - return ERROR_OK; - } - - if (gd32vf103_write_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to lock device"); - return ERROR_OK; - } - - command_print(CMD, "gd32vf103 unlocked.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect."); - - return ERROR_OK; -} - -COMMAND_HANDLER(gd32vf103_handle_options_read_command) -{ - uint32_t optionbyte; - struct target *target = NULL; - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - gd32vf103_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - retval = target_read_u32(target, FMC_OBSTAT_B0, &optionbyte); - if (retval != ERROR_OK) - return retval; - command_print(CMD, "Option Byte: 0x%" PRIx32 "", optionbyte); - - int user_data = optionbyte; - - if (optionbyte >> FMC_OBSTAT_OBERR & 1) - command_print(CMD, "Option Byte Complement Error"); - - if (optionbyte >> FMC_OBSTAT_SPC & 1) - command_print(CMD, "Readout Protection On"); - else - command_print(CMD, "Readout Protection Off"); - - /* user option bytes are offset depending on variant */ - optionbyte >>= gd32vf103_info->option_offset; - - if (optionbyte >> FMC_OBSTAT_WDG_SW & 1) - command_print(CMD, "Software Watchdog"); - else - command_print(CMD, "Hardware Watchdog"); - - if (optionbyte >> FMC_OBSTAT_RST_DSLEEP & 1) - command_print(CMD, "Stop: No reset generated"); - else - command_print(CMD, "Stop: Reset generated"); - - if (optionbyte >> FMC_OBSTAT_RST_STDBY & 1) - command_print(CMD, "Standby: No reset generated"); - else - command_print(CMD, "Standby: Reset generated"); - - if (gd32vf103_info->has_dual_banks) { - if (optionbyte >> FMC_OBSTAT_BB & 1) - command_print(CMD, "Boot: Bank 0"); - else - command_print(CMD, "Boot: Bank 1"); - } - - command_print(CMD, "User Option0: 0x%02" PRIx8, - (uint8_t)((user_data >> gd32vf103_info->user_data_offset) & 0xff)); - command_print(CMD, "User Option1: 0x%02" PRIx8, - (uint8_t)((user_data >> (gd32vf103_info->user_data_offset + 8)) & 0xff)); - - return ERROR_OK; -} - -COMMAND_HANDLER(gd32vf103_handle_options_write_command) -{ - struct target *target = NULL; - struct gd32vf103_flash_bank *gd32vf103_info = NULL; - uint16_t optionbyte; - - if (CMD_ARGC < 2) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - gd32vf103_info = bank->driver_priv; - - target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - retval = gd32vf103_check_operation_supported(bank); - if (ERROR_OK != retval) - return retval; - - retval = gd32vf103_read_options(bank); - if (ERROR_OK != retval) - return retval; - - /* start with current options */ - optionbyte = gd32vf103_info->option_bytes.user_options; - - /* skip over flash bank */ - CMD_ARGC--; - CMD_ARGV++; - - while (CMD_ARGC) { - if (strcmp("SWWDG", CMD_ARGV[0]) == 0) - optionbyte |= (1 << 0); - else if (strcmp("HWWDG", CMD_ARGV[0]) == 0) - optionbyte &= ~(1 << 0); - else if (strcmp("NORSTSTOP", CMD_ARGV[0]) == 0) - optionbyte |= (1 << 1); - else if (strcmp("RSTSTOP", CMD_ARGV[0]) == 0) - optionbyte &= ~(1 << 1); - else if (strcmp("NORSTSTNDBY", CMD_ARGV[0]) == 0) - optionbyte |= (1 << 2); - else if (strcmp("RSTSTNDBY", CMD_ARGV[0]) == 0) - optionbyte &= ~(1 << 2); - else if (gd32vf103_info->has_dual_banks) { - if (strcmp("BOOT0", CMD_ARGV[0]) == 0) - optionbyte |= (1 << 3); - else if (strcmp("BOOT1", CMD_ARGV[0]) == 0) - optionbyte &= ~(1 << 3); - else - return ERROR_COMMAND_SYNTAX_ERROR; - } else - return ERROR_COMMAND_SYNTAX_ERROR; - CMD_ARGC--; - CMD_ARGV++; - } - - if (gd32vf103_erase_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to erase options"); - return ERROR_OK; - } - - gd32vf103_info->option_bytes.user_options = optionbyte; - - if (gd32vf103_write_options(bank) != ERROR_OK) { - command_print(CMD, "gd32vf103 failed to write options"); - return ERROR_OK; - } - - command_print(CMD, "gd32vf103 write options complete.\n" - "INFO: a reset or power cycle is required " - "for the new settings to take effect."); - - return ERROR_OK; -} - -static int gd32vf103_mass_erase(struct flash_bank *bank) -{ - struct target *target = bank->target; - - if (target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; - } - - /* unlock option flash registers */ - int retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY0); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_KEY), UNLOCK_KEY1); - if (retval != ERROR_OK) - return retval; - - /* mass erase flash memory */ - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_MER); - if (retval != ERROR_OK) - return retval; - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), - FMC_CTL_MER | FMC_CTL_START); - if (retval != ERROR_OK) - return retval; - - retval = gd32vf103_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); - if (retval != ERROR_OK) - return retval; - - retval = target_write_u32(target, gd32vf103_get_flash_reg(bank, FMC_CTL), FMC_CTL_LK); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; -} - -COMMAND_HANDLER(gd32vf103_handle_mass_erase_command) -{ - if (CMD_ARGC < 1) - return ERROR_COMMAND_SYNTAX_ERROR; - - struct flash_bank *bank; - int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); - if (ERROR_OK != retval) - return retval; - - retval = gd32vf103_mass_erase(bank); - if (retval == ERROR_OK) { - /* set all sectors as erased */ - for (unsigned i = 0; i < bank->num_sectors; i++) - bank->sectors[i].is_erased = 1; - - command_print(CMD, "gd32vf103 mass erase complete"); - } else - command_print(CMD, "gd32vf103 mass erase failed"); - - return retval; -} - -static const struct command_registration gd32vf103_exec_command_handlers[] = { - { - .name = "lock", - .handler = gd32vf103_handle_lock_command, - .mode = COMMAND_EXEC, - .usage = "bank_id", - .help = "Lock entire flash device.", - }, - { - .name = "unlock", - .handler = gd32vf103_handle_unlock_command, - .mode = COMMAND_EXEC, - .usage = "bank_id", - .help = "Unlock entire protected flash device.", - }, - { - .name = "mass_erase", - .handler = gd32vf103_handle_mass_erase_command, - .mode = COMMAND_EXEC, - .usage = "bank_id", - .help = "Erase entire flash device.", - }, - { - .name = "options_read", - .handler = gd32vf103_handle_options_read_command, - .mode = COMMAND_EXEC, - .usage = "bank_id", - .help = "Read and display device option byte.", - }, - { - .name = "options_write", - .handler = gd32vf103_handle_options_write_command, - .mode = COMMAND_EXEC, - .usage = "bank_id ('SWWDG'|'HWWDG') " - "('RSTSTNDBY'|'NORSTSTNDBY') " - "('RSTSTOP'|'NORSTSTOP')", - .help = "Replace bits in device option byte.", - }, - COMMAND_REGISTRATION_DONE -}; - -static const struct command_registration gd32vf103_command_handlers[] = { - { - .name = "gd32vf103", - .mode = COMMAND_ANY, - .help = "gd32vf103 flash command group", - .usage = "", - .chain = gd32vf103_exec_command_handlers, - }, - COMMAND_REGISTRATION_DONE -}; - -struct flash_driver gd32vf103_flash = { - .name = "gd32vf103", - .commands = gd32vf103_command_handlers, - .flash_bank_command = gd32vf103_flash_bank_command, - .erase = gd32vf103_erase, - .protect = gd32vf103_protect, - .write = gd32vf103_write, - .read = default_flash_read, - .probe = gd32vf103_probe, - .auto_probe = gd32vf103_auto_probe, - .erase_check = default_flash_blank_check, - .protect_check = gd32vf103_protect_check, - .info = get_gd32vf103_info, - .free_driver_priv = default_flash_free_driver_priv, -}; diff --git a/src/flash/nor/sim3x.c b/src/flash/nor/sim3x.c index 2938ed1ab..891383860 100644 --- a/src/flash/nor/sim3x.c +++ b/src/flash/nor/sim3x.c @@ -983,7 +983,7 @@ COMMAND_HANDLER(sim3x_lock) if (!dap) { /* Used debug interface doesn't support direct DAP access */ - LOG_INFO("Target can't by unlocked by this debug interface"); + LOG_INFO("Target can't be unlocked by this debug interface"); /* Core check */ ret = target_read_u32(target, CPUID, &val); diff --git a/src/flash/nor/stm32f1x.c b/src/flash/nor/stm32f1x.c index 90cee6412..b5b10af4c 100644 --- a/src/flash/nor/stm32f1x.c +++ b/src/flash/nor/stm32f1x.c @@ -26,6 +26,8 @@ #include "config.h" #endif +#include + #include "imp.h" #include #include @@ -129,9 +131,8 @@ struct stm32x_flash_bank { }; static int stm32x_mass_erase(struct flash_bank *bank); -static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id); static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, - uint32_t address, uint32_t count); + uint32_t address, uint32_t hwords_count); /* flash bank stm32x 0 0 */ @@ -151,6 +152,9 @@ FLASH_BANK_COMMAND_HANDLER(stm32x_flash_bank_command) stm32x_info->register_base = FLASH_REG_BASE_B0; stm32x_info->user_bank_size = bank->size; + /* The flash write must be aligned to a halfword boundary */ + bank->write_start_alignment = bank->write_end_alignment = 2; + return ERROR_OK; } @@ -182,19 +186,19 @@ static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout) break; if (timeout-- <= 0) { LOG_ERROR("timed out waiting for flash"); - return ERROR_FAIL; + return ERROR_FLASH_BUSY; } alive_sleep(1); } if (status & FLASH_WRPRTERR) { LOG_ERROR("stm32x device protected"); - retval = ERROR_FAIL; + retval = ERROR_FLASH_PROTECTED; } if (status & FLASH_PGERR) { - LOG_ERROR("stm32x device programming failed"); - retval = ERROR_FAIL; + LOG_ERROR("stm32x device programming failed / flash not erased"); + retval = ERROR_FLASH_OPERATION_FAILED; } /* Clear but report errors */ @@ -258,36 +262,39 @@ static int stm32x_erase_options(struct flash_bank *bank) int retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY1); if (retval != ERROR_OK) return retval; - retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* unlock option flash registers */ retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* erase option bytes */ retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_OPTWRE); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTER | FLASH_STRT | FLASH_OPTWRE); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* clear read protection option byte * this will also force a device unlock if set */ stm32x_info->option_bytes.rdp = stm32x_info->default_rdp; return ERROR_OK; + +flash_lock: + target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK); + return retval; } static int stm32x_write_options(struct flash_bank *bank) @@ -303,20 +310,20 @@ static int stm32x_write_options(struct flash_bank *bank) return retval; retval = target_write_u32(target, STM32_FLASH_KEYR_B0, KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* unlock option flash registers */ retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY1); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, STM32_FLASH_OPTKEYR_B0, KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* program option bytes */ retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_OPTPG | FLASH_OPTWRE); if (retval != ERROR_OK) - return retval; + goto flash_lock; uint8_t opt_bytes[16]; @@ -329,18 +336,20 @@ static int stm32x_write_options(struct flash_bank *bank) target_buffer_set_u16(target, opt_bytes + 12, (stm32x_info->option_bytes.protection >> 16) & 0xff); target_buffer_set_u16(target, opt_bytes + 14, (stm32x_info->option_bytes.protection >> 24) & 0xff); + /* Block write is preferred in favour of operation with ancient ST-Link + * firmwares without 16-bit memory access. See + * 480: flash: stm32f1x: write option bytes using the loader + * https://review.openocd.org/c/openocd/+/480 + */ retval = stm32x_write_block(bank, opt_bytes, STM32_OB_RDP, sizeof(opt_bytes) / 2); - if (retval != ERROR_OK) { - if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) - LOG_ERROR("working area required to erase options bytes"); - return retval; - } - - retval = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK); - if (retval != ERROR_OK) - return retval; - return ERROR_OK; +flash_lock: + { + int retval2 = target_write_u32(target, STM32_FLASH_CR_B0, FLASH_LOCK); + if (retval == ERROR_OK) + retval = retval2; + } + return retval; } static int stm32x_protect_check(struct flash_bank *bank) @@ -384,31 +393,33 @@ static int stm32x_erase(struct flash_bank *bank, unsigned int first, return retval; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; for (unsigned int i = first; i <= last; i++) { retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PER); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_AR), bank->base + bank->sectors[i].offset); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PER | FLASH_STRT); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); if (retval != ERROR_OK) - return retval; + goto flash_lock; } - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); - if (retval != ERROR_OK) - return retval; - - return ERROR_OK; +flash_lock: + { + int retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); + if (retval == ERROR_OK) + retval = retval2; + } + return retval; } static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first, @@ -442,17 +453,16 @@ static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first, return stm32x_write_options(bank); } -static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, - uint32_t address, uint32_t count) +static int stm32x_write_block_async(struct flash_bank *bank, const uint8_t *buffer, + uint32_t address, uint32_t hwords_count) { struct stm32x_flash_bank *stm32x_info = bank->driver_priv; struct target *target = bank->target; - uint32_t buffer_size = 16384; + uint32_t buffer_size; struct working_area *write_algorithm; struct working_area *source; - struct reg_param reg_params[5]; struct armv7m_algorithm armv7m_info; - int retval = ERROR_OK; + int retval; static const uint8_t stm32x_flash_write_code[] = { #include "../../../contrib/loaders/flash/stm32/stm32f1x.inc" @@ -473,19 +483,28 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, } /* memory buffer */ - while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) { - buffer_size /= 2; - buffer_size &= ~3UL; /* Make sure it's 4 byte aligned */ - if (buffer_size <= 256) { - /* we already allocated the writing code, but failed to get a - * buffer, free the algorithm */ - target_free_working_area(target, write_algorithm); - - LOG_WARNING("no large enough working area available, can't do block memory writes"); - return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; - } + buffer_size = target_get_working_area_avail(target); + buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256)); + /* Normally we allocate all available working area. + * MIN shrinks buffer_size if the size of the written block is smaller. + * MAX prevents using async algo if the available working area is smaller + * than 256, the following allocation fails with + * ERROR_TARGET_RESOURCE_NOT_AVAILABLE and slow flashing takes place. + */ + + retval = target_alloc_working_area(target, buffer_size, &source); + /* Allocated size is always 32-bit word aligned */ + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + LOG_WARNING("no large enough working area available, can't do block memory writes"); + /* target_alloc_working_area() may return ERROR_FAIL if area backup fails: + * convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE + */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } + struct reg_param reg_params[5]; + init_reg_param(®_params[0], "r0", 32, PARAM_IN_OUT); /* flash base (in), status (out) */ init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* count (halfword-16bit) */ init_reg_param(®_params[2], "r2", 32, PARAM_OUT); /* buffer start */ @@ -493,7 +512,7 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, init_reg_param(®_params[4], "r4", 32, PARAM_IN_OUT); /* target address */ buf_set_u32(reg_params[0].value, 0, 32, stm32x_info->register_base); - buf_set_u32(reg_params[1].value, 0, 32, count); + buf_set_u32(reg_params[1].value, 0, 32, hwords_count); buf_set_u32(reg_params[2].value, 0, 32, source->address); buf_set_u32(reg_params[3].value, 0, 32, source->address + source->size); buf_set_u32(reg_params[4].value, 0, 32, address); @@ -501,195 +520,297 @@ static int stm32x_write_block(struct flash_bank *bank, const uint8_t *buffer, armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; armv7m_info.core_mode = ARM_MODE_THREAD; - retval = target_run_flash_async_algorithm(target, buffer, count, 2, + retval = target_run_flash_async_algorithm(target, buffer, hwords_count, 2, 0, NULL, - 5, reg_params, + ARRAY_SIZE(reg_params), reg_params, source->address, source->size, write_algorithm->address, 0, &armv7m_info); if (retval == ERROR_FLASH_OPERATION_FAILED) { - LOG_ERROR("flash write failed at address 0x%"PRIx32, - buf_get_u32(reg_params[4].value, 0, 32)); - - if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_PGERR) { - LOG_ERROR("flash memory not erased before writing"); - /* Clear but report errors */ - target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_PGERR); - } + /* Actually we just need to check for programming errors + * stm32x_wait_status_busy also reports error and clears status bits. + * + * Target algo returns flash status in r0 only if properly finished. + * It is safer to re-read status register. + */ + int retval2 = stm32x_wait_status_busy(bank, 5); + if (retval2 != ERROR_OK) + retval = retval2; - if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_WRPRTERR) { - LOG_ERROR("flash memory write protected"); - /* Clear but report errors */ - target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), FLASH_WRPRTERR); - } + LOG_ERROR("flash write failed just before address 0x%"PRIx32, + buf_get_u32(reg_params[4].value, 0, 32)); } + for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++) + destroy_reg_param(®_params[i]); + target_free_working_area(target, source); target_free_working_area(target, write_algorithm); - destroy_reg_param(®_params[0]); - destroy_reg_param(®_params[1]); - destroy_reg_param(®_params[2]); - destroy_reg_param(®_params[3]); - destroy_reg_param(®_params[4]); - return retval; } -static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, - uint32_t offset, uint32_t count) +static int stm32x_write_block_riscv(struct flash_bank *bank, const uint8_t *buffer, + uint32_t address, uint32_t hwords_count) { struct target *target = bank->target; - uint8_t *new_buffer = NULL; + uint32_t buffer_size; + struct working_area *write_algorithm; + struct working_area *source; + static const uint8_t gd32vf103_flash_write_code[] = { +#include "../../../contrib/loaders/flash/gd32vf103/gd32vf103.inc" + }; - if (bank->target->state != TARGET_HALTED) { - LOG_ERROR("Target not halted"); - return ERROR_TARGET_NOT_HALTED; + /* flash write code */ + if (target_alloc_working_area(target, sizeof(gd32vf103_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - if (offset & 0x1) { - LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); - return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + int retval = target_write_buffer(target, write_algorithm->address, + sizeof(gd32vf103_flash_write_code), gd32vf103_flash_write_code); + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + return retval; + } + + /* memory buffer */ + buffer_size = target_get_working_area_avail(target); + buffer_size = MIN(hwords_count * 2, MAX(buffer_size, 256)); + + retval = target_alloc_working_area(target, buffer_size, &source); + /* Allocated size is always word aligned */ + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + LOG_WARNING("no large enough working area available, can't do block memory writes"); + /* target_alloc_working_area() may return ERROR_FAIL if area backup fails: + * convert any error to ERROR_TARGET_RESOURCE_NOT_AVAILABLE + */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - /* If there's an odd number of bytes, the data has to be padded. Duplicate - * the buffer and use the normal code path with a single block write since - * it's probably cheaper than to special case the last odd write using - * discrete accesses. */ - if (count & 1) { - new_buffer = malloc(count + 1); - if (!new_buffer) { - LOG_ERROR("odd number of bytes to write and no memory for padding buffer"); - return ERROR_FAIL; + struct reg_param reg_params[4]; + + init_reg_param(®_params[0], "a0", 32, PARAM_OUT); /* poiner to FLASH_SR */ + init_reg_param(®_params[1], "a1", 32, PARAM_OUT); /* count (halfword-16bit) */ + init_reg_param(®_params[2], "a2", 32, PARAM_OUT); /* buffer start */ + init_reg_param(®_params[3], "a3", 32, PARAM_IN_OUT); /* target address */ + + while (hwords_count > 0) { + uint32_t thisrun_hwords = source->size / 2; + + /* Limit to the amount of data we actually want to write */ + if (thisrun_hwords > hwords_count) + thisrun_hwords = hwords_count; + + /* Write data to buffer */ + retval = target_write_buffer(target, source->address, + thisrun_hwords * 2, buffer); + if (retval != ERROR_OK) + break; + + buf_set_u32(reg_params[0].value, 0, 32, stm32x_get_flash_reg(bank, STM32_FLASH_SR)); + buf_set_u32(reg_params[1].value, 0, 32, thisrun_hwords); + buf_set_u32(reg_params[2].value, 0, 32, source->address); + buf_set_u32(reg_params[3].value, 0, 32, address); + + retval = target_run_algorithm(target, + 0, NULL, + ARRAY_SIZE(reg_params), reg_params, + write_algorithm->address, + write_algorithm->address + sizeof(gd32vf103_flash_write_code) - 4, + 10000, NULL); + + if (retval != ERROR_OK) { + LOG_ERROR("Failed to execute algorithm at 0x%" TARGET_PRIxADDR ": %d", + write_algorithm->address, retval); + break; } - LOG_INFO("odd number of bytes to write, padding with 0xff"); - buffer = memcpy(new_buffer, buffer, count); - new_buffer[count++] = 0xff; + + /* Actually we just need to check for programming errors + * stm32x_wait_status_busy also reports error and clears status bits + */ + retval = stm32x_wait_status_busy(bank, 5); + if (retval != ERROR_OK) { + LOG_ERROR("flash write failed at address 0x%"PRIx32, + buf_get_u32(reg_params[3].value, 0, 32)); + break; + } + + /* Update counters */ + buffer += thisrun_hwords * 2; + address += thisrun_hwords * 2; + hwords_count -= thisrun_hwords; } - uint32_t words_remaining = count / 2; - int retval, retval2; + for (unsigned int i = 0; i < ARRAY_SIZE(reg_params); i++) + destroy_reg_param(®_params[i]); - /* unlock flash registers */ - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1); - if (retval != ERROR_OK) - goto cleanup; - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); - if (retval != ERROR_OK) - goto cleanup; + target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG); - if (retval != ERROR_OK) - goto cleanup; + return retval; +} - /* try using a block write */ - retval = stm32x_write_block(bank, buffer, bank->base + offset, words_remaining); +/** Writes a block to flash either using target algorithm + * or use fallback, host controlled halfword-by-halfword access. + * Flash controller must be unlocked before this call. + */ +static int stm32x_write_block(struct flash_bank *bank, + const uint8_t *buffer, uint32_t address, uint32_t hwords_count) +{ + struct target *target = bank->target; + + /* The flash write must be aligned to a halfword boundary. + * The flash infrastructure ensures it, do just a security check + */ + assert(address % 2 == 0); + + int retval; + struct arm *arm = target_to_arm(target); + if (is_arm(arm)) { + /* try using a block write - on ARM architecture or... */ + retval = stm32x_write_block_async(bank, buffer, address, hwords_count); + } else { + /* ... RISC-V architecture */ + retval = stm32x_write_block_riscv(bank, buffer, address, hwords_count); + } if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) { /* if block write failed (no sufficient working area), * we use normal (slow) single halfword accesses */ LOG_WARNING("couldn't use block writes, falling back to single memory accesses"); - while (words_remaining > 0) { - uint16_t value; - memcpy(&value, buffer, sizeof(uint16_t)); - - retval = target_write_u16(target, bank->base + offset, value); + while (hwords_count > 0) { + retval = target_write_memory(target, address, 2, 1, buffer); if (retval != ERROR_OK) - goto reset_pg_and_lock; + return retval; retval = stm32x_wait_status_busy(bank, 5); if (retval != ERROR_OK) - goto reset_pg_and_lock; + return retval; - words_remaining--; + hwords_count--; buffer += 2; - offset += 2; + address += 2; } } + return retval; +} + +static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* The flash write must be aligned to a halfword boundary. + * The flash infrastructure ensures it, do just a security check + */ + assert(offset % 2 == 0); + assert(count % 2 == 0); + + int retval, retval2; + + /* unlock flash registers */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + /* enable flash programming */ + retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG); + if (retval != ERROR_OK) + goto reset_pg_and_lock; + + /* write to flash */ + retval = stm32x_write_block(bank, buffer, bank->base + offset, count / 2); reset_pg_and_lock: retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); if (retval == ERROR_OK) retval = retval2; -cleanup: - free(new_buffer); return retval; } -static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id) -{ - struct target *target = bank->target; - struct cortex_m_common *cortex_m = target_to_cm(target); - uint32_t device_id_register = 0; +struct stm32x_property_addr { + uint32_t device_id; + uint32_t flash_size; +}; +static int stm32x_get_property_addr(struct target *target, struct stm32x_property_addr *addr) +{ if (!target_was_examined(target)) { LOG_ERROR("Target not examined yet"); - return ERROR_FAIL; + return ERROR_TARGET_NOT_EXAMINED; } - switch (cortex_m->core_info->partno) { + switch (cortex_m_get_partno_safe(target)) { case CORTEX_M0_PARTNO: /* STM32F0x devices */ - device_id_register = 0x40015800; - break; + addr->device_id = 0x40015800; + addr->flash_size = 0x1FFFF7CC; + return ERROR_OK; case CORTEX_M3_PARTNO: /* STM32F1x devices */ - device_id_register = 0xE0042000; - break; + addr->device_id = 0xE0042000; + addr->flash_size = 0x1FFFF7E0; + return ERROR_OK; case CORTEX_M4_PARTNO: /* STM32F3x devices */ - device_id_register = 0xE0042000; - break; + addr->device_id = 0xE0042000; + addr->flash_size = 0x1FFFF7CC; + return ERROR_OK; case CORTEX_M23_PARTNO: /* GD32E23x devices */ - device_id_register = 0x40015800; - break; + addr->device_id = 0x40015800; + addr->flash_size = 0x1FFFF7E0; + return ERROR_OK; + case CORTEX_M_PARTNO_INVALID: + /* Check for GD32VF103 with RISC-V CPU */ + if (strcmp(target_type_name(target), "riscv") == 0 + && target_address_bits(target) == 32) { + /* There is nothing like arm common_magic in riscv_info_t + * check text name of target and if target is 32-bit + */ + addr->device_id = 0xE0042000; + addr->flash_size = 0x1FFFF7E0; + return ERROR_OK; + } + /* fallthrough */ default: LOG_ERROR("Cannot identify target as a stm32x"); return ERROR_FAIL; } +} - /* read stm32 device id register */ - int retval = target_read_u32(target, device_id_register, device_id); +static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + struct target *target = bank->target; + struct stm32x_property_addr addr; + + int retval = stm32x_get_property_addr(target, &addr); if (retval != ERROR_OK) return retval; - return retval; + return target_read_u32(target, addr.device_id, device_id); } static int stm32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb) { struct target *target = bank->target; - struct cortex_m_common *cortex_m = target_to_cm(target); - uint32_t flash_size_reg; - - if (!target_was_examined(target)) { - LOG_ERROR("Target not examined yet"); - return ERROR_FAIL; - } - - switch (cortex_m->core_info->partno) { - case CORTEX_M0_PARTNO: /* STM32F0x devices */ - flash_size_reg = 0x1FFFF7CC; - break; - case CORTEX_M3_PARTNO: /* STM32F1x devices */ - flash_size_reg = 0x1FFFF7E0; - break; - case CORTEX_M4_PARTNO: /* STM32F3x devices */ - flash_size_reg = 0x1FFFF7CC; - break; - case CORTEX_M23_PARTNO: /* GD32E23x devices */ - flash_size_reg = 0x1FFFF7E0; - break; - default: - LOG_ERROR("Cannot identify target as a stm32x"); - return ERROR_FAIL; - } + struct stm32x_property_addr addr; - int retval = target_read_u16(target, flash_size_reg, flash_size_in_kb); + int retval = stm32x_get_property_addr(target, &addr); if (retval != ERROR_OK) return retval; - return retval; + return target_read_u16(target, addr.flash_size, flash_size_in_kb); } static int stm32x_probe(struct flash_bank *bank) @@ -775,15 +896,20 @@ static int stm32x_probe(struct flash_bank *bank) stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; max_flash_size_in_kb = 64; + stm32x_info->can_load_options = true; break; case 0x1704: /* gd32f3x0 */ stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; + stm32x_info->can_load_options = true; + break; + case 0x1906: /* gd32vf103 */ break; case 0x1909: /* gd32e23x */ stm32x_info->user_data_offset = 16; stm32x_info->option_offset = 6; max_flash_size_in_kb = 64; + stm32x_info->can_load_options = true; break; } break; @@ -995,6 +1121,10 @@ static int get_stm32x_info(struct flash_bank *bank, struct command_invocation *c device_str = "GD32F3x0"; break; + case 0x1906: + device_str = "GD32VF103"; + break; + case 0x1909: /* gd32e23x */ device_str = "GD32E23x"; break; @@ -1478,8 +1608,10 @@ COMMAND_HANDLER(stm32x_handle_options_load_command) if (retval != ERROR_OK) return retval; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); - if (retval != ERROR_OK) + if (retval != ERROR_OK) { + (void)target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); return retval; + } /* force re-load of option bytes - generates software reset */ retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_OBL_LAUNCH); @@ -1504,26 +1636,26 @@ static int stm32x_mass_erase(struct flash_bank *bank) return retval; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_KEYR), KEY2); if (retval != ERROR_OK) - return retval; + goto flash_lock; /* mass erase flash memory */ retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_MER); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_MER | FLASH_STRT); if (retval != ERROR_OK) - return retval; + goto flash_lock; retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); - if (retval != ERROR_OK) - return retval; - - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); - if (retval != ERROR_OK) - return retval; - return ERROR_OK; +flash_lock: + { + int retval2 = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_LOCK); + if (retval == ERROR_OK) + retval = retval2; + } + return retval; } COMMAND_HANDLER(stm32x_handle_mass_erase_command) @@ -1619,3 +1751,55 @@ const struct flash_driver stm32f1x_flash = { .info = get_stm32x_info, .free_driver_priv = default_flash_free_driver_priv, }; + +/* flash bank gd32vf103 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(gd32vf103_flash_bank_command) +{ + struct stm32x_flash_bank *stm32x_info; + + LOG_WARNING("DEPRECATED: The gd32vf103 flash target will be removed in June of 2023, please use stm32f1x instead."); + /* The reset code are just copy from stm32x_flash_bank_command function */ + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + stm32x_info = malloc(sizeof(struct stm32x_flash_bank)); + + bank->driver_priv = stm32x_info; + stm32x_info->probed = false; + stm32x_info->has_dual_banks = false; + stm32x_info->can_load_options = false; + stm32x_info->register_base = FLASH_REG_BASE_B0; + stm32x_info->user_bank_size = bank->size; + + /* The flash write must be aligned to a halfword boundary */ + bank->write_start_alignment = bank->write_end_alignment = 2; + return ERROR_OK; +} + +static const struct command_registration gd32vf103_command_handlers[] = { + { + .name = "gd32vf103", + .mode = COMMAND_ANY, + .help = "gd32vf103 flash command group (identical to flash commands from stm32f1x)", + .usage = "", + .chain = stm32f1x_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver gd32vf103_flash = { + .name = "gd32vf103", + .commands = gd32vf103_command_handlers, + .flash_bank_command = gd32vf103_flash_bank_command, + .erase = stm32x_erase, + .protect = stm32x_protect, + .write = stm32x_write, + .read = default_flash_read, + .probe = stm32x_probe, + .auto_probe = stm32x_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = stm32x_protect_check, + .info = get_stm32x_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/stm32f2x.c b/src/flash/nor/stm32f2x.c index d3e7d709c..622ef3423 100644 --- a/src/flash/nor/stm32f2x.c +++ b/src/flash/nor/stm32f2x.c @@ -636,8 +636,8 @@ static int stm32x_erase(struct flash_bank *bank, unsigned int first, for (unsigned int i = first; i <= last; i++) { unsigned int snb; - if (stm32x_info->has_large_mem && i >= 12) - snb = (i - 12) | 0x10; + if (stm32x_info->has_large_mem && i >= (bank->num_sectors / 2)) + snb = (i - (bank->num_sectors / 2)) | 0x10; else snb = i; @@ -856,15 +856,12 @@ static int stm32x_write(struct flash_bank *bank, const uint8_t *buffer, Wait for the BSY bit to be cleared */ while (words_remaining > 0) { - uint16_t value; - memcpy(&value, buffer + bytes_written, sizeof(uint16_t)); - retval = target_write_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_CR), FLASH_PG | FLASH_PSIZE_16); if (retval != ERROR_OK) return retval; - retval = target_write_u16(target, address, value); + retval = target_write_memory(target, address, 2, 1, buffer + bytes_written); if (retval != ERROR_OK) return retval; @@ -966,14 +963,14 @@ static int stm32x_get_device_id(struct flash_bank *bank, uint32_t *device_id) * Only effects Rev A silicon */ struct target *target = bank->target; - struct cortex_m_common *cortex_m = target_to_cm(target); /* read stm32 device id register */ int retval = target_read_u32(target, 0xE0042000, device_id); if (retval != ERROR_OK) return retval; - if ((*device_id & 0xfff) == 0x411 && cortex_m->core_info->partno == CORTEX_M4_PARTNO) { + if ((*device_id & 0xfff) == 0x411 + && cortex_m_get_partno_safe(target) == CORTEX_M4_PARTNO) { *device_id &= ~((0xFFFF << 16) | 0xfff); *device_id |= (0x1000 << 16) | 0x413; LOG_INFO("stm32f4x errata detected - fixing incorrect MCU_IDCODE"); @@ -1011,6 +1008,11 @@ static int stm32x_probe(struct flash_bank *bank) bank->num_prot_blocks = 0; bank->prot_blocks = NULL; + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_TARGET_NOT_EXAMINED; + } + /* if explicitly called out as OTP bank, short circuit probe */ if (stm32x_is_otp(bank)) { if (stm32x_otp_is_f7(bank)) { diff --git a/src/flash/nor/stm32h7x.c b/src/flash/nor/stm32h7x.c index d3f17b2b1..d2914eb39 100644 --- a/src/flash/nor/stm32h7x.c +++ b/src/flash/nor/stm32h7x.c @@ -531,6 +531,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first, unsigned int last) { struct target *target = bank->target; + struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; uint32_t protection; if (target->state != TARGET_HALTED) { @@ -553,7 +554,7 @@ static int stm32x_protect(struct flash_bank *bank, int set, unsigned int first, } /* apply WRPSN mask */ - protection &= 0xff; + protection &= stm32x_info->part_info->wps_mask; LOG_DEBUG("stm32x_protect, option_bytes written WPSN 0x%" PRIx32, protection); @@ -759,7 +760,6 @@ static int stm32x_read_id_code(struct flash_bank *bank, uint32_t *id) static int stm32x_probe(struct flash_bank *bank) { struct target *target = bank->target; - struct cortex_m_common *cortex_m = target_to_cm(target); struct stm32h7x_flash_bank *stm32x_info = bank->driver_priv; uint16_t flash_size_in_kb; uint32_t device_id; @@ -767,6 +767,11 @@ static int stm32x_probe(struct flash_bank *bank) stm32x_info->probed = false; stm32x_info->part_info = NULL; + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_TARGET_NOT_EXAMINED; + } + int retval = stm32x_read_id_code(bank, &stm32x_info->idcode); if (retval != ERROR_OK) return retval; @@ -800,7 +805,8 @@ static int stm32x_probe(struct flash_bank *bank) /* get flash size from target */ /* STM32H74x/H75x, the second core (Cortex-M4) cannot read the flash size */ retval = ERROR_FAIL; - if (device_id == DEVID_STM32H74_H75XX && cortex_m->core_info->partno == CORTEX_M4_PARTNO) + if (device_id == DEVID_STM32H74_H75XX + && cortex_m_get_partno_safe(target) == CORTEX_M4_PARTNO) LOG_WARNING("%s cannot read the flash size register", target_name(target)); else retval = target_read_u16(target, stm32x_info->part_info->fsize_addr, &flash_size_in_kb); diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index e5100a015..7b35a0635 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -1632,13 +1632,14 @@ static int stm32l4_write(struct flash_bank *bank, const uint8_t *buffer, static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) { - int retval; + int retval = ERROR_OK; + struct target *target = bank->target; /* try reading possible IDCODE registers, in the following order */ uint32_t dbgmcu_idcode[] = {DBGMCU_IDCODE_L4_G4, DBGMCU_IDCODE_G0, DBGMCU_IDCODE_L5}; for (unsigned int i = 0; i < ARRAY_SIZE(dbgmcu_idcode); i++) { - retval = target_read_u32(bank->target, dbgmcu_idcode[i], id); + retval = target_read_u32(target, dbgmcu_idcode[i], id); if ((retval == ERROR_OK) && ((*id & 0xfff) != 0) && ((*id & 0xfff) != 0xfff)) return ERROR_OK; } @@ -1647,12 +1648,16 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) * DBGMCU_IDCODE cannot be read using CPU1 (Cortex-M0+) at AP1, * to solve this read the UID64 (IEEE 64-bit unique device ID register) */ - struct cortex_m_common *cortex_m = target_to_cm(bank->target); + struct armv7m_common *armv7m = target_to_armv7m_safe(target); + if (!armv7m) { + LOG_ERROR("Flash requires Cortex-M target"); + return ERROR_TARGET_INVALID; + } /* CPU2 (Cortex-M0+) is supported only with non-hla adapters because it is on AP1. * Using HLA adapters armv7m.debug_ap is null, and checking ap_num triggers a segfault */ - if (cortex_m->core_info->partno == CORTEX_M0P_PARTNO && - cortex_m->armv7m.debug_ap && cortex_m->armv7m.debug_ap->ap_num == 1) { + if (cortex_m_get_partno_safe(target) == CORTEX_M0P_PARTNO && + armv7m->debug_ap && armv7m->debug_ap->ap_num == 1) { uint32_t uid64_ids; /* UID64 is contains @@ -1662,7 +1667,7 @@ static int stm32l4_read_idcode(struct flash_bank *bank, uint32_t *id) * * read only the fixed values {STID,DEVID} from UID64_IDS to identify the device as STM32WLx */ - retval = target_read_u32(bank->target, UID64_IDS, &uid64_ids); + retval = target_read_u32(target, UID64_IDS, &uid64_ids); if (retval == ERROR_OK && uid64_ids == UID64_IDS_STM32WL) { /* force the DEV_ID to DEVID_STM32WLE_WL5XX and the REV_ID to unknown */ *id = DEVID_STM32WLE_WL5XX; @@ -1700,11 +1705,21 @@ static const char *get_stm32l4_bank_type_str(struct flash_bank *bank) static int stm32l4_probe(struct flash_bank *bank) { struct target *target = bank->target; - struct armv7m_common *armv7m = target_to_armv7m(target); struct stm32l4_flash_bank *stm32l4_info = bank->driver_priv; const struct stm32l4_part_info *part_info; uint16_t flash_size_kb = 0xffff; + if (!target_was_examined(target)) { + LOG_ERROR("Target not examined yet"); + return ERROR_TARGET_NOT_EXAMINED; + } + + struct armv7m_common *armv7m = target_to_armv7m_safe(target); + if (!armv7m) { + LOG_ERROR("Flash requires Cortex-M target"); + return ERROR_TARGET_INVALID; + } + stm32l4_info->probed = false; /* read stm32 device id registers */ @@ -1742,7 +1757,7 @@ static int stm32l4_probe(struct flash_bank *bank) * Ask the flash infrastructure to ensure required alignment */ bank->write_start_alignment = bank->write_end_alignment = stm32l4_info->data_width; - /* initialise the flash registers layout */ + /* Initialize the flash registers layout */ if (part_info->flags & F_HAS_L5_FLASH_REGS) stm32l4_info->flash_regs = stm32l5_ns_flash_regs; else @@ -1755,7 +1770,7 @@ static int stm32l4_probe(struct flash_bank *bank) stm32l4_sync_rdp_tzen(bank); - /* for devices with trustzone, use flash secure registers when TZEN=1 and RDP is LEVEL_0 */ + /* for devices with TrustZone, use flash secure registers when TZEN=1 and RDP is LEVEL_0 */ if (stm32l4_info->tzen && (stm32l4_info->rdp == RDP_LEVEL_0)) { if (part_info->flags & F_HAS_L5_FLASH_REGS) { stm32l4_info->flash_regs_base |= STM32L5_REGS_SEC_OFFSET; @@ -2031,8 +2046,19 @@ static int stm32l4_auto_probe(struct flash_bank *bank) if (stm32l4_info->probed) { uint32_t optr_cur; + /* save flash_regs_base */ + uint32_t saved_flash_regs_base = stm32l4_info->flash_regs_base; + + /* for devices with TrustZone, use NS flash registers to read OPTR */ + if (stm32l4_info->part_info->flags & F_HAS_L5_FLASH_REGS) + stm32l4_info->flash_regs_base &= ~STM32L5_REGS_SEC_OFFSET; + /* read flash option register and re-probe if optr value is changed */ int retval = stm32l4_read_flash_reg_by_index(bank, STM32_FLASH_OPTR_INDEX, &optr_cur); + + /* restore saved flash_regs_base */ + stm32l4_info->flash_regs_base = saved_flash_regs_base; + if (retval != ERROR_OK) return retval; diff --git a/src/flash/nor/tcl.c b/src/flash/nor/tcl.c old mode 100644 new mode 100755 index 05db31435..17bf32512 --- a/src/flash/nor/tcl.c +++ b/src/flash/nor/tcl.c @@ -29,7 +29,9 @@ * @file * Implements Tcl commands used to access NOR flash facilities. */ - +extern int wlnik_protect_check(void); +extern char riscvchip; +extern void wlink_softreset(void); static COMMAND_HELPER(flash_command_get_bank_maybe_probe, unsigned name_index, struct flash_bank **bank, bool do_probe) { @@ -400,6 +402,34 @@ COMMAND_HANDLER(handle_flash_protect_command) return retval; } +COMMAND_HANDLER(handle_flash_protect_check_command){ + + struct flash_bank *p; + int retval; + int num_blocks; + + retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &p); + if (retval != ERROR_OK) + return retval; + if (strncmp(p->driver->name, "wch_riscv", 9) == 0){ + if(riscvchip==1) + wlink_softreset(); + if((riscvchip==1)||(riscvchip==5)||(riscvchip==6)||(riscvchip==9)||(riscvchip==0x0c)||(riscvchip==0x0e)){ + int retval=wlnik_protect_check(); + if(retval==4) + LOG_INFO("Code Read-Protect Status Enable"); + else + LOG_INFO("Code Read-Protect status Disable"); + + }else{ + LOG_ERROR("This chip do not support function"); + return ERROR_OK; + } + } + return ERROR_OK; +} + + COMMAND_HANDLER(handle_flash_write_image_command) { struct target *target = get_current_target(CMD_CTX); @@ -1240,6 +1270,13 @@ static const struct command_registration flash_exec_command_handlers[] = { .usage = "bank_id value", .help = "Set default flash padded value", }, + { + .name = "protect_check", + .handler = handle_flash_protect_check_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/flash/nor/wcharm.c b/src/flash/nor/wcharm.c new file mode 100755 index 000000000..5403674d4 --- /dev/null +++ b/src/flash/nor/wcharm.c @@ -0,0 +1,897 @@ +/*************************************************************************** + * Copyright (C) 2005 by Dominic Rath * + * Dominic.Rath@gmx.de * + * * + * Copyright (C) 2008 by Spencer Oliver * + * spen@spen-soft.co.uk * + * * + * Copyright (C) 2011 by Andreas Fritiofson * + * andreas.fritiofson@gmail.com * + * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +#include + +/* ch32x register locations */ + +#define FLASH_REG_BASE_B0 0x40022000 +#define FLASH_REG_BASE_B1 0x40022040 + +#define ch32_FLASH_ACR 0x00 +#define ch32_FLASH_KEYR 0x04 +#define ch32_FLASH_OPTKEYR 0x08 +#define ch32_FLASH_SR 0x0C +#define ch32_FLASH_CR 0x10 +#define ch32_FLASH_AR 0x14 +#define ch32_FLASH_OBR 0x1C +#define ch32_FLASH_WRPR 0x20 +#define CH32_FLASH_MODEKEYP 0x24 //chf103�����Ĵ��� + +/* TODO: Check if code using these really should be hard coded to bank 0. + * There are valid cases, on dual flash devices the protection of the + * second bank is done on the bank0 reg's. */ +#define ch32_FLASH_ACR_B0 0x40022000 +#define ch32_FLASH_KEYR_B0 0x40022004 +#define ch32_FLASH_OPTKEYR_B0 0x40022008 +#define ch32_FLASH_SR_B0 0x4002200C +#define ch32_FLASH_CR_B0 0x40022010 +#define ch32_FLASH_AR_B0 0x40022014 +#define ch32_FLASH_OBR_B0 0x4002201C +#define ch32_FLASH_WRPR_B0 0x40022020 + +/* option byte location */ + +#define ch32_OB_RDP 0x1FFFF800 +#define ch32_OB_USER 0x1FFFF802 +#define ch32_OB_DATA0 0x1FFFF804 +#define ch32_OB_DATA1 0x1FFFF806 +#define ch32_OB_WRP0 0x1FFFF808 +#define ch32_OB_WRP1 0x1FFFF80A +#define ch32_OB_WRP2 0x1FFFF80C +#define ch32_OB_WRP3 0x1FFFF80E + +/* FLASH_CR register bits */ + +#define FLASH_PG (1 << 0) +#define FLASH_PER (1 << 1) +#define FLASH_MER (1 << 2) +#define FLASH_OPTPG (1 << 4) +#define FLASH_OPTER (1 << 5) +#define FLASH_STRT (1 << 6) +#define FLASH_LOCK (1 << 7) +#define FLASH_OPTWRE (1 << 9) +#define FLASH_OBL_LAUNCH (1 << 13) /* except ch32f1x series */ + +/* CHf��Flash_CR�Ĵ���������λ */ +#define FLASH_PAGE_PROGRAM 0x00010000 //ҳ��̣�128Byteһҳ�� +#define FLASH_PAGE_ERASE 0x00020000 //ҳ������128Byteһҳ�� +#define FLASH_STD_PAGE_ERASE 0x00000002 //��׼ҳ������1024Byte һҳ�� +#define FLASH_STD_PAGE_PRG 0x00000001 //��׼ҳ��̣�1024Byte һҳ�� +#define FLASH_BUF_LOAD 0x00040000 //�������ݵ�FLASH�ڲ������� +#define FLASH_BUF_RTS 0x00080000 //FLASH�ڲ���������λ + + + +/* FLASH_SR register bits */ + +#define FLASH_BSY (1 << 0) +#define FLASH_PGERR (1 << 2) +#define FLASH_WRPRTERR (1 << 4) +#define FLASH_EOP (1 << 5) + +/* ch32_FLASH_OBR bit definitions (reading) */ + +#define OPT_ERROR 0 +#define OPT_READOUT 1 +#define OPT_RDWDGSW 2 +#define OPT_RDRSTSTOP 3 +#define OPT_RDRSTSTDBY 4 +#define OPT_BFB2 5 /* dual flash bank only */ + +/* register unlock keys */ + +#define KEY1 0x45670123 +#define KEY2 0xCDEF89AB + +/* timeout values */ + +#define FLASH_WRITE_TIMEOUT 10 +#define FLASH_ERASE_TIMEOUT 1000 + +int wch_arm_chip; +static const uint8_t ch32f2_flash_write_code[] = { + 0x80, 0xB4, 0x89, 0xB0, 0x00, 0xAF, 0x78, 0x60, 0x39, 0x60, 0x7B, 0x68, 0xBB, 0x61, 0x35, 0x4B, + 0xFB, 0x61, 0x3B, 0x68, 0xFF, 0x33, 0x1B, 0x0A, 0x3B, 0x61, 0x33, 0x4B, 0x1B, 0x69, 0x32, 0x4A, + 0x43, 0xF4, 0x80, 0x33, 0x13, 0x61, 0x30, 0x4B, 0x1B, 0x69, 0x2F, 0x4A, 0x43, 0xF4, 0x80, 0x33, + 0x13, 0x61, 0x00, 0xBF, 0x2C, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x01, 0x03, 0x00, 0x2B, 0xF9, 0xD1, + 0xBB, 0x69, 0x7B, 0x61, 0x40, 0x23, 0xFB, 0x60, 0x12, 0xE0, 0xFB, 0x69, 0x1A, 0x1D, 0xFA, 0x61, + 0x7A, 0x69, 0x1B, 0x68, 0x13, 0x60, 0x7B, 0x69, 0x04, 0x33, 0x7B, 0x61, 0xFB, 0x68, 0x01, 0x3B, + 0xFB, 0x60, 0x00, 0xBF, 0x20, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x02, 0x03, 0x00, 0x2B, 0xF9, 0xD1, + 0xFB, 0x68, 0x00, 0x2B, 0xE9, 0xD1, 0x1C, 0x4B, 0x1B, 0x69, 0x1B, 0x4A, 0x43, 0xF4, 0x00, 0x13, + 0x13, 0x61, 0x00, 0xBF, 0x18, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x01, 0x03, 0x00, 0x2B, 0xF9, 0xD1, + 0x15, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x10, 0x03, 0x00, 0x2B, 0x0D, 0xD0, 0x12, 0x4B, 0xDB, 0x68, + 0x11, 0x4A, 0x43, 0xF0, 0x10, 0x03, 0xD3, 0x60, 0x0F, 0x4B, 0x1B, 0x69, 0x0E, 0x4A, 0x23, 0xF4, + 0x80, 0x33, 0x13, 0x61, 0x08, 0x23, 0x0F, 0xE0, 0xBB, 0x69, 0x03, 0xF5, 0x80, 0x73, 0xBB, 0x61, + 0x3B, 0x69, 0x01, 0x3B, 0x3B, 0x61, 0x00, 0x2B, 0xAD, 0xD1, 0x07, 0x4B, 0x1B, 0x69, 0x06, 0x4A, + 0x23, 0xF4, 0x80, 0x33, 0x13, 0x61, 0x00, 0x23, 0x18, 0x46, 0x24, 0x37, 0xBD, 0x46, 0x80, 0xBC, + 0x00, 0xBE, 0x00, 0xBF, 0x00, 0x10, 0x00, 0x20, 0x00, 0x20, 0x02, 0x40, +}; +static const uint8_t ch32f1_flash_write_code[]= +{ + 0x80, 0xB4, 0x89, 0xB0, 0x00, 0xAF, 0x78, 0x60, 0x39, 0x60, 0x7B, 0x68, 0xBB, 0x61, 0x42, 0x4B, + 0xFB, 0x61, 0x3B, 0x68, 0x7F, 0x33, 0xDB, 0x09, 0x3B, 0x61, 0x40, 0x4B, 0x1B, 0x69, 0x3F, 0x4A, + 0x43, 0xF4, 0x80, 0x33, 0x13, 0x61, 0x3D, 0x4B, 0x1B, 0x69, 0x3C, 0x4A, 0x43, 0xF4, 0x00, 0x23, + 0x13, 0x61, 0x00, 0xBF, 0x39, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x01, 0x03, 0x00, 0x2B, 0xF9, 0xD1, + 0xBB, 0x69, 0x7B, 0x61, 0x08, 0x23, 0xFB, 0x60, 0xFB, 0x69, 0x1A, 0x1D, 0xFA, 0x61, 0x7A, 0x69, + 0x1B, 0x68, 0x13, 0x60, 0xFB, 0x69, 0x1A, 0x1D, 0xFA, 0x61, 0x7A, 0x69, 0x04, 0x32, 0x1B, 0x68, + 0x13, 0x60, 0xFB, 0x69, 0x1A, 0x1D, 0xFA, 0x61, 0x7A, 0x69, 0x08, 0x32, 0x1B, 0x68, 0x13, 0x60, + 0xFB, 0x69, 0x1A, 0x1D, 0xFA, 0x61, 0x7A, 0x69, 0x0C, 0x32, 0x1B, 0x68, 0x13, 0x60, 0x27, 0x4B, + 0x1B, 0x69, 0x26, 0x4A, 0x43, 0xF4, 0x80, 0x23, 0x13, 0x61, 0x00, 0xBF, 0x23, 0x4B, 0xDB, 0x68, + 0x03, 0xF0, 0x01, 0x03, 0x00, 0x2B, 0xF9, 0xD1, 0x7B, 0x69, 0x10, 0x33, 0x7B, 0x61, 0xFB, 0x68, + 0x01, 0x3B, 0xFB, 0x60, 0x00, 0x2B, 0xCF, 0xD1, 0x1C, 0x4A, 0xBB, 0x69, 0x53, 0x61, 0x1B, 0x4B, + 0x1B, 0x69, 0x1A, 0x4A, 0x43, 0xF0, 0x40, 0x03, 0x13, 0x61, 0x00, 0xBF, 0x17, 0x4B, 0xDB, 0x68, + 0x03, 0xF0, 0x01, 0x03, 0x00, 0x2B, 0xF9, 0xD1, 0x14, 0x4B, 0xDB, 0x68, 0x03, 0xF0, 0x14, 0x03, + 0x00, 0x2B, 0x0D, 0xD0, 0x11, 0x4B, 0xDB, 0x68, 0x10, 0x4A, 0x43, 0xF0, 0x14, 0x03, 0xD3, 0x60, + 0x0E, 0x4B, 0x1B, 0x69, 0x0D, 0x4A, 0x23, 0xF4, 0x50, 0x23, 0x13, 0x61, 0x08, 0x23, 0x0E, 0xE0, + 0xBB, 0x69, 0x80, 0x33, 0xBB, 0x61, 0x3B, 0x69, 0x01, 0x3B, 0x3B, 0x61, 0x00, 0x2B, 0x92, 0xD1, + 0x06, 0x4B, 0x1B, 0x69, 0x05, 0x4A, 0x23, 0xF4, 0x50, 0x23, 0x13, 0x61, 0x00, 0x23, 0x18, 0x46, + 0x24, 0x37, 0xBD, 0x46, 0x80, 0xBC, 0x00, 0xBE, 0x00, 0x10, 0x00, 0x20, 0x00, 0x20, 0x02, 0x40, + +}; + +struct ch32x_options { + uint8_t rdp; + uint8_t user; + uint16_t data; + uint32_t protection; +}; + +struct ch32x_flash_bank { + struct ch32x_options option_bytes; + int ppage_size; + int probed; + + bool has_dual_banks; + /* used to access dual flash bank ch32xl */ + bool can_load_options; + uint32_t register_base; + uint8_t default_rdp; + int user_data_offset; + int option_offset; + uint32_t user_bank_size; +}; + + + + +static int ch32x_mass_erase(struct flash_bank *bank); +static int ch32x_get_device_id(struct flash_bank *bank, uint32_t *device_id); +static int ch32x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t address, uint32_t count); + +/* flash bank ch32x 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(ch32x_flash_bank_command) +{ + struct ch32x_flash_bank *ch32x_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + ch32x_info = malloc(sizeof(struct ch32x_flash_bank)); + + bank->driver_priv = ch32x_info; + ch32x_info->probed = 0; + ch32x_info->has_dual_banks = false; + ch32x_info->can_load_options = false; + ch32x_info->register_base = FLASH_REG_BASE_B0; + ch32x_info->user_bank_size = bank->size; + + return ERROR_OK; +} +static int get_ch32x_info(struct flash_bank *bank, char *buf, int buf_size) +{ + return ERROR_OK; +} +static inline int ch32x_get_flash_reg(struct flash_bank *bank, uint32_t reg) +{ + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + return reg + ch32x_info->register_base; +} + +static inline int ch32x_get_flash_status(struct flash_bank *bank, uint32_t *status) +{ + struct target *target = bank->target; + return target_read_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_SR), status); +} + +static int ch32x_wait_status_busy(struct flash_bank *bank, int timeout) +{ + struct target *target = bank->target; + uint32_t status; + int retval = ERROR_OK; + + /* wait for busy to clear */ + for (;;) { + retval = ch32x_get_flash_status(bank, &status); + if (retval != ERROR_OK) + return retval; + LOG_DEBUG("status: 0x%" PRIx32 "", status); + if ((status & FLASH_BSY) == 0) + break; + if (timeout-- <= 0) { + LOG_ERROR("timed out waiting for flash"); + return ERROR_FAIL; + } + alive_sleep(1); + } + + if (status & FLASH_WRPRTERR) { + LOG_ERROR("ch32x device protected"); + retval = ERROR_FAIL; + } + + if (status & FLASH_PGERR) { + LOG_ERROR("ch32x device programming failed"); + retval = ERROR_FAIL; + } + + /* Clear but report errors */ + if (status & (FLASH_WRPRTERR | FLASH_PGERR)) { + /* If this operation fails, we ignore it and report the original + * retval + */ + target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_SR), + FLASH_WRPRTERR | FLASH_PGERR); + } + return retval; +} + +static int ch32x_check_operation_supported(struct flash_bank *bank) +{ + + + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + + /* if we have a dual flash bank device then + * we need to perform option byte stuff on bank0 only */ + if (ch32x_info->register_base != FLASH_REG_BASE_B0) { + LOG_ERROR("Option Byte Operation's must use bank0"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; +} + +static int ch32x_read_options(struct flash_bank *bank) +{ + + + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t option_bytes; + int retval; + + /* read user and read protection option bytes */ + retval = target_read_u32(target, ch32_OB_RDP, &option_bytes); + if (retval != ERROR_OK) + return retval; + + ch32x_info->option_bytes.rdp = option_bytes & 0xFF; + ch32x_info->option_bytes.user = (option_bytes >> 16) & 0xFF; + + /* read user data option bytes */ + retval = target_read_u32(target, ch32_OB_DATA0, &option_bytes); + if (retval != ERROR_OK) + return retval; + + ch32x_info->option_bytes.data = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + /* read write protection option bytes */ + retval = target_read_u32(target, ch32_OB_WRP0, &option_bytes); + if (retval != ERROR_OK) + return retval; + + ch32x_info->option_bytes.protection = ((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF); + + retval = target_read_u32(target, ch32_OB_WRP2, &option_bytes); + if (retval != ERROR_OK) + return retval; + + ch32x_info->option_bytes.protection |= (((option_bytes >> 8) & 0xFF00) | (option_bytes & 0xFF)) << 16; + + return ERROR_OK; +} + +static int ch32x_erase_options(struct flash_bank *bank) +{ + + uint32_t option_bytes; + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + struct target *target = bank->target; + + /* read current options */ + ch32x_read_options(bank); + + /* unlock flash registers */ + int retval = target_write_u32(target, ch32_FLASH_KEYR_B0, KEY1); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, ch32_FLASH_KEYR_B0, KEY2); + if (retval != ERROR_OK) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, ch32_FLASH_OPTKEYR_B0, KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_OPTKEYR_B0, KEY2); + if (retval != ERROR_OK) + return retval; + + /* erase option bytes */ + retval = target_write_u32(target, ch32_FLASH_CR_B0, FLASH_OPTER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_OB_RDP,((ch32x_info->option_bytes.rdp & 0xffff0000)| ch32x_info->default_rdp) ); + if (retval != ERROR_OK) + return retval; + + ch32x_info->option_bytes.rdp = ch32x_info->default_rdp; + + retval = ch32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + for(int i=0;i<8;i++){ + retval = target_write_u16(target, ch32_OB_RDP+16*i,0xffff ); + if (retval != ERROR_OK) + return retval; + } + retval = target_read_u32(target, ch32_FLASH_CR_B0, &option_bytes); + if (retval != ERROR_OK) + return retval; + option_bytes &=0xffffffef; + + retval = target_write_u32(target, ch32_FLASH_CR_B0, option_bytes); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int ch32x_write_options(struct flash_bank *bank) +{ + + + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + struct target *target = bank->target; + uint16_t pbuf[8]; + uint32_t option_bytes; + ch32x_info = bank->driver_priv; + + /* unlock flash registers */ + int retval = target_write_u32(target, ch32_FLASH_KEYR_B0, KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_KEYR_B0, KEY2); + if (retval != ERROR_OK) + return retval; + + /* unlock option flash registers */ + retval = target_write_u32(target, ch32_FLASH_OPTKEYR_B0, KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_OPTKEYR_B0, KEY2); + if (retval != ERROR_OK) + return retval; + + /* program option bytes */ + for(int i=0;i<8;i++){ + retval = target_read_u16(target, ch32_OB_RDP+ 16*i , pbuf[i]); + if (retval != ERROR_OK) + return retval; + } + if(ch32x_info->option_bytes.protection) + pbuf[0]=0x5aa5; + else + pbuf[0]=0x00ff; + + retval = target_write_u32(target, ch32_FLASH_CR_B0, FLASH_OPTER); + if (retval != ERROR_OK) + return retval; + for(int i=0;i<8;i++){ + retval = target_write_u16(target, ch32_OB_RDP+16*i,pbuf[i]); + if (retval != ERROR_OK) + return retval; + } + retval = target_read_u32(target, ch32_FLASH_CR_B0, &option_bytes); + if (retval != ERROR_OK) + return retval; + option_bytes &=0xffffffef; + + retval = target_write_u32(target, ch32_FLASH_CR_B0, option_bytes); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int ch32x_protect_check(struct flash_bank *bank) +{ + + + struct target *target = bank->target; + uint32_t protection; + + int retval = ch32x_check_operation_supported(bank); + if (ERROR_OK != retval) + return retval; + + /* medium density - each bit refers to a 4 sector protection block + * high density - each bit refers to a 2 sector protection block + * bit 31 refers to all remaining sectors in a bank */ + retval = target_read_u32(target, ch32_FLASH_WRPR_B0, &protection); + if (retval != ERROR_OK) + return retval; + + for (int i = 0; i < bank->num_prot_blocks; i++) + bank->prot_blocks[i].is_protected = (protection & (1 << i)) ? 0 : 1; + + return ERROR_OK; +} + +static int ch32x_erase(struct flash_bank *bank, int first, int last) +{ + + + struct target *target = bank->target; + int i; + uint32_t cr_reg; uint32_t sr_reg; + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* unlock flash registers */ + + int retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + + retval = target_read_u32(target, ch32_FLASH_CR_B0, &cr_reg); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_CR_B0, cr_reg | FLASH_MER); + if (retval != ERROR_OK) + return retval; + + retval = target_read_u32(target, ch32_FLASH_CR_B0, &cr_reg); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_CR_B0, cr_reg | FLASH_STRT); + if (retval != ERROR_OK) + return retval; + + + retval = ch32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + + retval = target_read_u32(target, ch32_FLASH_CR_B0, &cr_reg); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32_FLASH_CR_B0, (cr_reg)&(~(1<<2))); + if (retval != ERROR_OK) + return retval; + alive_sleep(300); + + return ERROR_OK; +} + +static int ch32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + + struct target *target = bank->target; + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + int retval = ch32x_check_operation_supported(bank); + if (retval != ERROR_OK) + return retval; + + retval = ch32x_erase_options(bank); + if (retval != ERROR_OK) { + LOG_ERROR("ch32x failed to erase options"); + return retval; + } + + for (int i = first; i <= last; i++) { + if (set) + ch32x_info->option_bytes.protection &= ~(1 << i); + else + ch32x_info->option_bytes.protection |= (1 << i); + } + + return ch32x_write_options(bank); +} + +static int ch32x_write_block(struct flash_bank *bank, const uint8_t *buffer, + uint32_t address, uint32_t count) +{ + + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + struct target *target = bank->target; + uint32_t buffer_size = 16384; + struct working_area *write_algorithm; + struct working_area *source; + struct reg_param reg_params[4]; + struct armv7m_algorithm armv7m_info; + uint32_t basaddr=0x08000000; + uint32_t pagesize=0x100; + uint32_t sp=0x20002800; + int retval = ERROR_OK; + uint8_t ch32x_flash_write_code[320]={0}; + uint8_t bufpage[256]; + if(wch_arm_chip==1) + memcpy(ch32x_flash_write_code,ch32f1_flash_write_code,sizeof(ch32f1_flash_write_code)); + else + memcpy(ch32x_flash_write_code,ch32f2_flash_write_code,sizeof(ch32f2_flash_write_code)); + + /* flash write code */ + if (target_alloc_working_area(target, sizeof(ch32x_flash_write_code), + &write_algorithm) != ERROR_OK) { + LOG_WARNING("no working area available, can't do block memory writes"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + retval = target_write_buffer(target, write_algorithm->address, + sizeof(ch32x_flash_write_code) , ch32x_flash_write_code); + LOG_INFO("write_algorithm->address%x",write_algorithm->address); + if (retval != ERROR_OK) { + target_free_working_area(target, write_algorithm); + return retval; + } + + init_reg_param(®_params[0], "r0", 32, PARAM_OUT); /* flash base (in), status (out) */ + init_reg_param(®_params[1], "r1", 32, PARAM_OUT); /* count (halfword-16bit) */ + init_reg_param(®_params[2], "sp", 32, PARAM_OUT); /* buffer start */ + + buf_set_u32(reg_params[0].value, 0, 32, basaddr); + buf_set_u32(reg_params[1].value, 0, 32, pagesize ); + buf_set_u32(reg_params[2].value, 0, 32, sp); + armv7m_info.common_magic = ARMV7M_COMMON_MAGIC; + armv7m_info.core_mode = ARM_MODE_THREAD; + int mod =address%256; + int len=count+mod; + uint8_t *buffer1; + uint8_t *buffer2; + int loop=0; + if(mod){ + if(address<256) + address=0; + else + address-=mod; + + buffer1=malloc(len); + buffer2=malloc(mod); + + target_read_memory(bank->target,address,1,mod,buffer2); + memcpy(buffer1,buffer2,mod); + memcpy(&buffer1[mod],buffer,count); + }else{ + buffer1=malloc(len); + memcpy(buffer1,buffer,count); + } + while(len>0){ + buf_set_u32(reg_params[0].value, 0, 32, basaddr); + if(len<256){ + memset(bufpage,0xff,256); + memcpy(bufpage,buffer1+loop*256,len); + }else + memcpy(bufpage,buffer1+loop*256,256); + target_write_buffer(target, 0x20001000,256,bufpage); + retval=target_run_algorithm(target,0,NULL,3,reg_params,0x20000000,0,100000,&armv7m_info); + len-=256; + loop++; + basaddr +=256; + + } + if (retval == ERROR_FLASH_OPERATION_FAILED) { + LOG_ERROR("flash write failed at address 0x%"PRIx32, + buf_get_u32(reg_params[4].value, 0, 32)); + + if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_PGERR) { + LOG_ERROR("flash memory not erased before writing"); + /* Clear but report errors */ + target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_SR), FLASH_PGERR); + } + + if (buf_get_u32(reg_params[0].value, 0, 32) & FLASH_WRPRTERR) { + LOG_ERROR("flash memory write protected"); + /* Clear but report errors */ + target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_SR), FLASH_WRPRTERR); + } + } + + // target_free_working_area(target, source); + target_free_working_area(target, write_algorithm); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + destroy_reg_param(®_params[2]); + + return retval; +} + +static int ch32x_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + + struct target *target = bank->target; + uint8_t *new_buffer = NULL; + uint32_t choffset=offset; + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (offset & 0x1) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required 2-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + int retval; + + /* unlock flash registers */ + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + retval=target_write_u32(target,ch32x_get_flash_reg(bank, CH32_FLASH_MODEKEYP) , KEY1); + if (retval != ERROR_OK) + return retval; //��������ģʽ + retval =target_write_u32(target, ch32x_get_flash_reg(bank, CH32_FLASH_MODEKEYP), KEY2); + if (retval != ERROR_OK) + return retval; + + retval = ch32x_write_block(bank, buffer, bank->base + offset, count); + + return retval; +} + +static int ch32x_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + + struct target *target = bank->target; + int retval = target_read_u32(target, 0x1ffff884, device_id); + if (retval != ERROR_OK) + return retval; + if(*device_id >>24 ==0x20){ + wch_arm_chip=1; + return ERROR_OK; + } + retval = target_read_u32(target, 0x1ffff704, device_id); + if (retval != ERROR_OK) + return retval; + if((*device_id >>20 ==0x203)||(*device_id >>20 ==0x205)||(*device_id >>20 ==0x207)||(*device_id >>20 ==0x208)){ + wch_arm_chip=2; + return ERROR_OK; + + } + return ERROR_FAIL; + +} + +static int ch32x_get_flash_size(struct flash_bank *bank, uint16_t *flash_size_in_kb) +{ + struct target *target = bank->target; + uint32_t cpuid, flash_size_reg; + uint32_t temp; + int retval = target_read_u32(target, 0x1ffff7e0, flash_size_in_kb); + if (retval != ERROR_OK) + return retval; + + return retval; + +} + +static int ch32x_probe(struct flash_bank *bank) +{ + + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + uint16_t flash_size_in_kb; + uint16_t max_flash_size_in_kb; + uint32_t device_id; + int page_size; + uint32_t base_address = 0x08000000; + uint32_t rid=0; + ch32x_info->probed = 0; + ch32x_info->register_base = FLASH_REG_BASE_B0; + ch32x_info->user_data_offset = 10; + ch32x_info->option_offset = 0; + + /* default factory read protection level 0 */ + ch32x_info->default_rdp = 0xA5; + + /* read ch32 device id register */ + int retval = ch32x_get_device_id(bank, &device_id); + if (retval != ERROR_OK) + return retval; + + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + + page_size = 1024; + ch32x_info->ppage_size = 4; + max_flash_size_in_kb = 128; + + /* get flash size from target. */ + retval = ch32x_get_flash_size(bank, &flash_size_in_kb); + + /* failed reading flash size or flash size invalid (early silicon), + * default to max target family */ + if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) { + //LOG_WARNING("ch32 flash size failed, probe inaccurate - assuming %dk flash", + //max_flash_size_in_kb); + flash_size_in_kb = max_flash_size_in_kb; + } + + if (ch32x_info->has_dual_banks) { + /* split reported size into matching bank */ + if (bank->base != 0x08080000) { + /* bank 0 will be fixed 512k */ + flash_size_in_kb = 512; + } else { + flash_size_in_kb -= 512; + /* bank1 also uses a register offset */ + ch32x_info->register_base = FLASH_REG_BASE_B1; + base_address = 0x08080000; + } + } + LOG_INFO("flash size = %dkbytes", flash_size_in_kb); + + /* did we assign flash size? */ + assert(flash_size_in_kb != 0xffff); + + /* calculate numbers of pages */ + int num_pages = flash_size_in_kb * 1024 / page_size; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + if (bank->prot_blocks) { + free(bank->prot_blocks); + bank->prot_blocks = NULL; + } + + bank->base = base_address; + bank->size = (num_pages * page_size); + + bank->num_sectors = num_pages; + bank->sectors = alloc_block_array(0, page_size, num_pages); + if (!bank->sectors) + return ERROR_FAIL; + + /* calculate number of write protection blocks */ + int num_prot_blocks = num_pages / ch32x_info->ppage_size; + if (num_prot_blocks > 32) + num_prot_blocks = 32; + + bank->num_prot_blocks = num_prot_blocks; + bank->prot_blocks = alloc_block_array(0, ch32x_info->ppage_size * page_size, num_prot_blocks); + if (!bank->prot_blocks) + return ERROR_FAIL; + + if (num_prot_blocks == 32) + bank->prot_blocks[31].size = (num_pages - (31 * ch32x_info->ppage_size)) * page_size; + + ch32x_info->probed = 1; + + return ERROR_OK; +} + +static int ch32x_auto_probe(struct flash_bank *bank) +{ + struct ch32x_flash_bank *ch32x_info = bank->driver_priv; + if (ch32x_info->probed) + return ERROR_OK; + return ch32x_probe(bank); +} +static int ch32x_mass_erase(struct flash_bank *bank) +{ + + struct target *target = bank->target; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* unlock option flash registers */ + int retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_KEYR), KEY2); + if (retval != ERROR_OK) + return retval; + + /* mass erase flash memory */ + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_CR), FLASH_MER); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_CR), + FLASH_MER | FLASH_STRT); + if (retval != ERROR_OK) + return retval; + + retval = ch32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT); + if (retval != ERROR_OK) + return retval; + + retval = target_write_u32(target, ch32x_get_flash_reg(bank, ch32_FLASH_CR), FLASH_LOCK); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + + +static const struct command_registration ch32x_command_handlers[] = { + { + .name = "ch32f1x", + .mode = COMMAND_ANY, + .help = "ch32f1x flash command group", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +const struct flash_driver wch_arm_flash = { + .name = "wch_arm", + .commands = ch32x_command_handlers, + .flash_bank_command = ch32x_flash_bank_command, + .erase = ch32x_erase, + .protect = ch32x_protect, + .write = ch32x_write, + .read = default_flash_read, + .probe = ch32x_probe, + .auto_probe = ch32x_auto_probe, + .erase_check = default_flash_blank_check, + .protect_check = ch32x_protect_check, + .info = get_ch32x_info, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/flash/nor/wchriscv.c b/src/flash/nor/wchriscv.c new file mode 100755 index 000000000..6246daf0e --- /dev/null +++ b/src/flash/nor/wchriscv.c @@ -0,0 +1,324 @@ +/*************************************************************************** + * WCH RISC-V mcu :CH32V103X CH32V20X CH32V30X CH56X CH57X CH58X * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include +#include +extern int wlink_erase(void); +extern unsigned char riscvchip; +extern void wlink_reset(); +extern void wlink_chip_reset(void); +extern void wlink_getromram(uint32_t *rom, uint32_t *ram); +extern int wlink_write(const uint8_t *buffer, uint32_t offset, uint32_t count); +extern bool noloadflag; +extern int wlink_flash_protect(bool stat); +extern int wlnik_protect_check(void); +extern void wlink_clean(void); +extern unsigned long wlink_address; +extern bool pageerase; +int writeloop =0 ; +extern unsigned int chip_type; +bool flash_unfreeze=false; +struct ch32vx_options +{ + uint8_t rdp; + uint8_t user; + uint16_t data; + uint32_t protection; +}; + +struct ch32vx_flash_bank +{ + struct ch32vx_options option_bytes; + int ppage_size; + int probed; + + bool has_dual_banks; + bool can_load_options; + uint32_t register_base; + uint8_t default_rdp; + int user_data_offset; + int option_offset; + uint32_t user_bank_size; +}; + +FLASH_BANK_COMMAND_HANDLER(ch32vx_flash_bank_command) +{ + struct ch32vx_flash_bank *ch32vx_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + ch32vx_info = malloc(sizeof(struct ch32vx_flash_bank)); + + bank->driver_priv = ch32vx_info; + ch32vx_info->probed = 0; + ch32vx_info->has_dual_banks = false; + ch32vx_info->can_load_options = false; + ch32vx_info->user_bank_size = bank->size; + + return ERROR_OK; +} +static int ch32x_protect(struct flash_bank *bank, int set, int first, int last) +{ + + if ((riscvchip == 1) || (riscvchip == 5) || (riscvchip == 6) || (riscvchip == 9) || (riscvchip ==0x0c)||(riscvchip==0x0e)) + { + int retval = wlink_flash_protect(set); + if (retval == ERROR_OK) + { + if (set) + LOG_INFO("Success to Enable Read-Protect"); + else + LOG_INFO("Success to Disable Read-Protect"); + return ERROR_OK; + } + else + { + LOG_ERROR("Operation Failed"); + return ERROR_FAIL; + } + } + else + { + LOG_ERROR("This chip do not support function"); + return ERROR_FAIL; + } +} + +static int ch32vx_erase(struct flash_bank *bank, int first, int last) +{ + if (pageerase) + return ERROR_OK; + if ((riscvchip == 5) || (riscvchip == 6) || (riscvchip == 9)|| (riscvchip == 0x0c)||(riscvchip==0x0e)) + { + int retval = wlnik_protect_check(); + if (retval == 4) + { + LOG_ERROR("Read-Protect Status Currently Enabled"); + return ERROR_FAIL; + } + } + if (noloadflag) + return ERROR_OK; + + int ret = wlink_erase(); + target_halt(bank->target); + if (ret) + return ERROR_OK; + else + return ERROR_FAIL; + return ERROR_OK; +} + + +static int ch32vx_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + + struct target *target = bank->target; + if (((riscvchip == 5) || (riscvchip == 6) || (riscvchip == 9)|| (riscvchip == 0x0c)||(riscvchip==0x0e)) && (writeloop==0)) + { + int retval = wlnik_protect_check(); + if (retval == 4) + { + LOG_ERROR("Read-Protect Status Currently Enabled"); + return ERROR_FAIL; + } + } + if (noloadflag) + return ERROR_OK; + + if(writeloop) + wlink_clean(); + int ret = 0; + int mod = offset % 256; + if (mod) + { + if (offset < 256) + offset = 0; + else + offset -= mod; + uint8_t *buffer1; + uint8_t *buffer2; + buffer1 = malloc(count + mod); + buffer2 = malloc(mod); + target_read_memory(bank->target, offset, 1, mod, buffer2); + memcpy(buffer1, buffer2, mod); + memcpy(&buffer1[mod], buffer, count); + ret = wlink_write(buffer1, offset, count + mod); + } + else + { + target_halt(target); + ret = wlink_write(buffer, offset, count); + } + wlink_chip_reset(); + writeloop++; + return ret; +} + +static int ch32vx_get_device_id(struct flash_bank *bank, uint32_t *device_id) +{ + if ((riscvchip != 0x02) && (riscvchip != 0x03)&& (riscvchip != 0x07)&& (riscvchip != 0x0b)) + { + struct target *target = bank->target; + int retval = target_read_u32(target, 0x1ffff7e8, device_id); + if (retval != ERROR_OK) + return retval; + } + return ERROR_OK; +} + +static int ch32vx_get_flash_size(struct flash_bank *bank, uint32_t *flash_size_in_kb) +{ + + struct target *target = bank->target; + if(riscvchip == 0x09) + { + *flash_size_in_kb = 0x7fffe; + return ERROR_OK; + + + } + if ((riscvchip == 0x02) || (riscvchip == 0x03) || (riscvchip == 0x07)|| (riscvchip == 0x0b)) + { + if((chip_type ==0x71000000) || (chip_type ==0x81000000) || (chip_type ==0x91000000)) + *flash_size_in_kb = 192; + else + *flash_size_in_kb = 448; + return ERROR_OK; + } + if (riscvchip == 0x0c) + { + if(chip_type==0x03570601) + *flash_size_in_kb = 48; + *flash_size_in_kb = 62; + return ERROR_OK; + } + if (riscvchip == 0x0e) + { + if(chip_type==0x10370700) + *flash_size_in_kb = 32; + *flash_size_in_kb = 64; + return ERROR_OK; + } + int retval = target_read_u16(target, 0x1ffff7e0, flash_size_in_kb); + if (retval != ERROR_OK) + return retval; + return ERROR_OK; +} + +static int ch32vx_probe(struct flash_bank *bank) +{ + struct ch32vx_flash_bank *ch32vx_info = bank->driver_priv; + uint16_t delfault_max_flash_size = 512; + uint32_t flash_size_in_kb; + uint32_t device_id = 0; + uint32_t rom = 0; + uint32_t ram = 0; + int page_size; + uint32_t base_address = (uint32_t)wlink_address; + uint32_t rid = 0; + ch32vx_info->probed = 0; + + /* read ch32 device id register */ + int retval = ch32vx_get_device_id(bank, &device_id); + if (retval != ERROR_OK) + return retval; + if (device_id) + LOG_INFO("device id = 0x%08" PRIx32 "", device_id); + page_size = 1024; + ch32vx_info->ppage_size = 4; + + /* get flash size from target. */ + retval = ch32vx_get_flash_size(bank, &flash_size_in_kb); + + if ((flash_size_in_kb)&&(!flash_unfreeze)&&(riscvchip !=0x09)) + LOG_INFO("flash size = %dkbytes", flash_size_in_kb); + else + flash_size_in_kb = delfault_max_flash_size; + if ((riscvchip == 0x05) || (riscvchip == 0x06)) + { + wlink_getromram(&rom, &ram); + if ((rom != 0) && (ram != 0)) + LOG_INFO("ROM %d kbytes RAM %d kbytes", rom, ram); + } + // /* calculate numbers of pages */ + int num_pages = flash_size_in_kb * 1024 / page_size; + bank->base = base_address; + bank->size = (num_pages * page_size); + bank->num_sectors = num_pages; + bank->sectors = alloc_block_array(0, page_size, num_pages); + ch32vx_info->probed = 1; + + return ERROR_OK; +} + +static int ch32vx_auto_probe(struct flash_bank *bank) +{ + + struct ch32vx_flash_bank *ch32vx_info = bank->driver_priv; + if (ch32vx_info->probed) + return ERROR_OK; + return ch32vx_probe(bank); +} + +COMMAND_HANDLER(ch32vx_handle_unfreeze_command) +{ + + flash_unfreeze=true; + return ERROR_OK; +} + + +static const struct command_registration ch32vx_exec_command_handlers[] = { + { + .name = "unfreeze", + .handler = ch32vx_handle_unfreeze_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "unfreeze entire flash device.", + }, + COMMAND_REGISTRATION_DONE +}; +static const struct command_registration ch32vx_command_handlers[] = { + { + .name = "wch_riscv", + .mode = COMMAND_ANY, + .help = "wch_riscv flash command group", + .usage = "", + .chain = ch32vx_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE}; + +const struct flash_driver wch_riscv_flash = { + .name = "wch_riscv", + .commands = ch32vx_command_handlers, + .flash_bank_command = ch32vx_flash_bank_command, + .erase = ch32vx_erase, + .protect = ch32x_protect, + .write = ch32vx_write, + .read = default_flash_read, + .probe = ch32vx_probe, + .auto_probe = ch32vx_auto_probe, + .erase_check = default_flash_blank_check, + .free_driver_priv = default_flash_free_driver_priv, +}; diff --git a/src/helper/bits.h b/src/helper/bits.h index 00d3c0270..6151b3340 100644 --- a/src/helper/bits.h +++ b/src/helper/bits.h @@ -28,8 +28,12 @@ #include #define BIT(nr) (1UL << (nr)) +#define BIT_ULL(nr) (1ULL << (nr)) #define BITS_PER_BYTE 8 #define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long)) +#define BITS_PER_LONG_LONG (BITS_PER_BYTE * sizeof(long long)) +#define GENMASK(h, l) (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#define GENMASK_ULL(h, l) (((~0ULL) - (1ULL << (l)) + 1) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) diff --git a/src/helper/command.c b/src/helper/command.c old mode 100644 new mode 100755 index 1e769d719..b2d6a76af --- a/src/helper/command.c +++ b/src/helper/command.c @@ -497,6 +497,7 @@ void command_print(struct command_invocation *cmd, const char *format, ...) * * The latter bit isn't precisely neat, but will do for now. */ + Jim_AppendString(cmd->ctx->interp, cmd->output, string, -1); /* We already printed it above * command_output_text(context, string); */ diff --git a/src/helper/jim-nvp.c b/src/helper/jim-nvp.c index 738ed7943..0409a83cd 100644 --- a/src/helper/jim-nvp.c +++ b/src/helper/jim-nvp.c @@ -41,6 +41,10 @@ * official policies, either expressed or implied, of the Jim Tcl Project. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include "jim-nvp.h" #include diff --git a/src/helper/list.h b/src/helper/list.h index a7cd4ad37..552a3202a 100644 --- a/src/helper/list.h +++ b/src/helper/list.h @@ -656,6 +656,20 @@ static inline void list_splice_tail_init(struct list_head *list, !list_entry_is_head(pos, head, member); \ pos = list_prev_entry(pos, member)) +/** + * list_for_each_entry_direction - iterate forward/backward over list of given type + * @param forward the iterate direction, true for forward, false for backward. + * @param pos the type * to use as a loop cursor. + * @param head the head for your list. + * @param member the name of the list_head within the struct. + */ +#define list_for_each_entry_direction(forward, pos, head, member) \ + for (pos = forward ? list_first_entry(head, typeof(*pos), member) \ + : list_last_entry(head, typeof(*pos), member); \ + !list_entry_is_head(pos, head, member); \ + pos = forward ? list_next_entry(pos, member) \ + : list_prev_entry(pos, member)) + /** * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() * @param pos the type * to use as a start point diff --git a/src/helper/log.c b/src/helper/log.c index caa0a66bf..106d22867 100644 --- a/src/helper/log.c +++ b/src/helper/log.c @@ -30,6 +30,7 @@ #include "command.h" #include "replacements.h" #include "time_support.h" +#include #include @@ -47,7 +48,6 @@ static FILE *log_output; static struct log_callback *log_callbacks; static int64_t last_time; -static int64_t current_time; static int64_t start; @@ -111,32 +111,27 @@ static void log_puts(enum log_levels level, if (f) file = f + 1; - if (strlen(string) > 0) { - if (debug_level >= LOG_LVL_DEBUG) { - /* print with count and time information */ - int64_t t = timeval_ms() - start; + if (debug_level >= LOG_LVL_DEBUG) { + /* print with count and time information */ + int64_t t = timeval_ms() - start; #ifdef _DEBUG_FREE_SPACE_ - struct mallinfo info; - info = mallinfo(); + struct mallinfo info; + info = mallinfo(); #endif - fprintf(log_output, "%s%d %" PRId64 " %s:%d %s()" + fprintf(log_output, "%s%d %" PRId64 " %s:%d %s()" #ifdef _DEBUG_FREE_SPACE_ - " %d" + " %d" #endif - ": %s", log_strings[level + 1], count, t, file, line, function, + ": %s", log_strings[level + 1], count, t, file, line, function, #ifdef _DEBUG_FREE_SPACE_ - info.fordblks, + info.fordblks, #endif - string); - } else { - /* if we are using gdb through pipes then we do not want any output - * to the pipe otherwise we get repeated strings */ - fprintf(log_output, "%s%s", - (level > LOG_LVL_USER) ? log_strings[level + 1] : "", string); - } + string); } else { - /* Empty strings are sent to log callbacks to keep e.g. gdbserver alive, here we do - *nothing. */ + /* if we are using gdb through pipes then we do not want any output + * to the pipe otherwise we get repeated strings */ + fprintf(log_output, "%s%s", + (level > LOG_LVL_USER) ? log_strings[level + 1] : "", string); } fflush(log_output); @@ -302,6 +297,15 @@ void log_init(void) start = last_time = timeval_ms(); } +void log_exit(void) +{ + if (log_output && log_output != stderr) { + /* Close log file, if it was open and wasn't stderr. */ + fclose(log_output); + } + log_output = NULL; +} + int set_log_output(struct command_context *cmd_ctx, FILE *output) { log_output = output; @@ -431,8 +435,7 @@ static void gdb_timeout_warning(int64_t delta_time) void keep_alive(void) { - current_time = timeval_ms(); - + int64_t current_time = timeval_ms(); int64_t delta_time = current_time - last_time; if (delta_time > KEEP_ALIVE_TIMEOUT_MS) { @@ -445,7 +448,7 @@ void keep_alive(void) last_time = current_time; /* this will keep the GDB connection alive */ - LOG_USER_N("%s", ""); + server_keep_clients_alive(); /* DANGER!!!! do not add code to invoke e.g. target event processing, * jim timer processing, etc. it can cause infinite recursion + @@ -460,7 +463,7 @@ void keep_alive(void) /* reset keep alive timer without sending message */ void kept_alive(void) { - current_time = timeval_ms(); + int64_t current_time = timeval_ms(); int64_t delta_time = current_time - last_time; diff --git a/src/helper/log.h b/src/helper/log.h index 621d467b4..f0378ae79 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -72,6 +72,7 @@ __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 5, 6))); * Initialize logging module. Call during program startup. */ void log_init(void); +void log_exit(void); int set_log_output(struct command_context *cmd_ctx, FILE *output); int log_register_commands(struct command_context *cmd_ctx); diff --git a/src/jtag/Makefile.am b/src/jtag/Makefile.am old mode 100644 new mode 100755 index 23424f5a2..7a95178f9 --- a/src/jtag/Makefile.am +++ b/src/jtag/Makefile.am @@ -24,6 +24,7 @@ include %D%/drivers/Makefile.am %D%/interfaces.c \ %D%/tcl.c \ %D%/swim.c \ + %D%/sdi.c \ %D%/commands.h \ %D%/interface.h \ %D%/interfaces.h \ @@ -31,6 +32,7 @@ include %D%/drivers/Makefile.am %D%/jtag.h \ %D%/swd.h \ %D%/swim.h \ + %D%/sdi.h \ %D%/tcl.h STARTUP_TCL_SRCS += %D%/startup.tcl diff --git a/src/jtag/core.c b/src/jtag/core.c old mode 100644 new mode 100755 index 29ab6cc1c..d171c77af --- a/src/jtag/core.c +++ b/src/jtag/core.c @@ -1273,7 +1273,7 @@ static int jtag_examine_chain(void) jtag_tap_init(tap); } - if ((idcode & 1) == 0) { + if ((idcode & 1) == 0 && !tap->ignore_bypass) { /* Zero for LSB indicates a device in bypass */ LOG_INFO("TAP %s does not have valid IDCODE (idcode=0x%" PRIx32 ")", tap->dotted_name, idcode); @@ -1847,7 +1847,7 @@ int adapter_resets(int trst, int srst) return ERROR_OK; } else if (transport_is_swd() || transport_is_hla() || transport_is_dapdirect_swd() || transport_is_dapdirect_jtag() || - transport_is_swim()) { + transport_is_swim()|| transport_is_sdi()) { if (trst == TRST_ASSERT) { LOG_ERROR("transport %s has no trst signal", get_current_transport()->name); @@ -1881,7 +1881,7 @@ int adapter_assert_reset(void) return ERROR_OK; } else if (transport_is_swd() || transport_is_hla() || transport_is_dapdirect_jtag() || transport_is_dapdirect_swd() || - transport_is_swim()) + transport_is_swim()|| transport_is_sdi()) return adapter_system_reset(1); else if (get_current_transport()) LOG_ERROR("reset is not supported on %s", @@ -1898,7 +1898,7 @@ int adapter_deassert_reset(void) return ERROR_OK; } else if (transport_is_swd() || transport_is_hla() || transport_is_dapdirect_jtag() || transport_is_dapdirect_swd() || - transport_is_swim()) + transport_is_swim()|| transport_is_sdi()) return adapter_system_reset(0); else if (get_current_transport()) LOG_ERROR("reset is not supported on %s", diff --git a/src/jtag/drivers/Makefile.am b/src/jtag/drivers/Makefile.am old mode 100644 new mode 100755 index c2161523d..984bb141d --- a/src/jtag/drivers/Makefile.am +++ b/src/jtag/drivers/Makefile.am @@ -75,6 +75,9 @@ endif if JTAG_VPI DRIVERFILES += %D%/jtag_vpi.c endif +if VDEBUG +DRIVERFILES += %D%/vdebug.c +endif if JTAG_DPI DRIVERFILES += %D%/jtag_dpi.c endif @@ -182,6 +185,14 @@ endif if XDS110 DRIVERFILES += %D%/xds110.c endif +if WLINKE +DRIVERFILES += %D%/wlinke.c +endif +if CH347 +DRIVERFILES += %D%/ch347.c +endif + + DRIVERHEADERS = \ %D%/bitbang.h \ diff --git a/src/jtag/drivers/bcm2835gpio.c b/src/jtag/drivers/bcm2835gpio.c index fd6c28b96..b7a4d998c 100644 --- a/src/jtag/drivers/bcm2835gpio.c +++ b/src/jtag/drivers/bcm2835gpio.c @@ -565,6 +565,13 @@ static int bcm2835gpio_init(void) } if (transport_is_swd()) { + /* Make buffer an output before the GPIO connected to it */ + if (swdio_dir_gpio != -1) { + swdio_dir_gpio_mode = MODE_GPIO(swdio_dir_gpio); + GPIO_SET = 1 << swdio_dir_gpio; + OUT_GPIO(swdio_dir_gpio); + } + swclk_gpio_mode = MODE_GPIO(swclk_gpio); swdio_gpio_mode = MODE_GPIO(swdio_gpio); @@ -580,12 +587,6 @@ static int bcm2835gpio_init(void) OUT_GPIO(srst_gpio); } - if (swdio_dir_gpio != -1) { - swdio_dir_gpio_mode = MODE_GPIO(swdio_dir_gpio); - GPIO_SET = 1 << swdio_dir_gpio; - OUT_GPIO(swdio_dir_gpio); - } - LOG_DEBUG("saved pinmux settings: tck %d tms %d tdi %d " "tdo %d trst %d srst %d", tck_gpio_mode, tms_gpio_mode, tdi_gpio_mode, tdo_gpio_mode, trst_gpio_mode, srst_gpio_mode); diff --git a/src/jtag/drivers/ch347.c b/src/jtag/drivers/ch347.c new file mode 100644 index 000000000..a352e4789 --- /dev/null +++ b/src/jtag/drivers/ch347.c @@ -0,0 +1,852 @@ +/*************************************************************************** * + * Driver for CH347-JTAG interface V1.0 * + * * + * Copyright (C) 2022 Nanjing Qinheng Microelectronics Co., Ltd. * + * Web: http://wch.cn * + * Author: WCH@TECH53 * + * * + * CH347 is a high-speed USB bus converter chip that provides UART, I2C * + * and SPI synchronous serial ports and JTAG interface through USB bus. * + * * + * The USB2.0 to JTAG scheme based on CH347 can be used to build * + * customized USB high-speed JTAG debugger and other products. * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if IS_CYGWIN == 1 +#include "windows.h" +#undef LOG_ERROR +#endif + +/* project specific includes */ +#include +#include +#include +#include + +/* system includes */ +#include +#include +#include +#include +#include + +#define JTAGIO_STA_OUT_TDI (0x10) +#define JTAGIO_STA_OUT_TMS (0x02) +#define JTAGIO_STA_OUT_TCK (0x01) + +#define TDI_H JTAGIO_STA_OUT_TDI +#define TDI_L 0 +#define TMS_H JTAGIO_STA_OUT_TMS +#define TMS_L 0 +#define TCK_H JTAGIO_STA_OUT_TCK +#define TCK_L 0 + +#define KHZ(n) ((n)*UINT64_C(1000)) +#define MHZ(n) ((n)*UINT64_C(1000000)) +#define GHZ(n) ((n)*UINT64_C(1000000000)) + +#define HW_TDO_BUF_SIZE 4096 +#define SF_PACKET_BUF_SIZE 51200 //命令包长度 +#define UCMDPKT_DATA_MAX_BYTES_USBHS 507 // USB高速时每个命令包内包含数据长度 +#define USBC_PACKET_USBHS 512 // USB高速时单包最大数据长度 + +#define CH347_CMD_HEADER 3 //协议包头长度 + +// 协议传输格式:CMD(1字节)+ Length(2字节)+ Data +#define CH347_CMD_INFO_RD 0xCA //参数获取,用于获取固件版本、JTAG接口相关参数等 +#define CH347_CMD_JTAG_INIT 0xD0 // JTAG接口初始化命令 +#define CH347_CMD_JTAG_BIT_OP 0xD1 // JTAG接口引脚位控制命令 +#define CH347_CMD_JTAG_BIT_OP_RD 0xD2 // JTAG接口引脚位控制并读取命令 +#define CH347_CMD_JTAG_DATA_SHIFT 0xD3 // JTAG接口数据移位命令 +#define CH347_CMD_JTAG_DATA_SHIFT_RD 0xD4 // JTAG接口数据移位并读取命令 + +#pragma pack(1) + +typedef struct _CH347_info { // 记录CH347引脚状态 + int TMS; + int TDI; + int TCK; + + int buffer_idx; + uint8_t buffer[HW_TDO_BUF_SIZE]; +} _CH347_Info; + +#pragma pack() + +#ifdef _WIN32 +#include +typedef int(__stdcall *pCH347OpenDevice)(unsigned long iIndex); +typedef void(__stdcall *pCH347CloseDevice)(unsigned long iIndex); +typedef unsigned long(__stdcall *pCH347SetTimeout)(unsigned long iIndex, // 指定设备序号 + unsigned long iWriteTimeout, // 指定USB写出数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值) + unsigned long iReadTimeout); // 指定USB读取数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值) +typedef unsigned long(__stdcall *pCH347WriteData)(unsigned long iIndex, // 指定设备序号 + void *oBuffer, // 指向一个足够大的缓冲区,用于保存描述符 + unsigned long *ioLength); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 +typedef unsigned long(__stdcall *pCH347ReadData)(unsigned long iIndex, // 指定设备序号 + void *oBuffer, // 指向一个足够大的缓冲区,用于保存描述符 + unsigned long *ioLength); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 +typedef unsigned long(__stdcall *pCH347Jtag_INIT)(unsigned long iIndex, // 指定设备序号 + unsigned char iClockRate); // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 +HMODULE uhModule; +BOOL ugOpen; +unsigned long ugIndex; +pCH347OpenDevice CH347OpenDevice; +pCH347CloseDevice CH347CloseDevice; +pCH347SetTimeout CH347SetTimeout; +pCH347ReadData CH347ReadData; +pCH347WriteData CH347WriteData; +pCH347Jtag_INIT CH347Jtag_INIT; +#elif defined(__linux__) +#include +bool ugOpen; +unsigned long ugIndex = 0; +#endif + +int DevIsOpened; // 设备是否打开 +bool UsbHighDev = true; +unsigned long USBC_PACKET; + +_CH347_Info ch347 = {0, 0, 0, 0, ""}; // 初始化设备结构状态 + +/** + * HexToString - Hex转换字符串函数 + * @param buf 指向一个缓冲区,放置准备转换的Hex数据 + * @param size 指向需要转换数据的长度单元 + * + * @return 返回转换后字符串 + */ +static char *HexToString(uint8_t *buf, uint32_t size) +{ + uint32_t i; + char *str = calloc(size * 2 + 1, 1); + + for (i = 0; i < size; i++) + sprintf(str + 2 * i, "%02x ", buf[i]); + return str; +} + +/** + * CH347_Write - CH347 写方法 + * @param oBuffer 指向一个缓冲区,放置准备写出的数据 + * @param ioLength 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度 + * + * @return 写成功返回1,失败返回0 + */ +static int CH347_Write(void *oBuffer, unsigned long *ioLength) +{ + int ret = -1; + unsigned long wlength = *ioLength, WI; + + if (*ioLength >= HW_TDO_BUF_SIZE) + wlength = HW_TDO_BUF_SIZE; + WI = 0; + while (1) { + ret = CH347WriteData(ugIndex, oBuffer + WI, &wlength); + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, wlength, HexToString((uint8_t *)oBuffer, wlength), (uint32_t)wlength); + WI += wlength; + if (WI >= *ioLength) + break; + if ((*ioLength - WI) > HW_TDO_BUF_SIZE) + wlength = HW_TDO_BUF_SIZE; + else + wlength = *ioLength - WI; + } + + *ioLength = WI; + return ret; +} + +/** + * CH347_Read - CH347 读方法 + * @param oBuffer 指向一个足够大的缓冲区,用于保存读取的数据 + * @param ioLength 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度 + * + * @return 读成功返回1,失败返回0 + */ +static int CH347_Read(void *oBuffer, unsigned long *ioLength) +{ + unsigned long rlength = *ioLength; + // 单次读取最大允许读取4096B数据,超过则按4096B进行计算 + if (rlength > HW_TDO_BUF_SIZE) + rlength = HW_TDO_BUF_SIZE; + + if (!CH347ReadData(ugIndex, oBuffer, &rlength)) + { + LOG_ERROR("CH347_Read read data failure."); + return false; + } + + LOG_DEBUG_IO("(size=%lu, buf=[%s]) -> %" PRIu32, rlength, HexToString((uint8_t *)oBuffer, rlength), (uint32_t)rlength); + *ioLength = rlength; + return true; +} + +/** + * CH347_ClockTms - 功能函数,用于在TCK的上升沿改变TMS值,使其Tap状态切换 + * @param BitBangPkt 协议包 + * @param tms 需要改变的TMS值 + * @param BI 协议包长度 + * + * @return 返回协议包长度 + */ +static unsigned long CH347_ClockTms(unsigned char *BitBangPkt, int tms, unsigned long BI) +{ + unsigned char cmd = 0; + + if (tms == 1) + cmd = TMS_H; + else + cmd = TMS_L; + + BitBangPkt[BI++] = cmd | TDI_H | TCK_L; + BitBangPkt[BI++] = cmd | TDI_H | TCK_H; + + ch347.TMS = cmd; + ch347.TDI = TDI_H; + ch347.TCK = TCK_H; + + return BI; +} + +/** + * CH347_IdleClock - 功能函数,确保时钟处于拉低状态 + * @param BitBangPkt 协议包 + * @param BI 协议包长度 + * + * @return 返回协议包长度 + */ +static unsigned long CH347_IdleClock(unsigned char *BitBangPkt, unsigned long BI) +{ + unsigned char byte = 0; + byte |= ch347.TMS ? TMS_H : TMS_L; + byte |= ch347.TDI ? TDI_H : TDI_L; + BitBangPkt[BI++] = byte; + + return BI; +} + +/** + * CH347_TmsChange - 功能函数,通过改变TMS的值来进行状态切换 + * @param tmsValue 需要进行切换的TMS值按切换顺序组成一字节数据 + * @param step 需要读取tmsValue值的位值数 + * @param skip 从tmsValue的skip位处开始计数到step + * + */ +static void CH347_TmsChange(const unsigned char *tmsValue, int step, int skip) +{ + int i; + unsigned long BI, retlen, TxLen; + unsigned char BitBangPkt[4096] = ""; + + BI = CH347_CMD_HEADER; + retlen = CH347_CMD_HEADER; + LOG_DEBUG_IO("(TMS Value: %02x..., step = %d, skip = %d)", tmsValue[0], step, skip); + + for (i = skip; i < step; i++) { + retlen = CH347_ClockTms(BitBangPkt, (tmsValue[i / 8] >> (i % 8)) & 0x01, BI); + BI = retlen; + } + BI = CH347_IdleClock(BitBangPkt, BI); + BitBangPkt[0] = CH347_CMD_JTAG_BIT_OP; + BitBangPkt[1] = (unsigned char)BI - CH347_CMD_HEADER; + BitBangPkt[2] = 0; + + TxLen = BI; + + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("JTAG Write send usb data failure."); + return; + } +} + +/** + * CH347_TMS - 由ch347_execute_queue调用 + * @param cmd 上层传递命令参数 + * + */ +static void CH347_TMS(struct tms_command *cmd) +{ + LOG_DEBUG_IO("(step: %d)", cmd->num_bits); + CH347_TmsChange(cmd->bits, cmd->num_bits, 0); +} + +/** + * CH347_Reset - CH347 复位Tap状态函数 + * @brief 连续六个以上TCK且TMS为高将可将状态机置为Test-Logic Reset状态 + * + */ +static int CH347_Reset(void) +{ + unsigned char BitBang[512] = "", BI, i; + unsigned long TxLen; + + BI = CH347_CMD_HEADER; + for (i = 0; i < 7; i++) { + BitBang[BI++] = TMS_H | TDI_H | TCK_L; + BitBang[BI++] = TMS_H | TDI_H | TCK_H; + } + BitBang[BI++] = TMS_H | TDI_H | TCK_L; + + BitBang[0] = CH347_CMD_JTAG_BIT_OP; + BitBang[1] = BI - CH347_CMD_HEADER; + BitBang[2] = 0; + + TxLen = BI; + + if (!CH347_Write(BitBang, &TxLen) && (TxLen != BI)) { + LOG_ERROR("JTAG_Init send usb data failure."); + return false; + } + return true; +} + +/** + * CH347_MovePath - 获取当前Tap状态并切换至cmd传递下去的状态TMS值 + * @param cmd 上层传递命令参数 + * + */ +static void CH347_MovePath(struct pathmove_command *cmd) +{ + int i; + unsigned long BI, retlen = 0, TxLen; + unsigned char BitBangPkt[4096] = ""; + + BI = CH347_CMD_HEADER; + + LOG_DEBUG_IO("(num_states=%d, last_state=%d)", + cmd->num_states, cmd->path[cmd->num_states - 1]); + + for (i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), false) == cmd->path[i]) + retlen = CH347_ClockTms(BitBangPkt, 0, BI); + BI = retlen; + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + retlen = CH347_ClockTms(BitBangPkt, 1, BI); + BI = retlen; + tap_set_state(cmd->path[i]); + } + + BI = CH347_IdleClock(BitBangPkt, BI); + BitBangPkt[0] = CH347_CMD_JTAG_BIT_OP; + BitBangPkt[1] = (unsigned char)BI - CH347_CMD_HEADER; + BitBangPkt[2] = 0; + + TxLen = BI; + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("JTAG Write send usb data failure."); + return; + } +} + +/** + * CH347_MoveState - 切换Tap状态至目标状态stat + * @param stat 预切换目标路径 + * @param skip 需跳过的位数 + * + */ +static void CH347_MoveState(tap_state_t state, int skip) +{ + uint8_t tms_scan; + int tms_len; + + LOG_DEBUG_IO("(from %s to %s)", tap_state_name(tap_get_state()), + tap_state_name(state)); + if (tap_get_state() == state) + return; + tms_scan = tap_get_tms_path(tap_get_state(), state); + tms_len = tap_get_tms_path_len(tap_get_state(), state); + CH347_TmsChange(&tms_scan, tms_len, skip); + tap_set_state(state); +} + +/** + * CH347_WriteRead - CH347 批量读写函数 + * @param bits 此次进行读写数据 + * @param nb_bits 传入数据长度 + * @param scan 传入数据的传输方式来确定是否执行数据读取 + * + */ +static void CH347_WriteRead(uint8_t *bits, int nb_bits, enum scan_type scan) +{ + // uint32_t delay = 1000000; + int nb8 = nb_bits / 8; + int nb1 = nb_bits % 8; + int i; + bool IsRead = false; + uint8_t TMS_Bit, TDI_Bit = 0; + uint8_t *tdos = calloc(1, nb_bits / 8 + 32); + static uint8_t BitBangPkt[SF_PACKET_BUF_SIZE]; + static uint8_t byte0[SF_PACKET_BUF_SIZE]; + unsigned char temp[512] = ""; + unsigned char temp_a[512] = ""; + unsigned long BI = 0, TxLen, RxLen, DI, DII, PktDataLen, DLen; + int ret = ERROR_OK; + + // 最后一个TDI位将会按照位带模式输出,其nb1确保不为0,使其能在TMS变化时输出最后1bit数据 + if (nb8 > 0 && nb1 == 0) { + nb8--; + nb1 = 8; + } + + IsRead = (scan == SCAN_IN || scan == SCAN_IO); + DI = BI = 0; + while (DI < (unsigned long)nb8) { + // 构建数据包 + if ((nb8 - DI) > UCMDPKT_DATA_MAX_BYTES_USBHS) + PktDataLen = UCMDPKT_DATA_MAX_BYTES_USBHS; + else + PktDataLen = nb8 - DI; + + DII = PktDataLen; + + if (IsRead) + BitBangPkt[BI++] = CH347_CMD_JTAG_DATA_SHIFT_RD; + else + BitBangPkt[BI++] = CH347_CMD_JTAG_DATA_SHIFT; + + BitBangPkt[BI++] = (uint8_t)(PktDataLen >> 0) & 0xFF; + BitBangPkt[BI++] = (uint8_t)(PktDataLen >> 8) & 0xFF; + + if (bits) + memcpy(&BitBangPkt[BI], &bits[DI], PktDataLen); + else + memcpy(&BitBangPkt[BI], byte0, PktDataLen); + BI += PktDataLen; + + // 若需回读数据则判断当前BI值进行命令下发 + if (IsRead) { + TxLen = BI; + + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("CH347_WriteRead write usb data failure."); + return; + } + BI = 0; + + ret = ERROR_OK; + while (ret == ERROR_OK && PktDataLen > 0) { + RxLen = PktDataLen + CH347_CMD_HEADER; + if (!(ret = CH347_Read(temp, &RxLen))) { + LOG_ERROR("CH347_WriteRead read usb data failure.\n"); + return; + } + + if (RxLen != TxLen) { + if (!(ret = CH347_Read(temp_a, &TxLen))) { + LOG_ERROR("CH347_WriteRead read usb data failure.\n"); + return; + } + memcpy(&temp[RxLen], temp_a, TxLen); + RxLen += TxLen; + } + + if (RxLen != 0) + memcpy(&tdos[DI], &temp[CH347_CMD_HEADER], (RxLen - CH347_CMD_HEADER)); + PktDataLen -= RxLen; + } + } + + DI += DII; + + // 在传输过程中,若不回读则根据命令包长度将要达到饱和时将命令下发 + if (((SF_PACKET_BUF_SIZE - BI) < USBC_PACKET || (SF_PACKET_BUF_SIZE - BI) == USBC_PACKET)) { + TxLen = BI; + + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("CH347_WriteRead send usb data failure."); + return; + } + BI = 0; + } + } + + // 清空while循环中剩余的命令 + if (BI > 0) { + TxLen = BI; + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("CH347_WriteRead send usb data failure."); + return; + } + BI = 0; + } + + // 构建输出最后1位TDI数据的命令包 + if (bits) { + BitBangPkt[BI++] = IsRead ? CH347_CMD_JTAG_BIT_OP_RD : CH347_CMD_JTAG_BIT_OP; + DLen = (nb1 * 2) + 1; + BitBangPkt[BI++] = (uint8_t)(DLen >> 0) & 0xFF; + BitBangPkt[BI++] = (uint8_t)(DLen >> 8) & 0xFF; + TMS_Bit = TMS_L; + + for (i = 0; i < nb1; i++) { + if ((bits[nb8] >> i) & 1) + TDI_Bit = TDI_H; + else + TDI_Bit = TDI_L; + + if ((i + 1) == nb1) //最后一位在Exit1-DR状态输出 + TMS_Bit = TMS_H; + BitBangPkt[BI++] = TMS_Bit | TDI_Bit | TCK_L; + BitBangPkt[BI++] = TMS_Bit | TDI_Bit | TCK_H; + } + BitBangPkt[BI++] = TMS_Bit | TDI_Bit | TCK_L; + } + + // 读取Bit-Bang模式下的最后一字节数据 + if (nb1 && IsRead) { + TxLen = BI; + + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("CH347_WriteRead send usb data failure."); + return; + } + BI = 0; + + RxLen = TxLen + CH347_CMD_HEADER; + if (!(ret = CH347_Read(temp, &RxLen))) { + LOG_ERROR("CH347_WriteRead read usb data failure."); + return; + } + + for (i = 0; ret == true && i < nb1; i++) { + if (temp[CH347_CMD_HEADER + i] & 1) + tdos[nb8] |= (1 << i); + else + tdos[nb8] &= ~(1 << i); + } + } + + // 清空此次批量读写函数中未处理命令 + if (BI > 0) { + TxLen = BI; + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("CH347_WriteRead send usb data failure."); + return; + } + BI = 0; + } + + if (bits) { + memcpy(bits, tdos, DIV_ROUND_UP(nb_bits, 8)); + } + + free(tdos); + LOG_DEBUG_IO("bits %d str value: [%s].\n", DIV_ROUND_UP(nb_bits, 8), HexToString(bits, DIV_ROUND_UP(nb_bits, 8))); + + // 将TCK、TDI拉低为低电平,因TDI采样在TCK上升沿,若状态未改变,则TDI采样将可能发生在TCK下降沿 + BI = CH347_CMD_HEADER; + BI = CH347_IdleClock(BitBangPkt, BI); + + BitBangPkt[0] = CH347_CMD_JTAG_BIT_OP; + BitBangPkt[1] = (unsigned char)BI - CH347_CMD_HEADER; + BitBangPkt[2] = 0; + + TxLen = BI; + + if (!CH347_Write(BitBangPkt, &TxLen) && (TxLen != BI)) { + LOG_ERROR("JTAG Write send usb data failure."); + return; + } +} + +static void CH347_RunTest(int cycles, tap_state_t state) +{ + LOG_DEBUG_IO("%s(cycles=%i, end_state=%d)", __func__, cycles, state); + CH347_MoveState(TAP_IDLE, 0); + + CH347_WriteRead(NULL, cycles, SCAN_OUT); + CH347_MoveState(state, 0); +} + +static void CH347_TableClocks(int cycles) +{ + LOG_DEBUG_IO("%s(cycles=%i)", __func__, cycles); + CH347_WriteRead(NULL, cycles, SCAN_OUT); +} + +/** + * CH347_Scan - 切换至SHIFT-DR或者SHIFT-IR状态进行扫描 + * @param cmd 上层传递命令参数 + * + * @return 成功返回ERROR_OK + */ +static int CH347_Scan(struct scan_command *cmd) +{ + int scan_bits; + uint8_t *buf = NULL; + enum scan_type type; + int ret = ERROR_OK; + static const char *const type2str[] = {"", "SCAN_IN", "SCAN_OUT", "SCAN_IO"}; + char *log_buf = NULL; + + type = jtag_scan_type(cmd); + scan_bits = jtag_build_buffer(cmd, &buf); + + if (cmd->ir_scan) + CH347_MoveState(TAP_IRSHIFT, 0); + else + CH347_MoveState(TAP_DRSHIFT, 0); + + log_buf = HexToString(buf, DIV_ROUND_UP(scan_bits, 8)); + LOG_DEBUG_IO("Scan"); + LOG_DEBUG_IO("%s(scan=%s, type=%s, bits=%d, buf=[%s], end_state=%d)", __func__, + cmd->ir_scan ? "IRSCAN" : "DRSCAN", + type2str[type], + scan_bits, log_buf, cmd->end_state); + + free(log_buf); + + CH347_WriteRead(buf, scan_bits, type); + + ret = jtag_read_buffer(buf, cmd); + free(buf); + + CH347_MoveState(cmd->end_state, 1); + + return ret; +} + +static void CH347_Sleep(int us) +{ + LOG_DEBUG_IO("%s(us=%d)", __func__, us); + jtag_sleep(us); +} + +static int ch347_execute_queue(void) +{ + struct jtag_command *cmd; + static int first_call = 1; + int ret = ERROR_OK; + + if (first_call) { + first_call--; + CH347_Reset(); + } + + for (cmd = jtag_command_queue; ret == ERROR_OK && cmd; + cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RESET: + CH347_Reset(); + break; + case JTAG_RUNTEST: + CH347_RunTest(cmd->cmd.runtest->num_cycles, + cmd->cmd.runtest->end_state); + break; + case JTAG_STABLECLOCKS: + CH347_TableClocks(cmd->cmd.stableclocks->num_cycles); + break; + case JTAG_TLR_RESET: + CH347_MoveState(cmd->cmd.statemove->end_state, 0); + break; + case JTAG_PATHMOVE: + CH347_MovePath(cmd->cmd.pathmove); + break; + case JTAG_TMS: + CH347_TMS(cmd->cmd.tms); + break; + case JTAG_SLEEP: + CH347_Sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + ret = CH347_Scan(cmd->cmd.scan); + break; + default: + LOG_ERROR("BUG: unknown JTAG command type 0x%X", + cmd->type); + ret = ERROR_FAIL; + break; + } + } + return ret; +} + +/** + * ch347_init - CH347 初始化函数 + * + * 执行工作: + * 初始化动态库函数 + * 打开设备 + * @return 成功返回0,失败返回ERROR_FAIL + */ +static int ch347_init(void) +{ + unsigned char clearBuffer[4096] = ""; + unsigned long RxLen = 4096; +#ifdef _WIN32 + //printf("%s-%d-WIN32\n", __func__, __LINE__); + if (uhModule == 0) { + uhModule = LoadLibrary("CH347DLL.DLL"); + if (uhModule) { + CH347OpenDevice = (pCH347OpenDevice)GetProcAddress(uhModule, "CH347OpenDevice"); + CH347CloseDevice = (pCH347CloseDevice)GetProcAddress(uhModule, "CH347CloseDevice"); + CH347ReadData = (pCH347ReadData)GetProcAddress(uhModule, "CH347ReadData"); + CH347WriteData = (pCH347WriteData)GetProcAddress(uhModule, "CH347WriteData"); + CH347SetTimeout = (pCH347SetTimeout)GetProcAddress(uhModule, "CH347SetTimeout"); + CH347Jtag_INIT = (pCH347Jtag_INIT)GetProcAddress(uhModule, "CH347Jtag_INIT"); + if (CH347OpenDevice == NULL || CH347CloseDevice == NULL || CH347SetTimeout == NULL || CH347ReadData == NULL || CH347WriteData == NULL || CH347Jtag_INIT == NULL) { + LOG_ERROR("Jtag_init error "); + return ERROR_FAIL; + } + } + } + DevIsOpened = CH347OpenDevice(ugIndex); +#elif defined(__linux__) + //printf("%s-%d-LINUX\n", __func__, __LINE__); + DevIsOpened = CH347OpenDevice(ugIndex); + ugIndex = DevIsOpened; +#endif + if (!DevIsOpened) { + LOG_ERROR("CH347 Open Error."); + return ERROR_FAIL; + } + + USBC_PACKET = USBC_PACKET_USBHS; // 默认为USB2.0高速,其单次传输USB包大小512字节 + + if (!CH347_Read(clearBuffer, &RxLen)) { + LOG_ERROR("CH347 clear Buffer Error."); + return ERROR_FAIL; + } + + // CH347SetTimeout(ugIndex, 5000, 5000); + + tap_set_state(TAP_RESET); + return 0; +} + +/** + * ch347_quit - CH347 设备释放函数 + * + * 执行工作: + * 复位JTAG引脚信号 + * 关闭 + * @return 一直返回0 + */ +static int ch347_quit(void) +{ + // 退出前将信号线全部设置为低电平 + unsigned long retlen = 5; + unsigned char byte[5] = {CH347_CMD_JTAG_BIT_OP, 0x01, 0x00, 0x00, 0x00}; + + CH347_Write(byte, &retlen); + + if (DevIsOpened) { + CH347CloseDevice(ugIndex); + LOG_INFO("Close the CH347."); + DevIsOpened = false; + } + return 0; +} + +/** + * ch347_speed - CH347 TCK频率设置 + * @param speed 设置的频率大小 + * @return 成功返回ERROR_OK,失败返回FALSE + */ +static int ch347_speed(int speed) +{ + unsigned long i = 0; + int retval = -1; + int speed_clock[6] = {MHZ(1.875), MHZ(3.75), MHZ(7.5), MHZ(15), MHZ(30), MHZ(60)}; + + for (i = 0; i < (sizeof(speed_clock) / sizeof(int)); i++) { + if ((speed >= speed_clock[i]) && (speed <= speed_clock[i + 1])) { + retval = CH347Jtag_INIT(ugIndex, i + 1); + if (!retval) { + LOG_ERROR("Couldn't set CH347 TCK speed"); + return retval; + } else { + break; + } + } else if (speed < speed_clock[0]) { + retval = CH347Jtag_INIT(ugIndex, 0); + if (!retval) { + LOG_ERROR("Couldn't set CH347 TCK speed"); + return retval; + } else { + break; + } + } + } + + return ERROR_OK; +} + +static int ch347_speed_div(int speed, int *khz) +{ + *khz = speed / 1000; + return ERROR_OK; +} + +static int ch347_khz(int khz, int *jtag_speed) +{ + if (khz == 0) { + LOG_ERROR("Couldn't support the adapter speed"); + return ERROR_FAIL; + } + *jtag_speed = khz * 1000; + return ERROR_OK; +} + +COMMAND_HANDLER(ch347_handle_vid_pid_command) +{ + // TODO + return ERROR_OK; +} + +static const struct command_registration ch347_subcommand_handlers[] = { + { + .name = "vid_pid", + .handler = ch347_handle_vid_pid_command, + .mode = COMMAND_CONFIG, + .help = "", + .usage = "", + }, + COMMAND_REGISTRATION_DONE}; + +static const struct command_registration ch347_command_handlers[] = { + { + .name = "ch347", + .mode = COMMAND_ANY, + .help = "perform ch347 management", + .chain = ch347_subcommand_handlers, + .usage = "", + }, + COMMAND_REGISTRATION_DONE}; + +static struct jtag_interface ch347_interface = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = ch347_execute_queue, +}; + +struct adapter_driver ch347_adapter_driver = { + .name = "ch347", + .transports = jtag_only, + .commands = ch347_command_handlers, + + .init = ch347_init, + .quit = ch347_quit, + .speed = ch347_speed, + .khz = ch347_khz, + .speed_div = ch347_speed_div, + + .jtag_ops = &ch347_interface, +}; \ No newline at end of file diff --git a/src/jtag/drivers/cmsis_dap.c b/src/jtag/drivers/cmsis_dap.c index e7562d087..eaa65abc6 100644 --- a/src/jtag/drivers/cmsis_dap.c +++ b/src/jtag/drivers/cmsis_dap.c @@ -48,6 +48,7 @@ #include #include "cmsis_dap.h" +#include "libusb_helper.h" static const struct cmsis_dap_backend *const cmsis_dap_backends[] = { #if BUILD_CMSIS_DAP_USB == 1 @@ -79,8 +80,6 @@ static uint16_t cmsis_dap_pid[MAX_USB_IDS + 1] = { 0 }; static int cmsis_dap_backend = -1; static bool swd_mode; -#define USB_TIMEOUT 1000 - /* CMSIS-DAP General Commands */ #define CMD_DAP_INFO 0x00 #define CMD_DAP_LED 0x01 @@ -210,7 +209,7 @@ static bool swd_mode; * None as yet... */ static const char * const info_caps_str[INFO_CAPS__NUM_CAPS] = { - "SWD supported", + "SWD supported", "JTAG supported", "SWO-UART supported", "SWO-MANCHESTER supported", @@ -360,12 +359,12 @@ static int cmsis_dap_xfer(struct cmsis_dap *dap, int txlen) } uint8_t current_cmd = cmsis_dap_handle->command[0]; - int retval = dap->backend->write(dap, txlen, USB_TIMEOUT); + int retval = dap->backend->write(dap, txlen, LIBUSB_TIMEOUT_MS); if (retval < 0) return retval; /* get reply */ - retval = dap->backend->read(dap, USB_TIMEOUT); + retval = dap->backend->read(dap, LIBUSB_TIMEOUT_MS); if (retval < 0) return retval; @@ -826,7 +825,7 @@ static void cmsis_dap_swd_write_from_queue(struct cmsis_dap *dap) } } - int retval = dap->backend->write(dap, idx, USB_TIMEOUT); + int retval = dap->backend->write(dap, idx, LIBUSB_TIMEOUT_MS); if (retval < 0) { queued_retval = retval; goto skip; @@ -854,7 +853,7 @@ static void cmsis_dap_swd_read_process(struct cmsis_dap *dap, int timeout_ms) /* get reply */ int retval = dap->backend->read(dap, timeout_ms); - if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < USB_TIMEOUT) + if (retval == ERROR_TIMEOUT_REACHED && timeout_ms < LIBUSB_TIMEOUT_MS) return; if (retval <= 0) { @@ -929,7 +928,7 @@ static int cmsis_dap_swd_run_queue(void) cmsis_dap_swd_write_from_queue(cmsis_dap_handle); while (pending_fifo_block_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); + cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS); pending_fifo_put_idx = 0; pending_fifo_get_idx = 0; @@ -953,7 +952,7 @@ static void cmsis_dap_swd_queue_cmd(uint8_t cmd, uint32_t *dst, uint32_t data) cmsis_dap_swd_write_from_queue(cmsis_dap_handle); if (pending_fifo_block_count >= cmsis_dap_handle->packet_count) - cmsis_dap_swd_read_process(cmsis_dap_handle, USB_TIMEOUT); + cmsis_dap_swd_read_process(cmsis_dap_handle, LIBUSB_TIMEOUT_MS); } if (queued_retval != ERROR_OK) @@ -1406,18 +1405,18 @@ static void debug_parse_cmsis_buf(const uint8_t *cmd, int cmdlen) for (int i = 0; i < cmdlen; ++i) printf(" %02x", cmd[i]); printf("\n"); - switch (cmd[1]) { + switch (cmd[0]) { case CMD_DAP_JTAG_SEQ: { - printf("cmsis-dap jtag sequence command %02x (n=%d)\n", cmd[1], cmd[2]); + printf("cmsis-dap jtag sequence command %02x (n=%d)\n", cmd[0], cmd[1]); /* - * #2 = number of sequences - * #3 = sequence info 1 - * #4...4+n_bytes-1 = sequence 1 + * #1 = number of sequences + * #2 = sequence info 1 + * #3...4+n_bytes-1 = sequence 1 * #4+n_bytes = sequence info 2 * #5+n_bytes = sequence 2 (single bit) */ - int pos = 3; - for (int seq = 0; seq < cmd[2]; ++seq) { + int pos = 2; + for (int seq = 0; seq < cmd[1]; ++seq) { uint8_t info = cmd[pos++]; int len = info & DAP_JTAG_SEQ_TCK; if (len == 0) diff --git a/src/jtag/drivers/ftdi.c b/src/jtag/drivers/ftdi.c index 2ecff7f86..16cb0274b 100644 --- a/src/jtag/drivers/ftdi.c +++ b/src/jtag/drivers/ftdi.c @@ -85,7 +85,7 @@ /* FTDI access library includes */ #include "mpsse.h" -#if BUILD_FTDI_OSCAN1 == 1 +#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 @@ -105,21 +105,24 @@ static uint8_t ftdi_jtag_mode = JTAG_MODE; static bool swd_mode; -#if BUILD_FTDI_OSCAN1 == 1 +#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 + 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 + 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 + 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 + to the two-wire clocking and signaling of OScan1 protocol, if placed into OScan1 mode during initialization. */ -static void oscan1_reset_online_activate(void); 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, @@ -128,6 +131,11 @@ static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t * 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 @@ -275,7 +283,7 @@ static int ftdi_get_signal(const struct signal *s, uint16_t *value_out) return ERROR_OK; } -#if BUILD_FTDI_OSCAN1 == 1 +#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) { @@ -668,19 +676,20 @@ 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 BUILD_FTDI_OSCAN1 == 1 - oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */ -#endif + 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: - ftdi_execute_statemove(cmd); -#if BUILD_FTDI_OSCAN1 == 1 - oscan1_reset_online_activate(); /* put the target back into OSCAN1 mode */ +#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: ftdi_execute_pathmove(cmd); @@ -759,16 +768,20 @@ 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_OSCAN1 == 1 - } else if (oscan1_mode) { +#if BUILD_FTDI_CJTAG == 1 + } else if (oscan1_mode || jscan3_mode) { struct signal *sig = find_signal_by_name("JTAG_SEL"); if (!sig) { - LOG_ERROR("OSCAN1 mode is active but JTAG_SEL signal is not defined"); + 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 } @@ -801,20 +814,22 @@ static int ftdi_quit(void) return ERROR_OK; } -#if BUILD_FTDI_OSCAN1 == 1 +#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 */ + /* OScan1 uses 3 separate clocks */ /* drive TMSC to the *negation* of the desired TDI value */ bitnum = out_offset + i; @@ -833,8 +848,14 @@ static void oscan1_mpsse_clock_data(struct mpsse_ctx *ctx, const uint8_t *out, u 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 */ } } @@ -845,6 +866,8 @@ static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, 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++) { @@ -852,7 +875,7 @@ static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, uint8_t tmsbit; uint8_t tdibit; - /* OSCAN1 uses 3 separate clocks */ + /* OScan1 uses 3 separate clocks */ /* drive TMSC to the *negation* of the desired TDI value */ tdibit = tdi ? 0 : 1; @@ -871,8 +894,14 @@ static void oscan1_mpsse_clock_tms_cs(struct mpsse_ctx *ctx, const uint8_t *out, 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 */ } } @@ -884,7 +913,7 @@ static void oscan1_mpsse_clock_tms_cs_out(struct mpsse_ctx *ctx, const uint8_t * } -static void oscan1_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct signal *tms, +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); @@ -892,133 +921,139 @@ static void oscan1_set_tck_tms_tdi(struct signal *tck, char tckvalue, struct sig ftdi_set_signal(tck, tckvalue); } -static void oscan1_reset_online_activate(void) +static void cjtag_reset_online_activate(void) { - /* After TAP reset, the OSCAN1-to-JTAG adapter is in offline and - non-activated state. Escape sequences are needed to bring - the TAP online and activated into OSCAN1 mode. */ + /* 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 const struct { - int8_t tck; - int8_t tms; - int8_t tdi; + 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 activation */ - /* 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 (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) + 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"); @@ -1040,14 +1075,28 @@ static void oscan1_reset_online_activate(void) 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++) - oscan1_set_tck_tms_tdi(tck, sequence[i].tck, tms, sequence[i].tms, tdi, sequence[i].tdi); + 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_OSCAN1 == 1 */ +#endif /* #if BUILD_FTDI_CJTAG == 1 */ COMMAND_HANDLER(ftdi_handle_device_desc_command) { @@ -1260,7 +1309,7 @@ COMMAND_HANDLER(ftdi_handle_tdo_sample_edge_command) return ERROR_OK; } -#if BUILD_FTDI_OSCAN1 == 1 +#if BUILD_FTDI_CJTAG == 1 COMMAND_HANDLER(ftdi_handle_oscan1_mode_command) { if (CMD_ARGC > 1) @@ -1272,6 +1321,18 @@ COMMAND_HANDLER(ftdi_handle_oscan1_mode_command) 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[] = { @@ -1335,12 +1396,19 @@ static const struct command_registration ftdi_subcommand_handlers[] = { "allow signalling speed increase)", .usage = "(rising|falling)", }, -#if BUILD_FTDI_OSCAN1 == 1 +#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')", + .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 diff --git a/src/jtag/drivers/jlink.c b/src/jtag/drivers/jlink.c index fdf4ae778..5c218742b 100644 --- a/src/jtag/drivers/jlink.c +++ b/src/jtag/drivers/jlink.c @@ -114,8 +114,6 @@ static int jlink_flush(void); * @param in A pointer to store TDO data to, if NULL the data will be discarded. * @param in_offset A bit offset for TDO data. * @param length Amount of bits to transfer out and in. - * - * @retval This function doesn't return any value. */ static void jlink_clock_data(const uint8_t *out, unsigned out_offset, const uint8_t *tms_out, unsigned tms_offset, diff --git a/src/jtag/drivers/jtag_vpi.c b/src/jtag/drivers/jtag_vpi.c index 0fc688edf..eb53a5b06 100644 --- a/src/jtag/drivers/jtag_vpi.c +++ b/src/jtag/drivers/jtag_vpi.c @@ -38,8 +38,8 @@ #define NO_TAP_SHIFT 0 #define TAP_SHIFT 1 -#define SERVER_ADDRESS "127.0.0.1" -#define SERVER_PORT 5555 +#define DEFAULT_SERVER_ADDRESS "127.0.0.1" +#define DEFAULT_SERVER_PORT 5555 #define XFERT_MAX_SIZE 512 @@ -50,7 +50,7 @@ #define CMD_STOP_SIMU 4 /* jtag_vpi server port and address to connect to */ -static int server_port = SERVER_PORT; +static int server_port = DEFAULT_SERVER_PORT; static char *server_address; /* Send CMD_STOP_SIMU to server when OpenOCD exits? */ @@ -551,7 +551,7 @@ static int jtag_vpi_init(void) serv_addr.sin_port = htons(server_port); if (!server_address) - server_address = strdup(SERVER_ADDRESS); + server_address = strdup(DEFAULT_SERVER_ADDRESS); serv_addr.sin_addr.s_addr = inet_addr(server_address); @@ -604,27 +604,28 @@ static int jtag_vpi_quit(void) COMMAND_HANDLER(jtag_vpi_set_port) { - if (CMD_ARGC == 0) - LOG_WARNING("You need to set a port number"); - else - COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port); + if (CMD_ARGC == 0) { + LOG_ERROR("Command \"jtag_vpi set_port\" expects 1 argument (TCP port number)"); + return ERROR_COMMAND_SYNTAX_ERROR; + } - LOG_INFO("Set server port to %u", server_port); + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], server_port); + LOG_INFO("jtag_vpi: server port set to %u", server_port); return ERROR_OK; } COMMAND_HANDLER(jtag_vpi_set_address) { - free(server_address); if (CMD_ARGC == 0) { - LOG_WARNING("You need to set an address"); - server_address = strdup(SERVER_ADDRESS); - } else - server_address = strdup(CMD_ARGV[0]); + LOG_ERROR("Command \"jtag_vpi set_address\" expects 1 argument (IP address)"); + return ERROR_COMMAND_SYNTAX_ERROR; + } - LOG_INFO("Set server address to %s", server_address); + free(server_address); + server_address = strdup(CMD_ARGV[0]); + LOG_INFO("jtag_vpi: server address set to %s", server_address); return ERROR_OK; } @@ -632,11 +633,11 @@ COMMAND_HANDLER(jtag_vpi_set_address) COMMAND_HANDLER(jtag_vpi_stop_sim_on_exit_handler) { if (CMD_ARGC != 1) { - LOG_ERROR("jtag_vpi_stop_sim_on_exit expects 1 argument (on|off)"); + LOG_ERROR("Command \"jtag_vpi stop_sim_on_exit\" expects 1 argument (on|off)"); return ERROR_COMMAND_SYNTAX_ERROR; - } else { - COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit); } + + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], stop_sim_on_exit); return ERROR_OK; } @@ -645,14 +646,14 @@ static const struct command_registration jtag_vpi_subcommand_handlers[] = { .name = "set_port", .handler = &jtag_vpi_set_port, .mode = COMMAND_CONFIG, - .help = "set the port of the VPI server", + .help = "set the TCP port number of the jtag_vpi server (default: 5555)", .usage = "tcp_port_num", }, { .name = "set_address", .handler = &jtag_vpi_set_address, .mode = COMMAND_CONFIG, - .help = "set the address of the VPI server", + .help = "set the IP address of the jtag_vpi server (default: 127.0.0.1)", .usage = "ipv4_addr", }, { diff --git a/src/jtag/drivers/libusb_helper.h b/src/jtag/drivers/libusb_helper.h index 2ddb246b3..9d51464a7 100644 --- a/src/jtag/drivers/libusb_helper.h +++ b/src/jtag/drivers/libusb_helper.h @@ -22,6 +22,20 @@ #include +/* When we debug a target that works as a USB device, halting the target causes the + * USB communication with the USB host to become unresponsive. The host will try + * to reconnect/reset/setup the unresponsive device during which communication + * with other devices on the same USB bus can get stalled for several seconds. + * If the JTAG adapter is on the same bus, we need to make sure openOCD will wait + * for packets at least as long as the host USB stack. Otherwise the USB stack + * might deliver a valid packet, but openOCD would ignore it due to the timeout. + * The xHCI spec uses 5 sec timeouts, so let's use that in openOCD with some margin. + * + * Use this value in all libusb calls. HID API might have a libusb backend and + * would probably be victim to the same bug, so it should use this timeout, too. + */ +#define LIBUSB_TIMEOUT_MS (6000) + /* this callback should return a non NULL value only when the serial could not * be retrieved by the standard 'libusb_get_string_descriptor_ascii' */ typedef char * (*adapter_get_alternate_serial_fn)(struct libusb_device_handle *device, diff --git a/src/jtag/drivers/linuxgpiod.c b/src/jtag/drivers/linuxgpiod.c index 9f9f27a9f..288035f2e 100644 --- a/src/jtag/drivers/linuxgpiod.c +++ b/src/jtag/drivers/linuxgpiod.c @@ -27,6 +27,7 @@ static int trst_gpio = -1; static int srst_gpio = -1; static int swclk_gpio = -1; static int swdio_gpio = -1; +static int swdio_dir_gpio = -1; static int led_gpio = -1; static int gpiochip = -1; static int tck_gpiochip = -1; @@ -37,6 +38,7 @@ static int trst_gpiochip = -1; static int srst_gpiochip = -1; static int swclk_gpiochip = -1; static int swdio_gpiochip = -1; +static int swdio_dir_gpiochip = -1; static int led_gpiochip = -1; static struct gpiod_chip *gpiod_chip_tck; @@ -47,6 +49,7 @@ static struct gpiod_chip *gpiod_chip_trst; static struct gpiod_chip *gpiod_chip_srst; static struct gpiod_chip *gpiod_chip_swclk; static struct gpiod_chip *gpiod_chip_swdio; +static struct gpiod_chip *gpiod_chip_swdio_dir; static struct gpiod_chip *gpiod_chip_led; static struct gpiod_line *gpiod_tck; @@ -56,6 +59,7 @@ static struct gpiod_line *gpiod_tdo; static struct gpiod_line *gpiod_trst; static struct gpiod_line *gpiod_swclk; static struct gpiod_line *gpiod_swdio; +static struct gpiod_line *gpiod_swdio_dir; static struct gpiod_line *gpiod_srst; static struct gpiod_line *gpiod_led; @@ -63,6 +67,7 @@ static int last_swclk; static int last_swdio; static bool last_stored; static bool swdio_input; +static bool swdio_dir_is_active_high = true; /* Bitbang interface read of TDO */ static bb_value_t linuxgpiod_read(void) @@ -152,6 +157,11 @@ static void linuxgpiod_swdio_drive(bool is_output) gpiod_line_release(gpiod_swdio); if (is_output) { + if (gpiod_swdio_dir) { + retval = gpiod_line_set_value(gpiod_swdio_dir, swdio_dir_is_active_high ? 1 : 0); + if (retval < 0) + LOG_WARNING("Fail set swdio_dir"); + } retval = gpiod_line_request_output(gpiod_swdio, "OpenOCD", 1); if (retval < 0) LOG_WARNING("Fail request_output line swdio"); @@ -159,6 +169,11 @@ static void linuxgpiod_swdio_drive(bool is_output) retval = gpiod_line_request_input(gpiod_swdio, "OpenOCD"); if (retval < 0) LOG_WARNING("Fail request_input line swdio"); + if (gpiod_swdio_dir) { + retval = gpiod_line_set_value(gpiod_swdio_dir, swdio_dir_is_active_high ? 0 : 1); + if (retval < 0) + LOG_WARNING("Fail set swdio_dir"); + } } last_stored = false; @@ -297,6 +312,8 @@ static int linuxgpiod_quit(void) gpiod_chip_close(gpiod_chip_srst); if (gpiod_chip_swdio != NULL) gpiod_chip_close(gpiod_chip_swdio); + if (gpiod_chip_swdio_dir != NULL) + gpiod_chip_close(gpiod_chip_swdio_dir); if (gpiod_chip_swclk != NULL) gpiod_chip_close(gpiod_chip_swclk); if (gpiod_chip_trst != NULL) @@ -451,10 +468,26 @@ static int linuxgpiod_init(void) goto out_error; } + if (is_gpio_valid(swdio_dir_gpio)) { + gpiod_chip_swdio_dir = gpiod_chip_open_by_number(swdio_dir_gpiochip); + if (!gpiod_chip_swdio_dir) { + LOG_ERROR("Cannot open LinuxGPIOD swdio_dir_gpiochip %d", swdio_dir_gpiochip); + goto out_error; + } + } + gpiod_swclk = helper_get_output_line("swclk", gpiod_chip_swclk, swclk_gpio, 1); if (!gpiod_swclk) goto out_error; + /* Set buffer direction before making SWDIO an output */ + if (is_gpio_valid(swdio_dir_gpio)) { + gpiod_swdio_dir = helper_get_output_line("swdio_dir", gpiod_chip_swdio_dir, swdio_dir_gpio, + swdio_dir_is_active_high ? 1 : 0); + if (!gpiod_swdio_dir) + goto out_error; + } + gpiod_swdio = helper_get_output_line("swdio", gpiod_chip_swdio, swdio_gpio, 1); if (!gpiod_swdio) goto out_error; @@ -593,6 +626,12 @@ COMMAND_HANDLER(linuxgpiod_handle_swd_gpionum_swdio) &swdio_gpio); } +COMMAND_HANDLER(linuxgpiod_handle_swd_gpionum_swdio_dir) +{ + return CALL_COMMAND_HANDLER(linuxgpiod_helper_gpionum, "swdio_dir", &swdio_dir_gpiochip, + &swdio_dir_gpio); +} + COMMAND_HANDLER(linuxgpiod_handle_gpionum_led) { return CALL_COMMAND_HANDLER(linuxgpiod_helper_gpionum, "led", &led_gpiochip, @@ -611,6 +650,7 @@ COMMAND_HANDLER(linuxgpiod_handle_gpiochip) srst_gpiochip = gpiochip; swclk_gpiochip = gpiochip; swdio_gpiochip = gpiochip; + swdio_dir_gpiochip = gpiochip; led_gpiochip = gpiochip; } @@ -689,6 +729,13 @@ static const struct command_registration linuxgpiod_subcommand_handlers[] = { .help = "gpio chip number (optional) and gpio number for swdio.", .usage = "[chip] swdio", }, + { + .name = "swdio_dir_num", + .handler = linuxgpiod_handle_swd_gpionum_swdio_dir, + .mode = COMMAND_CONFIG, + .help = "gpio chip number (optional) and gpio number for swdio_dir.", + .usage = "[chip] swdio_dir", + }, { .name = "led_num", .handler = linuxgpiod_handle_gpionum_led, diff --git a/src/jtag/drivers/nulink_usb.c b/src/jtag/drivers/nulink_usb.c index d4b8b53bc..84a4420e8 100644 --- a/src/jtag/drivers/nulink_usb.c +++ b/src/jtag/drivers/nulink_usb.c @@ -33,7 +33,9 @@ #include -#define NULINK_READ_TIMEOUT 1000 +#include "libusb_helper.h" + +#define NULINK_READ_TIMEOUT LIBUSB_TIMEOUT_MS #define NULINK_HID_MAX_SIZE (64) #define NULINK2_HID_MAX_SIZE (1024) diff --git a/src/jtag/drivers/rlink.c b/src/jtag/drivers/rlink.c index 73be3c57e..0cf9dbbb2 100644 --- a/src/jtag/drivers/rlink.c +++ b/src/jtag/drivers/rlink.c @@ -59,8 +59,6 @@ #define USB_EP2IN_SIZE (USB_EP2OUT_SIZE) #define USB_EP2BANK_SIZE (512) -#define USB_TIMEOUT_MS (3 * 1000) - #define DTC_STATUS_POLL_BYTE (ST7_USB_BUF_EP0OUT + 0xff) #define ST7_PD_NBUSY_LED ST7_PD0 @@ -133,7 +131,7 @@ static int ep1_generic_commandl(struct libusb_device_handle *hdev_param, size_t hdev_param, USB_EP1OUT_ADDR, (char *)usb_buffer, sizeof(usb_buffer), - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -176,7 +174,7 @@ static ssize_t ep1_memory_read( usb_ret = jtag_libusb_bulk_write( hdev_param, USB_EP1OUT_ADDR, (char *)usb_buffer, sizeof(usb_buffer), - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -186,7 +184,7 @@ static ssize_t ep1_memory_read( usb_ret = jtag_libusb_bulk_read( hdev_param, USB_EP1IN_ADDR, (char *)buffer, length, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -241,7 +239,7 @@ static ssize_t ep1_memory_write(struct libusb_device_handle *hdev_param, uint16_ usb_ret = jtag_libusb_bulk_write( hdev_param, USB_EP1OUT_ADDR, (char *)usb_buffer, sizeof(usb_buffer), - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -432,7 +430,7 @@ static int dtc_start_download(void) usb_err = jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)&ep2txr, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err != ERROR_OK) @@ -462,7 +460,7 @@ static int dtc_start_download(void) usb_err = jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)&ep2txr, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -488,7 +486,7 @@ static int dtc_run_download( hdev_param, USB_EP2OUT_ADDR, (char *)command_buffer, USB_EP2BANK_SIZE, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err < 0) @@ -512,7 +510,7 @@ static int dtc_run_download( hdev_param, USB_EP1IN_ADDR, &dtc_status, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err < 0) @@ -533,7 +531,7 @@ static int dtc_run_download( hdev_param, USB_EP2IN_ADDR, (char *)reply_buffer, reply_buffer_size, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -954,7 +952,7 @@ static void rlink_reset(int trst, int srst) usb_err = jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)&bitmap, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err != ERROR_OK || transferred < 1) { @@ -990,7 +988,7 @@ static void rlink_reset(int trst, int srst) usb_err = jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)&bitmap, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err != ERROR_OK || transferred < 1) { @@ -1021,7 +1019,7 @@ static void rlink_reset(int trst, int srst) usb_err = jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)&bitmap, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); if (usb_err != ERROR_OK || transferred < 1) { @@ -1576,7 +1574,7 @@ static int rlink_init(void) jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)reply_buffer, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); @@ -1601,7 +1599,7 @@ static int rlink_init(void) jtag_libusb_bulk_read( hdev, USB_EP1IN_ADDR, (char *)reply_buffer, 1, - USB_TIMEOUT_MS, + LIBUSB_TIMEOUT_MS, &transferred ); diff --git a/src/jtag/drivers/stlink_usb.c b/src/jtag/drivers/stlink_usb.c index 2f61bf946..2785d9b96 100644 --- a/src/jtag/drivers/stlink_usb.c +++ b/src/jtag/drivers/stlink_usb.c @@ -71,8 +71,8 @@ #define ENDPOINT_IN 0x80 #define ENDPOINT_OUT 0x00 -#define STLINK_WRITE_TIMEOUT 1000 -#define STLINK_READ_TIMEOUT 1000 +#define STLINK_WRITE_TIMEOUT (LIBUSB_TIMEOUT_MS) +#define STLINK_READ_TIMEOUT (LIBUSB_TIMEOUT_MS) #define STLINK_RX_EP (1|ENDPOINT_IN) #define STLINK_TX_EP (2|ENDPOINT_OUT) @@ -156,6 +156,13 @@ struct stlink_usb_priv_s { struct libusb_transfer *trans; }; +struct stlink_tcp_version { + uint32_t api; + uint32_t major; + uint32_t minor; + uint32_t build; +}; + struct stlink_tcp_priv_s { /** */ int fd; @@ -169,6 +176,8 @@ struct stlink_tcp_priv_s { uint8_t *send_buf; /** */ uint8_t *recv_buf; + /** */ + struct stlink_tcp_version version; }; struct stlink_backend_s { @@ -3532,16 +3541,19 @@ static int stlink_tcp_open(void *handle, struct hl_interface_param_s *param) return ERROR_FAIL; } - uint32_t api_ver = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); - uint32_t ver_major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); - uint32_t ver_minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); - uint32_t ver_build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); + h->tcp_backend_priv.version.api = le_to_h_u32(&h->tcp_backend_priv.recv_buf[0]); + h->tcp_backend_priv.version.major = le_to_h_u32(&h->tcp_backend_priv.recv_buf[4]); + h->tcp_backend_priv.version.minor = le_to_h_u32(&h->tcp_backend_priv.recv_buf[8]); + h->tcp_backend_priv.version.build = le_to_h_u32(&h->tcp_backend_priv.recv_buf[12]); LOG_INFO("stlink-server API v%d, version %d.%d.%d", - api_ver, ver_major, ver_minor, ver_build); + h->tcp_backend_priv.version.api, + h->tcp_backend_priv.version.major, + h->tcp_backend_priv.version.minor, + h->tcp_backend_priv.version.build); /* in stlink-server API v1 sending more than 1428 bytes will cause stlink-server * to crash in windows: select a safe default value (1K) */ - if (api_ver < 2) + if (h->tcp_backend_priv.version.api < 2) h->max_mem_packet = (1 << 10); /* refresh stlink list (re-enumerate) */ @@ -4468,11 +4480,12 @@ static int stlink_usb_count_misc_rw_queue(void *handle, const struct dap_queue * if (!(h->version.flags & STLINK_F_HAS_RW_MISC)) return 0; /* - * RW_MISC sequence doesn't lock the st-link, so are not safe in shared mode. + * Before stlink-server API v3, RW_MISC sequence doesn't lock the st-link, + * so are not safe in shared mode. * Don't use it with TCP backend to prevent any issue in case of sharing. * This further degrades the performance, on top of TCP server overhead. */ - if (h->backend == &stlink_tcp_backend) + if (h->backend == &stlink_tcp_backend && h->tcp_backend_priv.version.api < 3) return 0; for (i = 0; i < len; i++) { diff --git a/src/jtag/drivers/ti_icdi_usb.c b/src/jtag/drivers/ti_icdi_usb.c index c94a1102f..e48d0e269 100644 --- a/src/jtag/drivers/ti_icdi_usb.c +++ b/src/jtag/drivers/ti_icdi_usb.c @@ -37,8 +37,8 @@ #define ICDI_WRITE_ENDPOINT 0x02 #define ICDI_READ_ENDPOINT 0x83 -#define ICDI_WRITE_TIMEOUT 1000 -#define ICDI_READ_TIMEOUT 1000 +#define ICDI_WRITE_TIMEOUT (LIBUSB_TIMEOUT_MS) +#define ICDI_READ_TIMEOUT (LIBUSB_TIMEOUT_MS) #define ICDI_PACKET_SIZE 2048 #define PACKET_START "$" diff --git a/src/jtag/drivers/ulink.c b/src/jtag/drivers/ulink.c index 3ae5cac62..50609a64b 100644 --- a/src/jtag/drivers/ulink.c +++ b/src/jtag/drivers/ulink.c @@ -56,9 +56,6 @@ /** USB interface number */ #define USB_INTERFACE 0 -/** libusb timeout in ms */ -#define USB_TIMEOUT 5000 - /** Delay (in microseconds) to wait while EZ-USB performs ReNumeration. */ #define ULINK_RENUMERATION_DELAY 1500000 @@ -335,7 +332,7 @@ static int ulink_cpu_reset(struct ulink *device, unsigned char reset_bit) ret = libusb_control_transfer(device->usb_device_handle, (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE), - REQUEST_FIRMWARE_LOAD, CPUCS_REG, 0, &reset_bit, 1, USB_TIMEOUT); + REQUEST_FIRMWARE_LOAD, CPUCS_REG, 0, &reset_bit, 1, LIBUSB_TIMEOUT_MS); /* usb_control_msg() returns the number of bytes transferred during the * DATA stage of the control transfer - must be exactly 1 in this case! */ @@ -478,7 +475,7 @@ static int ulink_write_firmware_section(struct ulink *device, ret = libusb_control_transfer(device->usb_device_handle, (LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE), REQUEST_FIRMWARE_LOAD, addr, FIRMWARE_ADDR, (unsigned char *)data_ptr, - chunk_size, USB_TIMEOUT); + chunk_size, LIBUSB_TIMEOUT_MS); if (ret != (int)chunk_size) { /* Abort if libusb sent less data than requested */ @@ -604,8 +601,6 @@ static int ulink_get_queue_size(struct ulink *device, * Clear the OpenULINK command queue. * * @param device pointer to struct ulink identifying ULINK driver instance. - * @return on success: ERROR_OK - * @return on failure: ERROR_FAIL */ static void ulink_clear_queue(struct ulink *device) { @@ -664,7 +659,7 @@ static int ulink_append_queue(struct ulink *device, struct ulink_cmd *ulink_cmd) if ((newsize_out > 64) || (newsize_in > 64)) { /* New command does not fit. Execute all commands in queue before starting * new queue with the current command as first entry. */ - ret = ulink_execute_queued_commands(device, USB_TIMEOUT); + ret = ulink_execute_queued_commands(device, LIBUSB_TIMEOUT_MS); if (ret == ERROR_OK) ret = ulink_post_process_queue(device); @@ -1962,7 +1957,7 @@ static int ulink_execute_queue(void) } if (ulink_handle->commands_in_queue > 0) { - ret = ulink_execute_queued_commands(ulink_handle, USB_TIMEOUT); + ret = ulink_execute_queued_commands(ulink_handle, LIBUSB_TIMEOUT_MS); if (ret != ERROR_OK) return ret; diff --git a/src/jtag/drivers/vdebug.c b/src/jtag/drivers/vdebug.c new file mode 100644 index 000000000..a81740cb1 --- /dev/null +++ b/src/jtag/drivers/vdebug.c @@ -0,0 +1,1076 @@ +/* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */ +/*---------------------------------------------------------------------------- + * Copyright 2020-2021 Cadence Design Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + *---------------------------------------------------------------------------- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *---------------------------------------------------------------------------- +*/ + +/*! + * @file + * + * @brief the virtual debug interface provides a connection between a sw debugger + * and the simulated, emulated core over a soft connection, implemented by DPI + * The vdebug debug driver currently supports JTAG transport + * TODO: implement support and test big endian platforms + * +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#ifdef HAVE_UNISTD_H +#include /* close */ +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#endif +#include +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include + +#include "jtag/interface.h" +#include "jtag/commands.h" +#include "transport/transport.h" +#include "helper/time_support.h" +#include "helper/replacements.h" +#include "helper/log.h" + +#define VD_VERSION 43 +#define VD_BUFFER_LEN 4024 +#define VD_CHEADER_LEN 24 +#define VD_SHEADER_LEN 16 + +#define VD_MAX_MEMORIES 4 +#define VD_POLL_INTERVAL 500 +#define VD_SCALE_PSTOMS 1000000000 + +/** + * @brief List of transactor types + */ +enum { + VD_BFM_JTDP = 0x0001, /* transactor DAP JTAG DP */ + VD_BFM_SWDP = 0x0002, /* transactor DAP SWD DP */ + VD_BFM_AHB = 0x0003, /* transactor AMBA AHB */ + VD_BFM_APB = 0x0004, /* transactor AMBA APB */ + VD_BFM_AXI = 0x0005, /* transactor AMBA AXI */ + VD_BFM_JTAG = 0x0006, /* transactor serial JTAG */ + VD_BFM_SWD = 0x0007, /* transactor serial SWD */ +}; + +/** + * @brief List of signals that can be read or written by the debugger + */ +enum { + VD_SIG_TCK = 0x0001, /* JTAG clock; tclk */ + VD_SIG_TDI = 0x0002, /* JTAG TDI; tdi */ + VD_SIG_TMS = 0x0004, /* JTAG TMS; tms */ + VD_SIG_RESET = 0x0008, /* DUT reset; rst */ + VD_SIG_TRST = 0x0010, /* JTAG Reset; trstn */ + VD_SIG_TDO = 0x0020, /* JTAG TDO; tdo */ + VD_SIG_POWER = 0x0100, /* BFM power; bfm_up */ + VD_SIG_TCKDIV = 0x0200, /* JTAG clock divider; tclkdiv */ + VD_SIG_BUF = 0x1000, /* memory buffer; mem */ +}; + +/** + * @brief List of errors + */ +enum { + VD_ERR_NONE = 0x0000, /* no error */ + VD_ERR_NOT_IMPL = 0x0100, /* feature not implemented */ + VD_ERR_USAGE = 0x0101, /* incorrect usage */ + VD_ERR_PARAM = 0x0102, /* incorrect parameter */ + VD_ERR_CONFIG = 0x0107, /* incorrect configuration */ + VD_ERR_NO_MEMORY = 0x0104, /* out of memory */ + VD_ERR_SHM_OPEN = 0x010a, /* cannot open shared memory */ + VD_ERR_SHM_MAP = 0x010b, /* cannot map shared memory */ + VD_ERR_SOC_OPEN = 0x011a, /* cannot open socket */ + VD_ERR_SOC_OPT = 0x011b, /* cannot set socket option */ + VD_ERR_SOC_ADDR = 0x011c, /* cannot resolve host address */ + VD_ERR_SOC_CONN = 0x011d, /* cannot connect to host */ + VD_ERR_SOC_SEND = 0x011e, /* error sending data on socket */ + VD_ERR_SOC_RECV = 0x011f, /* error receiving data from socket */ + VD_ERR_LOCKED = 0x0202, /* device locked */ + VD_ERR_NOT_RUN = 0x0204, /* transactor not running */ + VD_ERR_NOT_OPEN = 0x0205, /* transactor not open/connected */ + VD_ERR_LICENSE = 0x0206, /* cannot check out the license */ + VD_ERR_VERSION = 0x0207, /* transactor version mismatch */ + VD_ERR_TIME_OUT = 0x0301, /* time out, waiting */ + VD_ERR_NO_POWER = 0x0302, /* power out error */ + VD_ERR_BUS_ERROR = 0x0304, /* bus protocol error, like pslverr */ + VD_ERR_NO_ACCESS = 0x0306, /* no access to an object */ + VD_ERR_INV_HANDLE = 0x0307, /* invalid object handle */ + VD_ERR_INV_SCOPE = 0x0308, /* invalid scope */ +}; + +enum { + VD_CMD_OPEN = 0x01, + VD_CMD_CLOSE = 0x02, + VD_CMD_CONNECT = 0x04, + VD_CMD_DISCONNECT = 0x05, + VD_CMD_WAIT = 0x09, + VD_CMD_SIGSET = 0x0a, + VD_CMD_SIGGET = 0x0b, + VD_CMD_JTAGCLOCK = 0x0f, + VD_CMD_JTAGSHTAP = 0x1a, + VD_CMD_MEMOPEN = 0x21, + VD_CMD_MEMCLOSE = 0x22, + VD_CMD_MEMWRITE = 0x23, +}; + +enum { + VD_BATCH_NO = 0, + VD_BATCH_WO = 1, + VD_BATCH_WR = 2, +}; + +struct vd_shm { + struct { /* VD_CHEADER_LEN written by client */ + uint8_t cmd; /* 000; command */ + uint8_t type; /* 001; interface type */ + uint16_t waddr; /* 002; write pointer */ + uint16_t wbytes; /* 004; data bytes */ + uint16_t rbytes; /* 006; data bytes to read */ + uint16_t wwords; /* 008; data words */ + uint16_t rwords; /* 00a; data words to read */ + uint32_t rwdata; /* 00c; read/write data */ + uint32_t offset; /* 010; address offset */ + uint16_t offseth; /* 014; address offset 47:32 */ + uint16_t wid; /* 016; request id*/ + }; + union { /* 018; */ + uint8_t wd8[VD_BUFFER_LEN]; + uint16_t wd16[VD_BUFFER_LEN / 2]; + uint32_t wd32[VD_BUFFER_LEN / 4]; + uint64_t wd64[VD_BUFFER_LEN / 8]; + }; + struct { /* VD_SHEADER_LEN written by server */ + uint16_t rid; /* fd0: request id read */ + uint16_t awords; /* fd2: actual data words read back */ + int32_t status; /* fd4; */ + uint64_t duttime; /* fd8; */ + }; + union { /* fe0: */ + uint8_t rd8[VD_BUFFER_LEN]; + uint16_t rd16[VD_BUFFER_LEN / 2]; + uint32_t rd32[VD_BUFFER_LEN / 4]; + uint64_t rd64[VD_BUFFER_LEN / 8]; + }; + uint32_t state; /* 1f98; connection state */ + uint32_t count; /* 1f9c; */ + uint8_t dummy[96]; /* 1fa0; 48+40B+8B; */ +}; + +struct vd_client { + uint8_t trans_batch; + bool trans_first; + bool trans_last; + uint8_t mem_ndx; + uint8_t buf_width; + uint8_t addr_bits; + uint8_t bfm_type; + uint16_t sig_read; + uint16_t sig_write; + uint32_t bfm_period; + uint32_t mem_base[VD_MAX_MEMORIES]; + uint32_t mem_size[VD_MAX_MEMORIES]; + uint32_t mem_width[VD_MAX_MEMORIES]; + uint32_t mem_depth[VD_MAX_MEMORIES]; + uint16_t server_port; + uint32_t poll_cycles; + uint32_t poll_min; + uint32_t poll_max; + uint32_t targ_time; + int hsocket; + char server_name[32]; + char bfm_path[128]; + char mem_path[VD_MAX_MEMORIES][128]; + uint8_t *tdo; +}; + +struct vd_jtag_hdr { + uint64_t tlen:24; + uint64_t post:3; + uint64_t pre:3; + uint64_t cmd:2; + uint64_t wlen:16; + uint64_t rlen:16; +}; + +static struct vd_shm *pbuf; +static struct vd_client vdc; + +static int vdebug_socket_error(void) +{ +#ifdef _WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +static int vdebug_socket_open(char *server_addr, uint32_t port) +{ + int hsock; + int rc = 0; + uint32_t buflen = sizeof(struct vd_shm); /* size of the send and rcv buffer */ + struct addrinfo *ainfo = NULL; + struct addrinfo ahint = { 0, AF_INET, SOCK_STREAM, 0, 0, NULL, NULL, NULL }; + +#ifdef _WIN32 + hsock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (hsock == INVALID_SOCKET) + rc = vdebug_socket_error(); +#else + uint32_t rcvwat = VD_SHEADER_LEN; /* size of the rcv header, as rcv min watermark */ + hsock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (hsock < 0) + rc = errno; + else if (setsockopt(hsock, SOL_SOCKET, SO_RCVLOWAT, &rcvwat, sizeof(rcvwat)) < 0) + rc = errno; +#endif + else if (setsockopt(hsock, SOL_SOCKET, SO_SNDBUF, (const char *)&buflen, sizeof(buflen)) < 0) + rc = vdebug_socket_error(); + else if (setsockopt(hsock, SOL_SOCKET, SO_RCVBUF, (const char *)&buflen, sizeof(buflen)) < 0) + rc = vdebug_socket_error(); + + if (rc) { + LOG_ERROR("socket_open: cannot set socket option, error %d", rc); + } else if (getaddrinfo(server_addr, NULL, &ahint, &ainfo) != 0) { + LOG_ERROR("socket_open: cannot resolve address %s, error %d", server_addr, vdebug_socket_error()); + rc = VD_ERR_SOC_ADDR; + } else { + ((struct sockaddr_in *)(ainfo->ai_addr))->sin_port = htons(port); + if (connect(hsock, ainfo->ai_addr, sizeof(struct sockaddr)) < 0) { + LOG_ERROR("socket_open: cannot connect to %s:%d, error %d", server_addr, port, vdebug_socket_error()); + rc = VD_ERR_SOC_CONN; + } + } + + if (rc) { + close_socket(hsock); + hsock = 0; + } + + if (ainfo) + freeaddrinfo(ainfo); + + return hsock; +} + +static int vdebug_socket_receive(int hsock, struct vd_shm *pmem) +{ + int rc; + int dreceived = 0; + int offset = (uint8_t *)&pmem->rid - &pmem->cmd; + int to_receive = VD_SHEADER_LEN + pmem->rbytes; + char *pb = (char *)pmem; + + do { + rc = recv(hsock, pb + offset, to_receive, 0); + if (rc <= 0) { + LOG_WARNING("socket_receive: recv failed, error %d", rc < 0 ? vdebug_socket_error() : 0); + return rc; + } + to_receive -= rc; + offset += rc; + LOG_DEBUG_IO("socket_receive: received %d, to receive %d", rc, to_receive); + dreceived += rc; + } while (to_receive); + + return dreceived; +} + +static int vdebug_socket_send(int hsock, struct vd_shm *pmem) +{ + int rc = send(hsock, (const char *)&pmem->cmd, VD_CHEADER_LEN + pmem->wbytes, 0); + if (rc <= 0) + LOG_WARNING("socket_send: send failed, error %d", vdebug_socket_error()); + else + LOG_DEBUG_IO("socket_send: sent %d, to send 0", rc); + + return rc; +} + +static uint32_t vdebug_wait_server(int hsock, struct vd_shm *pmem) +{ + if (!hsock) + return VD_ERR_SOC_OPEN; + int st = vdebug_socket_send(hsock, pmem); + if (st <= 0) + return VD_ERR_SOC_SEND; + + int rd = vdebug_socket_receive(hsock, pmem); + if (rd <= 0) + return VD_ERR_SOC_RECV; + + int rc = pmem->status; + LOG_DEBUG_IO("wait_server: cmd %02" PRIx8 " done, sent %d, rcvd %d, status %d", + pmem->cmd, st, rd, rc); + + return rc; +} + +int vdebug_run_jtag_queue(int hsock, struct vd_shm *pm, unsigned int count) +{ + uint8_t num_pre, num_post, tdi, tms; + unsigned int num, anum, bytes, hwords, words; + unsigned int req, waddr, rwords; + int64_t ts, te; + uint8_t *tdo; + int rc; + struct vd_jtag_hdr *hdr; + + req = 0; /* beginning of request */ + waddr = 0; + rwords = 0; + pm->wbytes = pm->wwords * vdc.buf_width; + pm->rbytes = pm->rwords * vdc.buf_width; + ts = timeval_ms(); + rc = vdebug_wait_server(hsock, pm); + while (!rc && (req < count)) { /* loop over requests to read data and print out */ + hdr = (struct vd_jtag_hdr *)&pm->wd8[waddr * 4]; + hwords = hdr->wlen; + words = hdr->rlen; + anum = hdr->tlen; + num_pre = hdr->pre; + num_post = hdr->post; + if (num_post) + num = anum - num_pre - num_post + 1; + else + num = anum - num_pre; + bytes = (num + 7) / 8; + vdc.trans_last = (req + 1) < count ? 0 : 1; + vdc.trans_first = waddr ? 0 : 1; + if (hdr->cmd == 3) { /* read */ + tdo = vdc.tdo; + for (unsigned int j = 0; j < bytes; j++) { + tdo[j] = (pm->rd8[rwords * 8 + j] >> num_pre) | (pm->rd8[rwords * 8 + j + 1] << (8 - num_pre)); + LOG_DEBUG_IO("%04x D0[%02x]:%02x", pm->wid - count + req, j, tdo[j]); + } + rwords += words; /* read data offset */ + } else { + tdo = NULL; + } + waddr += sizeof(struct vd_jtag_hdr) / 4; /* waddr past header */ + tdi = (pm->wd8[waddr * 4] >> num_pre) | (pm->wd8[waddr * 4 + 1] << (8 - num_pre)); + tms = (pm->wd8[waddr * 4 + 4] >> num_pre) | (pm->wd8[waddr * 4 + 4 + 1] << (8 - num_pre)); + LOG_DEBUG("%04x L:%02d O:%05x @%03x DI:%02x MS:%02x DO:%02x", + pm->wid - count + req, num, (vdc.trans_first << 14) | (vdc.trans_last << 15), + waddr - 2, tdi, tms, (tdo ? tdo[0] : 0xdd)); + waddr += hwords * 2; /* start of next request */ + req += 1; + } + + if (rc) { + LOG_ERROR("0x%x executing transaction", rc); + rc = ERROR_FAIL; + } + + te = timeval_ms(); + vdc.targ_time += (uint32_t)(te - ts); + pm->offseth = 0; /* reset buffer write address */ + pm->offset = 0; + pm->rwords = 0; + pm->waddr = 0; + + return rc; +} + +static int vdebug_open(int hsock, struct vd_shm *pm, const char *path, + uint8_t type, uint32_t period_ps, uint32_t sig_mask) +{ + int rc = VD_ERR_NOT_OPEN; + + pm->cmd = VD_CMD_OPEN; + pm->wid = VD_VERSION; /* client version */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + rc = vdebug_wait_server(hsock, pm); + if (rc != 0) { /* communication problem */ + LOG_ERROR("0x%x connecting to server", rc); + } else if (pm->rid < pm->wid) { + LOG_ERROR("server version %d too old for the client %d", pm->rid, pm->wid); + pm->cmd = VD_CMD_CLOSE; /* let server close the connection */ + vdebug_wait_server(hsock, pm); + rc = VD_ERR_VERSION; + } else { + pm->cmd = VD_CMD_CONNECT; + pm->type = type; /* BFM type to connect to, here JTAG */ + pm->rwdata = sig_mask | VD_SIG_BUF | (VD_SIG_BUF << 16); + pm->wbytes = strlen(path) + 1; + pm->rbytes = 12; + pm->wid = 0; /* reset wid for transaction ID */ + pm->wwords = 0; + pm->rwords = 0; + memcpy(pm->wd8, path, pm->wbytes + 1); + rc = vdebug_wait_server(hsock, pm); + vdc.sig_read = pm->rwdata >> 16; /* signal read mask */ + vdc.sig_write = pm->rwdata; /* signal write mask */ + vdc.bfm_period = period_ps; + vdc.buf_width = pm->rd32[0] / 8;/* access width in bytes */ + vdc.addr_bits = pm->rd32[2]; /* supported address bits */ + } + + if (rc) { + LOG_ERROR("0x%x connecting to BFM %s", rc, path); + return ERROR_FAIL; + } + + LOG_DEBUG("%s type %0x, period %dps, buffer %dx%dB signals r%04xw%04x", + path, type, vdc.bfm_period, VD_BUFFER_LEN / vdc.buf_width, + vdc.buf_width, vdc.sig_read, vdc.sig_write); + + return ERROR_OK; +} + +static int vdebug_close(int hsock, struct vd_shm *pm, uint8_t type) +{ + pm->cmd = VD_CMD_DISCONNECT; + pm->type = type; /* BFM type, here JTAG */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + pm->cmd = VD_CMD_CLOSE; + pm->wid = VD_VERSION; /* client version */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + LOG_DEBUG("type %0x", type); + + return ERROR_OK; +} + +static int vdebug_wait(int hsock, struct vd_shm *pm, uint32_t cycles) +{ + if (cycles) { + pm->cmd = VD_CMD_WAIT; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = cycles; /* clock sycles to wait */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x waiting %" PRIx32 " cycles", rc, cycles); + return ERROR_FAIL; + } + LOG_DEBUG("%d cycles", cycles); + } + + return ERROR_OK; +} + +static int vdebug_sig_set(int hsock, struct vd_shm *pm, uint32_t write_mask, uint32_t value) +{ + pm->cmd = VD_CMD_SIGSET; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = (write_mask << 16) | (value & 0xffff); /* mask and value of signals to set */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x setting signals %04" PRIx32, rc, write_mask); + return ERROR_FAIL; + } + + LOG_DEBUG("setting signals %04" PRIx32 " to %04" PRIx32, write_mask, value); + + return ERROR_OK; +} + +static int vdebug_jtag_clock(int hsock, struct vd_shm *pm, uint32_t value) +{ + pm->cmd = VD_CMD_JTAGCLOCK; + pm->wbytes = 0; + pm->rbytes = 0; + pm->rwdata = value; /* divider value */ + int rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x setting jtag_clock", rc); + return ERROR_FAIL; + } + + LOG_DEBUG("setting jtag clock divider to %" PRIx32, value); + + return ERROR_OK; +} + +static int vdebug_jtag_shift_tap(int hsock, struct vd_shm *pm, uint8_t num_pre, + const uint8_t tms_pre, uint32_t num, const uint8_t *tdi, + uint8_t num_post, const uint8_t tms_post, uint8_t *tdo, + uint8_t f_last) +{ + const uint32_t tobits = 8; + uint16_t bytes, hwords, anum, words, waddr; + int rc = 0; + + pm->cmd = VD_CMD_JTAGSHTAP; + vdc.trans_last = f_last || (vdc.trans_batch == VD_BATCH_NO) || tdo; + if (vdc.trans_first) + waddr = 0; /* reset buffer offset */ + else + waddr = pm->offseth; /* continue from the previous transaction */ + if (num_post) /* actual number of bits to shift */ + anum = num + num_pre + num_post - 1; + else + anum = num + num_pre; + hwords = (anum + 4 * vdc.buf_width - 1) / (4 * vdc.buf_width); /* in 4B TDI/TMS words */ + words = (hwords + 1) / 2; /* in 8B TDO words to read */ + bytes = (num + 7) / 8; /* data only portion in bytes */ + /* buffer overflow check and flush */ + if (4 * waddr + sizeof(struct vd_jtag_hdr) + 8 * hwords + 64 > VD_BUFFER_LEN) { + vdc.trans_last = 1; /* force flush within 64B of buffer end */ + } else if (4 * waddr + sizeof(struct vd_jtag_hdr) + 8 * hwords > VD_BUFFER_LEN) { + /* this req does not fit, discard it */ + LOG_ERROR("%04x L:%02d O:%05x @%04x too many bits to shift", + pm->wid, anum, (vdc.trans_first << 14) | (vdc.trans_last << 15), waddr); + rc = ERROR_FAIL; + } + + if (!rc && anum) { + uint16_t i, j; + struct vd_jtag_hdr *hdr = (struct vd_jtag_hdr *)&pm->wd8[4 * waddr]; /* 8 bytes header */ + hdr->cmd = (tdo ? 3 : 1); /* R and W bits */ + hdr->pre = num_pre; + hdr->post = num_post; + hdr->tlen = anum; + hdr->wlen = hwords; + hdr->rlen = words; + pm->wid++; /* transaction ID */ + waddr += 2; /* waddr past header */ + /* TDI/TMS data follows as 32 bit word pairs {TMS,TDI} */ + pm->wd8[4 * waddr] = (tdi ? (tdi[0] << num_pre) : 0); + pm->wd8[4 * waddr + 4] = tms_pre; /* init with tms_pre */ + if (num + num_pre <= 8) /* and tms_post for num <=4 */ + pm->wd8[4 * waddr + 4] |= (tms_post << (num + num_pre - 1)); + for (i = 1, j = 4 * waddr; i < bytes; i++) { + if (i == bytes - 1 && num + num_pre <= bytes * tobits) + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8); + else + pm->wd8[j + i + 4] = 0x0;/* placing 4 bytes of TMS bits into high word */ + if (!tdi) /* placing 4 bytes of TDI bits into low word */ + pm->wd8[j + i] = 0x0; + else + pm->wd8[j + i] = (tdi[i] << num_pre) | (tdi[i - 1] >> (8 - num_pre)); + if (i % 4 == 3) + j += 4; + } + + if (tdi) + if (num + num_pre > bytes * tobits) /* in case 1 additional byte needed for TDI */ + pm->wd8[j + i] = (tdi[i - 1] >> (8 - num_pre)); /* put last TDI bits there */ + + if (num + num_pre <= bytes * tobits) { /* in case no or 1 additional byte needed */ + pm->wd8[j + i + 4] = tms_post >> (8 - (num + num_pre - 1) % 8); /* may need to add higher part */ + /* in case exactly 1 additional byte needed */ + } else if (num + num_pre > bytes * tobits && anum <= (bytes + 1) * tobits) { + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8); /* add whole tms_post */ + } else { /* in case 2 additional bytes, tms_post split */ + pm->wd8[j + i + 4] = tms_post << ((num + num_pre - 1) % 8);/* add lower part of tms_post */ + if (i % 4 == 3) /* next byte is in the next 32b word */ + pm->wd8[j + i + 4 + 5] = tms_post >> (8 - (num + num_pre - 1) % 8); /* and higher part */ + else /* next byte is in the same 32b word */ + pm->wd8[j + i + 4 + 1] = tms_post >> (8 - (num + num_pre - 1) % 8); /* and higher part */ + } + + if (tdo) { + pm->rwords += words; /* keep track of the words to read */ + vdc.tdo = tdo; + } + pm->wwords = waddr / 2 + hwords; /* payload size *2 to include both TDI and TMS data */ + pm->waddr++; + } + + if (!waddr) /* flush issued, but buffer empty */ + ; + else if (!vdc.trans_last) /* buffered request */ + pm->offseth = waddr + hwords * 2; /* offset for next transaction, must be even */ + else /* execute batch of requests */ + rc = vdebug_run_jtag_queue(hsock, pm, pm->waddr); + vdc.trans_first = vdc.trans_last; /* flush forces trans_first flag */ + + return rc; +} + +static int vdebug_mem_open(int hsock, struct vd_shm *pm, const char *path, uint8_t ndx) +{ + int rc; + + if (!path) + return ERROR_OK; + + pm->cmd = VD_CMD_MEMOPEN; + pm->wbytes = strlen(path) + 1; /* includes terminating 0 */ + pm->rbytes = 8; + pm->wwords = 0; + pm->rwords = 0; + memcpy(pm->wd8, path, pm->wbytes); + rc = vdebug_wait_server(hsock, pm); + if (rc) { + LOG_ERROR("0x%x opening memory %s", rc, path); + } else if (ndx != pm->rd16[1]) { + LOG_WARNING("Invalid memory index %" PRIu16 " returned. Direct memory access disabled", pm->rd16[1]); + } else { + vdc.mem_width[ndx] = pm->rd16[0] / 8; /* memory width in bytes */ + vdc.mem_depth[ndx] = pm->rd32[1]; /* memory depth in words */ + LOG_DEBUG("%" PRIx8 ": %s memory %" PRIu32 "x%" PRIu32 "B, buffer %" PRIu32 "x%" PRIu32 "B", ndx, path, + vdc.mem_depth[ndx], vdc.mem_width[ndx], VD_BUFFER_LEN / vdc.mem_width[ndx], vdc.mem_width[ndx]); + } + + return ERROR_OK; +} + +static void vdebug_mem_close(int hsock, struct vd_shm *pm, uint8_t ndx) +{ + pm->cmd = VD_CMD_MEMCLOSE; + pm->rwdata = ndx; /* which memory */ + pm->wbytes = 0; + pm->rbytes = 0; + pm->wwords = 0; + pm->rwords = 0; + vdebug_wait_server(hsock, pm); + LOG_DEBUG("%" PRIx8 ": %s", ndx, vdc.mem_path[ndx]); +} + +static int vdebug_init(void) +{ + vdc.hsocket = vdebug_socket_open(vdc.server_name, vdc.server_port); + pbuf = calloc(1, sizeof(struct vd_shm)); + if (!pbuf) { + close_socket(vdc.hsocket); + vdc.hsocket = 0; + LOG_ERROR("cannot allocate %lu bytes", sizeof(struct vd_shm)); + return ERROR_FAIL; + } + if (vdc.hsocket <= 0) { + free(pbuf); + pbuf = NULL; + LOG_ERROR("cannot connect to vdebug server %s:%" PRIu16, + vdc.server_name, vdc.server_port); + return ERROR_FAIL; + } + vdc.trans_first = 1; + vdc.poll_cycles = vdc.poll_max; + uint32_t sig_mask = VD_SIG_RESET | VD_SIG_TRST | VD_SIG_TCKDIV; + int rc = vdebug_open(vdc.hsocket, pbuf, vdc.bfm_path, vdc.bfm_type, vdc.bfm_period, sig_mask); + if (rc != 0) { + LOG_ERROR("cannot connect to %s, rc 0x%x", vdc.bfm_path, rc); + close_socket(vdc.hsocket); + vdc.hsocket = 0; + free(pbuf); + pbuf = NULL; + } else { + for (uint8_t i = 0; i < vdc.mem_ndx; i++) { + rc = vdebug_mem_open(vdc.hsocket, pbuf, vdc.mem_path[i], i); + if (rc != 0) + LOG_ERROR("cannot connect to %s, rc 0x%x", vdc.mem_path[i], rc); + } + + LOG_INFO("vdebug %d connected to %s through %s:%" PRIu16, + VD_VERSION, vdc.bfm_path, vdc.server_name, vdc.server_port); + } + + return rc; +} + +static int vdebug_quit(void) +{ + for (uint8_t i = 0; i < vdc.mem_ndx; i++) + if (vdc.mem_width[i]) + vdebug_mem_close(vdc.hsocket, pbuf, i); + int rc = vdebug_close(vdc.hsocket, pbuf, vdc.bfm_type); + LOG_INFO("vdebug %d disconnected from %s through %s:%" PRIu16 " rc:%d", VD_VERSION, + vdc.bfm_path, vdc.server_name, vdc.server_port, rc); + if (vdc.hsocket) + close_socket(vdc.hsocket); + free(pbuf); + pbuf = NULL; + + return ERROR_OK; +} + +static int vdebug_reset(int trst, int srst) +{ + uint16_t sig_val = 0xffff; + uint16_t sig_mask = 0; + + sig_mask |= VD_SIG_RESET; + if (srst) + sig_val &= ~VD_SIG_RESET;/* active low */ + if (transport_is_jtag()) { + sig_mask |= VD_SIG_TRST; + if (trst) + sig_val &= ~VD_SIG_TRST; /* active low */ + } + + LOG_INFO("rst trst:%d srst:%d mask:%" PRIx16 " val:%" PRIx16, trst, srst, sig_mask, sig_val); + int rc = vdebug_sig_set(vdc.hsocket, pbuf, sig_mask, sig_val); + if (rc == 0) + rc = vdebug_wait(vdc.hsocket, pbuf, 20); /* 20 clock cycles pulse */ + + return rc; +} + +static int vdebug_jtag_tms_seq(const uint8_t *tms, int num, uint8_t f_flush) +{ + LOG_INFO("tms len:%d tms:%x", num, *(const uint32_t *)tms); + + return vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num, *tms, 0, NULL, 0, 0, NULL, f_flush); +} + +static int vdebug_jtag_path_move(struct pathmove_command *cmd, uint8_t f_flush) +{ + uint8_t tms[DIV_ROUND_UP(cmd->num_states, 8)]; + LOG_INFO("path num states %d", cmd->num_states); + + memset(tms, 0, DIV_ROUND_UP(cmd->num_states, 8)); + + for (uint8_t i = 0; i < cmd->num_states; i++) { + if (tap_state_transition(tap_get_state(), true) == cmd->path[i]) + buf_set_u32(tms, i, 1, 1); + tap_set_state(cmd->path[i]); + } + + return vdebug_jtag_tms_seq(tms, cmd->num_states, f_flush); +} + +static int vdebug_jtag_tlr(tap_state_t state, uint8_t f_flush) +{ + int rc = ERROR_OK; + + uint8_t cur = tap_get_state(); + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + LOG_INFO("tlr from %" PRIx8 " to %" PRIx8, cur, state); + if (cur != state) { + rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num_pre, tms_pre, 0, NULL, 0, 0, NULL, f_flush); + tap_set_state(state); + } + + return rc; +} + +static int vdebug_jtag_scan(struct scan_command *cmd, uint8_t f_flush) +{ + int rc = ERROR_OK; + + uint8_t cur = tap_get_state(); + uint8_t state = cmd->ir_scan ? TAP_IRSHIFT : TAP_DRSHIFT; + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + uint8_t tms_post = tap_get_tms_path(state, cmd->end_state); + uint8_t num_post = tap_get_tms_path_len(state, cmd->end_state); + int num_bits = jtag_scan_size(cmd); + LOG_DEBUG("scan len:%d fields:%d ir/!dr:%d state cur:%x end:%x", + num_bits, cmd->num_fields, cmd->ir_scan, cur, cmd->end_state); + for (int i = 0; i < cmd->num_fields; i++) { + uint8_t cur_num_pre = i == 0 ? num_pre : 0; + uint8_t cur_tms_pre = i == 0 ? tms_pre : 0; + uint8_t cur_num_post = i == cmd->num_fields - 1 ? num_post : 0; + uint8_t cur_tms_post = i == cmd->num_fields - 1 ? tms_post : 0; + uint8_t cur_flush = i == cmd->num_fields - 1 ? f_flush : 0; + rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, cur_num_pre, cur_tms_pre, + cmd->fields[i].num_bits, cmd->fields[i].out_value, cur_num_post, cur_tms_post, + cmd->fields[i].in_value, cur_flush); + if (rc) + break; + } + + if (cur != cmd->end_state) + tap_set_state(cmd->end_state); + + return rc; +} + +static int vdebug_jtag_runtest(int cycles, tap_state_t state, uint8_t f_flush) +{ + uint8_t cur = tap_get_state(); + uint8_t tms_pre = tap_get_tms_path(cur, state); + uint8_t num_pre = tap_get_tms_path_len(cur, state); + LOG_DEBUG("idle len:%d state cur:%x end:%x", cycles, cur, state); + int rc = vdebug_jtag_shift_tap(vdc.hsocket, pbuf, num_pre, tms_pre, cycles, NULL, 0, 0, NULL, f_flush); + if (cur != state) + tap_set_state(state); + + return rc; +} + +static int vdebug_jtag_stableclocks(int num, uint8_t f_flush) +{ + LOG_INFO("stab len:%d state cur:%x", num, tap_get_state()); + + return vdebug_jtag_shift_tap(vdc.hsocket, pbuf, 0, 0, num, NULL, 0, 0, NULL, f_flush); +} + +static int vdebug_sleep(int us) +{ + LOG_INFO("sleep %d us", us); + + return vdebug_wait(vdc.hsocket, pbuf, us / 1000); +} + +static int vdebug_jtag_speed(int speed) +{ + unsigned int clkmax = VD_SCALE_PSTOMS / (vdc.bfm_period * 2); /* kHz */ + unsigned int divval = clkmax / speed; + LOG_INFO("jclk speed:%d kHz set, BFM divider %u", speed, divval); + + return vdebug_jtag_clock(vdc.hsocket, pbuf, divval); +} + +static int vdebug_jtag_khz(int khz, int *jtag_speed) +{ + unsigned int clkmax = VD_SCALE_PSTOMS / (vdc.bfm_period * 2); /* kHz */ + unsigned int divval = khz ? clkmax / khz : 1; + *jtag_speed = clkmax / divval; + LOG_DEBUG("khz speed:%d from khz:%d", *jtag_speed, khz); + + return ERROR_OK; +} + +static int vdebug_jtag_div(int speed, int *khz) +{ + *khz = speed; + LOG_DEBUG("div khz:%d from speed:%d", *khz, speed); + + return ERROR_OK; +} + +static int vdebug_jtag_execute_queue(void) +{ + int rc = ERROR_OK; + + for (struct jtag_command *cmd = jtag_command_queue; rc == ERROR_OK && cmd; cmd = cmd->next) { + switch (cmd->type) { + case JTAG_RUNTEST: + rc = vdebug_jtag_runtest(cmd->cmd.runtest->num_cycles, cmd->cmd.runtest->end_state, !cmd->next); + break; + case JTAG_STABLECLOCKS: + rc = vdebug_jtag_stableclocks(cmd->cmd.stableclocks->num_cycles, !cmd->next); + break; + case JTAG_TLR_RESET: + rc = vdebug_jtag_tlr(cmd->cmd.statemove->end_state, !cmd->next); + break; + case JTAG_PATHMOVE: + rc = vdebug_jtag_path_move(cmd->cmd.pathmove, !cmd->next); + break; + case JTAG_TMS: + rc = vdebug_jtag_tms_seq(cmd->cmd.tms->bits, cmd->cmd.tms->num_bits, !cmd->next); + break; + case JTAG_SLEEP: + rc = vdebug_sleep(cmd->cmd.sleep->us); + break; + case JTAG_SCAN: + rc = vdebug_jtag_scan(cmd->cmd.scan, !cmd->next); + break; + default: + LOG_ERROR("Unknown JTAG command type 0x%x encountered", cmd->type); + rc = ERROR_FAIL; + } + } + + return rc; +} + +COMMAND_HANDLER(vdebug_set_server) +{ + if ((CMD_ARGC != 1) || !strchr(CMD_ARGV[0], ':')) + return ERROR_COMMAND_SYNTAX_ERROR; + + char *pchar = strchr(CMD_ARGV[0], ':'); + *pchar = '\0'; + strncpy(vdc.server_name, CMD_ARGV[0], sizeof(vdc.server_name) - 1); + int port = atoi(++pchar); + if (port < 0 || port > UINT16_MAX) { + LOG_ERROR("invalid port number %d specified", port); + return ERROR_COMMAND_SYNTAX_ERROR; + } + vdc.server_port = port; + LOG_DEBUG("server: %s port %u", vdc.server_name, vdc.server_port); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_bfm) +{ + char prefix; + + if ((CMD_ARGC != 2) || (sscanf(CMD_ARGV[1], "%u%c", &vdc.bfm_period, &prefix) != 2)) + return ERROR_COMMAND_SYNTAX_ERROR; + + strncpy(vdc.bfm_path, CMD_ARGV[0], sizeof(vdc.bfm_path) - 1); + switch (prefix) { + case 'u': + vdc.bfm_period *= 1000000; + break; + case 'n': + vdc.bfm_period *= 1000; + break; + case 'p': + default: + break; + } + vdc.bfm_type = VD_BFM_JTAG; + LOG_DEBUG("bfm_path: %s clk_period %ups", vdc.bfm_path, vdc.bfm_period); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_mem) +{ + if (CMD_ARGC != 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (vdc.mem_ndx >= VD_MAX_MEMORIES) { + LOG_ERROR("mem_path declared more than %d allowed times", VD_MAX_MEMORIES); + return ERROR_FAIL; + } + + strncpy(vdc.mem_path[vdc.mem_ndx], CMD_ARGV[0], sizeof(vdc.mem_path[vdc.mem_ndx]) - 1); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], vdc.mem_base[vdc.mem_ndx]); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[2], vdc.mem_size[vdc.mem_ndx]); + LOG_DEBUG("mem_path: set %s @ 0x%08x+0x%08x", vdc.mem_path[vdc.mem_ndx], + vdc.mem_base[vdc.mem_ndx], vdc.mem_size[vdc.mem_ndx]); + vdc.mem_ndx++; + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_batching) +{ + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (isdigit((unsigned char)CMD_ARGV[0][0])) + vdc.trans_batch = (CMD_ARGV[0][0] == '0' ? 0 : (CMD_ARGV[0][0] == '1' ? 1 : 2)); + else if (CMD_ARGV[0][0] == 'r') + vdc.trans_batch = VD_BATCH_WR; + else if (CMD_ARGV[0][0] == 'w') + vdc.trans_batch = VD_BATCH_WO; + else + vdc.trans_batch = VD_BATCH_NO; + LOG_DEBUG("batching: set to %u", vdc.trans_batch); + + return ERROR_OK; +} + +COMMAND_HANDLER(vdebug_set_polling) +{ + if (CMD_ARGC != 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + vdc.poll_min = atoi(CMD_ARGV[0]); + vdc.poll_max = atoi(CMD_ARGV[1]); + LOG_DEBUG("polling: set min %u max %u", vdc.poll_min, vdc.poll_max); + + return ERROR_OK; +} + +static const struct command_registration vdebug_command_handlers[] = { + { + .name = "server", + .handler = &vdebug_set_server, + .mode = COMMAND_CONFIG, + .help = "set the vdebug server name or address", + .usage = "", + }, + { + .name = "bfm_path", + .handler = &vdebug_set_bfm, + .mode = COMMAND_CONFIG, + .help = "set the vdebug BFM hierarchical path", + .usage = " ", + }, + { + .name = "mem_path", + .handler = &vdebug_set_mem, + .mode = COMMAND_ANY, + .help = "set the design memory for the code load", + .usage = " ", + }, + { + .name = "batching", + .handler = &vdebug_set_batching, + .mode = COMMAND_CONFIG, + .help = "set the transaction batching no|wr|rd [0|1|2]", + .usage = "", + }, + { + .name = "polling", + .handler = &vdebug_set_polling, + .mode = COMMAND_CONFIG, + .help = "set the polling pause, executing hardware cycles between min and max", + .usage = " ", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration vdebug_command[] = { + { + .name = "vdebug", + .chain = vdebug_command_handlers, + .mode = COMMAND_ANY, + .help = "vdebug command group", + .usage = "", + }, + COMMAND_REGISTRATION_DONE +}; + +static struct jtag_interface vdebug_jtag_ops = { + .supported = DEBUG_CAP_TMS_SEQ, + .execute_queue = vdebug_jtag_execute_queue, +}; + +struct adapter_driver vdebug_adapter_driver = { + .name = "vdebug", + .transports = jtag_only, + .speed = vdebug_jtag_speed, + .khz = vdebug_jtag_khz, + .speed_div = vdebug_jtag_div, + .commands = vdebug_command, + .init = vdebug_init, + .quit = vdebug_quit, + .reset = vdebug_reset, + .jtag_ops = &vdebug_jtag_ops, +}; diff --git a/src/jtag/drivers/wlinke.c b/src/jtag/drivers/wlinke.c new file mode 100755 index 000000000..fdbe6ad6d --- /dev/null +++ b/src/jtag/drivers/wlinke.c @@ -0,0 +1,2043 @@ +/* + * WCH_LINLK port debug driver for WCH RISC-V mcu : + CH32V103X;CH32V20X,CH32V30X;CH56X;CH57X;CH58X + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#ifdef HAVE_ARPA_INET_H +#include +#endif +#include "cmsis_dap.h" +#ifdef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#include "hidapi.h" +typedef int(__stdcall *pCH375OpenDevice)(unsigned long iIndex); +typedef void(__stdcall *pCH375CloseDevice)(unsigned long iIndex); +typedef unsigned long(__stdcall *pCH375SetTimeout)(unsigned long iIndex, + unsigned long iWriteTimeout, + unsigned long iReadTimeout); +typedef unsigned long(__stdcall *pCH375ReadEndP)(unsigned long iIndex, + unsigned long iPipeNum, + void *iBuffer, + unsigned long *ioLength); +typedef unsigned long(__stdcall *pCH375WriteEndP)(unsigned long iIndex, + unsigned long iPipeNum, + void *iBuffer, + unsigned long *ioLength); +HMODULE hModule = 0; +BOOL gOpen = FALSE; +ULONG gIndex = 0; +pCH375OpenDevice pOpenDev; +pCH375CloseDevice pCloseDev; +pCH375SetTimeout pSetTimeout; +pCH375ReadEndP pReadData; +pCH375WriteEndP pWriteData; +#else +#include "libusb_helper.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +int gIndex = 0; +static const uint16_t wlink_vids[] = {0x1a86, 0}; +static const uint16_t wlink_pids[] = {0x8010, 0}; +struct jtag_libusb_device_handle *wfd = NULL; +int pWriteData(int dev, int endpoint, unsigned char *buf, unsigned long *length) +{ + int ret, pr; + length = (int *)length; + ret = jtag_libusb_bulk_write(wfd, endpoint, buf, *length, 3000, &pr); + if (ret == ERROR_OK) + return 1; + else + return ret; +} +int pReadData(int dev, int endpoint, unsigned char *buf, unsigned long *length) +{ + int ret, pr; + length = (int *)length; + if (endpoint == 1) + { + ret = jtag_libusb_bulk_read(wfd, 0x81, buf, *length, 3000, &pr); + } + else + { + ret = jtag_libusb_bulk_read(wfd, 0x82, buf, *length, 3000, &pr); + } + if (ret == ERROR_OK) + return 1; + else + return ret; +} +#endif +unsigned long wlink_address = 0; +bool wlink549 = false; +bool wlinkw = false; +bool wlinke = false; +bool pageerase = false; +bool powstat =false ; +int iocontrol =0; +unsigned long poutput = -1; +// bool protectcheck=false; +unsigned char riscvchip; +unsigned int chip_type; +unsigned long chipiaddr; +unsigned long pagesize; +unsigned long ramaddr; +bool noloadflag = false; +extern int writeloop; +uint8_t sp=1; +uint8_t flash_op103[ ] ={ + 0x01, 0x11, 0x02, 0xCE, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, 0x67, 0x45, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, 0x13, 0x07, 0xB7, 0x9A, + 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, 0xB7, 0x27, 0x02, 0x40, + 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x38, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, 0x98, 0xCB, 0x98, 0x4B, + + 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x63, 0x1F, + 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, 0xA9, 0xCB, 0x93, 0x07, + 0xF6, 0x07, 0x9D, 0x83, 0x2E, 0xC6, 0x2D, 0x68, 0x81, 0x76, 0x3E, 0xCA, 0xB7, 0x08, 0x02, 0x00, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x33, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + + 0x33, 0x67, 0x17, 0x01, 0x98, 0xCB, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, 0x07, 0x04, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x71, 0xEF, 0x98, 0x4B, 0x75, 0x8F, 0x98, 0xCB, 0x32, 0x47, + 0x13, 0x07, 0x07, 0x08, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x69, 0xFB, 0x93, 0x77, + 0x85, 0x00, 0xED, 0xC3, 0x93, 0x07, 0xF6, 0x07, 0x2E, 0xC6, 0x9D, 0x83, 0x37, 0x27, 0x02, 0x40, + + 0x3E, 0xCA, 0x1C, 0x4B, 0xC1, 0x66, 0x37, 0x08, 0x08, 0x00, 0xD5, 0x8F, 0x1C, 0xCB, 0xA1, 0x48, + 0x37, 0x17, 0x00, 0x20, 0xB7, 0x27, 0x02, 0x40, 0x37, 0x03, 0x04, 0x00, 0x94, 0x4B, 0xB3, 0xE6, + 0x06, 0x01, 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0xF5, 0xFE, 0xB2, 0x46, 0x3A, 0x8E, 0x36, 0xC8, + 0x46, 0xCC, 0xC2, 0x46, 0x83, 0x2E, 0x07, 0x00, 0x41, 0x07, 0x23, 0xA0, 0xD6, 0x01, 0xC2, 0x46, + + 0x83, 0x2E, 0x47, 0xFF, 0x23, 0xA2, 0xD6, 0x01, 0xC2, 0x46, 0x83, 0x2E, 0x87, 0xFF, 0x23, 0xA4, + 0xD6, 0x01, 0xC2, 0x46, 0x03, 0x2E, 0xCE, 0x00, 0x23, 0xA6, 0xC6, 0x01, 0x94, 0x4B, 0xB3, 0xE6, + 0x66, 0x00, 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0xF5, 0xFE, 0xC2, 0x46, 0x3A, 0x8E, 0xC1, 0x06, + 0x36, 0xC8, 0xE2, 0x46, 0xFD, 0x16, 0x36, 0xCC, 0xCD, 0xFE, 0xB2, 0x46, 0xD4, 0xCB, 0x94, 0x4B, + + 0x93, 0xE6, 0x06, 0x04, 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0xF5, 0xFE, 0xD4, 0x47, 0xD1, 0x8A, + 0x85, 0xC6, 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x47, 0x01, 0xD8, 0xC7, + 0x98, 0x4B, 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x05, 0x61, 0x02, 0x90, 0x23, 0x20, 0xD8, 0x00, + 0xE9, 0xBD, 0x23, 0x20, 0x03, 0x01, 0x31, 0xBF, 0xB2, 0x46, 0x93, 0x86, 0x06, 0x08, 0x36, 0xC6, + + 0xD2, 0x46, 0xFD, 0x16, 0x36, 0xCA, 0xB9, 0xFA, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, + 0x75, 0x8F, 0x98, 0xCB, 0x41, 0x89, 0x19, 0xE1, 0x01, 0x45, 0xF9, 0xB7, 0x2E, 0xC6, 0x0D, 0x06, + 0x02, 0xCA, 0x09, 0x82, 0x32, 0xCC, 0xB7, 0x17, 0x00, 0x20, 0x98, 0x43, 0x13, 0x86, 0x47, 0x00, + 0xD2, 0x47, 0xB2, 0x46, 0x8A, 0x07, 0xB6, 0x97, 0x9C, 0x43, 0x63, 0x18, 0xF7, 0x02, 0xD2, 0x47, + + 0x32, 0x47, 0x8A, 0x07, 0xBA, 0x97, 0x98, 0x43, 0xF2, 0x47, 0xBA, 0x97, 0x3E, 0xCE, 0xD2, 0x47, + 0x85, 0x07, 0x3E, 0xCA, 0xD2, 0x46, 0x62, 0x47, 0xB2, 0x87, 0xE3, 0xE8, 0xE6, 0xFC, 0xB7, 0x27, + 0x00, 0x20, 0x98, 0x4B, 0xF2, 0x47, 0xE3, 0x09, 0xF7, 0xFA, 0x41, 0x45, 0xB5, 0xBF, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +uint8_t flash_op569[ ] ={ + 0x79, 0x71, 0x22, 0xD4, 0x4A, 0xD0, 0x56, 0xCA, 0x06, 0xD6, 0x26, 0xD2, 0x4E, 0xCE, 0x52, 0xCC, + 0x5A, 0xC8, 0x5E, 0xC6, 0x62, 0xC4, 0x93, 0x77, 0x15, 0x00, 0x2A, 0x84, 0xAE, 0x8A, 0x32, 0x89, + 0xBD, 0xE7, 0x93, 0x77, 0x24, 0x00, 0x99, 0xC7, 0xB7, 0x85, 0x07, 0x00, 0x01, 0x45, 0xA9, 0x2C, + 0x89, 0x47, 0x35, 0xE1, 0x93, 0x77, 0x44, 0x00, 0x91, 0xC7, 0x85, 0x65, 0x56, 0x85, 0xA9, 0x24, + + 0x91, 0x47, 0x31, 0xE9, 0x93, 0x77, 0x84, 0x01, 0x01, 0x4A, 0xD9, 0xC7, 0xB7, 0x14, 0x00, 0x20, + 0x13, 0x09, 0xF9, 0x0F, 0x93, 0x84, 0x04, 0x10, 0x13, 0x59, 0x89, 0x00, 0x01, 0x4A, 0xB3, 0x8A, + 0x9A, 0x40, 0x93, 0x7B, 0x84, 0x00, 0x13, 0x7C, 0x04, 0x01, 0x33, 0x8B, 0x9A, 0x00, 0x63, 0x91, + 0x0B, 0x04, 0x63, 0x0E, 0x0C, 0x04, 0x93, 0x89, 0x04, 0xF0, 0x13, 0x06, 0x00, 0x10, 0xCE, 0x85, + + 0x5A, 0x85, 0x2D, 0x26, 0x13, 0x75, 0xF5, 0x0F, 0x0D, 0xCD, 0xC1, 0x47, 0x29, 0xA0, 0x01, 0x45, + 0x69, 0x2E, 0x85, 0x47, 0x59, 0xD5, 0xB2, 0x50, 0x22, 0x54, 0x92, 0x54, 0x02, 0x59, 0xF2, 0x49, + 0x62, 0x4A, 0xD2, 0x4A, 0x42, 0x4B, 0xB2, 0x4B, 0x22, 0x4C, 0x3E, 0x85, 0x45, 0x61, 0x02, 0x90, + 0x13, 0x06, 0x00, 0x10, 0x93, 0x85, 0x04, 0xF0, 0x5A, 0x85, 0xCD, 0x22, 0x5D, 0xD9, 0xA1, 0x47, + + 0xD9, 0xBF, 0x83, 0xA7, 0x09, 0x00, 0x91, 0x09, 0x3E, 0x9A, 0xE3, 0x9C, 0x34, 0xFF, 0x7D, 0x19, + 0x93, 0x84, 0x04, 0x10, 0xE3, 0x1B, 0x09, 0xF8, 0x41, 0x88, 0x81, 0x47, 0x4D, 0xDC, 0x37, 0x67, + 0x00, 0x20, 0x18, 0x4B, 0xE3, 0x09, 0x47, 0xFB, 0x4D, 0xB7, 0xB7, 0x17, 0x00, 0x40, 0x23, 0x8D, + 0x07, 0x00, 0x1D, 0x47, 0x23, 0x8D, 0xE7, 0x00, 0x23, 0x8C, 0xA7, 0x00, 0x82, 0x80, 0x37, 0x17, + + 0x00, 0x40, 0x83, 0x47, 0xA7, 0x01, 0xE2, 0x07, 0xE1, 0x87, 0xE3, 0xCC, 0x07, 0xFE, 0x23, 0x0D, + 0x07, 0x00, 0x82, 0x80, 0x37, 0x17, 0x00, 0x40, 0x83, 0x47, 0xA7, 0x01, 0xE2, 0x07, 0xE1, 0x87, + 0xE3, 0xCC, 0x07, 0xFE, 0x23, 0x0C, 0xA7, 0x00, 0x82, 0x80, 0x37, 0x17, 0x00, 0x40, 0x83, 0x47, + 0xA7, 0x01, 0xE2, 0x07, 0xE1, 0x87, 0xE3, 0xCC, 0x07, 0xFE, 0x03, 0x45, 0x87, 0x01, 0x82, 0x80, + + 0x41, 0x11, 0x22, 0xC4, 0x2A, 0x84, 0x41, 0x81, 0x13, 0x75, 0xF5, 0x0F, 0x06, 0xC6, 0xD9, 0x37, + 0x13, 0x55, 0x84, 0x00, 0x13, 0x75, 0xF5, 0x0F, 0x75, 0x3F, 0x13, 0x75, 0xF4, 0x0F, 0x22, 0x44, + 0xB2, 0x40, 0x41, 0x01, 0x45, 0xBF, 0x41, 0x11, 0x22, 0xC4, 0x2A, 0x84, 0x19, 0x45, 0x06, 0xC6, + 0xAD, 0x3F, 0x71, 0x37, 0x22, 0x85, 0x22, 0x44, 0xB2, 0x40, 0x41, 0x01, 0xBD, 0xB7, 0x01, 0x11, + + 0x22, 0xCC, 0x06, 0xCE, 0x37, 0x04, 0x28, 0x00, 0x9D, 0x3F, 0x15, 0x45, 0xB9, 0x3F, 0x71, 0x3F, + 0x69, 0x3F, 0x2A, 0xC6, 0xAD, 0x37, 0x32, 0x45, 0x05, 0x89, 0x09, 0xC9, 0x7D, 0x14, 0x75, 0xF4, + 0x13, 0x05, 0xF0, 0x0F, 0xF2, 0x40, 0x62, 0x44, 0x05, 0x61, 0x82, 0x80, 0x01, 0x45, 0xDD, 0xBF, + 0x85, 0x67, 0xFD, 0x17, 0x01, 0x11, 0xBE, 0x95, 0xE9, 0x8F, 0x26, 0xCA, 0xAE, 0x97, 0xFD, 0x74, + + 0x22, 0xCC, 0x06, 0xCE, 0x33, 0xF4, 0x97, 0x00, 0x4A, 0xC8, 0x4E, 0xC6, 0xE9, 0x8C, 0xB7, 0x06, + 0x08, 0x00, 0x63, 0xF8, 0xD4, 0x0A, 0xB3, 0x07, 0x94, 0x00, 0x2A, 0x87, 0x13, 0x05, 0xA0, 0x0F, + 0x63, 0xE3, 0xF6, 0x06, 0xB7, 0x17, 0x00, 0x40, 0x93, 0x06, 0x70, 0x05, 0x23, 0x80, 0xD7, 0x00, + 0x93, 0x06, 0x80, 0xFA, 0x23, 0x80, 0xD7, 0x00, 0x83, 0xC6, 0x47, 0x00, 0x3D, 0x65, 0x69, 0x8F, + + 0x93, 0xE6, 0xC6, 0x08, 0x23, 0x82, 0xD7, 0x00, 0x11, 0xE7, 0xB7, 0x09, 0x07, 0x00, 0x41, 0x69, + 0x63, 0xE2, 0x34, 0x05, 0xA2, 0x94, 0x05, 0x69, 0xB3, 0x89, 0x84, 0x40, 0x63, 0x79, 0x24, 0x05, + 0x01, 0x45, 0x37, 0x17, 0x00, 0x40, 0x93, 0x07, 0x70, 0x05, 0x23, 0x00, 0xF7, 0x00, 0x93, 0x07, + 0x80, 0xFA, 0x23, 0x00, 0xF7, 0x00, 0x83, 0x47, 0x47, 0x00, 0x93, 0xF7, 0x37, 0x07, 0x93, 0xE7, + + 0x07, 0x08, 0x23, 0x02, 0xF7, 0x00, 0xF2, 0x40, 0x62, 0x44, 0xD2, 0x44, 0x42, 0x49, 0xB2, 0x49, + 0x05, 0x61, 0x82, 0x80, 0xE3, 0x60, 0x24, 0xFD, 0x13, 0x05, 0x80, 0x0D, 0x29, 0x37, 0x26, 0x85, + 0xC5, 0x35, 0x31, 0x3F, 0x5D, 0xFD, 0xCA, 0x94, 0x33, 0x04, 0x24, 0x41, 0x55, 0xB7, 0x13, 0x05, + 0x00, 0x02, 0xD5, 0x3D, 0x4E, 0x85, 0xE9, 0x35, 0x19, 0x37, 0x45, 0xF5, 0x33, 0x04, 0x24, 0x41, + + 0x61, 0xBF, 0x13, 0x05, 0xE0, 0x0F, 0xC1, 0xB7, 0xB7, 0x17, 0x00, 0x40, 0x83, 0xC7, 0x47, 0x00, + 0xC1, 0x8B, 0x91, 0xE3, 0x31, 0xBF, 0x13, 0x05, 0xB0, 0x0F, 0x82, 0x80, 0x01, 0x11, 0x22, 0xCC, + 0x37, 0x04, 0x00, 0x10, 0x71, 0x14, 0x69, 0x8C, 0x21, 0x65, 0x2A, 0x94, 0x06, 0xCE, 0x26, 0xCA, + 0x4A, 0xC8, 0x4E, 0xC6, 0x37, 0x07, 0x08, 0x00, 0x13, 0x05, 0xE0, 0x0F, 0x63, 0x71, 0xE4, 0x0E, + + 0xB3, 0x07, 0xC4, 0x00, 0x13, 0x05, 0xA0, 0x0F, 0x63, 0x6B, 0xF7, 0x0C, 0xB7, 0x17, 0x00, 0x40, + 0x03, 0xC7, 0x47, 0x00, 0x13, 0x05, 0xB0, 0x0F, 0x41, 0x8B, 0x71, 0xE3, 0x13, 0x07, 0x70, 0x05, + 0x23, 0x80, 0xE7, 0x00, 0x13, 0x07, 0x80, 0xFA, 0x23, 0x80, 0xE7, 0x00, 0x03, 0xC7, 0x47, 0x00, + 0x2E, 0x89, 0x93, 0x59, 0x26, 0x00, 0x13, 0x67, 0xC7, 0x08, 0x23, 0x82, 0xE7, 0x00, 0xB7, 0x14, + + 0x00, 0x40, 0x63, 0x94, 0x09, 0x00, 0x01, 0x45, 0x8D, 0xA8, 0x09, 0x45, 0xA9, 0x3D, 0x22, 0x85, + 0x05, 0x3D, 0x33, 0x05, 0x24, 0x41, 0x11, 0x09, 0x83, 0x27, 0xC9, 0xFF, 0xDC, 0xC8, 0x83, 0xC7, + 0xA4, 0x01, 0x93, 0xE7, 0x07, 0x01, 0x03, 0xC7, 0xA4, 0x01, 0x62, 0x07, 0x61, 0x87, 0xE3, 0x4C, + 0x07, 0xFE, 0x23, 0x8D, 0xF4, 0x00, 0x03, 0xC7, 0xA4, 0x01, 0x62, 0x07, 0x61, 0x87, 0xE3, 0x4C, + + 0x07, 0xFE, 0x23, 0x8D, 0xF4, 0x00, 0x03, 0xC7, 0xA4, 0x01, 0x62, 0x07, 0x61, 0x87, 0xE3, 0x4C, + 0x07, 0xFE, 0x23, 0x8D, 0xF4, 0x00, 0x03, 0xC7, 0xA4, 0x01, 0x62, 0x07, 0x61, 0x87, 0xE3, 0x4C, + 0x07, 0xFE, 0x23, 0x8D, 0xF4, 0x00, 0xFD, 0x19, 0x33, 0x04, 0xA9, 0x00, 0x63, 0x85, 0x09, 0x00, + 0x93, 0x77, 0xF4, 0x0F, 0xCD, 0xF3, 0x21, 0x35, 0x49, 0xD5, 0x37, 0x17, 0x00, 0x40, 0x93, 0x07, + + 0x70, 0x05, 0x23, 0x00, 0xF7, 0x00, 0x93, 0x07, 0x80, 0xFA, 0x23, 0x00, 0xF7, 0x00, 0x83, 0x47, + 0x47, 0x00, 0x93, 0xF7, 0x37, 0x07, 0x93, 0xE7, 0x07, 0x08, 0x23, 0x02, 0xF7, 0x00, 0xF2, 0x40, + 0x62, 0x44, 0xD2, 0x44, 0x42, 0x49, 0xB2, 0x49, 0x05, 0x61, 0x82, 0x80, 0x01, 0x11, 0x4A, 0xC8, + 0x21, 0x69, 0x26, 0xCA, 0x06, 0xCE, 0x22, 0xCC, 0x4E, 0xC6, 0x52, 0xC4, 0x2A, 0x99, 0xB7, 0x07, + + 0x08, 0x00, 0x93, 0x04, 0xE0, 0x0F, 0x63, 0x7C, 0xF9, 0x02, 0xB7, 0x17, 0x00, 0x40, 0x83, 0xC7, + 0x47, 0x00, 0x93, 0x04, 0xB0, 0x0F, 0xC1, 0x8B, 0x9D, 0xE3, 0x2D, 0x45, 0x2E, 0x84, 0x93, 0x54, + 0x26, 0x00, 0x21, 0x33, 0x4A, 0x85, 0xA9, 0x3B, 0x89, 0x33, 0x81, 0x33, 0xFD, 0x59, 0x37, 0x1A, + 0x00, 0x40, 0x13, 0x89, 0xF4, 0xFF, 0x63, 0x1D, 0x39, 0x01, 0x81, 0x44, 0x09, 0x33, 0xF2, 0x40, + + 0x62, 0x44, 0x26, 0x85, 0x42, 0x49, 0xD2, 0x44, 0xB2, 0x49, 0x22, 0x4A, 0x05, 0x61, 0x82, 0x80, + 0x29, 0x3B, 0x21, 0x3B, 0x19, 0x3B, 0x11, 0x3B, 0x11, 0x04, 0x03, 0x27, 0x4A, 0x01, 0x83, 0x27, + 0xC4, 0xFF, 0xE3, 0x1D, 0xF7, 0xFC, 0xCA, 0x84, 0xE9, 0xB7, 0x01, 0x11, 0x22, 0xCC, 0x06, 0xCE, + 0x37, 0x04, 0x28, 0x00, 0xE9, 0x31, 0x15, 0x45, 0x4D, 0x39, 0xC5, 0x39, 0xFD, 0x31, 0x2A, 0xC6, + + 0x7D, 0x39, 0x32, 0x45, 0x93, 0x77, 0x15, 0x00, 0x8D, 0xCB, 0x7D, 0x14, 0x6D, 0xF4, 0x13, 0x05, + 0xF0, 0x0F, 0xF2, 0x40, 0x62, 0x44, 0x05, 0x61, 0x82, 0x80, 0x13, 0x07, 0x40, 0x04, 0x01, 0x45, + 0xE3, 0x89, 0xE7, 0xFE, 0x05, 0x45, 0x01, 0x33, 0x13, 0x05, 0x40, 0x04, 0x65, 0x31, 0x09, 0x45, + 0x55, 0x31, 0x62, 0x44, 0xF2, 0x40, 0x05, 0x61, 0x19, 0xB3, 0x93, 0x77, 0xC5, 0x07, 0xF1, 0xFF, + + 0x01, 0x45, 0xC1, 0xBF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +uint8_t flash_op573[ ] ={ + 0x79, 0x71, 0x22, 0xD4, 0x4A, 0xD0, 0x52, 0xCC, 0x06, 0xD6, 0x26, 0xD2, 0x4E, 0xCE, 0x56, 0xCA, + 0x5A, 0xC8, 0x5E, 0xC6, 0x93, 0x77, 0x15, 0x00, 0x2A, 0x84, 0x2E, 0x8A, 0x32, 0x89, 0x9D, 0xEF, + 0x93, 0x77, 0x24, 0x00, 0x99, 0xCB, 0xB7, 0x86, 0x07, 0x00, 0x01, 0x46, 0x81, 0x45, 0x05, 0x45, + 0x69, 0x22, 0x93, 0x77, 0xF5, 0x0F, 0x09, 0x45, 0x9D, 0xEB, 0x93, 0x77, 0x44, 0x00, 0x91, 0xCB, + + 0x85, 0x66, 0x01, 0x46, 0xD2, 0x85, 0x05, 0x45, 0x8D, 0x2A, 0x93, 0x77, 0xF5, 0x0F, 0x11, 0x45, + 0x99, 0xEF, 0x93, 0x77, 0x84, 0x01, 0x9D, 0xE7, 0x01, 0x45, 0x11, 0xA8, 0x81, 0x46, 0x01, 0x46, + 0x81, 0x45, 0x21, 0x45, 0x99, 0x2A, 0x93, 0x77, 0xF5, 0x0F, 0x05, 0x45, 0xD5, 0xDB, 0xB2, 0x50, + 0x22, 0x54, 0x92, 0x54, 0x02, 0x59, 0xF2, 0x49, 0x62, 0x4A, 0xD2, 0x4A, 0x42, 0x4B, 0xB2, 0x4B, + + 0x45, 0x61, 0x02, 0x90, 0xB7, 0x54, 0x00, 0x20, 0x13, 0x09, 0xF9, 0x0F, 0x93, 0x84, 0x04, 0x10, + 0x93, 0x7B, 0x84, 0x00, 0x13, 0x59, 0x89, 0x00, 0x81, 0x4A, 0x33, 0x0A, 0x9A, 0x40, 0x41, 0x88, + 0x33, 0x0B, 0x9A, 0x00, 0x63, 0x90, 0x0B, 0x02, 0x21, 0xC0, 0x93, 0x89, 0x04, 0xF0, 0x93, 0x06, + 0x00, 0x10, 0x4E, 0x86, 0xDA, 0x85, 0x0D, 0x45, 0x09, 0x22, 0x13, 0x75, 0xF5, 0x0F, 0x19, 0xCD, + + 0x41, 0x45, 0x75, 0xB7, 0x93, 0x06, 0x00, 0x10, 0x13, 0x86, 0x04, 0xF0, 0xDA, 0x85, 0x09, 0x45, + 0xED, 0x20, 0x13, 0x75, 0xF5, 0x0F, 0x69, 0xD9, 0x21, 0x45, 0x51, 0xBF, 0x83, 0xA7, 0x09, 0x00, + 0x91, 0x09, 0xBE, 0x9A, 0xE3, 0x9C, 0x99, 0xFE, 0x7D, 0x19, 0x93, 0x84, 0x04, 0x10, 0xE3, 0x19, + 0x09, 0xFA, 0x3D, 0xD0, 0xB7, 0x67, 0x00, 0x20, 0x9C, 0x4B, 0xE3, 0x8F, 0x57, 0xF5, 0xC9, 0xB7, + + 0x23, 0x03, 0x04, 0x80, 0x95, 0x47, 0x23, 0x03, 0xF4, 0x80, 0x23, 0x02, 0xA4, 0x80, 0x82, 0x80, + 0x83, 0x07, 0x64, 0x80, 0xE3, 0xCE, 0x07, 0xFE, 0x23, 0x03, 0x04, 0x80, 0x82, 0x80, 0x83, 0x07, + 0x64, 0x80, 0xE3, 0xCE, 0x07, 0xFE, 0x03, 0x45, 0x44, 0x80, 0x82, 0x80, 0x83, 0x07, 0x64, 0x80, + 0xE3, 0xCE, 0x07, 0xFE, 0x23, 0x02, 0xA4, 0x80, 0x82, 0x80, 0x41, 0x11, 0x26, 0xC4, 0x4A, 0xC2, + + 0x4E, 0xC0, 0x06, 0xC6, 0x13, 0x77, 0xF5, 0x0B, 0xAD, 0x47, 0xAA, 0x89, 0x2E, 0x89, 0x95, 0x44, + 0x63, 0x06, 0xF7, 0x00, 0x19, 0x45, 0x6D, 0x37, 0x65, 0x3F, 0x8D, 0x44, 0x4E, 0x85, 0x4D, 0x37, + 0xFD, 0x59, 0xFD, 0x14, 0x63, 0x98, 0x34, 0x01, 0xB2, 0x40, 0xA2, 0x44, 0x12, 0x49, 0x82, 0x49, + 0x41, 0x01, 0x82, 0x80, 0x13, 0x55, 0x09, 0x01, 0x13, 0x75, 0xF5, 0x0F, 0x45, 0x3F, 0x22, 0x09, + + 0xCD, 0xB7, 0x01, 0x11, 0x26, 0xCC, 0x06, 0xCE, 0xB7, 0x04, 0x08, 0x00, 0x51, 0x37, 0x15, 0x45, + 0x85, 0x3F, 0x71, 0x37, 0x69, 0x37, 0x2A, 0xC6, 0xA5, 0x3F, 0x32, 0x45, 0x93, 0x77, 0x15, 0x00, + 0x89, 0xEB, 0x13, 0x65, 0x15, 0x00, 0x13, 0x75, 0xF5, 0x0F, 0xF2, 0x40, 0xE2, 0x44, 0x05, 0x61, + 0x82, 0x80, 0xFD, 0x14, 0xE9, 0xFC, 0x01, 0x45, 0xCD, 0xBF, 0x39, 0x71, 0x26, 0xDC, 0x4A, 0xDA, + + 0x4E, 0xD8, 0x52, 0xD6, 0x56, 0xD4, 0x5A, 0xD2, 0x5E, 0xD0, 0x06, 0xDE, 0x62, 0xCE, 0x66, 0xCC, + 0xB7, 0xE7, 0x00, 0xE0, 0x7D, 0x57, 0x83, 0xAA, 0x07, 0x00, 0x22, 0xC6, 0x03, 0xAA, 0x47, 0x00, + 0x23, 0xA0, 0xE7, 0x18, 0x23, 0xA2, 0xE7, 0x18, 0xB7, 0x17, 0x00, 0x40, 0x13, 0x07, 0x70, 0x05, + 0x23, 0x80, 0xE7, 0x04, 0x13, 0x07, 0x80, 0xFA, 0x23, 0x80, 0xE7, 0x04, 0x83, 0xC7, 0x47, 0x04, + + 0x13, 0x09, 0x75, 0xFF, 0xB6, 0x84, 0xE2, 0x07, 0x13, 0x79, 0xF9, 0x0F, 0x85, 0x46, 0xAA, 0x89, + 0xAE, 0x8B, 0x32, 0x8B, 0x37, 0x24, 0x00, 0x40, 0xE1, 0x87, 0x01, 0x57, 0x63, 0xFA, 0x26, 0x01, + 0x63, 0x08, 0xD5, 0x00, 0x89, 0x46, 0x13, 0x07, 0x00, 0x02, 0x63, 0x13, 0xD5, 0x00, 0x01, 0x57, + 0xD9, 0x8F, 0x93, 0xF7, 0xF7, 0x0F, 0xB7, 0x1C, 0x00, 0x40, 0x23, 0x82, 0xFC, 0x04, 0x11, 0x47, + + 0x23, 0x03, 0xE4, 0x80, 0x13, 0x05, 0xF0, 0x0F, 0x65, 0x3D, 0x09, 0x4C, 0xD1, 0x35, 0x63, 0x69, + 0x2C, 0x11, 0xB7, 0x07, 0x07, 0x00, 0xBE, 0x9B, 0x37, 0x87, 0x07, 0x00, 0x79, 0x55, 0x63, 0xFE, + 0xEB, 0x02, 0xB3, 0x87, 0x9B, 0x00, 0x63, 0x6A, 0xF7, 0x02, 0xA9, 0x47, 0x63, 0x99, 0xF9, 0x06, + 0x89, 0xE4, 0x81, 0x44, 0x71, 0x3D, 0x26, 0x85, 0x0D, 0xA0, 0xDE, 0x85, 0x09, 0x45, 0x75, 0x3D, + + 0x05, 0x0B, 0x03, 0x45, 0xFB, 0xFF, 0xFD, 0x14, 0x85, 0x0B, 0x4D, 0x35, 0x81, 0xC4, 0x93, 0xF7, + 0xFB, 0x0F, 0xFD, 0xF7, 0xFD, 0x35, 0x69, 0xFD, 0x7D, 0x55, 0xB7, 0x17, 0x00, 0x40, 0x13, 0x07, + 0x70, 0x05, 0x23, 0x80, 0xE7, 0x04, 0x13, 0x07, 0x80, 0xFA, 0x23, 0x80, 0xE7, 0x04, 0x03, 0xC7, + 0x47, 0x04, 0x41, 0x8B, 0x23, 0x82, 0xE7, 0x04, 0xF2, 0x50, 0xB7, 0xE7, 0x00, 0xE0, 0x23, 0xA0, + + 0x57, 0x11, 0x23, 0xA2, 0x47, 0x11, 0xE2, 0x54, 0x32, 0x44, 0x52, 0x59, 0xC2, 0x59, 0x32, 0x5A, + 0xA2, 0x5A, 0x12, 0x5B, 0x82, 0x5B, 0x72, 0x4C, 0xE2, 0x4C, 0x21, 0x61, 0x82, 0x80, 0xA5, 0x47, + 0x63, 0x95, 0xF9, 0x06, 0x85, 0x69, 0x13, 0x09, 0xF0, 0x0F, 0xB3, 0x06, 0x99, 0x00, 0xB3, 0xF4, + 0x2B, 0x01, 0xB6, 0x94, 0x13, 0x49, 0xF9, 0xFF, 0xB3, 0x74, 0x99, 0x00, 0x41, 0x6B, 0x33, 0x79, + + 0x79, 0x01, 0x85, 0x6B, 0x93, 0x87, 0xF9, 0xFF, 0xB3, 0xF7, 0x27, 0x01, 0x99, 0xE3, 0x63, 0xFC, + 0x34, 0x01, 0x93, 0xD9, 0x49, 0x00, 0xC1, 0x47, 0xE3, 0xE6, 0x37, 0xFF, 0x99, 0xBF, 0x05, 0x69, + 0xC1, 0x69, 0x7D, 0x19, 0xD9, 0xB7, 0x13, 0x05, 0x80, 0x0D, 0x63, 0x88, 0x69, 0x01, 0x13, 0x05, + 0x00, 0x02, 0x63, 0x84, 0x79, 0x01, 0x13, 0x05, 0x10, 0x08, 0xCA, 0x85, 0xFD, 0x3B, 0x91, 0x35, + + 0x21, 0xDD, 0x4E, 0x99, 0xB3, 0x84, 0x34, 0x41, 0xD9, 0xB7, 0xDE, 0x85, 0x2D, 0x45, 0xF5, 0x33, + 0xDA, 0x94, 0xE3, 0x00, 0x9B, 0xF2, 0x05, 0x0B, 0xD9, 0x33, 0xA3, 0x0F, 0xAB, 0xFE, 0xD5, 0xBF, + 0x93, 0x87, 0xF9, 0xFF, 0x93, 0xF7, 0xF7, 0x0F, 0x63, 0x6C, 0xFC, 0x08, 0x03, 0xC7, 0x5C, 0x04, + 0xB7, 0x07, 0x08, 0x00, 0x13, 0x77, 0x07, 0x02, 0x19, 0xE3, 0xB7, 0x87, 0x07, 0x00, 0x79, 0x55, + + 0xE3, 0xFD, 0xFB, 0xF0, 0x33, 0x87, 0x9B, 0x00, 0xE3, 0xE9, 0xE7, 0xF0, 0x89, 0x47, 0x63, 0x90, + 0xF9, 0x04, 0x89, 0x80, 0x55, 0x49, 0xE3, 0x8E, 0x04, 0xEC, 0xDE, 0x85, 0x09, 0x45, 0x71, 0x3B, + 0x11, 0x0B, 0x03, 0x27, 0xCB, 0xFF, 0x91, 0x47, 0x23, 0x20, 0xE4, 0x80, 0x03, 0x07, 0x64, 0x80, + 0xE3, 0x4E, 0x07, 0xFE, 0x23, 0x03, 0x24, 0x81, 0xFD, 0x17, 0xED, 0xFB, 0xFD, 0x14, 0x91, 0x0B, + + 0x81, 0xC4, 0x93, 0xF7, 0xFB, 0x0F, 0xE9, 0xFF, 0x6D, 0x3B, 0x71, 0xF5, 0xF1, 0xB5, 0x85, 0x47, + 0xE3, 0x87, 0xF9, 0xF4, 0xDE, 0x85, 0x2D, 0x45, 0x8D, 0x33, 0x13, 0x89, 0xF4, 0xFF, 0xE3, 0x8A, + 0x04, 0xE8, 0x35, 0x3B, 0x93, 0x77, 0x39, 0x00, 0x91, 0xEB, 0x83, 0x26, 0x04, 0x80, 0x03, 0x27, + 0x0B, 0x00, 0x93, 0x07, 0x4B, 0x00, 0xE3, 0x9F, 0xE6, 0xE6, 0x3E, 0x8B, 0xCA, 0x84, 0xF1, 0xBF, + + 0xA1, 0x47, 0x63, 0x92, 0xF9, 0x04, 0xB5, 0x3B, 0x81, 0x44, 0x63, 0x8D, 0x0B, 0x00, 0x8D, 0x47, + 0x93, 0x04, 0xC0, 0x03, 0x63, 0x88, 0xFB, 0x00, 0x93, 0x04, 0x00, 0x05, 0x63, 0x84, 0x8B, 0x01, + 0x93, 0x04, 0x40, 0x04, 0x13, 0x75, 0xC5, 0x07, 0xE3, 0x05, 0x95, 0xE4, 0x19, 0x45, 0xC9, 0x39, + 0xC5, 0x31, 0x05, 0x45, 0xF1, 0x31, 0x26, 0x85, 0xD5, 0x39, 0x09, 0x45, 0xC5, 0x39, 0x91, 0x33, + + 0xE3, 0x19, 0x05, 0xE2, 0x91, 0xBD, 0xE3, 0x86, 0x09, 0xE2, 0xF1, 0x54, 0x25, 0xB5, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +uint8_t flash_op307[ ] ={ + 0x01, 0x11, 0x02, 0xCE, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, 0x67, 0x45, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, 0x13, 0x07, 0xB7, 0x9A, + 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x95, 0xC7, 0xB7, 0x27, 0x02, 0x40, + 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x38, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, 0x98, 0xCB, 0x98, 0x4B, + + 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x61, 0xEB, + 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, 0xA9, 0xCB, 0x93, 0x07, 0xF6, 0x0F, + 0xA1, 0x83, 0x2E, 0xC6, 0x2D, 0x68, 0x81, 0x76, 0x3E, 0xCA, 0xB7, 0x08, 0x02, 0x00, 0xB7, 0x27, + 0x02, 0x40, 0x37, 0x33, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0xFD, 0x16, 0x98, 0x4B, 0x33, 0x67, + + 0x17, 0x01, 0x98, 0xCB, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, + 0xD8, 0x47, 0x05, 0x8B, 0x41, 0xEB, 0x98, 0x4B, 0x75, 0x8F, 0x98, 0xCB, 0x32, 0x47, 0x13, 0x07, + 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x69, 0xFB, 0x93, 0x77, 0x85, 0x00, + 0xD5, 0xCB, 0x93, 0x07, 0xF6, 0x0F, 0x2E, 0xC6, 0xA1, 0x83, 0x3E, 0xCA, 0x37, 0x27, 0x02, 0x40, + + 0x1C, 0x4B, 0xC1, 0x66, 0x41, 0x68, 0xD5, 0x8F, 0x1C, 0xCB, 0xB7, 0x16, 0x00, 0x20, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x08, 0x00, 0x04, 0x37, 0x03, 0x20, 0x00, 0x98, 0x4B, 0x33, 0x67, 0x07, 0x01, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x75, 0xFF, 0x32, 0x47, 0x3A, 0xC8, 0x46, 0xCC, 0x62, 0x47, + 0x0D, 0xEF, 0x98, 0x4B, 0x33, 0x67, 0x67, 0x00, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x75, 0xFF, + + 0xD8, 0x47, 0x41, 0x8B, 0x39, 0xC3, 0xD8, 0x47, 0xC1, 0x76, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, + 0xD8, 0xC7, 0x98, 0x4B, 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x05, 0x61, 0x02, 0x90, 0x23, 0x20, + 0xD8, 0x00, 0x25, 0xB7, 0x23, 0x20, 0x03, 0x01, 0xA5, 0xB7, 0x42, 0x47, 0x13, 0x8E, 0x46, 0x00, + 0x94, 0x42, 0x14, 0xC3, 0x42, 0x47, 0x11, 0x07, 0x3A, 0xC8, 0x62, 0x47, 0x7D, 0x17, 0x3A, 0xCC, + + 0xD8, 0x47, 0x09, 0x8B, 0x75, 0xFF, 0xF2, 0x86, 0x5D, 0xB7, 0x32, 0x47, 0x13, 0x07, 0x07, 0x10, + 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x49, 0xF3, 0x98, 0x4B, 0xC1, 0x76, 0xFD, 0x16, + 0x75, 0x8F, 0x98, 0xCB, 0x41, 0x89, 0x19, 0xE1, 0x01, 0x45, 0x45, 0xBF, 0x2E, 0xC6, 0x0D, 0x06, + 0x02, 0xCA, 0x09, 0x82, 0x32, 0xCC, 0xB7, 0x17, 0x00, 0x20, 0x98, 0x43, 0x13, 0x86, 0x47, 0x00, + + 0xD2, 0x47, 0xB2, 0x46, 0x8A, 0x07, 0xB6, 0x97, 0x9C, 0x43, 0x63, 0x18, 0xF7, 0x02, 0xD2, 0x47, + 0x32, 0x47, 0x8A, 0x07, 0xBA, 0x97, 0x98, 0x43, 0xF2, 0x47, 0xBA, 0x97, 0x3E, 0xCE, 0xD2, 0x47, + 0x85, 0x07, 0x3E, 0xCA, 0xD2, 0x46, 0x62, 0x47, 0xB2, 0x87, 0xE3, 0xE8, 0xE6, 0xFC, 0xB7, 0x27, + 0x00, 0x20, 0x98, 0x4B, 0xF2, 0x47, 0xE3, 0x09, 0xF7, 0xFA, 0x41, 0x45, 0xB9, 0xBF, 0xff, 0xff, + + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +uint8_t flash_op003[ ] ={//v003 + 0x11, 0x11, 0x22, 0xCC, 0x26, 0xCA, 0x02, 0xC8, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, + 0x67, 0x45, 0xB7, 0x27, 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, + 0x13, 0x07, 0xB7, 0x9A, 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, + 0xB7, 0x27, 0x02, 0x40, 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x33, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, + + 0x98, 0xCB, 0x98, 0x4B, 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, + 0x05, 0x8B, 0x63, 0x16, 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, + 0xA9, 0xCB, 0x93, 0x07, 0xF6, 0x03, 0x99, 0x83, 0x2E, 0xC0, 0x2D, 0x63, 0x81, 0x76, 0x3E, 0xC4, + 0xB7, 0x32, 0x00, 0x40, 0xB7, 0x27, 0x02, 0x40, 0x13, 0x03, 0xA3, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + + 0xB7, 0x03, 0x02, 0x00, 0x33, 0x67, 0x77, 0x00, 0x98, 0xCB, 0x02, 0x47, 0xD8, 0xCB, 0x98, 0x4B, + 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x69, 0xE7, 0x98, 0x4B, 0x75, 0x8F, + 0x98, 0xCB, 0x02, 0x47, 0x13, 0x07, 0x07, 0x04, 0x3A, 0xC0, 0x22, 0x47, 0x7D, 0x17, 0x3A, 0xC4, + 0x79, 0xF7, 0x93, 0x77, 0x85, 0x00, 0xF1, 0xCF, 0x93, 0x07, 0xF6, 0x03, 0x2E, 0xC0, 0x99, 0x83, + + 0x37, 0x27, 0x02, 0x40, 0x3E, 0xC4, 0x1C, 0x4B, 0xC1, 0x66, 0x2D, 0x63, 0xD5, 0x8F, 0x1C, 0xCB, + 0x37, 0x07, 0x00, 0x20, 0x13, 0x07, 0x07, 0x20, 0xB7, 0x27, 0x02, 0x40, 0xB7, 0x03, 0x08, 0x00, + 0xB7, 0x32, 0x00, 0x40, 0x13, 0x03, 0xA3, 0xAA, 0x94, 0x4B, 0xB3, 0xE6, 0x76, 0x00, 0x94, 0xCB, + 0xD4, 0x47, 0x85, 0x8A, 0xF5, 0xFE, 0x82, 0x46, 0xBA, 0x84, 0x37, 0x04, 0x04, 0x00, 0x36, 0xC2, + + 0xC1, 0x46, 0x36, 0xC6, 0x92, 0x46, 0x84, 0x40, 0x11, 0x07, 0x84, 0xC2, 0x94, 0x4B, 0xC1, 0x8E, + 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0xB1, 0xEA, 0x92, 0x46, 0xBA, 0x84, 0x91, 0x06, 0x36, 0xC2, + 0xB2, 0x46, 0xFD, 0x16, 0x36, 0xC6, 0xF9, 0xFE, 0x82, 0x46, 0xD4, 0xCB, 0x94, 0x4B, 0x93, 0xE6, + 0x06, 0x04, 0x94, 0xCB, 0xD4, 0x47, 0x85, 0x8A, 0x85, 0xEE, 0xD4, 0x47, 0xC1, 0x8A, 0x85, 0xCE, + + 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, 0xD8, 0xC7, 0x98, 0x4B, + 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x62, 0x44, 0xD2, 0x44, 0x71, 0x01, 0x02, 0x90, 0x23, 0x20, + 0xD3, 0x00, 0xF5, 0xB5, 0x23, 0xA0, 0x62, 0x00, 0x3D, 0xB7, 0x23, 0xA0, 0x62, 0x00, 0x55, 0xB7, + 0x23, 0xA0, 0x62, 0x00, 0xC1, 0xB7, 0x82, 0x46, 0x93, 0x86, 0x06, 0x04, 0x36, 0xC0, 0xA2, 0x46, + + 0xFD, 0x16, 0x36, 0xC4, 0xB5, 0xF2, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x75, 0x8F, + 0x98, 0xCB, 0x41, 0x89, 0x19, 0xE1, 0x01, 0x45, 0x7D, 0xBF, 0x2E, 0xC0, 0x0D, 0x06, 0x02, 0xC4, + 0x09, 0x82, 0xB7, 0x07, 0x00, 0x20, 0x32, 0xC6, 0x93, 0x87, 0x07, 0x20, 0x94, 0x43, 0x13, 0x87, + 0x47, 0x00, 0xA2, 0x47, 0x02, 0x46, 0x8A, 0x07, 0xB2, 0x97, 0x9C, 0x43, 0x63, 0x99, 0xF6, 0x02, + + 0xA2, 0x47, 0x82, 0x46, 0x8A, 0x07, 0xB6, 0x97, 0x94, 0x43, 0xC2, 0x47, 0xB6, 0x97, 0x3E, 0xC8, + 0xA2, 0x47, 0x85, 0x07, 0x3E, 0xC4, 0x22, 0x46, 0xB2, 0x46, 0xBA, 0x87, 0xE3, 0x68, 0xD6, 0xFC, + 0xB7, 0x07, 0x00, 0x20, 0x03, 0xA7, 0x07, 0x61, 0xC2, 0x47, 0xE3, 0x06, 0xF7, 0xFA, 0x41, 0x45, + 0x9D, 0xB7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +uint8_t flash_op8571[ ]={//CH8571 + 0x01, 0x11, 0x22, 0xCE, 0x26, 0xCC, 0x2E, 0xC0, 0x32, 0xC2, 0x93, 0x77, 0x25, 0x00, 0x63, 0x8D, + 0x07, 0x22, 0xB7, 0x17, 0x00, 0x40, 0x83, 0xC7, 0x07, 0x08, 0x9D, 0x8B, 0x3E, 0xCA, 0xD2, 0x47, + 0x63, 0x9C, 0x07, 0x4A, 0x85, 0x47, 0x3E, 0xC8, 0xB7, 0x17, 0x00, 0x40, 0x02, 0xC4, 0x93, 0x04, + 0xF0, 0x04, 0x93, 0x82, 0xD7, 0x03, 0xFD, 0x53, 0x7D, 0x53, 0x79, 0x56, 0xF5, 0x55, 0x15, 0x44, + + 0x23, 0x8F, 0x97, 0x02, 0x23, 0x80, 0x62, 0x00, 0xA3, 0x8D, 0x07, 0x02, 0x23, 0x8E, 0xC7, 0x02, + 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x19, 0x47, 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, + 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, 0x83, 0xC6, + 0xF7, 0x03, 0x85, 0x8A, 0xED, 0xFE, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, + + 0xB7, 0x02, 0x93, 0x06, 0x80, 0xFD, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xC1, 0x82, 0x93, 0xF6, + 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xA1, 0x82, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, + 0xD7, 0x02, 0xA2, 0x46, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0x23, 0x8E, 0xC7, 0x02, + 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, 0x03, 0xC7, 0xF7, 0x03, + + 0x05, 0x8B, 0x6D, 0xFF, 0x23, 0x8F, 0x07, 0x02, 0x23, 0x80, 0x02, 0x00, 0x22, 0x47, 0xC1, 0x66, + 0x36, 0x97, 0x3A, 0xC4, 0x42, 0x47, 0x7D, 0x17, 0x3A, 0xC8, 0x3D, 0xF3, 0x85, 0x47, 0x3E, 0xC8, + 0xB7, 0x17, 0x00, 0x40, 0x93, 0x04, 0xF0, 0x04, 0x93, 0x82, 0xD7, 0x03, 0xFD, 0x53, 0x7D, 0x53, + 0x79, 0x56, 0xF5, 0x55, 0x15, 0x44, 0x23, 0x8F, 0x97, 0x02, 0x23, 0x80, 0x62, 0x00, 0xA3, 0x8D, + + 0x07, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x19, 0x47, + 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, + 0xA3, 0x8F, 0x87, 0x02, 0x83, 0xC6, 0xF7, 0x03, 0x85, 0x8A, 0xED, 0xFE, 0x23, 0x8E, 0xC7, 0x02, + 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x93, 0x06, 0x20, 0x05, 0xA3, 0x8F, 0xD7, 0x02, + + 0xA2, 0x46, 0xC1, 0x82, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xA1, 0x82, + 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, + 0xD7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, + 0x87, 0x02, 0x03, 0xC7, 0xF7, 0x03, 0x05, 0x8B, 0x6D, 0xFF, 0x23, 0x8F, 0x07, 0x02, 0x23, 0x80, + + 0x02, 0x00, 0x22, 0x47, 0xA1, 0x66, 0x36, 0x97, 0x3A, 0xC4, 0x42, 0x47, 0x7D, 0x17, 0x3A, 0xC8, + 0x3D, 0xF3, 0x99, 0x47, 0x3E, 0xC8, 0xB7, 0x17, 0x00, 0x40, 0x93, 0x04, 0xF0, 0x04, 0x93, 0x82, + 0xD7, 0x03, 0xFD, 0x53, 0x7D, 0x53, 0x79, 0x56, 0xF5, 0x55, 0x15, 0x44, 0x23, 0x8F, 0x97, 0x02, + 0x23, 0x80, 0x62, 0x00, 0xA3, 0x8D, 0x07, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, + + 0x23, 0x8E, 0xB7, 0x02, 0x19, 0x47, 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, + 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, 0x83, 0xC6, 0xF7, 0x03, 0x85, 0x8A, + 0xED, 0xFE, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x93, 0x06, + 0x00, 0x02, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xC1, 0x82, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, + + 0xD7, 0x02, 0xA2, 0x46, 0xA1, 0x82, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, + 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, + 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, 0x03, 0xC7, 0xF7, 0x03, 0x05, 0x8B, 0x6D, 0xFF, + 0x23, 0x8F, 0x07, 0x02, 0x23, 0x80, 0x02, 0x00, 0x22, 0x47, 0x85, 0x66, 0x36, 0x97, 0x3A, 0xC4, + + 0x42, 0x47, 0x7D, 0x17, 0x3A, 0xC8, 0x3D, 0xF3, 0x93, 0x77, 0x45, 0x00, 0xE9, 0xC3, 0x82, 0x47, + 0x93, 0x04, 0xF0, 0x04, 0xFD, 0x53, 0x3E, 0xC4, 0x92, 0x47, 0x7D, 0x53, 0x79, 0x56, 0x93, 0x87, + 0xF7, 0x0F, 0xA1, 0x83, 0x3E, 0xC6, 0xB7, 0x17, 0x00, 0x40, 0x93, 0x82, 0xD7, 0x03, 0xF5, 0x55, + 0x15, 0x44, 0x23, 0x8F, 0x97, 0x02, 0x23, 0x80, 0x62, 0x00, 0xA3, 0x8D, 0x07, 0x02, 0x23, 0x8E, + + 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x19, 0x47, 0xA3, 0x8F, 0xE7, 0x02, + 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x67, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, + 0x83, 0xC6, 0xF7, 0x03, 0x85, 0x8A, 0xED, 0xFE, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, + 0x23, 0x8E, 0xB7, 0x02, 0x93, 0x06, 0x10, 0xF8, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xC1, 0x82, + + 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0xA1, 0x82, 0x93, 0xF6, 0xF6, 0x0F, + 0xA3, 0x8F, 0xD7, 0x02, 0xA2, 0x46, 0x93, 0xF6, 0xF6, 0x0F, 0xA3, 0x8F, 0xD7, 0x02, 0x23, 0x8E, + 0xC7, 0x02, 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0xA3, 0x8F, 0x87, 0x02, 0x03, 0xC7, + 0xF7, 0x03, 0x05, 0x8B, 0x6D, 0xFF, 0x23, 0x8F, 0x07, 0x02, 0x23, 0x80, 0x02, 0x00, 0x22, 0x47, + + 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC4, 0x32, 0x47, 0x7D, 0x17, 0x3A, 0xC6, 0x3D, 0xF3, 0x93, 0x77, + 0x85, 0x00, 0xF5, 0xCF, 0x02, 0xCA, 0xB7, 0x27, 0x00, 0x40, 0x83, 0xC7, 0x27, 0x00, 0xBD, 0x8B, + 0x99, 0xEF, 0x37, 0x57, 0x00, 0x40, 0x83, 0x47, 0x77, 0x00, 0xE2, 0x07, 0xE1, 0x87, 0x63, 0xD8, + 0x07, 0x00, 0x93, 0x07, 0xA0, 0xFA, 0xA3, 0x02, 0xF7, 0x00, 0x85, 0x47, 0x3E, 0xCA, 0x82, 0x47, + + 0x37, 0x17, 0x00, 0x20, 0xFD, 0x53, 0x3E, 0xC4, 0x92, 0x47, 0xFD, 0x55, 0xF9, 0x56, 0x93, 0x87, + 0xF7, 0x0F, 0xA1, 0x83, 0x3E, 0xC6, 0xB7, 0x17, 0x00, 0x40, 0x93, 0x82, 0xD7, 0x03, 0x75, 0x56, + 0x19, 0x44, 0x93, 0x04, 0xF0, 0x04, 0x23, 0x8F, 0x97, 0x02, 0x23, 0x80, 0xB2, 0x00, 0xA3, 0x8D, + 0x07, 0x02, 0x23, 0x8E, 0xD7, 0x02, 0xA3, 0x8F, 0xB7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, + + 0x87, 0x02, 0x23, 0x8E, 0xD7, 0x02, 0xA3, 0x8F, 0xB7, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0x09, 0x43, + 0xA3, 0x8F, 0x67, 0x02, 0x22, 0x43, 0x93, 0x04, 0xF0, 0x0F, 0x13, 0x53, 0x03, 0x01, 0x13, 0x73, + 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, 0x22, 0x43, 0x13, 0x53, 0x83, 0x00, 0x13, 0x73, 0xF3, 0x0F, + 0xA3, 0x8F, 0x67, 0x02, 0x22, 0x43, 0x13, 0x73, 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, 0x02, 0xC8, + + 0x42, 0x43, 0x63, 0xFD, 0x64, 0x12, 0x23, 0x8E, 0xD7, 0x02, 0xA3, 0x8F, 0x77, 0x02, 0x23, 0x8E, + 0xC7, 0x02, 0x15, 0x43, 0xA3, 0x8F, 0x67, 0x02, 0x03, 0xC3, 0xF7, 0x03, 0x13, 0x73, 0x13, 0x00, + 0xE3, 0x1C, 0x03, 0xFE, 0x23, 0x8F, 0x07, 0x02, 0x23, 0x80, 0x02, 0x00, 0x22, 0x43, 0x13, 0x03, + 0x03, 0x10, 0x1A, 0xC4, 0x32, 0x43, 0x7D, 0x13, 0x1A, 0xC6, 0xE3, 0x14, 0x03, 0xF6, 0xD2, 0x47, + + 0x99, 0xC7, 0xB7, 0x57, 0x00, 0x40, 0x13, 0x07, 0x50, 0x05, 0x23, 0x83, 0xE7, 0x00, 0x41, 0x89, + 0x71, 0xC1, 0x82, 0x47, 0xB7, 0x16, 0x00, 0x20, 0x93, 0x02, 0xF0, 0x04, 0x3E, 0xC4, 0x92, 0x47, + 0x7D, 0x57, 0xF5, 0x55, 0x13, 0x86, 0xF7, 0x0F, 0x21, 0x82, 0xB7, 0x17, 0x00, 0x40, 0x32, 0xC6, + 0x13, 0x85, 0xE7, 0x03, 0x79, 0x56, 0x23, 0x00, 0x55, 0x00, 0xA3, 0x8E, 0xE7, 0x02, 0xA3, 0x8D, + + 0x07, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x19, 0x44, + 0xA3, 0x8F, 0x87, 0x02, 0x23, 0x8E, 0xC7, 0x02, 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, 0xB7, 0x02, + 0x2D, 0x44, 0xA3, 0x8F, 0x87, 0x02, 0x22, 0x43, 0x93, 0x03, 0xF0, 0x0F, 0x13, 0x53, 0x03, 0x01, + 0x13, 0x73, 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, 0x22, 0x43, 0x13, 0x53, 0x83, 0x00, 0x13, 0x73, + + 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, 0x22, 0x43, 0x13, 0x73, 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, + 0xA3, 0x8F, 0x07, 0x02, 0x02, 0xC8, 0x42, 0x43, 0x63, 0xFD, 0x63, 0x06, 0x23, 0x8E, 0xC7, 0x02, + 0xA3, 0x8F, 0xE7, 0x02, 0x23, 0x8E, 0xB7, 0x02, 0x15, 0x43, 0xA3, 0x8F, 0x67, 0x02, 0x03, 0xC3, + 0xF7, 0x03, 0x13, 0x73, 0x13, 0x00, 0xE3, 0x1C, 0x03, 0xFE, 0x23, 0x00, 0x05, 0x00, 0xA3, 0x8E, + + 0x07, 0x02, 0x22, 0x43, 0x13, 0x03, 0x03, 0x10, 0x1A, 0xC4, 0x32, 0x43, 0x7D, 0x13, 0x1A, 0xC6, + 0xE3, 0x13, 0x03, 0xF6, 0x01, 0x45, 0x49, 0xA0, 0x52, 0x47, 0x85, 0x47, 0x63, 0x14, 0xF7, 0x00, + 0x8D, 0x47, 0x91, 0xB6, 0x52, 0x47, 0x89, 0x47, 0x63, 0x14, 0xF7, 0x00, 0x9D, 0x47, 0x25, 0xBE, + 0x52, 0x47, 0x8D, 0x47, 0xE3, 0x1A, 0xF7, 0xB2, 0xBD, 0x47, 0x35, 0xB6, 0x03, 0x43, 0x07, 0x00, + + 0x05, 0x07, 0x13, 0x73, 0xF3, 0x0F, 0xA3, 0x8F, 0x67, 0x02, 0x42, 0x43, 0x05, 0x03, 0x1A, 0xC8, + 0x45, 0xBD, 0x83, 0xC4, 0xF7, 0x03, 0x13, 0x84, 0x16, 0x00, 0x83, 0xC6, 0x06, 0x00, 0x13, 0x83, + 0xF7, 0x03, 0x93, 0xF6, 0xF6, 0x0F, 0x63, 0x8D, 0x96, 0x02, 0x79, 0x57, 0x23, 0x8E, 0xE7, 0x02, + 0x7D, 0x57, 0xA3, 0x8F, 0xE7, 0x02, 0x75, 0x57, 0x23, 0x8E, 0xE7, 0x02, 0x95, 0x47, 0x23, 0x00, + + 0xF3, 0x00, 0xB7, 0x17, 0x00, 0x40, 0x03, 0xC7, 0xF7, 0x03, 0x05, 0x8B, 0x6D, 0xFF, 0x23, 0x8F, + 0x07, 0x02, 0xA3, 0x8E, 0x07, 0x02, 0x41, 0x45, 0x72, 0x44, 0xE2, 0x44, 0x05, 0x61, 0x02, 0x90, + 0xC2, 0x46, 0x85, 0x06, 0x36, 0xC8, 0xA2, 0x86, 0x3D, 0xB7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + +}; +uint8_t flash_op643[ ] = /* CH64x */ +{ + 0x01, 0x11, 0x02, 0xCE, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, 0x67, 0x45, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, 0x13, 0x07, 0xB7, 0x9A, + 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, 0xB7, 0x27, 0x02, 0x40, + 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x38, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, 0x98, 0xCB, 0x98, 0x4B, + + 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x63, 0x16, + 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, 0xA9, 0xCB, 0x93, 0x07, + 0xF6, 0x0F, 0xA1, 0x83, 0x2E, 0xC6, 0x2D, 0x68, 0x81, 0x76, 0x3E, 0xCA, 0xB7, 0x08, 0x02, 0x00, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x33, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + + 0x33, 0x67, 0x17, 0x01, 0x98, 0xCB, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, 0x07, 0x04, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x69, 0xE7, 0x98, 0x4B, 0x75, 0x8F, 0x98, 0xCB, 0x32, 0x47, + 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x69, 0xFB, 0x93, 0x77, + 0x85, 0x00, 0xF1, 0xCF, 0x93, 0x07, 0xF6, 0x0F, 0x2E, 0xC6, 0xA1, 0x83, 0x3E, 0xCA, 0x37, 0x27, + + 0x02, 0x40, 0x1C, 0x4B, 0xC1, 0x66, 0x2D, 0x68, 0xD5, 0x8F, 0x1C, 0xCB, 0xB7, 0x16, 0x00, 0x20, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x03, 0x08, 0x00, 0x13, 0x0E, 0x00, 0x04, 0xB7, 0x0E, 0x04, 0x00, + 0xB7, 0x38, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0x98, 0x4B, 0x33, 0x67, 0x67, 0x00, 0x98, 0xCB, + 0xD8, 0x47, 0x05, 0x8B, 0x75, 0xFF, 0x32, 0x47, 0x36, 0x8F, 0x3A, 0xC8, 0x72, 0xCC, 0x42, 0x47, + + 0x03, 0x2F, 0x0F, 0x00, 0x91, 0x06, 0x23, 0x20, 0xE7, 0x01, 0x98, 0x4B, 0x33, 0x67, 0xD7, 0x01, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x21, 0xEB, 0x42, 0x47, 0x36, 0x8F, 0x11, 0x07, 0x3A, 0xC8, + 0x62, 0x47, 0x7D, 0x17, 0x3A, 0xCC, 0x61, 0xFF, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, + 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x15, 0xEB, 0xD8, 0x47, 0x41, 0x8B, 0x15, 0xCB, + + 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, 0xD8, 0xC7, 0x98, 0x4B, + 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x05, 0x61, 0x02, 0x90, 0x23, 0x20, 0xD8, 0x00, 0xF5, 0xB5, + 0x23, 0x20, 0x03, 0x01, 0x3D, 0xB7, 0x23, 0xA0, 0x08, 0x01, 0x65, 0xB7, 0x23, 0xA0, 0x08, 0x01, + 0xD1, 0xB7, 0x32, 0x47, 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, + + 0x25, 0xF7, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x75, 0x8F, 0x98, 0xCB, 0x41, 0x89, + 0x19, 0xE1, 0x01, 0x45, 0xC9, 0xB7, 0x2E, 0xC6, 0x0D, 0x06, 0x02, 0xCA, 0x09, 0x82, 0x32, 0xCC, + 0xB7, 0x17, 0x00, 0x20, 0x98, 0x43, 0x13, 0x86, 0x47, 0x00, 0xD2, 0x47, 0xB2, 0x46, 0x8A, 0x07, + 0xB6, 0x97, 0x9C, 0x43, 0x63, 0x18, 0xF7, 0x02, 0xD2, 0x47, 0x32, 0x47, 0x8A, 0x07, 0xBA, 0x97, + + 0x98, 0x43, 0xF2, 0x47, 0xBA, 0x97, 0x3E, 0xCE, 0xD2, 0x47, 0x85, 0x07, 0x3E, 0xCA, 0xD2, 0x46, + 0x62, 0x47, 0xB2, 0x87, 0xE3, 0xE8, 0xE6, 0xFC, 0xB7, 0x27, 0x00, 0x20, 0x98, 0x4B, 0xF2, 0x47, + 0xE3, 0x09, 0xF7, 0xFA, 0x41, 0x45, 0x85, 0xBF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +uint8_t flash_op583[ ]={ + 0x79, 0x71, 0x22, 0xD4, 0x4A, 0xD0, 0x56, 0xCA, 0x06, 0xD6, 0x26, 0xD2, 0x4E, 0xCE, 0x52, 0xCC, + 0x5A, 0xC8, 0x5E, 0xC6, 0x62, 0xC4, 0x93, 0x77, 0x15, 0x00, 0x2A, 0x84, 0xAE, 0x8A, 0x32, 0x89, + 0xC1, 0xE3, 0x93, 0x77, 0x24, 0x00, 0x99, 0xCB, 0xB7, 0x86, 0x07, 0x00, 0x01, 0x46, 0x81, 0x45, + 0x05, 0x45, 0x49, 0x2A, 0x93, 0x77, 0xF5, 0x0F, 0x09, 0x45, 0xA5, 0xEF, 0x93, 0x77, 0x44, 0x00, + + 0x91, 0xCB, 0x85, 0x66, 0x01, 0x46, 0xD6, 0x85, 0x05, 0x45, 0xAD, 0x2A, 0x93, 0x77, 0xF5, 0x0F, + 0x11, 0x45, 0xA5, 0xE3, 0x93, 0x77, 0x84, 0x01, 0x01, 0x4A, 0xD9, 0xCF, 0xB7, 0x54, 0x00, 0x20, + 0x13, 0x09, 0xF9, 0x0F, 0x93, 0x84, 0x04, 0x10, 0x13, 0x59, 0x89, 0x00, 0x01, 0x4A, 0xB3, 0x8A, + 0x9A, 0x40, 0x93, 0x7B, 0x84, 0x00, 0x13, 0x7C, 0x04, 0x01, 0x33, 0x8B, 0x9A, 0x00, 0x63, 0x96, + + 0x0B, 0x04, 0x63, 0x06, 0x0C, 0x06, 0x93, 0x89, 0x04, 0xF0, 0x93, 0x06, 0x00, 0x10, 0x4E, 0x86, + 0xDA, 0x85, 0x0D, 0x45, 0x05, 0x2A, 0x13, 0x75, 0xF5, 0x0F, 0x21, 0xC5, 0x41, 0x45, 0x11, 0xA8, + 0x81, 0x46, 0x01, 0x46, 0x81, 0x45, 0x21, 0x45, 0x31, 0x2A, 0x93, 0x77, 0xF5, 0x0F, 0x05, 0x45, + 0xAD, 0xDB, 0xB2, 0x50, 0x22, 0x54, 0x92, 0x54, 0x02, 0x59, 0xF2, 0x49, 0x62, 0x4A, 0xD2, 0x4A, + + 0x42, 0x4B, 0xB2, 0x4B, 0x22, 0x4C, 0x45, 0x61, 0x02, 0x90, 0x93, 0x06, 0x00, 0x10, 0x13, 0x86, + 0x04, 0xF0, 0xDA, 0x85, 0x09, 0x45, 0xFD, 0x20, 0x13, 0x75, 0xF5, 0x0F, 0x5D, 0xD1, 0x21, 0x45, + 0xC9, 0xBF, 0x83, 0xA7, 0x09, 0x00, 0x91, 0x09, 0x3E, 0x9A, 0xE3, 0x9C, 0x34, 0xFF, 0x7D, 0x19, + 0x93, 0x84, 0x04, 0x10, 0xE3, 0x13, 0x09, 0xF8, 0x41, 0x88, 0x01, 0x45, 0x5D, 0xD8, 0xB7, 0x67, + + 0x00, 0x20, 0x9C, 0x4B, 0xE3, 0x87, 0x47, 0xFB, 0x51, 0xBF, 0x23, 0x03, 0x04, 0x80, 0x95, 0x47, + 0x23, 0x03, 0xF4, 0x80, 0x23, 0x02, 0xA4, 0x80, 0x82, 0x80, 0x83, 0x07, 0x64, 0x80, 0xE3, 0xCE, + 0x07, 0xFE, 0x23, 0x03, 0x04, 0x80, 0x82, 0x80, 0x83, 0x07, 0x64, 0x80, 0xE3, 0xCE, 0x07, 0xFE, + 0x03, 0x45, 0x44, 0x80, 0x82, 0x80, 0x83, 0x07, 0x64, 0x80, 0xE3, 0xCE, 0x07, 0xFE, 0x23, 0x02, + + 0xA4, 0x80, 0x82, 0x80, 0x41, 0x11, 0x26, 0xC4, 0x4A, 0xC2, 0x4E, 0xC0, 0x06, 0xC6, 0x13, 0x77, + 0xF5, 0x0B, 0xAD, 0x47, 0xAA, 0x89, 0x2E, 0x89, 0x95, 0x44, 0x63, 0x06, 0xF7, 0x00, 0x19, 0x45, + 0x6D, 0x37, 0x65, 0x3F, 0x8D, 0x44, 0x4E, 0x85, 0x4D, 0x37, 0xFD, 0x59, 0xFD, 0x14, 0x63, 0x98, + 0x34, 0x01, 0xB2, 0x40, 0xA2, 0x44, 0x12, 0x49, 0x82, 0x49, 0x41, 0x01, 0x82, 0x80, 0x13, 0x55, + + 0x09, 0x01, 0x13, 0x75, 0xF5, 0x0F, 0x45, 0x3F, 0x22, 0x09, 0xCD, 0xB7, 0x01, 0x11, 0x26, 0xCC, + 0x06, 0xCE, 0xB7, 0x04, 0x08, 0x00, 0x51, 0x37, 0x15, 0x45, 0x85, 0x3F, 0x71, 0x37, 0x69, 0x37, + 0x2A, 0xC6, 0xA5, 0x3F, 0x32, 0x45, 0x93, 0x77, 0x15, 0x00, 0x89, 0xEB, 0x13, 0x65, 0x15, 0x00, + 0x13, 0x75, 0xF5, 0x0F, 0xF2, 0x40, 0xE2, 0x44, 0x05, 0x61, 0x82, 0x80, 0xFD, 0x14, 0xE9, 0xFC, + + 0x01, 0x45, 0xCD, 0xBF, 0x39, 0x71, 0x26, 0xDC, 0x4A, 0xDA, 0x4E, 0xD8, 0x52, 0xD6, 0x56, 0xD4, + 0x5A, 0xD2, 0x5E, 0xD0, 0x06, 0xDE, 0x62, 0xCE, 0x66, 0xCC, 0xB7, 0xE7, 0x00, 0xE0, 0x7D, 0x57, + 0x83, 0xAA, 0x07, 0x00, 0x22, 0xC6, 0x03, 0xAA, 0x47, 0x00, 0x23, 0xA0, 0xE7, 0x18, 0x23, 0xA2, + 0xE7, 0x18, 0xB7, 0x17, 0x00, 0x40, 0x13, 0x07, 0x70, 0x05, 0x23, 0x80, 0xE7, 0x04, 0x13, 0x07, + + 0x80, 0xFA, 0x23, 0x80, 0xE7, 0x04, 0x83, 0xC7, 0x47, 0x04, 0x93, 0x0B, 0x75, 0xFF, 0xB6, 0x84, + 0xE2, 0x07, 0x93, 0xFB, 0xFB, 0x0F, 0x85, 0x46, 0xAA, 0x89, 0x2E, 0x8B, 0x32, 0x89, 0x37, 0x24, + 0x00, 0x40, 0xE1, 0x87, 0x01, 0x57, 0x63, 0xFA, 0x76, 0x01, 0x63, 0x08, 0xD5, 0x00, 0x89, 0x46, + 0x13, 0x07, 0x00, 0x02, 0x63, 0x13, 0xD5, 0x00, 0x01, 0x57, 0xD9, 0x8F, 0x93, 0xF7, 0xF7, 0x0F, + + 0xB7, 0x1C, 0x00, 0x40, 0x23, 0x82, 0xFC, 0x04, 0x11, 0x47, 0x23, 0x03, 0xE4, 0x80, 0x13, 0x05, + 0xF0, 0x0F, 0x65, 0x3D, 0x09, 0x4C, 0xD1, 0x35, 0x63, 0x6D, 0x7C, 0x11, 0xB7, 0x05, 0x07, 0x00, + 0xDA, 0x95, 0x37, 0x87, 0x07, 0x00, 0x79, 0x55, 0x63, 0xF2, 0xE5, 0x04, 0xB3, 0x87, 0x95, 0x00, + 0x63, 0x6E, 0xF7, 0x02, 0x37, 0x0B, 0x08, 0x00, 0xA9, 0x47, 0x33, 0xEB, 0x65, 0x01, 0x63, 0x99, + + 0xF9, 0x06, 0x89, 0xE4, 0x81, 0x44, 0x51, 0x3D, 0x26, 0x85, 0x0D, 0xA0, 0xDA, 0x85, 0x09, 0x45, + 0x55, 0x3D, 0x05, 0x09, 0x03, 0x45, 0xF9, 0xFF, 0xFD, 0x14, 0x05, 0x0B, 0x69, 0x3D, 0x81, 0xC4, + 0x93, 0x77, 0xFB, 0x0F, 0xFD, 0xF7, 0xDD, 0x35, 0x69, 0xFD, 0x7D, 0x55, 0xB7, 0x17, 0x00, 0x40, + 0x13, 0x07, 0x70, 0x05, 0x23, 0x80, 0xE7, 0x04, 0x13, 0x07, 0x80, 0xFA, 0x23, 0x80, 0xE7, 0x04, + + 0x03, 0xC7, 0x47, 0x04, 0x41, 0x8B, 0x23, 0x82, 0xE7, 0x04, 0xF2, 0x50, 0xB7, 0xE7, 0x00, 0xE0, + 0x23, 0xA0, 0x57, 0x11, 0x23, 0xA2, 0x47, 0x11, 0xE2, 0x54, 0x32, 0x44, 0x52, 0x59, 0xC2, 0x59, + 0x32, 0x5A, 0xA2, 0x5A, 0x12, 0x5B, 0x82, 0x5B, 0x72, 0x4C, 0xE2, 0x4C, 0x21, 0x61, 0x82, 0x80, + 0xA5, 0x47, 0x63, 0x95, 0xF9, 0x06, 0x85, 0x69, 0x13, 0x09, 0xF0, 0x0F, 0xB3, 0x06, 0x99, 0x00, + + 0xB3, 0x74, 0x2B, 0x01, 0xB6, 0x94, 0x13, 0x49, 0xF9, 0xFF, 0xB3, 0x74, 0x99, 0x00, 0x85, 0x6B, + 0x33, 0x79, 0x69, 0x01, 0x41, 0x6B, 0x93, 0x87, 0xF9, 0xFF, 0xB3, 0xF7, 0x27, 0x01, 0x99, 0xE3, + 0x63, 0xFC, 0x34, 0x01, 0x93, 0xD9, 0x49, 0x00, 0xC1, 0x47, 0xE3, 0xE6, 0x37, 0xFF, 0x99, 0xBF, + 0x05, 0x69, 0xC1, 0x69, 0x7D, 0x19, 0xD9, 0xB7, 0x13, 0x05, 0x80, 0x0D, 0x63, 0x88, 0x69, 0x01, + + 0x13, 0x05, 0x00, 0x02, 0x63, 0x84, 0x79, 0x01, 0x13, 0x05, 0x10, 0x08, 0xCA, 0x85, 0xDD, 0x3B, + 0x35, 0x3D, 0x21, 0xDD, 0x4E, 0x99, 0xB3, 0x84, 0x34, 0x41, 0xD9, 0xB7, 0xDA, 0x85, 0x2D, 0x45, + 0xD5, 0x33, 0xCA, 0x94, 0xE3, 0x00, 0x99, 0xF2, 0x05, 0x09, 0x7D, 0x3B, 0xA3, 0x0F, 0xA9, 0xFE, + 0xD5, 0xBF, 0x93, 0x87, 0xF9, 0xFF, 0x93, 0xF7, 0xF7, 0x0F, 0x63, 0x61, 0xFC, 0x0C, 0x83, 0xC7, + + 0x1C, 0x04, 0x13, 0x07, 0x30, 0x08, 0x63, 0x1F, 0xF7, 0x04, 0x37, 0x07, 0x08, 0x00, 0x63, 0x6B, + 0xEB, 0x04, 0xB3, 0x07, 0x9B, 0x00, 0xB7, 0x06, 0x10, 0x00, 0x63, 0xF5, 0xD7, 0x04, 0x33, 0x4B, + 0xEB, 0x00, 0x89, 0x47, 0x63, 0x93, 0xF9, 0x06, 0x89, 0x80, 0xD5, 0x49, 0xE3, 0x8C, 0x04, 0xEC, + 0xDA, 0x85, 0x09, 0x45, 0x41, 0x3B, 0x11, 0x09, 0x03, 0x27, 0xC9, 0xFF, 0x91, 0x47, 0x23, 0x20, + + 0xE4, 0x80, 0x03, 0x07, 0x64, 0x80, 0xE3, 0x4E, 0x07, 0xFE, 0x23, 0x03, 0x34, 0x81, 0xFD, 0x17, + 0xED, 0xFB, 0xFD, 0x14, 0x11, 0x0B, 0x81, 0xC4, 0x93, 0x77, 0xFB, 0x0F, 0xE9, 0xFF, 0x7D, 0x33, + 0x71, 0xF5, 0xE1, 0xB5, 0xB7, 0x17, 0x00, 0x40, 0x03, 0xC7, 0x57, 0x04, 0xB7, 0x07, 0x08, 0x00, + 0x13, 0x77, 0x07, 0x02, 0x19, 0xE3, 0xB7, 0x87, 0x07, 0x00, 0x79, 0x55, 0xE3, 0x78, 0xFB, 0xEA, + + 0x33, 0x07, 0x9B, 0x00, 0xE3, 0xFF, 0xE7, 0xF8, 0x55, 0xB5, 0x85, 0x47, 0xE3, 0x82, 0xF9, 0xF2, + 0xDA, 0x85, 0x2D, 0x45, 0x05, 0x3B, 0x93, 0x89, 0xF4, 0xFF, 0xE3, 0x85, 0x04, 0xE6, 0x29, 0x33, + 0x93, 0xF7, 0x39, 0x00, 0x91, 0xEB, 0x83, 0x26, 0x04, 0x80, 0x03, 0x27, 0x09, 0x00, 0x93, 0x07, + 0x49, 0x00, 0xE3, 0x9A, 0xE6, 0xE4, 0x3E, 0x89, 0xCE, 0x84, 0xF1, 0xBF, 0xB5, 0x47, 0x63, 0x96, + + 0xF9, 0x00, 0x13, 0x05, 0x90, 0x0B, 0xD1, 0x31, 0x35, 0xBD, 0xB1, 0x47, 0x13, 0x05, 0xB0, 0x0A, + 0xE3, 0x8B, 0xF9, 0xFE, 0x99, 0x47, 0x63, 0x91, 0xF9, 0x04, 0xB7, 0x05, 0x08, 0x00, 0xB3, 0x65, + 0xBB, 0x00, 0x2D, 0x45, 0xC5, 0x31, 0x81, 0x44, 0x8D, 0x4B, 0xA1, 0x49, 0x75, 0x39, 0x63, 0x96, + 0x74, 0x01, 0x83, 0x27, 0x04, 0x80, 0x23, 0x20, 0xF9, 0x00, 0x85, 0x04, 0xE3, 0x98, 0x34, 0xFF, + + 0x83, 0x27, 0x04, 0x80, 0x13, 0x17, 0x2B, 0x01, 0x63, 0x55, 0x07, 0x00, 0x23, 0x12, 0xF9, 0x00, + 0xD5, 0xBB, 0x23, 0x22, 0xF9, 0x00, 0xFD, 0xB3, 0x9D, 0x47, 0x63, 0x99, 0xF9, 0x02, 0x81, 0x45, + 0x13, 0x05, 0xB0, 0x04, 0x45, 0x31, 0xBD, 0x44, 0x23, 0x20, 0x09, 0x00, 0x23, 0x22, 0x09, 0x00, + 0xFD, 0x59, 0x9D, 0x39, 0x93, 0xF7, 0x74, 0x00, 0xCA, 0x97, 0x03, 0xC7, 0x07, 0x00, 0xFD, 0x14, + + 0x39, 0x8D, 0x23, 0x80, 0xA7, 0x00, 0xE3, 0x96, 0x34, 0xFF, 0x6D, 0xBB, 0xA1, 0x47, 0x63, 0x92, + 0xF9, 0x04, 0x6D, 0x39, 0x81, 0x44, 0x63, 0x0D, 0x0B, 0x00, 0x8D, 0x47, 0x93, 0x04, 0xC0, 0x03, + 0x63, 0x08, 0xFB, 0x00, 0x93, 0x04, 0x00, 0x05, 0x63, 0x04, 0x8B, 0x01, 0x93, 0x04, 0x40, 0x04, + 0x13, 0x75, 0xC5, 0x07, 0xE3, 0x08, 0x95, 0xD8, 0x19, 0x45, 0x01, 0x39, 0x39, 0x39, 0x05, 0x45, + + 0x29, 0x31, 0x26, 0x85, 0x0D, 0x39, 0x09, 0x45, 0x3D, 0x31, 0x49, 0x31, 0xE3, 0x1C, 0x05, 0xD6, + 0x69, 0xBB, 0x91, 0x47, 0x63, 0x99, 0xF9, 0x00, 0x13, 0x05, 0x60, 0x06, 0xFD, 0x36, 0xF5, 0x3E, + 0x13, 0x05, 0x90, 0x09, 0x0D, 0xB7, 0xE3, 0x8F, 0x09, 0xD4, 0xF1, 0x54, 0xA9, 0xBB, 0xff ,0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; +uint8_t flash_opl103[ ]={ + 0x01, 0x11, 0x02, 0xCE, 0x93, 0x77, 0x15, 0x00, 0x99, 0xCF, 0xB7, 0x06, 0x67, 0x45, 0xB7, 0x27, + 0x02, 0x40, 0x93, 0x86, 0x36, 0x12, 0x37, 0x97, 0xEF, 0xCD, 0xD4, 0xC3, 0x13, 0x07, 0xB7, 0x9A, + 0xD8, 0xC3, 0xD4, 0xD3, 0xD8, 0xD3, 0x93, 0x77, 0x25, 0x00, 0x9D, 0xC7, 0xB7, 0x27, 0x02, 0x40, + 0x98, 0x4B, 0xAD, 0x66, 0x37, 0x38, 0x00, 0x40, 0x13, 0x67, 0x47, 0x00, 0x98, 0xCB, 0x98, 0x4B, + + 0x93, 0x86, 0xA6, 0xAA, 0x13, 0x67, 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x63, 0x16, + 0x07, 0x10, 0x98, 0x4B, 0x6D, 0x9B, 0x98, 0xCB, 0x93, 0x77, 0x45, 0x00, 0xA9, 0xCB, 0x93, 0x07, + 0xF6, 0x0F, 0xA1, 0x83, 0x2E, 0xC6, 0x2D, 0x68, 0x81, 0x76, 0x3E, 0xCA, 0xB7, 0x08, 0x02, 0x00, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x33, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0xFD, 0x16, 0x98, 0x4B, + + 0x33, 0x67, 0x17, 0x01, 0x98, 0xCB, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, 0x07, 0x04, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x69, 0xE7, 0x98, 0x4B, 0x75, 0x8F, 0x98, 0xCB, 0x32, 0x47, + 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, 0x69, 0xFB, 0x93, 0x77, + 0x85, 0x00, 0xF1, 0xCF, 0x93, 0x07, 0xF6, 0x0F, 0x2E, 0xC6, 0xA1, 0x83, 0x3E, 0xCA, 0x37, 0x27, + + 0x02, 0x40, 0x1C, 0x4B, 0xC1, 0x66, 0x2D, 0x68, 0xD5, 0x8F, 0x1C, 0xCB, 0xB7, 0x16, 0x00, 0x20, + 0xB7, 0x27, 0x02, 0x40, 0x37, 0x03, 0x08, 0x00, 0x13, 0x0E, 0x00, 0x04, 0xB7, 0x0E, 0x04, 0x00, + 0xB7, 0x38, 0x00, 0x40, 0x13, 0x08, 0xA8, 0xAA, 0x98, 0x4B, 0x33, 0x67, 0x67, 0x00, 0x98, 0xCB, + 0xD8, 0x47, 0x05, 0x8B, 0x75, 0xFF, 0x32, 0x47, 0x36, 0x8F, 0x3A, 0xC8, 0x72, 0xCC, 0x42, 0x47, + + 0x03, 0x2F, 0x0F, 0x00, 0x91, 0x06, 0x23, 0x20, 0xE7, 0x01, 0x98, 0x4B, 0x33, 0x67, 0xD7, 0x01, + 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x21, 0xEB, 0x42, 0x47, 0x36, 0x8F, 0x11, 0x07, 0x3A, 0xC8, + 0x62, 0x47, 0x7D, 0x17, 0x3A, 0xCC, 0x61, 0xFF, 0x32, 0x47, 0xD8, 0xCB, 0x98, 0x4B, 0x13, 0x67, + 0x07, 0x04, 0x98, 0xCB, 0xD8, 0x47, 0x05, 0x8B, 0x15, 0xEB, 0xD8, 0x47, 0x41, 0x8B, 0x15, 0xCB, + + 0xD8, 0x47, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x13, 0x67, 0x07, 0x01, 0xD8, 0xC7, 0x98, 0x4B, + 0x21, 0x45, 0x75, 0x8F, 0x98, 0xCB, 0x05, 0x61, 0x02, 0x90, 0x23, 0x20, 0xD8, 0x00, 0xF5, 0xB5, + 0x23, 0x20, 0x03, 0x01, 0x3D, 0xB7, 0x23, 0xA0, 0x08, 0x01, 0x65, 0xB7, 0x23, 0xA0, 0x08, 0x01, + 0xD1, 0xB7, 0x32, 0x47, 0x13, 0x07, 0x07, 0x10, 0x3A, 0xC6, 0x52, 0x47, 0x7D, 0x17, 0x3A, 0xCA, + + 0x25, 0xF7, 0x98, 0x4B, 0xB7, 0x06, 0xF3, 0xFF, 0xFD, 0x16, 0x75, 0x8F, 0x98, 0xCB, 0x41, 0x89, + 0x19, 0xE1, 0x01, 0x45, 0xC9, 0xB7, 0x2E, 0xC6, 0x0D, 0x06, 0x02, 0xCA, 0x09, 0x82, 0x32, 0xCC, + 0xB7, 0x17, 0x00, 0x20, 0x98, 0x43, 0x13, 0x86, 0x47, 0x00, 0xD2, 0x47, 0xB2, 0x46, 0x8A, 0x07, + 0xB6, 0x97, 0x9C, 0x43, 0x63, 0x18, 0xF7, 0x02, 0xD2, 0x47, 0x32, 0x47, 0x8A, 0x07, 0xBA, 0x97, + + 0x98, 0x43, 0xF2, 0x47, 0xBA, 0x97, 0x3E, 0xCE, 0xD2, 0x47, 0x85, 0x07, 0x3E, 0xCA, 0xD2, 0x46, + 0x62, 0x47, 0xB2, 0x87, 0xE3, 0xE8, 0xE6, 0xFC, 0xB7, 0x27, 0x00, 0x20, 0x98, 0x4B, 0xF2, 0x47, + 0xE3, 0x09, 0xF7, 0xFA, 0x41, 0x45, 0x85, 0xBF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + + + +void wlink_ramcodewrite(uint8_t *buffer, int size) +{ + unsigned long len = 64; + int i = 0; + while (size >= 64) + { + pWriteData(0, 2, buffer + 64 * i, &len); + size -= 64; + i++; + } + if (size) + { + len = size; + pWriteData(0, 2, buffer + 64 * i, &len); + } +} + +void wlink_getromram(uint32_t *rom, uint32_t *ram) +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + bool type_A = false; + bool type_B = false; + bool type_C = false; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x04; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if (chip_type == 0x30700508 || chip_type == 0x30710508 || chip_type == 0x30730508 || chip_type == 0x30300504 || chip_type == 0x30310504) + type_A = true; + if (chip_type == 0x30720508 || chip_type == 0x30740508) + type_C = true; + if (chip_type == 0x2034050c || chip_type == 0x2080050c || chip_type == 0x2081050c || chip_type == 0x2082050c || chip_type == 0x2083050c) + type_B = true; + switch (rxbuf[3]) + { + case 0: + if (type_B) + { + *rom = 128; + *ram = 64; + } + else if (type_A) + { + *rom = 192; + *ram = 128; + } + else if (type_C) + { + *rom = 192; + *ram = 128; + } + else if (riscvchip == 3) + { + *rom = 96; + *ram = 32; + } + else + { + *rom = 0; + *ram = 0; + } + break; + case 1: + if (type_B) + { + *rom = 144; + *ram = 48; + } + else if (type_A) + { + *rom = 224; + *ram = 96; + } + else if (type_C) + { + *rom = 224; + *ram = 96; + } + else if (riscvchip == 3) + { + *rom = 64; + *ram = 64; + } + else + { + *rom = 0; + *ram = 0; + } + break; + case 2: + if (type_B) + { + *rom = 160; + *ram = 32; + } + else if (type_A) + { + *rom = 256; + *ram = 64; + } + else if (type_C) + { + *rom = 0; + *ram = 0; + } + else if (riscvchip == 3) + { + *rom = 32; + *ram = 96; + } + else + { + *rom = 0; + *ram = 0; + } + break; + case 3: + if (type_B) + { + *rom = 160; + *ram = 32; + } + else if (type_A) + { + *rom = 288; + *ram = 32; + } + else if (type_C) + { + *rom = 0; + *ram = 0; + } + else if (riscvchip == 3) + { + *rom = 32; + *ram = 96; + } + else + { + *rom = 0; + *ram = 0; + } + break; + default: + *rom = 0; + *ram = 0; + break; + } +} +void readmcause(); +unsigned char DMI_OP( + unsigned long iIndex, + unsigned char iAddr, + unsigned long iData, + unsigned char iOP, + unsigned char *oAddr, + unsigned long *oData, + unsigned char *oOP) +{ + unsigned char Txbuf[9]; + unsigned char Rxbuf[9]; + unsigned long len; + unsigned char *pData; + unsigned char retrytime; + Txbuf[0] = 0x81; + Txbuf[1] = 0x08; + Txbuf[2] = 0x06; + Txbuf[3] = iAddr; + Txbuf[4] = (unsigned char)(iData >> 24); + Txbuf[5] = (unsigned char)(iData >> 16); + Txbuf[6] = (unsigned char)(iData >> 8); + Txbuf[7] = (unsigned char)(iData); + Txbuf[8] = iOP; + retrytime = 0; +RETRY: + len = 9; + if (pWriteData(gIndex, 1, Txbuf, &len)) + { + memset(Rxbuf, 0, sizeof(Rxbuf)); + len = 9; + + if (pReadData(gIndex, 1, Rxbuf, &len)) + { + + *oAddr = Rxbuf[3]; + if (oData) + { + pData = (unsigned char *)oData; + *pData = Rxbuf[7]; + *(pData + 1) = Rxbuf[6]; + *(pData + 2) = Rxbuf[5]; + *(pData + 3) = Rxbuf[4]; + } + *oOP = Rxbuf[8]; + + retrytime++; + + if (Rxbuf[8] == 2 && Rxbuf[6] == 3) + { + readmcause(); + } + + return true; + } + } + return false; +} + +void wlink_reset() +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0b; + txbuf[2] = 0x01; + txbuf[3] = 0x03; + unsigned char oAddr; + unsigned long oData; + unsigned char oOP; + unsigned char iAddr; + DMI_OP(0, 0x10, 0x80000001, 2, &oAddr, &oData, &oOP); + usleep(1000); + oAddr = 0; + oData = 0; + oOP = 0; + iAddr = 0; + DMI_OP(0, 0x11, iAddr, 1, &oAddr, &oData, &oOP); + usleep(1000); + len = 4; + pWriteData(0, 1, txbuf, &len); + usleep(1000); + usleep(1000); + len = 4; + pReadData(0, 1, rxbuf, &len); + DMI_OP(0, 0x10, 0x80000001, 2, &oAddr, &oData, &oOP); + usleep(1000); + DMI_OP(0, 0x11, iAddr, 1, &oAddr, &oData, &oOP); +} +void wlink_quitreset(void) +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + unsigned char oAddr; + unsigned long oData; + unsigned char oOP; + unsigned char iAddr; + txbuf[0] = 0x81; + txbuf[1] = 0x0b; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + usleep(300000); + DMI_OP(0, 0x10, 0x40000001, 2, &oAddr, &oData, &oOP); + usleep(1000); + oAddr = 0; + oData = 0; + oOP = 0; + iAddr = 0; + DMI_OP(0, 0x11, iAddr, 1, &oAddr, &oData, &oOP); +} +void wlink_softreset(void){ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0b; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + usleep(300000); +} + + +void wlink_clean(void){ + unsigned char txbuf[10]; + unsigned char rxbuf[10]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + pWriteData(0, 1, txbuf, &len); + len = 7; + pReadData(0, 1, rxbuf, &len); + txbuf[1] = 0x0c; + txbuf[2] = 0x02; + txbuf[4] = 0x01; + len=5; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x02; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 8; + pReadData(0, 1, rxbuf, &len); + txbuf[1]=0x0c; + txbuf[2]=0x02; + txbuf[3]=riscvchip; + txbuf[4]=0x01; + len=5; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); +} + +void wlink_chip_reset() +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0b; + txbuf[2] = 0x01; + txbuf[3] = 0x02; + unsigned char oAddr; + unsigned long oData; + unsigned char oOP; + unsigned char iAddr; + DMI_OP(0, 0x10, 0x80000001, 2, &oAddr, &oData, &oOP); + usleep(1000); + oAddr = 0; + oData = 0; + oOP = 0; + iAddr = 0; + DMI_OP(0, 0x11, iAddr, 1, &oAddr, &oData, &oOP); + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); +} +int wlnik_protect_check(void) +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x06; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if (((rxbuf[0] == 0x82) && (rxbuf[1] == 0x06) && (rxbuf[2] == 0x01) && (rxbuf[3] == 0x01))) + return 4; + if (((rxbuf[0] == 0x82) && (rxbuf[1] == 0x06) && (rxbuf[2] == 0x01) && (rxbuf[3] == 0x02))) + return 5; + return ERROR_FAIL; +} +int wlink_flash_protect(bool stat) +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x06; + txbuf[2] = 0x01; + if (stat) + txbuf[3] = 0x03; + else + txbuf[3] = 0x02; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if (((rxbuf[0] == 0x82) && (rxbuf[1] == 0x06) && (rxbuf[2] == 0x01) && (rxbuf[3] == txbuf[3]))) + { + wlink_reset(); + usleep(300000); + int ret = wlnik_protect_check(); + if ((stat) && (ret == 4)) + return ERROR_OK; + if ((!stat) && (ret == 5)) + return ERROR_OK; + } + return ERROR_FAIL; +} + +int wlink_ready_write(uint32_t address, uint32_t count) +{ + unsigned char txbuf[24]; + unsigned char rxbuf[24]; + unsigned long len = 4; + unsigned long chipiaddr1 = chipiaddr + address; + if (chipiaddr1 >= 0x10000000) + chipiaddr1 -= 0x08000000; + txbuf[0] = 0x81; + txbuf[1] = 0x02; + txbuf[2] = 0x01; + txbuf[3] = 0x06; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + txbuf[0] = 0x81; + txbuf[1] = 0x01; + txbuf[2] = 0x08; + txbuf[3] = (uint8_t)(chipiaddr1 >> 24); + txbuf[4] = (uint8_t)(chipiaddr1 >> 16); + txbuf[5] = (uint8_t)(chipiaddr1 >> 8); + txbuf[6] = (uint8_t)chipiaddr1; + txbuf[7] = (uint8_t)(count >> 24); + txbuf[8] = (uint8_t)(count >> 16); + txbuf[9] = (uint8_t)(count >> 8); + txbuf[10] = (uint8_t)count; + len = 11; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + txbuf[0] = 0x81; + txbuf[1] = 0x02; + txbuf[2] = 0x01; + txbuf[3] = 0x05; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + switch (riscvchip) + { + case 3: + wlink_ramcodewrite(flash_op569, sizeof(flash_op569)); + break; + case 1: + wlink_ramcodewrite(flash_op103, sizeof(flash_op103)); + break; + case 2: + wlink_ramcodewrite(flash_op573, sizeof(flash_op573)); + break; + case 7: + case 0x0b: + wlink_ramcodewrite(flash_op583, sizeof(flash_op583)); + break; + case 5: + case 6: + wlink_ramcodewrite(flash_op307, sizeof(flash_op307)); + break; + case 9: + wlink_ramcodewrite(flash_op003, sizeof(flash_op003)); + break; + case 0x0a: + wlink_ramcodewrite(flash_op8571, sizeof(flash_op8571)); + break; + case 0x0c: + wlink_ramcodewrite(flash_op643, sizeof(flash_op8571)); + break; + case 0x0e: + wlink_ramcodewrite(flash_opl103, sizeof(flash_opl103)); + break; + default:; + } + txbuf[0] = 0x81; + txbuf[1] = 0x02; + txbuf[2] = 0x01; + txbuf[3] = 0x07; + if ((riscvchip == 0x1) || (pageerase)|| (writeloop>0)) + { + txbuf[3] = 0x0b; + } + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + return rxbuf[3]; +} + +void wlink_endprogram(void) +{ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x02; + txbuf[2] = 0x01; + txbuf[3] = 0x08; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); +} + +int wlink_fastprogram(uint8_t *buffer, int packsize) +{ + unsigned long len = 64; + unsigned char rxbuf[4]; + for (int i = 0; i < packsize / 64; i++) + { + pWriteData(0, 2, (buffer + i * 64), &len); + len = 64; + } + len = 4; + if (pReadData(0, 2, rxbuf, &len)) + { + if ((rxbuf[3] == 0x02) || (rxbuf[3] == 0x04)) + return ERROR_OK; + } + return ERROR_FAIL; +} + +int WriteNonFullPage(unsigned long iaddr, + unsigned char *ibuff, + unsigned long ilen) +{ + unsigned char txbuf[8]; + unsigned char rxbuf[8]; + unsigned long len; + txbuf[0] = 0x81; + txbuf[1] = 0x0A; + txbuf[2] = 0x05; + iaddr += chipiaddr; + txbuf[3] = (unsigned char)(iaddr >> 24); + txbuf[4] = (unsigned char)(iaddr >> 16); + txbuf[5] = (unsigned char)(iaddr >> 8); + txbuf[6] = (unsigned char)(iaddr); + txbuf[7] = (unsigned char)ilen; + len = 8; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + len = ilen; + pWriteData(0, 2, ibuff, &len); + len = 4; + if (pReadData(0, 2, rxbuf, &len)) + { + if (((rxbuf[0] == 0x41) && (rxbuf[1] == 0x01) && (rxbuf[2] == 0x01) && (rxbuf[3] == 0x02))) + return ERROR_OK; + } + return ERROR_FAIL; +} + +int wlink_erase(void) +{ + int ret; + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x02; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + ret = pWriteData(0, 1, txbuf, &len); + len = 4; + ret = pReadData(0, 1, rxbuf, &len); + return ret; +} +void readmcause() +{ + unsigned char oAddr; + unsigned long oData; + unsigned char oOP; + DMI_OP(0, 0x16, 0x00000700, 2, &oAddr, &oData, &oOP); + DMI_OP(0, 0x17, 0x220342, 2, &oAddr, &oData, &oOP); + DMI_OP(0, 0x16, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x4, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x17, 0x220343, 2, &oAddr, &oData, &oOP); + DMI_OP(0, 0x16, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x4, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x20, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x21, 0, 1, &oAddr, &oData, &oOP); + DMI_OP(0, 0x22, 0, 1, &oAddr, &oData, &oOP); +} + +void wlink_endprocess(void) +{ + + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0xff; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); +} + +void wlink_pow3v3( bool set){ + +if(wlinkw || wlinke){ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + if(set) + txbuf[3] = 0x09; + else + txbuf[3] =0x0a; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if(rxbuf[3]==0x09) + LOG_INFO(" POWER 3V3 ENABLE"); + else if(rxbuf[3]==0x0a){ + LOG_INFO(" POWER 3V3 DISABLE"); + // exit(0); + } + else + LOG_ERROR("POWER SET FAILED "); + + }else{ + + LOG_ERROR("This LINK dont support this function"); + + } + + +} + +void wlink_pow5v( bool set){ + + if(wlinkw || wlinke){ + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + if(set) + txbuf[3] = 0x0b; + else + txbuf[3] =0x0c; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if(rxbuf[3]==0x0b) + LOG_INFO(" POWER 5V ENABLE"); + else if(rxbuf[3]==0x0c) + LOG_INFO(" POWER 5V DISABLE"); + else + LOG_ERROR("POWER SET FAILED "); + + + }else{ + + LOG_ERROR("This LINK dont support this function"); + + } + + +} + +void wlink_rstout(int state){ + + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + if(state==0) + txbuf[3] = 0x13; + else if(state==1) + txbuf[3] = 0x14; + else if(state==2) + txbuf[3] = 0x15; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + if((rxbuf[2]==0x01) &&(rxbuf[3]==0x00)){ + if(state==0) + LOG_INFO(" RST PUSH_PULL OUTPUT 0"); + else if(state==1) + LOG_INFO(" RST PUSH_PULL OUTPUT 1"); + else if(state==2) + LOG_INFO(" RST FLOATING INPUT"); + + }else + LOG_ERROR(" RST SET FAILED"); +} + + +int wlink_init(void) +{ + unsigned char txbuf[6]; + unsigned char rxbuf[20]; + char *wlink_name = NULL; + unsigned long len = 5; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + +#ifdef _WIN32 + OSVERSIONINFO version; + + version.dwOSVersionInfoSize = sizeof version; + if (!GetVersionEx(&version)) + { + LOG_ERROR("GetVersionEx error"); + return ERROR_FAIL; + } + if (version.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + LOG_ERROR("Not Win32 Platform"); + return ERROR_FAIL; + } + if (hModule == 0) + { + hModule = LoadLibrary("WCHLinkDll.dll"); + if (hModule) + { + pOpenDev = (pCH375OpenDevice)GetProcAddress(hModule, "CH375OpenDevice"); + pCloseDev = (pCH375CloseDevice)GetProcAddress(hModule, "CH375CloseDevice"); + pSetTimeout = (pCH375SetTimeout)GetProcAddress(hModule, "CH375SetTimeout"); + pReadData = (pCH375ReadEndP)GetProcAddress(hModule, "CH375ReadEndP"); + pWriteData = (pCH375WriteEndP)GetProcAddress(hModule, "CH375WriteEndP"); + if (pOpenDev == NULL || pCloseDev == NULL || pSetTimeout == NULL || pReadData == NULL || pWriteData == NULL) + { + LOG_ERROR("GetProcAddress error"); + return ERROR_FAIL; + } + if (pOpenDev(gIndex) == -1) + { + gOpen = FALSE; + LOG_ERROR("WLink Open Error"); + return ERROR_FAIL; + } + pSetTimeout(gIndex, 5000, 5000); + } + } +#else if + + if (jtag_libusb_open(wlink_vids, wlink_pids, &wfd, NULL) != ERROR_OK) + { + LOG_ERROR("open failed"); + goto error_wlink; + } + jtag_libusb_set_configuration(wfd, 0); + if (libusb_claim_interface(wfd, 0) != ERROR_OK) + { + LOG_ERROR("claim interface failed"); + goto error_wlink; + } + +#endif + + + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 7; + if (pReadData(0, 1, rxbuf, &len)) + { + + switch (rxbuf[5]) + { + case 1: + wlink_name = "WCH-Link-CH549 mode:RV"; + wlink549 = true; + break; + case 2: + case 0x12: + wlink_name = "WCH-LinkE mode:RV"; + wlinke = true; + break; + case 3: + wlink_name = "WCH-LinkS mode:RV"; + break; + case 5: + case 0x85: + wlink_name = "WCH-LinkW mode:RV"; + wlinkw = true; + break; + default: + LOG_ERROR("unknow WCH-LINK "); + goto error_wlink; + break; + } + LOG_INFO("%s version %d.%d ", wlink_name, rxbuf[3], rxbuf[4]); + + + + txbuf[0] = 0x81; + txbuf[1] = 0x0c; + txbuf[2] = 0x02; + txbuf[3] = 0x01; + txbuf[4] = sp; + len=5; + pWriteData(0, 1, txbuf, &len); + len=4; + pReadData(0, 1, rxbuf, &len); + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x02; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 8; + pReadData(0, 1, rxbuf, &len); + if (((rxbuf[0] == 0x81) && (rxbuf[1] == 0x55) && (rxbuf[2] == 0x01) && (rxbuf[3] == 0x01))) + { + LOG_ERROR(" WCH-Link failed to connect with riscvchip"); + LOG_ERROR(" 1.Make sure the two-line debug interface has been opened. If not, set board to boot mode then use ISP tool to open it"); + LOG_ERROR(" 2.Please check your physical link connection"); + if (wlink549) + LOG_ERROR(" if your chip is CH32V00X,WCH-Link-CH549 does not support this chip, please use WCH-LinkE"); + if(wlinkw) + LOG_ERROR(" if your chip is CH56X/CH57X/CH58X/CH59X,WCH-LinkW does not support this chip, please use WCH-LinkE"); + goto error_wlink; + } + chip_type = (((unsigned int)rxbuf[4]) << 24) + (((unsigned int)rxbuf[5]) << 16) + (((unsigned int)rxbuf[6]) << 8) + (((unsigned int)rxbuf[7])); + chip_type = chip_type & 0xffffff0f; + if((chip_type==0x30520508) || (chip_type == 0x305b0508)) + pageerase=true; + switch (rxbuf[3]) + { + case 1: + { + riscvchip = 0x01; + chipiaddr = 0x08000000; + pagesize = 128; + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x03; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + txbuf[3] = 0x10; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + + break; + } + case 2: + { + riscvchip = 0x02; + chipiaddr = 0x00000000; + pagesize = 256; + LOG_WARNING(" The debug interface has been opened,there is a risk of code leakage ,ensure that the debug interface has been closed before leaving factory !"); + break; + } + case 3: + { + riscvchip = 0x03; + chipiaddr = 0x0000; + pagesize = 256; + LOG_WARNING(" The debug interface has been opened,there is a risk of code leakage ,ensure that the debug interface has been closed before leaving factory !"); + txbuf[0] = 0x81; + txbuf[1] = 0x0d; + txbuf[2] = 0x01; + txbuf[3] = 0x04; + + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + + switch (rxbuf[3]) + { + case 0: + { + ramaddr = 0x8000; + break; + } + case 1: + { + ramaddr = 0x10000; + break; + } + case 3: + { + ramaddr = 0x18000; + break; + } + default: + LOG_ERROR(" unknow CH56X riscvchip"); + goto error_wlink; + } + + break; + } + case 5: + { + riscvchip = 0x05; + chipiaddr = 0x08000000; + pagesize = 256; + break; + } + case 6: + { + riscvchip = 0x06; + chipiaddr = 0x08000000; + pagesize = 256; + txbuf[3] = 0x03; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + break; + } + case 7: // + { + riscvchip = 0x07; + chipiaddr = 0x00000000; + pagesize = 256; + LOG_WARNING(" The debug interface has been opened,there is a risk of code leakage ,ensure that the debug interface has been closed before leaving factory !"); + break; + } + case 9: // + { + riscvchip = 0x09; + chipiaddr = 0x08000000; + pagesize = 64; + txbuf[3] = 0x03; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + break; + } + case 0x0a: + { + riscvchip = 0x0a; + chipiaddr = 0x00000000; + pagesize = 256; + txbuf[3] = 0x03; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + + break; + } + case 0x0b: // + { + riscvchip = 0x0b; + chipiaddr = 0x00000000; + pagesize = 256; + break; + } + case 0x0c: + case 0x0d: // + { + riscvchip = 0x0c; + chipiaddr = 0x08000000; + pagesize = 256; + break; + } + case 0x0e: // + { + riscvchip = 0x0e; + chipiaddr = 0x08000000; + pagesize = 256; + break; + } + default: + LOG_ERROR(" communication fail,please contact [support@mounriver.com]"); + goto error_wlink; + } +#ifdef _WIN32 + gOpen = TRUE; +#endif + } + else + { + return ERROR_FAIL; + } + LOG_INFO("wlink_init ok"); + if(iocontrol){ + switch (iocontrol) + { + case 1: + wlink_pow3v3(powstat); + break; + case 2: + wlink_pow5v(powstat); + break; + case 3: + wlink_rstout(poutput); + break; + default: + + goto error_wlink; + break; + } + usleep(10000); + return ERROR_OK; + } + + return ERROR_OK; +#ifdef _WIN32 +error_wlink: + + wlink_endprocess(); + pCloseDev(gIndex); + if (hModule) + { + FreeLibrary(hModule); + hModule = 0; + } + return ERROR_FAIL; +#else +error_wlink: + if (wfd) + { + + wlink_endprocess(); + jtag_libusb_close(wfd); + } + return ERROR_FAIL; +#endif +} + +int wlink_quit(void) +{ +#ifdef _WIN32 + if (gOpen) + { + wlink_endprocess(); + pCloseDev(gIndex); + gOpen = FALSE; + } + if (hModule) + { + FreeLibrary(hModule); + hModule = 0; + } +#else if + wlink_endprocess(); + jtag_libusb_close(wfd); +#endif + return ERROR_OK; +} + +void wlink_disabledebug(void) +{ + if ((riscvchip == 0x02) || (riscvchip == 0x03) || (riscvchip == 0x07)|| (riscvchip == 0x0b)) + { + unsigned char txbuf[4]; + unsigned char rxbuf[4]; + unsigned long len = 4; + txbuf[0] = 0x81; + txbuf[1] = 0x0e; + txbuf[2] = 0x01; + txbuf[3] = 0x01; + len = 4; + pWriteData(0, 1, txbuf, &len); + len = 4; + pReadData(0, 1, rxbuf, &len); + } +} + + + +int wlink_write(const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int packsize = 4096; + + if (riscvchip == 0x09) + packsize = 1024; + uint8_t *buf_bin; + buf_bin = malloc(packsize); + memset(buf_bin, 0xff, packsize); + + int binlength = count; + wlink_ready_write(offset, count); + + if (binlength <= packsize) + { + for (int i = 0; i < count; i++) + { + buf_bin[i] = *(buffer + i); + } + int ret = wlink_fastprogram(buf_bin, packsize); + if (ret != ERROR_OK) + { + return ERROR_FAIL; + } + } + else + { + while (binlength >= packsize) + { + for (int i = 0; i < packsize; i++) + { + buf_bin[i] = *(buffer + i); + } + int ret = wlink_fastprogram(buf_bin, packsize); + if (ret != ERROR_OK) + { + return ERROR_FAIL; + } + binlength -= packsize; + buffer += packsize; + } + if (binlength > 0) + { + memset(buf_bin, 0xff, packsize); + for (int i = 0; i < binlength; i++) + { + buf_bin[i] = *(buffer + i); + } + int ret = wlink_fastprogram(buf_bin, packsize); + if (ret != ERROR_OK) + { + return ERROR_FAIL; + } + } + } + wlink_endprogram(); + return ERROR_OK; +} +int wlink_speed(int speed ){ + unsigned char txbuf[5]; + unsigned char rxbuf[4]; + unsigned long len = 5; + txbuf[0]=0x81; + txbuf[1]=0x0c; + txbuf[2]=0x02; + txbuf[3]=riscvchip; + + switch (speed) + { + case 6000: + txbuf[4]=1; + break; + case 4000: + txbuf[4]=2; + break; + case 400: + txbuf[4]=3; + break; + default: + LOG_WARNING("unknow speed config"); + txbuf[4]=1; + break; + } + sp=txbuf[4]; + pWriteData(0, 1, txbuf, &len); + len=4; + pReadData(0, 1, rxbuf, &len); + if (((rxbuf[0] == 0x82) && (rxbuf[1] == 0x0c) && (rxbuf[2] == 0x01) && (rxbuf[3] == 0x01))) + return ERROR_OK; + return ERROR_FAIL; +} + +int wlink_speed_div(int speed, int *khz) +{ + *khz = speed; + + return ERROR_OK; +} +static int wlink_khz(int khz, int *speed) +{ + *speed = khz; + + return ERROR_OK; +} + + +COMMAND_HANDLER(wlink_set_address) +{ + + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[0], wlink_address); + return ERROR_OK; +} + +COMMAND_HANDLER(rst_set) +{ + iocontrol=3; + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(ulong, CMD_ARGV[0], poutput); + + + + + if((poutput !=1)&&(poutput !=0)&&(poutput !=2)){ + + LOG_ERROR("invalid parameter"); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +COMMAND_HANDLER(wlink_set_index) +{ + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], gIndex); + return ERROR_OK; +} + +COMMAND_HANDLER(noload) +{ + noloadflag = true; + return ERROR_OK; +} +COMMAND_HANDLER(pow3v3) +{ + iocontrol=1; + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], powstat); + + return ERROR_OK; +} +COMMAND_HANDLER(pow5v) +{ + iocontrol=2; + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], powstat); + + return ERROR_OK; +} +COMMAND_HANDLER(disabledebug) +{ + wlink_disabledebug(); + return ERROR_OK; +} +COMMAND_HANDLER(page_erase) +{ + pageerase = true; + return ERROR_OK; +} +// COMMAND_HANDLER(protect_check) +// { +// protectcheck=true; +// return ERROR_OK; +// } +static const struct command_registration wlink_command_handlers[] = { + { + .name = "wlink_set_index", + .handler = &wlink_set_index, + .mode = COMMAND_CONFIG, + .help = "wlink_set_index num", + .usage = "set Wlink index", + }, + { + .name = "wlink_set_address", + .handler = &wlink_set_address, + .mode = COMMAND_CONFIG, + .help = "wlink_set_address num", + .usage = "[count]", + }, + { + .name = "noload", + .handler = &noload, + .mode = COMMAND_CONFIG, + .help = "noload", + .usage = "dont download", + }, + { + .name = "disabledebug", + .handler = &disabledebug, + .mode = COMMAND_CONFIG, + .help = "disabledebug", + .usage = "disable debug", + }, + { + .name = "page_erase", + .handler = &page_erase, + .mode = COMMAND_CONFIG, + .help = "page_erase", + .usage = "page erase", + }, + { + .name = "pow3v3", + .handler = &pow3v3, + .mode = COMMAND_CONFIG, + .help = "3.3v", + .usage = "('on'|'off')", + }, + { + .name = "pow5v", + .handler = &pow5v, + .mode = COMMAND_CONFIG, + .help = "5v", + .usage = "('on'|'off')", + }, + { + .name = "rst_set", + .handler = &rst_set, + .mode = COMMAND_CONFIG, + .help = "", + .usage = "[count]", + }, + COMMAND_REGISTRATION_DONE}; +static struct sdi_driver wlink_interface = { + + .transfer = &DMI_OP, +}; + +struct adapter_driver wlinke_adapter_driver = { + .name = "wlinke", + + .commands = wlink_command_handlers, + .transports = sdi_transports, + .speed=wlink_speed, + .khz = wlink_khz, + .speed_div = wlink_speed_div, + .init = wlink_init, + .quit = wlink_quit, + .sdi_ops = &wlink_interface, +}; diff --git a/src/jtag/hla/hla_tcl.c b/src/jtag/hla/hla_tcl.c index 5aa330145..6b206d231 100644 --- a/src/jtag/hla/hla_tcl.c +++ b/src/jtag/hla/hla_tcl.c @@ -59,6 +59,7 @@ static int jim_newtap_expected_id(struct jim_nvp *n, struct jim_getopt_info *goi #define NTAP_OPT_DISABLED 4 #define NTAP_OPT_EXPECTED_ID 5 #define NTAP_OPT_VERSION 6 +#define NTAP_OPT_BYPASS 7 static int jim_hl_newtap_cmd(struct jim_getopt_info *goi) { @@ -75,6 +76,7 @@ static int jim_hl_newtap_cmd(struct jim_getopt_info *goi) { .name = "-disable", .value = NTAP_OPT_DISABLED }, { .name = "-expected-id", .value = NTAP_OPT_EXPECTED_ID }, { .name = "-ignore-version", .value = NTAP_OPT_VERSION }, + { .name = "-ignore-bypass", .value = NTAP_OPT_BYPASS }, { .name = NULL, .value = -1}, }; diff --git a/src/jtag/interface.h b/src/jtag/interface.h old mode 100644 new mode 100755 index 58bfd02b0..a2477e3b7 --- a/src/jtag/interface.h +++ b/src/jtag/interface.h @@ -27,6 +27,7 @@ #include #include +#include #include /* @file @@ -363,10 +364,12 @@ struct adapter_driver { /* SWIM APIs */ const struct swim_driver *swim_ops; + /* SDI APIs */ + const struct sdi_driver *sdi_ops; }; extern const char * const jtag_only[]; - +extern const char *sdi_transports[]; int adapter_resets(int assert_trst, int assert_srst); int adapter_assert_reset(void); int adapter_deassert_reset(void); diff --git a/src/jtag/interfaces.c b/src/jtag/interfaces.c old mode 100644 new mode 100755 index 63faa9561..54669eeff --- a/src/jtag/interfaces.c +++ b/src/jtag/interfaces.c @@ -57,6 +57,9 @@ extern struct adapter_driver usb_blaster_adapter_driver; #if BUILD_JTAG_VPI == 1 extern struct adapter_driver jtag_vpi_adapter_driver; #endif +#if BUILD_VDEBUG == 1 +extern struct adapter_driver vdebug_adapter_driver; +#endif #if BUILD_JTAG_DPI == 1 extern struct adapter_driver jtag_dpi_adapter_driver; #endif @@ -147,6 +150,12 @@ extern struct adapter_driver stlink_dap_adapter_driver; #if BUILD_RSHIM == 1 extern struct adapter_driver rshim_dap_adapter_driver; #endif +#if BUILD_WLINKE == 1 +extern struct adapter_driver wlinke_adapter_driver; +#endif +#if BUILD_CH347 == 1 +extern struct adapter_driver ch347_adapter_driver; +#endif /** * The list of built-in JTAG interfaces, containing entries for those @@ -168,6 +177,9 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_JTAG_VPI == 1 &jtag_vpi_adapter_driver, #endif +#if BUILD_VDEBUG == 1 + &vdebug_adapter_driver, +#endif #if BUILD_JTAG_DPI == 1 &jtag_dpi_adapter_driver, #endif @@ -258,5 +270,12 @@ struct adapter_driver *adapter_drivers[] = { #if BUILD_RSHIM == 1 &rshim_dap_adapter_driver, #endif +#if BUILD_WLINKE == 1 + &wlinke_adapter_driver, +#endif +#if BUILD_CH347 == 1 + &ch347_adapter_driver, +#endif + NULL, }; diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h index d7d7d977c..4ec2f1e31 100644 --- a/src/jtag/jtag.h +++ b/src/jtag/jtag.h @@ -135,6 +135,9 @@ struct jtag_tap { /** Flag saying whether to ignore version field in expected_ids[] */ bool ignore_version; + /** Flag saying whether to ignore the bypass bit in the code */ + bool ignore_bypass; + /** current instruction */ uint8_t *cur_instr; /** Bypass register selected */ @@ -543,7 +546,8 @@ int jtag_srst_asserted(int *srst_asserted); * @param field Pointer to scan field. * @param value Pointer to scan value. * @param mask Pointer to scan mask; may be NULL. - * @returns Nothing, but calls jtag_set_error() on any error. + * + * returns Nothing, but calls jtag_set_error() on any error. */ void jtag_check_value_mask(struct scan_field *field, uint8_t *value, uint8_t *mask); diff --git a/src/jtag/sdi.c b/src/jtag/sdi.c new file mode 100755 index 000000000..dce2dcfdf --- /dev/null +++ b/src/jtag/sdi.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "interface.h" + +#include +#include + +extern struct adapter_driver *adapter_driver; + +int transfer(unsigned long iIndex, unsigned char iAddr,unsigned long iData,unsigned char iOP,unsigned char *oAddr,unsigned long *oData,unsigned char *oOP) +{ + assert(adapter_driver->sdi_ops); + + return adapter_driver->sdi_ops->transfer(iIndex,iAddr,iData,iOP,oAddr,oData,oOP); +} + + + + + + +COMMAND_HANDLER(handle_sdi_newtap_command) +{ + struct jtag_tap *tap; + + /* + * only need "basename" and "tap_type", but for backward compatibility + * ignore extra parameters + */ + if (CMD_ARGC < 2) + return ERROR_COMMAND_SYNTAX_ERROR; + + tap = calloc(1, sizeof(*tap)); + if (!tap) { + LOG_ERROR("Out of memory"); + return ERROR_FAIL; + } + + tap->chip = strdup(CMD_ARGV[0]); + tap->tapname = strdup(CMD_ARGV[1]); + tap->dotted_name = alloc_printf("%s.%s", CMD_ARGV[0], CMD_ARGV[1]); + if (!tap->chip || !tap->tapname || !tap->dotted_name) { + LOG_ERROR("Out of memory"); + free(tap->dotted_name); + free(tap->tapname); + free(tap->chip); + free(tap); + return ERROR_FAIL; + } + + LOG_DEBUG("Creating new sdi \"tap\", Chip: %s, Tap: %s, Dotted: %s", + tap->chip, tap->tapname, tap->dotted_name); + + /* default is enabled-after-reset */ + tap->enabled = true; + + jtag_tap_init(tap); + return ERROR_OK; +} + +static const struct command_registration sdi_transport_subcommand_handlers[] = { + { + .name = "newtap", + .handler = handle_sdi_newtap_command, + .mode = COMMAND_CONFIG, + .help = "Create a new TAP instance named basename.tap_type, " + "and appends it to the scan chain.", + .usage = "basename tap_type", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration sdi_transport_command_handlers[] = { + { + .name = "sdi", + .mode = COMMAND_ANY, + .help = "perform sdi adapter actions", + .usage = "", + .chain = sdi_transport_subcommand_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +static int sdi_transport_select(struct command_context *cmd_ctx) +{ + LOG_DEBUG(__func__); + + return register_commands(cmd_ctx, NULL, sdi_transport_command_handlers); +} + +static int sdi_transport_init(struct command_context *cmd_ctx) +{ + // enum reset_types jtag_reset_config = jtag_get_reset_config(); + + // LOG_DEBUG(__func__); + + // if (jtag_reset_config & RESET_CNCT_UNDER_SRST) { + // if (jtag_reset_config & RESET_SRST_NO_GATING) + // adapter_assert_reset(); + // else + // LOG_WARNING("\'srst_nogate\' reset_config option is required"); + // } else + // adapter_deassert_reset(); + + return ERROR_OK; +} + +static struct transport sdi_transport = { + .name = "sdi", + .select = sdi_transport_select, + .init = sdi_transport_init, +}; +const char *sdi_transports[] = { "sdi", NULL }; +static void sdi_constructor(void) __attribute__ ((constructor)); +static void sdi_constructor(void) +{ + transport_register(&sdi_transport); +} + +bool transport_is_sdi(void) +{ + return get_current_transport() == &sdi_transport; +} diff --git a/src/jtag/sdi.h b/src/jtag/sdi.h new file mode 100755 index 000000000..a9d2806a9 --- /dev/null +++ b/src/jtag/sdi.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENOCD_JTAG_SDI_H +#define OPENOCD_JTAG_SDI_H + + + +struct sdi_driver { + unsigned char (* transfer)( unsigned long iIndex, unsigned char iAddr,unsigned long iData,unsigned char iOP,unsigned char *oAddr,unsigned long *oData,unsigned char *oOP); +}; + +extern int transfer(unsigned long iIndex, unsigned char iAddr,unsigned long iData,unsigned char iOP,unsigned char *oAddr,unsigned long *oData,unsigned char *oOP); + +#endif \ No newline at end of file diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c index 566c406b9..e6e976d77 100644 --- a/src/jtag/tcl.c +++ b/src/jtag/tcl.c @@ -470,6 +470,7 @@ static int jim_newtap_expected_id(struct jim_nvp *n, struct jim_getopt_info *goi #define NTAP_OPT_DISABLED 4 #define NTAP_OPT_EXPECTED_ID 5 #define NTAP_OPT_VERSION 6 +#define NTAP_OPT_BYPASS 7 static int jim_newtap_ir_param(struct jim_nvp *n, struct jim_getopt_info *goi, struct jtag_tap *tap) @@ -532,6 +533,7 @@ static int jim_newtap_cmd(struct jim_getopt_info *goi) { .name = "-disable", .value = NTAP_OPT_DISABLED }, { .name = "-expected-id", .value = NTAP_OPT_EXPECTED_ID }, { .name = "-ignore-version", .value = NTAP_OPT_VERSION }, + { .name = "-ignore-bypass", .value = NTAP_OPT_BYPASS }, { .name = NULL, .value = -1 }, }; @@ -617,6 +619,9 @@ static int jim_newtap_cmd(struct jim_getopt_info *goi) case NTAP_OPT_VERSION: tap->ignore_version = true; break; + case NTAP_OPT_BYPASS: + tap->ignore_bypass = true; + break; } /* switch (n->value) */ } /* while (goi->argc) */ @@ -887,6 +892,7 @@ static const struct command_registration jtag_subcommand_handlers[] = { "['-enable'|'-disable'] " "['-expected_id' number] " "['-ignore-version'] " + "['-ignore-bypass'] " "['-ircapture' number] " "['-mask' number]", }, diff --git a/src/openocd.c b/src/openocd.c index 0292ba445..3c96d3214 100644 --- a/src/openocd.c +++ b/src/openocd.c @@ -437,6 +437,8 @@ int openocd_main(int argc, char *argv[]) rtt_exit(); free_config(); + log_exit(); + if (ret == ERROR_FAIL) return EXIT_FAILURE; else if (ret != ERROR_OK) diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c index 441b7abc5..4161e63fa 100644 --- a/src/rtos/ThreadX.c +++ b/src/rtos/ThreadX.c @@ -175,6 +175,18 @@ static const struct threadx_params threadx_params_list[] = { get_stacking_info_arm926ejs, /* fn_get_stacking_info */ is_thread_id_valid_arm926ejs, /* fn_is_thread_id_valid */ }, + { + "hla_target", /* target_name */ + 4, /* pointer_width; */ + 8, /* thread_stack_offset; */ + 40, /* thread_name_offset; */ + 48, /* thread_state_offset; */ + 136, /* thread_next_offset */ + &rtos_standard_cortex_m3_stacking, /* stacking_info */ + 1, /* stacking_info_nb */ + NULL, /* fn_get_stacking_info */ + NULL, /* fn_is_thread_id_valid */ + }, }; enum threadx_symbol_values { diff --git a/src/rtos/chromium-ec.c b/src/rtos/chromium-ec.c index 1c8f4e3f4..95a228d0d 100644 --- a/src/rtos/chromium-ec.c +++ b/src/rtos/chromium-ec.c @@ -7,6 +7,11 @@ * Chromium-EC RTOS Task Awareness */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include #include #include #include @@ -16,7 +21,6 @@ #define CROS_EC_MAX_TASKS 32 #define CROS_EC_MAX_NAME 200 #define CROS_EC_IDLE_STRING "<< idle >>" -#define BIT(x) (1 << (x)) struct chromium_ec_params { const char *target_name; diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c index a8f1b594e..b7d5c5f58 100644 --- a/src/rtos/hwthread.c +++ b/src/rtos/hwthread.c @@ -23,6 +23,7 @@ #include "target/target.h" #include "target/target_type.h" #include "target/register.h" +#include #include "rtos.h" #include "helper/log.h" #include "helper/types.h" @@ -43,6 +44,8 @@ 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) @@ -66,6 +69,7 @@ const struct rtos_type hwthread_rtos = { .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 { @@ -106,7 +110,7 @@ static int hwthread_update_threads(struct rtos *rtos) /* determine the number of "threads" */ if (target->smp) { - for (head = target->head; head; head = head->next) { + foreach_smp_target(head, target->smp_targets) { struct target *curr = head->target; if (!target_was_examined(curr)) @@ -127,7 +131,7 @@ static int hwthread_update_threads(struct rtos *rtos) if (target->smp) { /* loop over all threads */ - for (head = target->head; head; head = head->next) { + foreach_smp_target(head, target->smp_targets) { struct target *curr = head->target; if (!target_was_examined(curr)) @@ -222,7 +226,8 @@ static struct target *hwthread_find_thread(struct target *target, int64_t thread if (!target) return NULL; if (target->smp) { - for (struct target_list *head = target->head; head; head = head->next) { + struct target_list *head; + foreach_smp_target(head, target->smp_targets) { if (thread_id == threadid_from_target(head->target)) return head->target; } @@ -444,3 +449,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/linux.c b/src/rtos/linux.c index 84b4c6524..d147c1cf0 100644 --- a/src/rtos/linux.c +++ b/src/rtos/linux.c @@ -30,6 +30,7 @@ #include "rtos.h" #include "rtos_standard_stackings.h" #include +#include #include "server/gdb_server.h" #define LINUX_USER_KERNEL_BORDER 0xc0000000 @@ -191,16 +192,14 @@ static int linux_os_thread_reg_list(struct rtos *rtos, /* search target to perform the access */ struct reg **gdb_reg_list; struct target_list *head; - head = target->head; found = 0; - do { + foreach_smp_target(head, target->smp_targets) { if (head->target->coreid == next->core_id) { target = head->target; found = 1; break; } - head = head->next; - } while (head); + } if (found == 0) { LOG_ERROR @@ -397,7 +396,6 @@ static int get_name(struct target *target, struct threads *t) static int get_current(struct target *target, int create) { struct target_list *head; - head = target->head; uint8_t *buf; uint32_t val; uint32_t ti_addr; @@ -413,7 +411,7 @@ static int get_current(struct target *target, int create) ctt = ctt->next; } - while (head) { + foreach_smp_target(head, target->smp_targets) { struct reg **reg_list; int reg_list_size; int retval; @@ -474,7 +472,6 @@ static int get_current(struct target *target, int create) } free(reg_list); - head = head->next; } free(buffer); @@ -1394,9 +1391,8 @@ static int linux_os_smp_init(struct target *target) struct linux_os *os_linux = (struct linux_os *)rtos->rtos_specific_params; struct current_thread *ct; - head = target->head; - while (head) { + foreach_smp_target(head, target->smp_targets) { if (head->target->rtos != rtos) { struct linux_os *smp_os_linux = (struct linux_os *)head->target->rtos->rtos_specific_params; @@ -1413,8 +1409,6 @@ static int linux_os_smp_init(struct target *target) os_linux->nr_cpus++; free(smp_os_linux); } - - head = head->next; } return ERROR_OK; diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index fafbda231..b82ecb338 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -22,6 +22,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" @@ -97,7 +98,6 @@ static int os_alloc(struct target *target, struct rtos_type *ostype, /* RTOS drivers can override the packet handler in _create(). */ os->gdb_thread_packet = rtos_thread_packet; - os->gdb_v_packet = NULL; os->gdb_target_for_threadid = rtos_target_for_threadid; os->cmd_ctx = cmd_ctx; @@ -573,10 +573,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, @@ -789,10 +788,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; } @@ -836,3 +854,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 2e41c1465..745bea7bb 100644 --- a/src/rtos/rtos.h +++ b/src/rtos/rtos.h @@ -20,6 +20,7 @@ #define OPENOCD_RTOS_RTOS_H #include "server/server.h" +#include "target/breakpoints.h" #include "target/target.h" #include @@ -57,7 +58,6 @@ 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_v_packet)(struct connection *connection, char const *packet, int packet_size); 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. */ @@ -103,6 +103,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 { @@ -166,5 +172,8 @@ 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); #endif /* OPENOCD_RTOS_RTOS_H */ diff --git a/src/rtos/zephyr.c b/src/rtos/zephyr.c index 630511636..7f3325fea 100644 --- a/src/rtos/zephyr.c +++ b/src/rtos/zephyr.c @@ -375,15 +375,15 @@ static const struct symbol_table_elem zephyr_symbol_list[] = { .optional = false }, { - .symbol_name = "_kernel_openocd_offsets", + .symbol_name = "_kernel_thread_info_offsets", .optional = false }, { - .symbol_name = "_kernel_openocd_size_t_size", + .symbol_name = "_kernel_thread_info_size_t_size", .optional = false }, { - .symbol_name = "_kernel_openocd_num_offsets", + .symbol_name = "_kernel_thread_info_num_offsets", .optional = true }, { diff --git a/src/rtt/rtt.c b/src/rtt/rtt.c index bf3cca51b..3da3cce81 100644 --- a/src/rtt/rtt.c +++ b/src/rtt/rtt.c @@ -15,6 +15,10 @@ * along with this program. If not, see . */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/src/rtt/tcl.c b/src/rtt/tcl.c index f5abf2e5e..4a34d8b98 100644 --- a/src/rtt/tcl.c +++ b/src/rtt/tcl.c @@ -15,6 +15,10 @@ * along with this program. If not, see . */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include diff --git a/src/server/Makefile.am b/src/server/Makefile.am index fb5248bfd..e3699f181 100644 --- a/src/server/Makefile.am +++ b/src/server/Makefile.am @@ -6,7 +6,6 @@ noinst_LTLIBRARIES += %D%/libserver.la %D%/server.h \ %D%/telnet_server.h \ %D%/gdb_server.h \ - %D%/server_stubs.c \ %D%/tcl_server.c \ %D%/tcl_server.h \ %D%/rtt_server.c \ diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c old mode 100644 new mode 100755 index 0269e5895..21d68e20c --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -61,6 +61,13 @@ * found in most modern embedded processors. */ +enum gdb_output_flag { + /* GDB doesn't accept 'O' packets */ + GDB_OUTPUT_NO, + /* GDB accepts 'O' packets */ + GDB_OUTPUT_ALL, +}; + struct target_desc_format { char *tdesc; uint32_t tdesc_length; @@ -97,6 +104,8 @@ struct gdb_connection { struct target_desc_format target_desc; /* temporarily used for thread list support */ char *thread_list; + /* flag to mask the output from gdb_log_callback() */ + enum gdb_output_flag output_flag; }; #if 0 @@ -362,11 +371,13 @@ static int gdb_write(struct connection *connection, void *data, int len) return ERROR_SERVER_REMOTE_CLOSED; } -static void gdb_log_incoming_packet(char *packet) +static void gdb_log_incoming_packet(struct connection *connection, char *packet) { if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) return; + struct target *target = get_target_from_connection(connection); + /* Avoid dumping non-printable characters to the terminal */ const unsigned packet_len = strlen(packet); const char *nonprint = find_nonprint_char(packet, packet_len); @@ -380,25 +391,31 @@ static void gdb_log_incoming_packet(char *packet) if (packet_prefix_printable) { const unsigned int prefix_len = colon - packet + 1; /* + 1 to include the ':' */ const unsigned int payload_len = packet_len - prefix_len; - LOG_DEBUG("received packet: %.*s", prefix_len, packet, payload_len); + LOG_TARGET_DEBUG(target, "received packet: %.*s", prefix_len, + packet, payload_len); } else { - LOG_DEBUG("received packet: ", packet_len); + LOG_TARGET_DEBUG(target, "received packet: ", packet_len); } } else { /* All chars printable, dump the packet as is */ - LOG_DEBUG("received packet: %s", packet); + LOG_TARGET_DEBUG(target, "received packet: %s", packet); } } -static void gdb_log_outgoing_packet(char *packet_buf, unsigned int packet_len, unsigned char checksum) +static void gdb_log_outgoing_packet(struct connection *connection, char *packet_buf, + unsigned int packet_len, unsigned char checksum) { if (!LOG_LEVEL_IS(LOG_LVL_DEBUG)) return; + struct target *target = get_target_from_connection(connection); + if (find_nonprint_char(packet_buf, packet_len)) - LOG_DEBUG("sending packet: $#%2.2x", packet_len, checksum); + LOG_TARGET_DEBUG(target, "sending packet: $#%2.2x", + packet_len, checksum); else - LOG_DEBUG("sending packet: $%.*s#%2.2x'", packet_len, packet_buf, checksum); + LOG_TARGET_DEBUG(target, "sending packet: $%.*s#%2.2x", packet_len, packet_buf, + checksum); } static int gdb_put_packet_inner(struct connection *connection, @@ -443,7 +460,7 @@ static int gdb_put_packet_inner(struct connection *connection, #endif while (1) { - gdb_log_outgoing_packet(buffer, len, my_checksum); + gdb_log_outgoing_packet(connection, buffer, len, my_checksum); char local_buffer[1024]; local_buffer[0] = '$'; @@ -476,22 +493,27 @@ static int gdb_put_packet_inner(struct connection *connection, if (retval != ERROR_OK) return retval; - if (reply == '+') + if (reply == '+') { + gdb_log_incoming_packet(connection, "+"); break; - else if (reply == '-') { + } else if (reply == '-') { /* Stop sending output packets for now */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative reply, retrying"); } else if (reply == 0x3) { gdb_con->ctrl_c = true; + gdb_log_incoming_packet(connection, ""); retval = gdb_get_char(connection, &reply); if (retval != ERROR_OK) return retval; - if (reply == '+') + if (reply == '+') { + gdb_log_incoming_packet(connection, "+"); break; - else if (reply == '-') { + } else if (reply == '-') { /* Stop sending output packets for now */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative reply, retrying"); } else if (reply == '$') { LOG_ERROR("GDB missing ack(1) - assumed good"); @@ -668,6 +690,7 @@ static int gdb_get_packet_inner(struct connection *connection, case '$': break; case '+': + gdb_log_incoming_packet(connection, "+"); /* According to the GDB documentation * (https://sourceware.org/gdb/onlinedocs/gdb/Packet-Acknowledgment.html): * "gdb sends a final `+` acknowledgment of the stub's `OK` @@ -685,9 +708,11 @@ static int gdb_get_packet_inner(struct connection *connection, } break; case '-': + gdb_log_incoming_packet(connection, "-"); LOG_WARNING("negative acknowledgment, but no packet pending"); break; case 0x3: + gdb_log_incoming_packet(connection, ""); gdb_con->ctrl_c = true; *len = 0; return ERROR_OK; @@ -778,9 +803,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; } @@ -817,9 +845,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); @@ -934,7 +962,7 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec */ if (gdb_connection->frontend_state == TARGET_RUNNING) { /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; /* check fileio first */ if (target_get_gdb_fileio_info(target, target->fileio_info) == ERROR_OK) @@ -994,6 +1022,7 @@ static int gdb_new_connection(struct connection *connection) gdb_connection->target_desc.tdesc = NULL; gdb_connection->target_desc.tdesc_length = 0; gdb_connection->thread_list = NULL; + gdb_connection->output_flag = GDB_OUTPUT_NO; /* send ACK to GDB for debug request */ gdb_write(connection, "+", 1); @@ -1078,6 +1107,8 @@ static int gdb_new_connection(struct connection *connection) * register callback to be informed about target events */ target_register_event_callback(gdb_target_callback_event_handler, connection); + log_add_callback(gdb_log_callback, connection); + return ERROR_OK; } @@ -2273,21 +2304,25 @@ static int smp_reg_list_noread(struct target *target, return target_get_gdb_reg_list_noread(target, combined_list, combined_list_size, REG_CLASS_ALL); - int combined_allocated = 256; - *combined_list = malloc(combined_allocated * sizeof(struct reg *)); - if (*combined_list == NULL) { - LOG_ERROR("malloc(%d) failed", (int) (combined_allocated * sizeof(struct reg *))); + unsigned int combined_allocated = 256; + struct reg **local_list = malloc(combined_allocated * sizeof(struct reg *)); + if (!local_list) { + LOG_ERROR("malloc(%zu) failed", combined_allocated * sizeof(struct reg *)); return ERROR_FAIL; } - *combined_list_size = 0; + unsigned int local_list_size = 0; + struct target_list *head; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { + if (!target_was_examined(head->target)) + continue; + struct reg **reg_list = NULL; int reg_list_size; int result = target_get_gdb_reg_list_noread(head->target, ®_list, ®_list_size, reg_class); if (result != ERROR_OK) { - free(*combined_list); + free(local_list); return result; } for (int i = 0; i < reg_list_size; i++) { @@ -2297,8 +2332,8 @@ static int smp_reg_list_noread(struct target *target, /* Nested loop makes this O(n^2), but this entire function with * 5 RISC-V targets takes just 2ms on my computer. Fast enough * for me. */ - for (int j = 0; j < *combined_list_size; j++) { - struct reg *b = (*combined_list)[j]; + for (unsigned int j = 0; j < local_list_size; j++) { + struct reg *b = local_list[j]; if (!strcmp(a->name, b->name)) { found = true; if (a->size != b->size) { @@ -2306,7 +2341,7 @@ static int smp_reg_list_noread(struct target *target, "target, but %d bits on another target.", a->name, a->size, b->size); free(reg_list); - free(*combined_list); + free(local_list); return ERROR_FAIL; } break; @@ -2314,22 +2349,62 @@ static int smp_reg_list_noread(struct target *target, } if (!found) { LOG_DEBUG("[%s] %s not found in combined list", target_name(target), a->name); - if (*combined_list_size >= combined_allocated) { + if (local_list_size >= combined_allocated) { combined_allocated *= 2; - *combined_list = realloc(*combined_list, combined_allocated * sizeof(struct reg *)); - if (*combined_list == NULL) { - LOG_ERROR("realloc(%d) failed", (int) (combined_allocated * sizeof(struct reg *))); + local_list = realloc(local_list, combined_allocated * sizeof(struct reg *)); + if (!local_list) { + LOG_ERROR("realloc(%zu) failed", combined_allocated * sizeof(struct reg *)); return ERROR_FAIL; } } - (*combined_list)[*combined_list_size] = a; - (*combined_list_size)++; + local_list[local_list_size] = a; + local_list_size++; } } } free(reg_list); } + if (local_list_size == 0) { + LOG_ERROR("Unable to get register list"); + free(local_list); + return ERROR_FAIL; + } + + /* Now warn the user about any registers that weren't found in every target. */ + foreach_smp_target(head, target->smp_targets) { + if (!target_was_examined(head->target)) + continue; + + struct reg **reg_list = NULL; + int reg_list_size; + int result = target_get_gdb_reg_list_noread(head->target, ®_list, + ®_list_size, reg_class); + if (result != ERROR_OK) { + free(local_list); + return result; + } + for (unsigned int i = 0; i < local_list_size; i++) { + bool found = false; + struct reg *a = local_list[i]; + for (int j = 0; j < reg_list_size; j++) { + struct reg *b = reg_list[j]; + if (b->exist && !strcmp(a->name, b->name)) { + found = true; + break; + } + } + if (!found) { + LOG_WARNING("Register %s does not exist in %s, which is part of an SMP group where " + "this register does exist.", + a->name, target_name(head->target)); + } + } + free(reg_list); + } + + *combined_list = local_list; + *combined_list_size = local_list_size; return ERROR_OK; } @@ -2592,8 +2667,14 @@ static int gdb_generate_thread_list(struct target *target, char **thread_list_ou if (!thread_detail->exists) continue; - xml_printf(&retval, &thread_list, &pos, &size, - "", thread_detail->threadid); + if (thread_detail->thread_name_str) + xml_printf(&retval, &thread_list, &pos, &size, + "", + thread_detail->threadid, + thread_detail->thread_name_str); + else + xml_printf(&retval, &thread_list, &pos, &size, + "", thread_detail->threadid); if (thread_detail->thread_name_str) xml_printf(&retval, &thread_list, &pos, &size, @@ -2675,6 +2756,7 @@ static int gdb_query_packet(struct connection *connection, if (strncmp(packet, "qRcmd,", 6) == 0) { if (packet_size > 6) { + Jim_Interp *interp = cmd_ctx->interp; char *cmd; cmd = malloc((packet_size - 6) / 2 + 1); size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2); @@ -2682,16 +2764,68 @@ static int gdb_query_packet(struct connection *connection, LOG_DEBUG("qRcmd: %s", cmd); /* We want to print all debug output to GDB connection */ - log_add_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_ALL; target_call_timer_callbacks_now(); /* some commands need to know the GDB connection, make note of current * GDB connection. */ current_gdb_connection = gdb_connection; - command_run_line(cmd_ctx, cmd); + + struct target *saved_target_override = cmd_ctx->current_target_override; + cmd_ctx->current_target_override = NULL; + + struct command_context *old_context = Jim_GetAssocData(interp, "context"); + Jim_DeleteAssocData(interp, "context"); + int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx); + if (retval == JIM_OK) { + retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1)); + Jim_DeleteAssocData(interp, "context"); + } + int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context); + if (retval == JIM_OK) + retval = inner_retval; + + cmd_ctx->current_target_override = saved_target_override; + current_gdb_connection = NULL; target_call_timer_callbacks_now(); - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; free(cmd); + if (retval == JIM_RETURN) + retval = interp->returnCode; + int lenmsg; + const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg); + char *retmsg; + if (lenmsg && cretmsg[lenmsg - 1] != '\n') { + retmsg = alloc_printf("%s\n", cretmsg); + lenmsg++; + } else { + retmsg = strdup(cretmsg); + } + if (!retmsg) + return ERROR_GDB_BUFFER_TOO_SMALL; + + if (retval == JIM_OK) { + if (lenmsg) { + char *hex_buffer = malloc(lenmsg * 2 + 1); + if (!hex_buffer) { + free(retmsg); + return ERROR_GDB_BUFFER_TOO_SMALL; + } + + size_t pkt_len = hexify(hex_buffer, (const uint8_t *)retmsg, lenmsg, + lenmsg * 2 + 1); + gdb_put_packet(connection, hex_buffer, pkt_len); + free(hex_buffer); + } else { + gdb_put_packet(connection, "OK", 2); + } + } else { + if (lenmsg) + gdb_output_con(connection, retmsg); + gdb_send_error(connection, retval); + } + free(retmsg); + return ERROR_OK; } gdb_put_packet(connection, "OK", 2); return ERROR_OK; @@ -2871,7 +3005,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p if (parse[0] == 'c') { gdb_running_type = 'c'; LOG_DEBUG("target %s continue", target_name(target)); - log_add_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_ALL; retval = target_resume(target, 1, 0, 0, 0); if (retval == ERROR_TARGET_NOT_HALTED) LOG_INFO("target %s was not halted when resume was requested", target_name(target)); @@ -2975,7 +3109,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p } LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id); - log_add_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_ALL; target_call_event_callbacks(ct, TARGET_EVENT_GDB_START); if (fake_step) { @@ -2992,7 +3126,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p "T05thread:%016"PRIx64";", thread_id); gdb_put_packet(connection, sig_reply, sig_reply_len); - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; return true; } @@ -3004,7 +3138,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p LOG_DEBUG("stepi ignored. GDB will now fetch the register state " "from the target."); gdb_sig_halted(connection); - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; } else gdb_connection->frontend_state = TARGET_RUNNING; return true; @@ -3022,7 +3156,7 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p /* send back signal information */ gdb_signal_reply(ct, connection); /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + gdb_connection->output_flag = GDB_OUTPUT_NO; } else gdb_connection->frontend_state = TARGET_RUNNING; } else { @@ -3126,7 +3260,9 @@ static bool gdb_handle_vrun_packet(struct connection *connection, const char *pa gdb_put_packet(connection, "S00", 3); return true; } - +extern void wlink_quitreset(void); +extern int register_write_direct(struct target *target, unsigned number, + uint64_t value); static int gdb_v_packet(struct connection *connection, char const *packet, int packet_size) { @@ -3134,11 +3270,6 @@ static int gdb_v_packet(struct connection *connection, int result; struct target *target = get_target_from_connection(connection); - if (target->rtos != NULL && target->rtos->gdb_v_packet != NULL) { - int out = target->rtos->gdb_v_packet(connection, packet, packet_size); - if (out != GDB_THREAD_PACKET_NOT_CONSUMED) - return out; - } if (strncmp(packet, "vCont", 5) == 0) { bool handled; @@ -3287,7 +3418,13 @@ static int gdb_v_packet(struct connection *connection, return ERROR_OK; } - + if (strncmp(packet, "vKill", 5) == 0) { + if (strncmp(target->type->name, "wch_riscv", 9) == 0) { + // uint64_t value=0; + // register_write_direct(target, 0x7f2,0); + wlink_quitreset(); + } + } gdb_put_packet(connection, "", 0); return ERROR_OK; } @@ -3357,6 +3494,10 @@ static void gdb_log_callback(void *priv, const char *file, unsigned line, struct connection *connection = priv; struct gdb_connection *gdb_con = connection->priv; + if (gdb_con->output_flag == GDB_OUTPUT_NO) + /* No out allowed */ + return; + if (gdb_con->busy) { /* do not reply this using the O packet */ return; @@ -3405,10 +3546,29 @@ static int gdb_input_inner(struct connection *connection) /* terminate with zero */ gdb_packet_buffer[packet_size] = '\0'; + // if(1){ + // char buf[64]; + // unsigned offset = 0; + // int i = 0; + // while (i < packet_size && offset < 56) { + // if (packet[i] == '\\') { + // buf[offset++] = '\\'; + // buf[offset++] = '\\'; + // } else if (isprint(packet[i])) { + // buf[offset++] = packet[i]; + // } else { + // sprintf(buf + offset, "\\x%02x", (unsigned char) packet[i]); + // offset += 4; + // } + // i++; + // } + // buf[offset] = 0; + // LOG_INFO("received packet: '%s'%s", buf, i < packet_size ? "..." : ""); + // } + if (packet_size > 0) { - gdb_log_incoming_packet(gdb_packet_buffer); + gdb_log_incoming_packet(connection, gdb_packet_buffer); - if (packet_size > 0) { retval = ERROR_OK; switch (packet[0]) { case 'T': /* Is thread alive? */ @@ -3459,7 +3619,7 @@ static int gdb_input_inner(struct connection *connection) case 's': { gdb_thread_packet(connection, packet, packet_size); - log_add_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_ALL; if (gdb_con->mem_write_error) { LOG_ERROR("Memory write failure!"); @@ -3502,7 +3662,7 @@ static int gdb_input_inner(struct connection *connection) gdb_sig_halted(connection); /* stop forwarding log packets! */ - log_remove_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_NO; } else { /* We're running/stepping, in which case we can * forward log output until the target is halted @@ -3574,7 +3734,7 @@ static int gdb_input_inner(struct connection *connection) * Fretcode,errno,Ctrl-C flag;call-specific attachment */ gdb_con->frontend_state = TARGET_RUNNING; - log_add_callback(gdb_log_callback, connection); + gdb_con->output_flag = GDB_OUTPUT_ALL; gdb_fileio_response_packet(connection, packet, packet_size); break; @@ -3627,6 +3787,37 @@ static int gdb_input(struct connection *connection) return ERROR_OK; } +static void gdb_keep_client_alive(struct connection *connection) +{ + struct gdb_connection *gdb_con = connection->priv; + + if (gdb_con->busy) { + /* do not send packets, retry asap */ + return; + } + + switch (gdb_con->output_flag) { + case GDB_OUTPUT_NO: + /* no need for keep-alive */ + break; + case GDB_OUTPUT_ALL: + /* send an empty O packet */ + gdb_output_con(connection, ""); + break; + default: + break; + } +} + +static const struct service_driver gdb_service_driver = { + .name = "gdb", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = gdb_new_connection, + .input_handler = gdb_input, + .connection_closed_handler = gdb_connection_closed, + .keep_client_alive_handler = gdb_keep_client_alive, +}; + static int gdb_target_start(struct target *target, const char *port) { struct gdb_service *gdb_service; @@ -3643,19 +3834,14 @@ static int gdb_target_start(struct target *target, const char *port) gdb_service->core[1] = -1; target->gdb_service = gdb_service; - ret = add_service("gdb", - port, target->gdb_max_connections, &gdb_new_connection, &gdb_input, - &gdb_connection_closed, gdb_service); + ret = add_service(&gdb_service_driver, port, target->gdb_max_connections, gdb_service); /* initialize all targets gdb service with the same pointer */ { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr != target) curr->gdb_service = gdb_service; - head = head->next; } } return ret; diff --git a/src/server/ipdbg.c b/src/server/ipdbg.c index ec2fae8c0..3bbcf0714 100644 --- a/src/server/ipdbg.c +++ b/src/server/ipdbg.c @@ -587,6 +587,15 @@ static int ipdbg_on_connection_closed(struct connection *connection) return ipdbg_stop_polling(connection->service->priv); } +static const struct service_driver ipdbg_service_driver = { + .name = "ipdbg", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = ipdbg_on_new_connection, + .input_handler = ipdbg_on_connection_input, + .connection_closed_handler = ipdbg_on_connection_closed, + .keep_client_alive_handler = NULL, +}; + static int ipdbg_start(uint16_t port, struct jtag_tap *tap, uint32_t user_instruction, uint8_t data_register_length, struct ipdbg_virtual_ir_info *virtual_ir, uint8_t tool) { @@ -618,8 +627,7 @@ static int ipdbg_start(uint16_t port, struct jtag_tap *tap, uint32_t user_instru char port_str_buffer[IPDBG_TCP_PORT_STR_MAX_LENGTH]; snprintf(port_str_buffer, IPDBG_TCP_PORT_STR_MAX_LENGTH, "%u", port); - retval = add_service("ipdbg", port_str_buffer, 1, &ipdbg_on_new_connection, - &ipdbg_on_connection_input, &ipdbg_on_connection_closed, service); + retval = add_service(&ipdbg_service_driver, port_str_buffer, 1, service); if (retval == ERROR_OK) { ipdbg_add_service(service); if (hub->active_services == 0 && hub->active_connections == 0) diff --git a/src/server/rtt_server.c b/src/server/rtt_server.c index d49e4d000..3850c2687 100644 --- a/src/server/rtt_server.c +++ b/src/server/rtt_server.c @@ -15,6 +15,10 @@ * along with this program. If not, see . */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include @@ -110,6 +114,15 @@ static int rtt_input(struct connection *connection) return ERROR_OK; } +static const struct service_driver rtt_service_driver = { + .name = "rtt", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = rtt_new_connection, + .input_handler = rtt_input, + .connection_closed_handler = rtt_connection_closed, + .keep_client_alive_handler = NULL, +}; + COMMAND_HANDLER(handle_rtt_start_command) { int ret; @@ -125,8 +138,7 @@ COMMAND_HANDLER(handle_rtt_start_command) COMMAND_PARSE_NUMBER(uint, CMD_ARGV[1], service->channel); - ret = add_service("rtt", CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, - rtt_new_connection, rtt_input, rtt_connection_closed, service); + ret = add_service(&rtt_service_driver, CMD_ARGV[0], CONNECTION_LIMIT_UNLIMITED, service); if (ret != ERROR_OK) { free(service); diff --git a/src/server/server.c b/src/server/server.c index 3f579bfc6..7b0004d64 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -205,13 +205,8 @@ static void free_service(struct service *c) free(c); } -int add_service(char *name, - const char *port, - int max_connections, - new_connection_handler_t new_connection_handler, - input_handler_t input_handler, - connection_closed_handler_t connection_closed_handler, - void *priv) +int add_service(const struct service_driver *driver, const char *port, + int max_connections, void *priv) { struct service *c, **p; struct hostent *hp; @@ -219,14 +214,16 @@ int add_service(char *name, c = malloc(sizeof(struct service)); - c->name = strdup(name); + c->name = strdup(driver->name); c->port = strdup(port); c->max_connections = 1; /* Only TCP/IP ports can support more than one connection */ c->fd = -1; c->connections = NULL; - c->new_connection = new_connection_handler; - c->input = input_handler; - c->connection_closed = connection_closed_handler; + c->new_connection_during_keep_alive = driver->new_connection_during_keep_alive_handler; + c->new_connection = driver->new_connection_handler; + c->input = driver->input_handler; + c->connection_closed = driver->connection_closed_handler; + c->keep_client_alive = driver->keep_client_alive_handler; c->priv = priv; c->next = NULL; long portnumber; @@ -278,7 +275,7 @@ int add_service(char *name, c->sin.sin_port = htons(c->portnumber); if (bind(c->fd, (struct sockaddr *)&c->sin, sizeof(c->sin)) == -1) { - LOG_ERROR("couldn't bind %s to socket on port %d: %s", name, c->portnumber, strerror(errno)); + LOG_ERROR("couldn't bind %s to socket on port %d: %s", c->name, c->portnumber, strerror(errno)); close_socket(c->fd); free_service(c); return ERROR_FAIL; @@ -309,7 +306,7 @@ int add_service(char *name, socklen_t addr_in_size = sizeof(addr_in); if (getsockname(c->fd, (struct sockaddr *)&addr_in, &addr_in_size) == 0) LOG_INFO("Listening on port %hu for %s connections", - ntohs(addr_in.sin_port), name); + ntohs(addr_in.sin_port), c->name); } else if (c->type == CONNECTION_STDINOUT) { c->fd = fileno(stdin); @@ -424,6 +421,14 @@ static int remove_services(void) return ERROR_OK; } +void server_keep_clients_alive(void) +{ + for (struct service *s = services; s; s = s->next) + if (s->keep_client_alive) + for (struct connection *c = s->connections; c; c = c->next) + s->keep_client_alive(c); +} + int server_loop(struct command_context *command_context) { struct service *service; @@ -487,10 +492,8 @@ int server_loop(struct command_context *command_context) timeout_ms = polling_period; tv.tv_usec = timeout_ms * 1000; /* Only while we're sleeping we'll let others run */ - openocd_sleep_prelude(); kept_alive(); retval = socket_select(fd_max + 1, &read_fds, NULL, NULL, &tv); - openocd_sleep_postlude(); } if (retval == -1) { diff --git a/src/server/server.h b/src/server/server.h index de18d2b4b..a6b1963a6 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -55,9 +55,25 @@ struct connection { struct connection *next; }; -typedef int (*new_connection_handler_t)(struct connection *connection); -typedef int (*input_handler_t)(struct connection *connection); -typedef int (*connection_closed_handler_t)(struct connection *connection); +struct service_driver { + /** the name of the server */ + const char *name; + /** optional minimal setup to accept a connection during keep-alive */ + int (*new_connection_during_keep_alive_handler)(struct connection *connection); + /** + * complete code to accept a new connection. + * If 'new_connection_during_keep_alive_handler' above is present, this can be + * either called alone during the server_loop, or after the function above. + * Check the implementation in gdb_server. + * */ + int (*new_connection_handler)(struct connection *connection); + /** callback to handle incoming data */ + int (*input_handler)(struct connection *connection); + /** callback to tear down the connection */ + int (*connection_closed_handler)(struct connection *connection); + /** called periodically to send keep-alive messages on the connection */ + void (*keep_client_alive_handler)(struct connection *connection); +}; struct service { char *name; @@ -68,17 +84,17 @@ struct service { struct sockaddr_in sin; int max_connections; struct connection *connections; - new_connection_handler_t new_connection; - input_handler_t input; - connection_closed_handler_t connection_closed; + int (*new_connection_during_keep_alive)(struct connection *connection); + int (*new_connection)(struct connection *connection); + int (*input)(struct connection *connection); + int (*connection_closed)(struct connection *connection); + void (*keep_client_alive)(struct connection *connection); void *priv; struct service *next; }; -int add_service(char *name, const char *port, - int max_connections, new_connection_handler_t new_connection_handler, - input_handler_t in_handler, connection_closed_handler_t close_handler, - void *priv); +int add_service(const struct service_driver *driver, const char *port, + int max_connections, void *priv); int remove_service(const char *name, const char *port); int server_host_os_entry(void); @@ -90,6 +106,8 @@ int server_quit(void); void server_free(void); void exit_on_signal(int sig); +void server_keep_clients_alive(void); + int server_loop(struct command_context *command_context); int server_register_commands(struct command_context *context); @@ -97,15 +115,6 @@ int server_register_commands(struct command_context *context); int connection_write(struct connection *connection, const void *data, int len); int connection_read(struct connection *connection, void *data, int len); -/** - * Used by server_loop(), defined in server_stubs.c - */ -void openocd_sleep_prelude(void); -/** - * Used by server_loop(), defined in server_stubs.c - */ -void openocd_sleep_postlude(void); - /** * Defines an extended command handler function declaration to enable * access to (and manipulation of) the server port number. diff --git a/src/server/server_stubs.c b/src/server/server_stubs.c deleted file mode 100644 index a4c017289..000000000 --- a/src/server/server_stubs.c +++ /dev/null @@ -1,30 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 Zachary T Welch * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ***************************************************************************/ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include "server.h" - -void openocd_sleep_prelude(void) -{ - /* no-op */ -} -void openocd_sleep_postlude(void) -{ - /* no-op */ -} diff --git a/src/server/tcl_server.c b/src/server/tcl_server.c index e08823224..458d7eada 100644 --- a/src/server/tcl_server.c +++ b/src/server/tcl_server.c @@ -276,6 +276,15 @@ static int tcl_closed(struct connection *connection) return ERROR_OK; } +static const struct service_driver tcl_service_driver = { + .name = "tcl", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = tcl_new_connection, + .input_handler = tcl_input, + .connection_closed_handler = tcl_closed, + .keep_client_alive_handler = NULL, +}; + int tcl_init(void) { if (strcmp(tcl_port, "disabled") == 0) { @@ -283,9 +292,7 @@ int tcl_init(void) return ERROR_OK; } - return add_service("tcl", tcl_port, CONNECTION_LIMIT_UNLIMITED, - &tcl_new_connection, &tcl_input, - &tcl_closed, NULL); + return add_service(&tcl_service_driver, tcl_port, CONNECTION_LIMIT_UNLIMITED, NULL); } COMMAND_HANDLER(handle_tcl_port_command) diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 2ebcff163..791a1a548 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -946,6 +946,15 @@ static int telnet_connection_closed(struct connection *connection) return ERROR_OK; } +static const struct service_driver telnet_service_driver = { + .name = "telnet", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = telnet_new_connection, + .input_handler = telnet_input, + .connection_closed_handler = telnet_connection_closed, + .keep_client_alive_handler = NULL, +}; + int telnet_init(char *banner) { if (strcmp(telnet_port, "disabled") == 0) { @@ -963,8 +972,7 @@ int telnet_init(char *banner) telnet_service->banner = banner; - int ret = add_service("telnet", telnet_port, CONNECTION_LIMIT_UNLIMITED, - telnet_new_connection, telnet_input, telnet_connection_closed, + int ret = add_service(&telnet_service_driver, telnet_port, CONNECTION_LIMIT_UNLIMITED, telnet_service); if (ret != ERROR_OK) { diff --git a/src/target/Makefile.am b/src/target/Makefile.am old mode 100644 new mode 100755 index 49e882fe6..9aeb360db --- a/src/target/Makefile.am +++ b/src/target/Makefile.am @@ -14,6 +14,7 @@ noinst_LTLIBRARIES += %D%/libtarget.la $(ARMV7_SRC) \ $(ARM_MISC_SRC) \ $(AVR32_SRC) \ + $(WCH_RISCV_SRC) \ $(MIPS32_SRC) \ $(NDS32_SRC) \ $(STM8_SRC) \ @@ -115,6 +116,9 @@ AVR32_SRC = \ %D%/avr32_mem.c \ %D%/avr32_regs.c +WCH_RISCV_SRC = \ + %D%/wch_riscv.c\ + %D%/wch_riscv-013.c MIPS32_SRC = \ %D%/mips32.c \ %D%/mips_m4k.c \ @@ -256,6 +260,7 @@ ARC_SRC = \ %D%/arc_cmd.h \ %D%/arc_jtag.h \ %D%/arc_mem.h \ + %D%/wch_riscv.h \ %D%/rtt.h include %D%/openrisc/Makefile.am diff --git a/src/target/a64_disassembler.c b/src/target/a64_disassembler.c index bd78129dd..58ddf603e 100644 --- a/src/target/a64_disassembler.c +++ b/src/target/a64_disassembler.c @@ -26,7 +26,7 @@ #if HAVE_CAPSTONE -#include +#include static void print_opcode(struct command_invocation *cmd, const cs_insn *insn) { diff --git a/src/target/aarch64.c b/src/target/aarch64.c index fc6bd6b30..8838da927 100644 --- a/src/target/aarch64.c +++ b/src/target/aarch64.c @@ -102,6 +102,7 @@ static int aarch64_restore_system_control_reg(struct target *target) case ARM_MODE_FIQ: case ARM_MODE_IRQ: case ARM_MODE_HYP: + case ARM_MODE_UND: case ARM_MODE_SYS: instr = ARMV4_5_MCR(15, 0, 0, 1, 0, 0); break; @@ -180,6 +181,7 @@ static int aarch64_mmu_modify(struct target *target, int enable) case ARM_MODE_FIQ: case ARM_MODE_IRQ: case ARM_MODE_HYP: + case ARM_MODE_UND: case ARM_MODE_SYS: instr = ARMV4_5_MCR(15, 0, 0, 1, 0, 0); break; @@ -331,15 +333,14 @@ static int aarch64_wait_halt_one(struct target *target) static int aarch64_prepare_halt_smp(struct target *target, bool exc_target, struct target **p_first) { int retval = ERROR_OK; - struct target_list *head = target->head; + struct target_list *head; struct target *first = NULL; LOG_DEBUG("target %s exc %i", target_name(target), exc_target); - while (head) { + foreach_smp_target(head, target->smp_targets) { struct target *curr = head->target; struct armv8_common *armv8 = target_to_armv8(curr); - head = head->next; if (exc_target && curr == target) continue; @@ -428,7 +429,7 @@ static int aarch64_halt_smp(struct target *target, bool exc_target) struct target_list *head; struct target *curr; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { int halted; curr = head->target; @@ -478,7 +479,7 @@ static int update_halt_gdb(struct target *target, enum target_debug_reason debug } /* poll all targets in the group, but skip the target that serves GDB */ - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { curr = head->target; /* skip calling context */ if (curr == target) @@ -743,7 +744,7 @@ static int aarch64_prep_restart_smp(struct target *target, int handle_breakpoint struct target *first = NULL; uint64_t address; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { struct target *curr = head->target; /* skip calling target */ @@ -798,7 +799,7 @@ static int aarch64_step_restart_smp(struct target *target) struct target *curr = target; bool all_resumed = true; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { uint32_t prsr; int resumed; @@ -886,7 +887,7 @@ static int aarch64_resume(struct target *target, int current, struct target_list *head; bool all_resumed = true; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { uint32_t prsr; int resumed; @@ -1049,6 +1050,7 @@ static int aarch64_post_debug_entry(struct target *target) case ARM_MODE_FIQ: case ARM_MODE_IRQ: case ARM_MODE_HYP: + case ARM_MODE_UND: case ARM_MODE_SYS: instr = ARMV4_5_MRC(15, 0, 0, 1, 0, 0); break; @@ -1234,7 +1236,7 @@ static int aarch64_set_breakpoint(struct target *target, struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *brp_list = aarch64->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -1247,7 +1249,7 @@ static int aarch64_set_breakpoint(struct target *target, LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = brp_i + 1; + breakpoint_hw_set(breakpoint, brp_i); if (breakpoint->length == 2) byte_addr_select = (3 << (breakpoint->address & 0x02)); control = ((matchmode & 0x7) << 20) @@ -1331,7 +1333,7 @@ static int aarch64_set_breakpoint(struct target *target, breakpoint->address & 0xFFFFFFFFFFFFFFFE, breakpoint->length); - breakpoint->set = 0x11; /* Any nice value but 0 */ + breakpoint->is_set = true; } /* Ensure that halting debug mode is enable */ @@ -1355,7 +1357,7 @@ static int aarch64_set_context_breakpoint(struct target *target, struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *brp_list = aarch64->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return retval; } @@ -1369,7 +1371,7 @@ static int aarch64_set_context_breakpoint(struct target *target, return ERROR_FAIL; } - breakpoint->set = brp_i + 1; + breakpoint_hw_set(breakpoint, brp_i); control = ((matchmode & 0x7) << 20) | (1 << 13) | (byte_addr_select << 5) @@ -1408,7 +1410,7 @@ static int aarch64_set_hybrid_breakpoint(struct target *target, struct breakpoin struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *brp_list = aarch64->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return retval; } @@ -1433,7 +1435,7 @@ static int aarch64_set_hybrid_breakpoint(struct target *target, struct breakpoin return ERROR_FAIL; } - breakpoint->set = brp_1 + 1; + breakpoint_hw_set(breakpoint, brp_1); breakpoint->linked_brp = brp_2; control_ctx = ((ctx_machmode & 0x7) << 20) | (brp_2 << 16) @@ -1488,16 +1490,16 @@ static int aarch64_unset_breakpoint(struct target *target, struct breakpoint *br struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *brp_list = aarch64->brp_list; - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } if (breakpoint->type == BKPT_HARD) { if ((breakpoint->address != 0) && (breakpoint->asid != 0)) { - int brp_i = breakpoint->set - 1; + int brp_i = breakpoint->number; int brp_j = breakpoint->linked_brp; - if ((brp_i < 0) || (brp_i >= aarch64->brp_num)) { + if (brp_i >= aarch64->brp_num) { LOG_DEBUG("Invalid BRP number in breakpoint"); return ERROR_OK; } @@ -1547,12 +1549,12 @@ static int aarch64_unset_breakpoint(struct target *target, struct breakpoint *br return retval; breakpoint->linked_brp = 0; - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } else { - int brp_i = breakpoint->set - 1; - if ((brp_i < 0) || (brp_i >= aarch64->brp_num)) { + int brp_i = breakpoint->number; + if (brp_i >= aarch64->brp_num) { LOG_DEBUG("Invalid BRP number in breakpoint"); return ERROR_OK; } @@ -1577,7 +1579,7 @@ static int aarch64_unset_breakpoint(struct target *target, struct breakpoint *br (uint32_t)brp_list[brp_i].value); if (retval != ERROR_OK) return retval; - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } } else { @@ -1609,7 +1611,7 @@ static int aarch64_unset_breakpoint(struct target *target, struct breakpoint *br breakpoint->address & 0xFFFFFFFFFFFFFFFE, breakpoint->length); } - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } @@ -1674,7 +1676,7 @@ static int aarch64_remove_breakpoint(struct target *target, struct breakpoint *b } #endif - if (breakpoint->set) { + if (breakpoint->is_set) { aarch64_unset_breakpoint(target, breakpoint); if (breakpoint->type == BKPT_HARD) aarch64->brp_num_available++; @@ -1694,7 +1696,7 @@ static int aarch64_set_watchpoint(struct target *target, struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *wp_list = aarch64->wp_list; - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return ERROR_OK; } @@ -1762,7 +1764,7 @@ static int aarch64_set_watchpoint(struct target *target, } wp_list[wp_i].used = 1; - watchpoint->set = wp_i + 1; + watchpoint_set(watchpoint, wp_i); return ERROR_OK; } @@ -1771,18 +1773,18 @@ static int aarch64_set_watchpoint(struct target *target, static int aarch64_unset_watchpoint(struct target *target, struct watchpoint *watchpoint) { - int retval, wp_i; + int retval; struct aarch64_common *aarch64 = target_to_aarch64(target); struct armv8_common *armv8 = &aarch64->armv8_common; struct aarch64_brp *wp_list = aarch64->wp_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - wp_i = watchpoint->set - 1; - if ((wp_i < 0) || (wp_i >= aarch64->wp_num)) { + int wp_i = watchpoint->number; + if (wp_i >= aarch64->wp_num) { LOG_DEBUG("Invalid WP number in watchpoint"); return ERROR_OK; } @@ -1807,7 +1809,7 @@ static int aarch64_unset_watchpoint(struct target *target, (uint32_t)wp_list[wp_i].value); if (retval != ERROR_OK) return retval; - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -1835,7 +1837,7 @@ static int aarch64_remove_watchpoint(struct target *target, { struct aarch64_common *aarch64 = target_to_aarch64(target); - if (watchpoint->set) { + if (watchpoint->is_set) { aarch64_unset_watchpoint(target, watchpoint); aarch64->wp_num_available++; } @@ -2635,8 +2637,10 @@ static int aarch64_examine_first(struct target *target) LOG_DEBUG("ttypr = 0x%08" PRIx64, ttypr); LOG_DEBUG("debug = 0x%08" PRIx64, debug); - if (!pc->cti) + if (!pc->cti) { + LOG_TARGET_ERROR(target, "CTI not specified"); return ERROR_FAIL; + } armv8->cti = pc->cti; diff --git a/src/target/arc.c b/src/target/arc.c index 4b546c3b4..471f16a98 100644 --- a/src/target/arc.c +++ b/src/target/arc.c @@ -1500,7 +1500,7 @@ static int arc_configure_actionpoint(struct target *target, uint32_t ap_num, static int arc_set_breakpoint(struct target *target, struct breakpoint *breakpoint) { - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -1542,7 +1542,7 @@ static int arc_set_breakpoint(struct target *target, return ERROR_COMMAND_ARGUMENT_INVALID; } - breakpoint->set = 64; /* Any nice value but 0 */ + breakpoint->is_set = true; } else if (breakpoint->type == BKPT_HARD) { struct arc_common *arc = target_to_arc(target); struct arc_actionpoint *ap_list = arc->actionpoints_list; @@ -1563,7 +1563,7 @@ static int arc_set_breakpoint(struct target *target, breakpoint->address, AP_AC_TT_READWRITE, AP_AC_AT_INST_ADDR); if (retval == ERROR_OK) { - breakpoint->set = bp_num + 1; + breakpoint_hw_set(breakpoint, bp_num); ap_list[bp_num].used = 1; ap_list[bp_num].bp_value = breakpoint->address; ap_list[bp_num].type = ARC_AP_BREAKPOINT; @@ -1588,7 +1588,7 @@ static int arc_unset_breakpoint(struct target *target, { int retval = ERROR_OK; - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } @@ -1633,14 +1633,14 @@ static int arc_unset_breakpoint(struct target *target, LOG_ERROR("Invalid breakpoint length: target supports only 2 or 4"); return ERROR_COMMAND_ARGUMENT_INVALID; } - breakpoint->set = 0; + breakpoint->is_set = false; } else if (breakpoint->type == BKPT_HARD) { struct arc_common *arc = target_to_arc(target); struct arc_actionpoint *ap_list = arc->actionpoints_list; - unsigned int bp_num = breakpoint->set - 1; + unsigned int bp_num = breakpoint->number; - if ((breakpoint->set == 0) || (bp_num >= arc->actionpoints_num)) { + if (bp_num >= arc->actionpoints_num) { LOG_DEBUG("Invalid actionpoint ID: %u in breakpoint: %" PRIu32, bp_num, breakpoint->unique_id); return ERROR_OK; @@ -1650,11 +1650,11 @@ static int arc_unset_breakpoint(struct target *target, breakpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_INST_ADDR); if (retval == ERROR_OK) { - breakpoint->set = 0; + breakpoint->is_set = false; ap_list[bp_num].used = 0; ap_list[bp_num].bp_value = 0; - LOG_DEBUG("bpid: %" PRIu32 " - released actionpoint ID: %i", + LOG_DEBUG("bpid: %" PRIu32 " - released actionpoint ID: %u", breakpoint->unique_id, bp_num); } } else { @@ -1684,7 +1684,7 @@ static int arc_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) { if (target->state == TARGET_HALTED) { - if (breakpoint->set) + if (breakpoint->is_set) CHECK_RETVAL(arc_unset_breakpoint(target, breakpoint)); } else { LOG_WARNING("target not halted"); @@ -1818,7 +1818,7 @@ static int arc_set_watchpoint(struct target *target, struct arc_common *arc = target_to_arc(target); struct arc_actionpoint *ap_list = arc->actionpoints_list; - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return ERROR_OK; } @@ -1859,7 +1859,7 @@ static int arc_set_watchpoint(struct target *target, watchpoint->address, enable, AP_AC_AT_MEMORY_ADDR); if (retval == ERROR_OK) { - watchpoint->set = wp_num + 1; + watchpoint_set(watchpoint, wp_num); ap_list[wp_num].used = 1; ap_list[wp_num].bp_value = watchpoint->address; ap_list[wp_num].type = ARC_AP_WATCHPOINT; @@ -1878,13 +1878,13 @@ static int arc_unset_watchpoint(struct target *target, struct arc_common *arc = target_to_arc(target); struct arc_actionpoint *ap_list = arc->actionpoints_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - unsigned int wp_num = watchpoint->set - 1; - if ((watchpoint->set == 0) || (wp_num >= arc->actionpoints_num)) { + unsigned int wp_num = watchpoint->number; + if (wp_num >= arc->actionpoints_num) { LOG_DEBUG("Invalid actionpoint ID: %u in watchpoint: %" PRIu32, wp_num, watchpoint->unique_id); return ERROR_OK; @@ -1894,7 +1894,7 @@ static int arc_unset_watchpoint(struct target *target, watchpoint->address, AP_AC_TT_DISABLE, AP_AC_AT_MEMORY_ADDR); if (retval == ERROR_OK) { - watchpoint->set = 0; + watchpoint->is_set = false; ap_list[wp_num].used = 0; ap_list[wp_num].bp_value = 0; @@ -1926,7 +1926,7 @@ static int arc_remove_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) CHECK_RETVAL(arc_unset_watchpoint(target, watchpoint)); return ERROR_OK; @@ -1953,8 +1953,8 @@ static int arc_hit_watchpoint(struct target *target, struct watchpoint **hit_wat watchpoint = watchpoint->next) { if (actionpoint->bp_value == watchpoint->address) { *hit_watchpoint = watchpoint; - LOG_DEBUG("Hit watchpoint, wpid: %" PRIu32 ", watchpoint num: %i", - watchpoint->unique_id, watchpoint->set - 1); + LOG_DEBUG("Hit watchpoint, wpid: %" PRIu32 ", watchpoint num: %u", + watchpoint->unique_id, watchpoint->number); return ERROR_OK; } } diff --git a/src/target/arm7_9_common.c b/src/target/arm7_9_common.c index cf77a81a7..da047c3d0 100644 --- a/src/target/arm7_9_common.c +++ b/src/target/arm7_9_common.c @@ -93,19 +93,20 @@ static void arm7_9_assign_wp(struct arm7_9_common *arm7_9, struct breakpoint *br { if (!arm7_9->wp0_used) { arm7_9->wp0_used = 1; - breakpoint->set = 1; + breakpoint_hw_set(breakpoint, 0); arm7_9->wp_available--; } else if (!arm7_9->wp1_used) { arm7_9->wp1_used = 1; - breakpoint->set = 2; + breakpoint_hw_set(breakpoint, 1); arm7_9->wp_available--; - } else + } else { LOG_ERROR("BUG: no hardware comparator available"); + } - LOG_DEBUG("BPID: %" PRIu32 " (0x%08" TARGET_PRIxADDR ") using hw wp: %d", + LOG_DEBUG("BPID: %" PRIu32 " (0x%08" TARGET_PRIxADDR ") using hw wp: %u", breakpoint->unique_id, breakpoint->address, - breakpoint->set); + breakpoint->number); } /** @@ -203,16 +204,16 @@ static int arm7_9_set_breakpoint(struct target *target, struct breakpoint *break uint32_t mask = (breakpoint->length == 4) ? 0x3u : 0x1u; /* reassign a hw breakpoint */ - if (breakpoint->set == 0) + if (!breakpoint->is_set) arm7_9_assign_wp(arm7_9, breakpoint); - if (breakpoint->set == 1) { + if (breakpoint->number == 0) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_VALUE], breakpoint->address); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_ADDR_MASK], mask); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_DATA_MASK], 0xffffffffu); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_MASK], ~EICE_W_CTRL_NOPC & 0xff); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], EICE_W_CTRL_ENABLE); - } else if (breakpoint->set == 2) { + } else if (breakpoint->number == 1) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], breakpoint->address); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_MASK], mask); embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_DATA_MASK], 0xffffffffu); @@ -226,7 +227,7 @@ static int arm7_9_set_breakpoint(struct target *target, struct breakpoint *break retval = jtag_execute_queue(); } else if (breakpoint->type == BKPT_SOFT) { /* did we already set this breakpoint? */ - if (breakpoint->set) + if (breakpoint->is_set) return ERROR_OK; if (breakpoint->length == 4) { @@ -277,7 +278,7 @@ static int arm7_9_set_breakpoint(struct target *target, struct breakpoint *break arm7_9->sw_breakpoint_count++; - breakpoint->set = 1; + breakpoint->is_set = true; } return retval; @@ -304,7 +305,7 @@ static int arm7_9_unset_breakpoint(struct target *target, struct breakpoint *bre breakpoint->unique_id, breakpoint->address); - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } @@ -312,18 +313,18 @@ static int arm7_9_unset_breakpoint(struct target *target, struct breakpoint *bre if (breakpoint->type == BKPT_HARD) { LOG_DEBUG("BPID: %" PRIu32 " Releasing hw wp: %d", breakpoint->unique_id, - breakpoint->set); - if (breakpoint->set == 1) { + breakpoint->is_set); + if (breakpoint->number == 0) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); arm7_9->wp0_used = 0; arm7_9->wp_available++; - } else if (breakpoint->set == 2) { + } else if (breakpoint->number == 1) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); arm7_9->wp1_used = 0; arm7_9->wp_available++; } retval = jtag_execute_queue(); - breakpoint->set = 0; + breakpoint->is_set = false; } else { /* restore original instruction (kept in target endianness) */ if (breakpoint->length == 4) { @@ -368,7 +369,7 @@ static int arm7_9_unset_breakpoint(struct target *target, struct breakpoint *bre EICE_W1_CONTROL_VALUE], 0); } - breakpoint->set = 0; + breakpoint->is_set = false; } return retval; @@ -491,7 +492,7 @@ static int arm7_9_set_watchpoint(struct target *target, struct watchpoint *watch retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; - watchpoint->set = 1; + watchpoint_set(watchpoint, 1); arm7_9->wp0_used = 2; } else if (!arm7_9->wp1_used) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_ADDR_VALUE], @@ -510,7 +511,7 @@ static int arm7_9_set_watchpoint(struct target *target, struct watchpoint *watch retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; - watchpoint->set = 2; + watchpoint_set(watchpoint, 2); arm7_9->wp1_used = 2; } else { LOG_ERROR("BUG: no hardware comparator available"); @@ -538,25 +539,25 @@ static int arm7_9_unset_watchpoint(struct target *target, struct watchpoint *wat return ERROR_TARGET_NOT_HALTED; } - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } - if (watchpoint->set == 1) { + if (watchpoint->number == 1) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W0_CONTROL_VALUE], 0x0); retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; arm7_9->wp0_used = 0; - } else if (watchpoint->set == 2) { + } else if (watchpoint->number == 2) { embeddedice_set_reg(&arm7_9->eice_cache->reg_list[EICE_W1_CONTROL_VALUE], 0x0); retval = jtag_execute_queue(); if (retval != ERROR_OK) return retval; arm7_9->wp1_used = 0; } - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -597,7 +598,7 @@ int arm7_9_remove_watchpoint(struct target *target, struct watchpoint *watchpoin int retval = ERROR_OK; struct arm7_9_common *arm7_9 = target_to_arm7_9(target); - if (watchpoint->set) { + if (watchpoint->is_set) { retval = arm7_9_unset_watchpoint(target, watchpoint); if (retval != ERROR_OK) return retval; @@ -1684,7 +1685,7 @@ static void arm7_9_enable_watchpoints(struct target *target) struct watchpoint *watchpoint = target->watchpoints; while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->is_set) arm7_9_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } diff --git a/src/target/arm_cti.c b/src/target/arm_cti.c index c776e9c2a..96927bf94 100644 --- a/src/target/arm_cti.c +++ b/src/target/arm_cti.c @@ -435,8 +435,13 @@ static int cti_configure(struct jim_getopt_info *goi, struct arm_cti *cti) /* parse config or cget options ... */ while (goi->argc > 0) { int e = adiv5_jim_mem_ap_spot_configure(&cti->spot, goi); + + if (e == JIM_CONTINUE) + Jim_SetResultFormatted(goi->interp, "unknown option '%s'", + Jim_String(goi->argv[0])); + if (e != JIM_OK) - return e; + return JIM_ERR; } if (!cti->spot.dap) { diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c index 661859379..d3d27a93c 100644 --- a/src/target/arm_disassembler.c +++ b/src/target/arm_disassembler.c @@ -27,7 +27,7 @@ #include #if HAVE_CAPSTONE -#include +#include #endif /* diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c index 3e55e2e3a..e60ef225d 100644 --- a/src/target/arm_dpm.c +++ b/src/target/arm_dpm.c @@ -398,7 +398,7 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm) * or running debugger code. */ static int dpm_maybe_update_bpwp(struct arm_dpm *dpm, bool bpwp, - struct dpm_bpwp *xp, int *set_p) + struct dpm_bpwp *xp, bool *set_p) { int retval = ERROR_OK; bool disable; @@ -473,7 +473,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) struct breakpoint *bp = dbp->bp; retval = dpm_maybe_update_bpwp(dpm, bpwp, &dbp->bpwp, - bp ? &bp->set : NULL); + bp ? &bp->is_set : NULL); if (retval != ERROR_OK) goto done; } @@ -485,7 +485,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) struct watchpoint *wp = dwp->wp; retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp, - wp ? &wp->set : NULL); + wp ? &wp->is_set : NULL); if (retval != ERROR_OK) goto done; } diff --git a/src/target/arm_semihosting.c b/src/target/arm_semihosting.c index 792474acf..507d1cd2c 100644 --- a/src/target/arm_semihosting.c +++ b/src/target/arm_semihosting.c @@ -367,10 +367,13 @@ int arm_semihosting(struct target *target, int *retval) } /* Check for ARM operation numbers. */ - if (semihosting->op >= 0 && semihosting->op <= 0x31) { + if ((semihosting->op >= 0 && semihosting->op <= 0x31) || + (semihosting->op >= 0x100 && semihosting->op <= 0x107)) { + *retval = semihosting_common(target); if (*retval != ERROR_OK) { - LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); + LOG_ERROR("Failed semihosting operation (0x%02X)", + semihosting->op); return 0; } } else { diff --git a/src/target/arm_tpiu_swo.c b/src/target/arm_tpiu_swo.c index 024521364..fba3fec4f 100644 --- a/src/target/arm_tpiu_swo.c +++ b/src/target/arm_tpiu_swo.c @@ -582,6 +582,15 @@ static int wrap_read_u32(struct target *target, struct adiv5_ap *tpiu_ap, return mem_ap_read_atomic_u32(tpiu_ap, address, value); } +static const struct service_driver arm_tpiu_swo_service_driver = { + .name = "tpiu_swo_trace", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = arm_tpiu_swo_service_new_connection, + .input_handler = arm_tpiu_swo_service_input, + .connection_closed_handler = arm_tpiu_swo_service_connection_closed, + .keep_client_alive_handler = NULL, +}; + static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { struct command *c = jim_to_command(interp); @@ -616,10 +625,8 @@ static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const } if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART) - if (!obj->swo_pin_freq) { - LOG_ERROR("SWO pin frequency not set"); - return JIM_ERR; - } + if (!obj->swo_pin_freq) + LOG_DEBUG("SWO pin frequency not set, will be autodetected by the adapter"); struct target *target = get_current_target(cmd_ctx); @@ -700,10 +707,8 @@ static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const } priv->obj = obj; LOG_INFO("starting trace server for %s on %s", obj->name, &obj->out_filename[1]); - retval = add_service("tpiu_swo_trace", &obj->out_filename[1], - CONNECTION_LIMIT_UNLIMITED, arm_tpiu_swo_service_new_connection, - arm_tpiu_swo_service_input, arm_tpiu_swo_service_connection_closed, - priv); + retval = add_service(&arm_tpiu_swo_service_driver, &obj->out_filename[1], + CONNECTION_LIMIT_UNLIMITED, priv); if (retval != ERROR_OK) { LOG_ERROR("Can't configure trace TCP port %s", &obj->out_filename[1]); return JIM_ERR; @@ -724,6 +729,17 @@ static int jim_arm_tpiu_swo_enable(Jim_Interp *interp, int argc, Jim_Obj *const return JIM_ERR; } + if (obj->pin_protocol == TPIU_SPPR_PROTOCOL_MANCHESTER || obj->pin_protocol == TPIU_SPPR_PROTOCOL_UART) + if (!swo_pin_freq) { + if (obj->swo_pin_freq) + LOG_ERROR("Adapter rejected SWO pin frequency %d Hz", obj->swo_pin_freq); + else + LOG_ERROR("Adapter does not support auto-detection of SWO pin frequency nor a default value"); + + arm_tpiu_swo_close_output(obj); + return JIM_ERR; + } + if (obj->swo_pin_freq != swo_pin_freq) LOG_INFO("SWO pin data rate adjusted by adapter to %d Hz", swo_pin_freq); obj->swo_pin_freq = swo_pin_freq; @@ -886,7 +902,8 @@ static int arm_tpiu_swo_create(Jim_Interp *interp, struct arm_tpiu_swo_object *o /* does this command exist? */ cmd = Jim_GetCommand(interp, Jim_NewStringObj(interp, obj->name, -1), JIM_NONE); if (cmd) { - Jim_SetResultFormatted(interp, "Command: %s Exists", obj->name); + Jim_SetResultFormatted(interp, "cannot create TPIU object because a command with name '%s' already exists", + obj->name); return JIM_ERR; } @@ -915,7 +932,7 @@ static int jim_arm_tpiu_swo_create(Jim_Interp *interp, int argc, Jim_Obj *const struct jim_getopt_info goi; jim_getopt_setup(&goi, interp, argc - 1, argv + 1); if (goi.argc < 1) { - Jim_WrongNumArgs(goi.interp, 1, goi.argv, "?name? ..options..."); + Jim_WrongNumArgs(interp, 1, argv, "name ?option option ...?"); return JIM_ERR; } @@ -1160,7 +1177,7 @@ static const struct command_registration arm_tpiu_swo_subcommand_handlers[] = { .name = "create", .mode = COMMAND_ANY, .jim_handler = jim_arm_tpiu_swo_create, - .usage = "name [-dap dap] [-ap-num num] [-address baseaddr]", + .usage = "name [-dap dap] [-ap-num num] [-baseaddr baseaddr]", .help = "Creates a new TPIU or SWO object", }, { diff --git a/src/target/armv7a.c b/src/target/armv7a.c index 2259fa560..d564f19ae 100644 --- a/src/target/armv7a.c +++ b/src/target/armv7a.c @@ -38,6 +38,7 @@ #include "arm_opcodes.h" #include "target.h" #include "target_type.h" +#include "smp.h" static void armv7a_show_fault_registers(struct target *target) { @@ -193,8 +194,7 @@ int armv7a_read_ttbcr(struct target *target) static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way) { struct armv7a_l2x_cache *l2x_cache; - struct target_list *head = target->head; - struct target *curr; + struct target_list *head; struct armv7a_common *armv7a = target_to_armv7a(target); l2x_cache = calloc(1, sizeof(struct armv7a_l2x_cache)); @@ -207,15 +207,14 @@ static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache; /* initialize all target in this cluster (smp target) * l2 cache must be configured after smp declaration */ - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr != target) { armv7a = target_to_armv7a(curr); if (armv7a->armv7a_mmu.armv7a_cache.outer_cache) LOG_ERROR("smp target : outer cache already initialized\n"); armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache; } - head = head->next; } return JIM_OK; } diff --git a/src/target/armv7a_cache.c b/src/target/armv7a_cache.c index 4078fdde2..ba6f076f0 100644 --- a/src/target/armv7a_cache.c +++ b/src/target/armv7a_cache.c @@ -26,6 +26,7 @@ #include "armv7a_cache.h" #include #include "arm_opcodes.h" +#include "smp.h" static int armv7a_l1_d_cache_sanity_check(struct target *target) { @@ -138,14 +139,10 @@ int armv7a_cache_auto_flush_all_data(struct target *target) if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr->state == TARGET_HALTED) retval = armv7a_l1_d_cache_clean_inval_all(curr); - - head = head->next; } } else retval = armv7a_l1_d_cache_clean_inval_all(target); diff --git a/src/target/armv7a_cache_l2x.c b/src/target/armv7a_cache_l2x.c index 6b42fae53..c26d05173 100644 --- a/src/target/armv7a_cache_l2x.c +++ b/src/target/armv7a_cache_l2x.c @@ -27,6 +27,7 @@ #include #include "target.h" #include "target_type.h" +#include "smp.h" static int arm7a_l2x_sanity_check(struct target *target) { @@ -194,8 +195,7 @@ static int arm7a_handle_l2x_cache_info_command(struct command_invocation *cmd, static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t way) { struct armv7a_l2x_cache *l2x_cache; - struct target_list *head = target->head; - struct target *curr; + struct target_list *head; struct armv7a_common *armv7a = target_to_armv7a(target); if (armv7a->armv7a_mmu.armv7a_cache.outer_cache) { @@ -210,8 +210,8 @@ static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t /* initialize all targets in this cluster (smp target) * l2 cache must be configured after smp declaration */ - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr != target) { armv7a = target_to_armv7a(curr); if (armv7a->armv7a_mmu.armv7a_cache.outer_cache) { @@ -220,7 +220,6 @@ static int armv7a_l2x_cache_init(struct target *target, uint32_t base, uint32_t } armv7a->armv7a_mmu.armv7a_cache.outer_cache = l2x_cache; } - head = head->next; } return ERROR_OK; } diff --git a/src/target/armv7m.c b/src/target/armv7m.c old mode 100644 new mode 100755 index 0a51ad4d6..6e704bfe9 --- a/src/target/armv7m.c +++ b/src/target/armv7m.c @@ -494,6 +494,7 @@ int armv7m_run_algorithm(struct target *target, target_addr_t entry_point, target_addr_t exit_point, int timeout_ms, void *arch_info) { + int retval; retval = armv7m_start_algorithm(target, @@ -577,6 +578,7 @@ int armv7m_start_algorithm(struct target *target, /* regvalue = buf_get_u32(reg_params[i].value, 0, 32); */ armv7m_set_core_reg(reg, reg_params[i].value); + } { @@ -614,9 +616,8 @@ int armv7m_start_algorithm(struct target *target, /* save previous core mode */ armv7m_algorithm_info->core_mode = core_mode; - retval = target_resume(target, 0, entry_point, 1, 1); - + return retval; } @@ -626,7 +627,8 @@ int armv7m_wait_algorithm(struct target *target, int num_reg_params, struct reg_param *reg_params, target_addr_t exit_point, int timeout_ms, void *arch_info) -{ +{ + struct armv7m_common *armv7m = target_to_armv7m(target); struct armv7m_algorithm *armv7m_algorithm_info = arch_info; int retval = ERROR_OK; @@ -650,7 +652,7 @@ int armv7m_wait_algorithm(struct target *target, return retval; return ERROR_TARGET_TIMEOUT; } - + if (exit_point) { /* PC value has been cached in cortex_m_debug_entry() */ uint32_t pc = buf_get_u32(armv7m->arm.pc->value, 0, 32); diff --git a/src/target/armv7m.h b/src/target/armv7m.h index d33e57492..9ac121ac3 100644 --- a/src/target/armv7m.h +++ b/src/target/armv7m.h @@ -255,15 +255,48 @@ struct armv7m_common { void (*pre_restore_context)(struct target *target); }; +static inline bool is_armv7m(const struct armv7m_common *armv7m) +{ + return armv7m->common_magic == ARMV7M_COMMON_MAGIC; +} + +/** + * @returns the pointer to the target specific struct + * without matching a magic number. + * Use in target specific service routines, where the correct + * type of arch_info is certain. + */ static inline struct armv7m_common * target_to_armv7m(struct target *target) { return container_of(target->arch_info, struct armv7m_common, arm); } -static inline bool is_armv7m(const struct armv7m_common *armv7m) +/** + * @returns the pointer to the target specific struct + * or NULL if the magic number does not match. + * Use in a flash driver or any place where mismatch of the arch_info + * type can happen. + */ +static inline struct armv7m_common * +target_to_armv7m_safe(struct target *target) { - return armv7m->common_magic == ARMV7M_COMMON_MAGIC; + if (!target) + return NULL; + + if (!target->arch_info) + return NULL; + + /* Check the parent type first to prevent peeking memory too far + * from arch_info pointer */ + if (!is_arm(target_to_arm(target))) + return NULL; + + struct armv7m_common *armv7m = target_to_armv7m(target); + if (!is_armv7m(armv7m)) + return NULL; + + return armv7m; } struct armv7m_algorithm { diff --git a/src/target/armv8.c b/src/target/armv8.c index 26116bb33..2de115712 100644 --- a/src/target/armv8.c +++ b/src/target/armv8.c @@ -77,6 +77,10 @@ static const struct { .name = "HYP", .psr = ARM_MODE_HYP, }, + { + .name = "UND", + .psr = ARM_MODE_UND, + }, { .name = "SYS", .psr = ARM_MODE_SYS, diff --git a/src/target/armv8_cache.c b/src/target/armv8_cache.c index f05ac07cd..5b58d3f9f 100644 --- a/src/target/armv8_cache.c +++ b/src/target/armv8_cache.c @@ -23,6 +23,7 @@ #include "armv8_cache.h" #include "armv8_dpm.h" #include "armv8_opcodes.h" +#include "smp.h" /* CLIDR cache types */ #define CACHE_LEVEL_HAS_UNIFIED_CACHE 0x4 @@ -250,15 +251,12 @@ static int armv8_flush_all_data(struct target *target) /* look if all the other target have been flushed in order to flush level * 2 */ struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if (curr->state == TARGET_HALTED) { LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid); retval = _armv8_flush_all_data(curr); } - head = head->next; } } else retval = _armv8_flush_all_data(target); diff --git a/src/target/armv8_dpm.c b/src/target/armv8_dpm.c index 188e58822..765f1b777 100644 --- a/src/target/armv8_dpm.c +++ b/src/target/armv8_dpm.c @@ -818,7 +818,7 @@ int armv8_dpm_read_current_registers(struct arm_dpm *dpm) * or running debugger code. */ static int dpmv8_maybe_update_bpwp(struct arm_dpm *dpm, bool bpwp, - struct dpm_bpwp *xp, int *set_p) + struct dpm_bpwp *xp, bool *set_p) { int retval = ERROR_OK; bool disable; @@ -892,7 +892,7 @@ int armv8_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) struct breakpoint *bp = dbp->bp; retval = dpmv8_maybe_update_bpwp(dpm, bpwp, &dbp->bpwp, - bp ? &bp->set : NULL); + bp ? &bp->is_set : NULL); if (retval != ERROR_OK) goto done; } @@ -904,7 +904,7 @@ int armv8_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp) struct watchpoint *wp = dwp->wp; retval = dpmv8_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp, - wp ? &wp->set : NULL); + wp ? &wp->is_set : NULL); if (retval != ERROR_OK) goto done; } diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c index dd901ef25..3bfdbde91 100644 --- a/src/target/breakpoints.c +++ b/src/target/breakpoints.c @@ -26,6 +26,8 @@ #include "target.h" #include #include "breakpoints.h" +#include "rtos/rtos.h" +#include "smp.h" static const char * const breakpoint_type_strings[] = { "hardware", @@ -70,7 +72,7 @@ static int breakpoint_add_internal(struct target *target, (*breakpoint_p)->asid = 0; (*breakpoint_p)->length = length; (*breakpoint_p)->type = type; - (*breakpoint_p)->set = 0; + (*breakpoint_p)->is_set = false; (*breakpoint_p)->orig_instr = malloc(length); (*breakpoint_p)->next = NULL; (*breakpoint_p)->unique_id = bpwp_unique_id++; @@ -133,7 +135,7 @@ static int context_breakpoint_add_internal(struct target *target, (*breakpoint_p)->asid = asid; (*breakpoint_p)->length = length; (*breakpoint_p)->type = type; - (*breakpoint_p)->set = 0; + (*breakpoint_p)->is_set = false; (*breakpoint_p)->orig_instr = malloc(length); (*breakpoint_p)->next = NULL; (*breakpoint_p)->unique_id = bpwp_unique_id++; @@ -187,7 +189,7 @@ static int hybrid_breakpoint_add_internal(struct target *target, (*breakpoint_p)->asid = asid; (*breakpoint_p)->length = length; (*breakpoint_p)->type = type; - (*breakpoint_p)->set = 0; + (*breakpoint_p)->is_set = false; (*breakpoint_p)->orig_instr = malloc(length); (*breakpoint_p)->next = NULL; (*breakpoint_p)->unique_id = bpwp_unique_id++; @@ -216,22 +218,25 @@ int breakpoint_add(struct target *target, uint32_t length, enum breakpoint_type type) { - int retval = ERROR_OK; if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - if (type == BKPT_SOFT) - return breakpoint_add_internal(head->target, address, length, type); - - while (head) { - curr = head->target; - retval = breakpoint_add_internal(curr, address, length, type); + + if (type == BKPT_SOFT) { + head = list_first_entry(target->smp_targets, struct target_list, lh); + struct target *curr = head->target; + if (target->rtos) + curr = rtos_swbp_target(target, address, length, type); + return breakpoint_add_internal(curr, address, length, type); + } + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + int retval = breakpoint_add_internal(curr, address, length, type); if (retval != ERROR_OK) return retval; - head = head->next; } - return retval; + + return ERROR_OK; } else { return breakpoint_add_internal(target, address, length, type); } @@ -242,19 +247,17 @@ int context_breakpoint_add(struct target *target, uint32_t length, enum breakpoint_type type) { - int retval = ERROR_OK; if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; - retval = context_breakpoint_add_internal(curr, asid, length, type); + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + int retval = context_breakpoint_add_internal(curr, asid, length, type); if (retval != ERROR_OK) return retval; - head = head->next; } - return retval; + + return ERROR_OK; } else { return context_breakpoint_add_internal(target, asid, length, type); } @@ -266,19 +269,17 @@ int hybrid_breakpoint_add(struct target *target, uint32_t length, enum breakpoint_type type) { - int retval = ERROR_OK; if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; - retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type); + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + int retval = hybrid_breakpoint_add_internal(curr, address, asid, length, type); if (retval != ERROR_OK) return retval; - head = head->next; } - return retval; + + return ERROR_OK; } else return hybrid_breakpoint_add_internal(target, address, asid, length, type); } @@ -345,12 +346,10 @@ void breakpoint_remove(struct target *target, target_addr_t address) if (target->smp) { unsigned int num_breakpoints = 0; struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; num_breakpoints += breakpoint_remove_internal(curr, address); - head = head->next; } if (!num_breakpoints) LOG_ERROR("no breakpoint at address " TARGET_ADDR_FMT " found", address); @@ -363,12 +362,10 @@ void breakpoint_remove_all(struct target *target) { if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; breakpoint_remove_all_internal(curr); - head = head->next; } } else { breakpoint_remove_all_internal(target); @@ -387,12 +384,10 @@ void breakpoint_clear_target(struct target *target) { if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; breakpoint_clear_target_internal(curr); - head = head->next; } } else { breakpoint_clear_target_internal(target); @@ -482,21 +477,17 @@ int watchpoint_add_internal(struct target *target, target_addr_t address, int watchpoint_add(struct target *target, target_addr_t address, uint32_t length, enum watchpoint_rw rw, uint32_t value, uint32_t mask) { - int retval = ERROR_OK; if (target->smp) { struct target_list *head; - struct target *curr; - head = target->head; - while (head != (struct target_list *)NULL) { - curr = head->target; - retval = watchpoint_add_internal(curr, address, length, rw, value, - mask); + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; + int retval = watchpoint_add_internal(curr, address, length, rw, value, mask); if (retval != ERROR_OK) return retval; - head = head->next; } - return retval; + + return ERROR_OK; } else { return watchpoint_add_internal(target, address, length, rw, value, mask); @@ -549,12 +540,10 @@ void watchpoint_remove(struct target *target, target_addr_t address) if (target->smp) { unsigned int num_watchpoints = 0; struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; num_watchpoints += watchpoint_remove_internal(curr, address); - head = head->next; } if (num_watchpoints == 0) LOG_ERROR("no watchpoint at address " TARGET_ADDR_FMT " num_watchpoints", address); diff --git a/src/target/breakpoints.h b/src/target/breakpoints.h index b4a3511d2..fc5b50b18 100644 --- a/src/target/breakpoints.h +++ b/src/target/breakpoints.h @@ -39,7 +39,8 @@ struct breakpoint { uint32_t asid; int length; enum breakpoint_type type; - int set; + bool is_set; + unsigned int number; uint8_t *orig_instr; struct breakpoint *next; uint32_t unique_id; @@ -52,9 +53,10 @@ struct watchpoint { uint32_t mask; uint32_t value; enum watchpoint_rw rw; - int set; + bool is_set; + unsigned int number; struct watchpoint *next; - int unique_id; + uint32_t unique_id; }; void breakpoint_clear_target(struct target *target); @@ -69,6 +71,12 @@ void breakpoint_remove_all(struct target *target); struct breakpoint *breakpoint_find(struct target *target, target_addr_t address); +static inline void breakpoint_hw_set(struct breakpoint *breakpoint, unsigned int hw_number) +{ + breakpoint->is_set = true; + breakpoint->number = hw_number; +} + void watchpoint_clear_target(struct target *target); int watchpoint_add(struct target *target, target_addr_t address, uint32_t length, @@ -79,4 +87,10 @@ void watchpoint_remove(struct target *target, target_addr_t address); int watchpoint_hit(struct target *target, enum watchpoint_rw *rw, target_addr_t *address); +static inline void watchpoint_set(struct watchpoint *watchpoint, unsigned int number) +{ + watchpoint->is_set = true; + watchpoint->number = number; +} + #endif /* OPENOCD_TARGET_BREAKPOINTS_H */ diff --git a/src/target/cortex_a.c b/src/target/cortex_a.c index bf65544f5..2dc109108 100644 --- a/src/target/cortex_a.c +++ b/src/target/cortex_a.c @@ -639,14 +639,11 @@ static int cortex_a_dpm_setup(struct cortex_a_common *a, uint32_t didr) static struct target *get_cortex_a(struct target *target, int32_t coreid) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if ((curr->coreid == coreid) && (curr->state == TARGET_HALTED)) return curr; - head = head->next; } return target; } @@ -656,14 +653,12 @@ static int cortex_a_halt_smp(struct target *target) { int retval = 0; struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if ((curr != target) && (curr->state != TARGET_HALTED) && target_was_examined(curr)) retval += cortex_a_halt(curr); - head = head->next; } return retval; } @@ -684,7 +679,7 @@ static int update_halt_gdb(struct target *target) if (target->gdb_service) gdb_target = target->gdb_service->target; - foreach_smp_target(head, target->head) { + foreach_smp_target(head, target->smp_targets) { curr = head->target; /* skip calling context */ if (curr == target) @@ -951,11 +946,10 @@ static int cortex_a_restore_smp(struct target *target, int handle_breakpoints) { int retval = 0; struct target_list *head; - struct target *curr; target_addr_t address; - head = target->head; - while (head) { - curr = head->target; + + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if ((curr != target) && (curr->state != TARGET_RUNNING) && target_was_examined(curr)) { /* resume current address , not in step mode */ @@ -963,8 +957,6 @@ static int cortex_a_restore_smp(struct target *target, int handle_breakpoints) handle_breakpoints, 0); retval += cortex_a_internal_restart(curr); } - head = head->next; - } return retval; } @@ -1193,7 +1185,7 @@ static int cortex_a_step(struct target *target, int current, target_addr_t addre stepbreakpoint.length = (arm->core_state == ARM_STATE_THUMB) ? 2 : 4; stepbreakpoint.type = BKPT_HARD; - stepbreakpoint.set = 0; + stepbreakpoint.is_set = false; /* Disable interrupts during single step if requested */ if (cortex_a->isrmasking_mode == CORTEX_A_ISRMASK_ON) { @@ -1273,7 +1265,7 @@ static int cortex_a_set_breakpoint(struct target *target, struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_brp *brp_list = cortex_a->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -1285,7 +1277,7 @@ static int cortex_a_set_breakpoint(struct target *target, LOG_ERROR("ERROR Can not find free Breakpoint Register Pair"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = brp_i + 1; + breakpoint_hw_set(breakpoint, brp_i); if (breakpoint->length == 2) byte_addr_select = (3 << (breakpoint->address & 0x02)); control = ((matchmode & 0x7) << 20) @@ -1350,7 +1342,7 @@ static int cortex_a_set_breakpoint(struct target *target, armv7a_l1_i_cache_inval_virt(target, breakpoint->address, breakpoint->length); - breakpoint->set = 0x11; /* Any nice value but 0 */ + breakpoint->is_set = true; } return ERROR_OK; @@ -1367,7 +1359,7 @@ static int cortex_a_set_context_breakpoint(struct target *target, struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_brp *brp_list = cortex_a->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return retval; } @@ -1381,7 +1373,7 @@ static int cortex_a_set_context_breakpoint(struct target *target, return ERROR_FAIL; } - breakpoint->set = brp_i + 1; + breakpoint_hw_set(breakpoint, brp_i); control = ((matchmode & 0x7) << 20) | (byte_addr_select << 5) | (3 << 1) | 1; @@ -1419,7 +1411,7 @@ static int cortex_a_set_hybrid_breakpoint(struct target *target, struct breakpoi struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_brp *brp_list = cortex_a->brp_list; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return retval; } @@ -1444,7 +1436,7 @@ static int cortex_a_set_hybrid_breakpoint(struct target *target, struct breakpoi return ERROR_FAIL; } - breakpoint->set = brp_1 + 1; + breakpoint_hw_set(breakpoint, brp_1); breakpoint->linked_brp = brp_2; control_ctx = ((ctx_machmode & 0x7) << 20) | (brp_2 << 16) @@ -1493,16 +1485,16 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_brp *brp_list = cortex_a->brp_list; - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } if (breakpoint->type == BKPT_HARD) { if ((breakpoint->address != 0) && (breakpoint->asid != 0)) { - int brp_i = breakpoint->set - 1; + int brp_i = breakpoint->number; int brp_j = breakpoint->linked_brp; - if ((brp_i < 0) || (brp_i >= cortex_a->brp_num)) { + if (brp_i >= cortex_a->brp_num) { LOG_DEBUG("Invalid BRP number in breakpoint"); return ERROR_OK; } @@ -1541,12 +1533,12 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b if (retval != ERROR_OK) return retval; breakpoint->linked_brp = 0; - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } else { - int brp_i = breakpoint->set - 1; - if ((brp_i < 0) || (brp_i >= cortex_a->brp_num)) { + int brp_i = breakpoint->number; + if (brp_i >= cortex_a->brp_num) { LOG_DEBUG("Invalid BRP number in breakpoint"); return ERROR_OK; } @@ -1565,7 +1557,7 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b brp_list[brp_i].value); if (retval != ERROR_OK) return retval; - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } } else { @@ -1597,7 +1589,7 @@ static int cortex_a_unset_breakpoint(struct target *target, struct breakpoint *b armv7a_l1_i_cache_inval_virt(target, breakpoint->address, breakpoint->length); } - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } @@ -1663,7 +1655,7 @@ static int cortex_a_remove_breakpoint(struct target *target, struct breakpoint * } #endif - if (breakpoint->set) { + if (breakpoint->is_set) { cortex_a_unset_breakpoint(target, breakpoint); if (breakpoint->type == BKPT_HARD) cortex_a->brp_num_available++; @@ -1696,7 +1688,7 @@ static int cortex_a_set_watchpoint(struct target *target, struct watchpoint *wat struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_wrp *wrp_list = cortex_a->wrp_list; - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return retval; } @@ -1749,7 +1741,7 @@ static int cortex_a_set_watchpoint(struct target *target, struct watchpoint *wat break; } - watchpoint->set = wrp_i + 1; + watchpoint_set(watchpoint, wrp_i); control = (address_mask << 24) | (byte_address_select << 5) | (load_store_access_control << 3) | @@ -1792,13 +1784,13 @@ static int cortex_a_unset_watchpoint(struct target *target, struct watchpoint *w struct armv7a_common *armv7a = &cortex_a->armv7a_common; struct cortex_a_wrp *wrp_list = cortex_a->wrp_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - int wrp_i = watchpoint->set - 1; - if (wrp_i < 0 || wrp_i >= cortex_a->wrp_num) { + int wrp_i = watchpoint->number; + if (wrp_i >= cortex_a->wrp_num) { LOG_DEBUG("Invalid WRP number in watchpoint"); return ERROR_OK; } @@ -1817,7 +1809,7 @@ static int cortex_a_unset_watchpoint(struct target *target, struct watchpoint *w wrp_list[wrp_i].value); if (retval != ERROR_OK) return retval; - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -1859,7 +1851,7 @@ static int cortex_a_remove_watchpoint(struct target *target, struct watchpoint * { struct cortex_a_common *cortex_a = target_to_cortex_a(target); - if (watchpoint->set) { + if (watchpoint->is_set) { cortex_a->wrp_num_available++; cortex_a_unset_watchpoint(target, watchpoint); } diff --git a/src/target/cortex_m.c b/src/target/cortex_m.c index 7125e9e83..344cfcf61 100644 --- a/src/target/cortex_m.c +++ b/src/target/cortex_m.c @@ -185,7 +185,7 @@ static int cortex_m_load_core_reg_u32(struct target *target, break; cortex_m->slow_register_read = true; /* Polling (still) needed. */ if (timeval_ms() > then + DHCSR_S_REGRDY_TIMEOUT) { - LOG_ERROR("Timeout waiting for DCRDR transfer ready"); + LOG_TARGET_ERROR(target, "Timeout waiting for DCRDR transfer ready"); return ERROR_TIMEOUT_REACHED; } keep_alive(); @@ -223,7 +223,7 @@ static int cortex_m_slow_read_all_regs(struct target *target) } if (!cortex_m->slow_register_read) - LOG_DEBUG("Switching back to fast register reads"); + LOG_TARGET_DEBUG(target, "Switching back to fast register reads"); return ERROR_OK; } @@ -319,7 +319,7 @@ static int cortex_m_fast_read_all_regs(struct target *target) for (unsigned int i = 0; i < wi; i++) { if ((dhcsr[i] & S_REGRDY) == 0) { not_ready = true; - LOG_DEBUG("Register %u was not ready during fast read", i); + LOG_TARGET_DEBUG(target, "Register %u was not ready during fast read", i); } cortex_m_cumulate_dhcsr_sticky(cortex_m, dhcsr[i]); } @@ -330,7 +330,7 @@ static int cortex_m_fast_read_all_regs(struct target *target) return ERROR_TIMEOUT_REACHED; } - LOG_DEBUG("read %u 32-bit registers", wi); + LOG_TARGET_DEBUG(target, "read %u 32-bit registers", wi); unsigned int ri = 0; /* read index from r_vals array */ for (reg_id = 0; reg_id < num_regs; reg_id++) { @@ -403,7 +403,7 @@ static int cortex_m_store_core_reg_u32(struct target *target, if (cortex_m->dcb_dhcsr & S_REGRDY) break; if (timeval_ms() > then + DHCSR_S_REGRDY_TIMEOUT) { - LOG_ERROR("Timeout waiting for DCRDR transfer ready"); + LOG_TARGET_ERROR(target, "Timeout waiting for DCRDR transfer ready"); return ERROR_TIMEOUT_REACHED; } keep_alive(); @@ -528,7 +528,7 @@ static int cortex_m_clear_halt(struct target *target) retval = mem_ap_write_atomic_u32(armv7m->debug_ap, NVIC_DFSR, cortex_m->nvic_dfsr); if (retval != ERROR_OK) return retval; - LOG_DEBUG(" NVIC_DFSR 0x%" PRIx32 "", cortex_m->nvic_dfsr); + LOG_TARGET_DEBUG(target, "NVIC_DFSR 0x%" PRIx32 "", cortex_m->nvic_dfsr); return ERROR_OK; } @@ -550,7 +550,7 @@ static int cortex_m_single_step_core(struct target *target) retval = cortex_m_write_debug_halt_mask(target, C_STEP, C_HALT); if (retval != ERROR_OK) return retval; - LOG_DEBUG(" "); + LOG_TARGET_DEBUG(target, "single step"); /* restore dhcsr reg */ cortex_m_clear_halt(target); @@ -590,7 +590,7 @@ static int cortex_m_endreset_event(struct target *target) retval = mem_ap_read_atomic_u32(armv7m->debug_ap, DCB_DEMCR, &dcb_demcr); if (retval != ERROR_OK) return retval; - LOG_DEBUG("DCB_DEMCR = 0x%8.8" PRIx32 "", dcb_demcr); + LOG_TARGET_DEBUG(target, "DCB_DEMCR = 0x%8.8" PRIx32 "", dcb_demcr); /* this register is used for emulated dcc channel */ retval = mem_ap_write_u32(armv7m->debug_ap, DCB_DCRDR, 0); @@ -629,7 +629,7 @@ static int cortex_m_endreset_event(struct target *target) /* Enable FPB */ retval = cortex_m_enable_fpb(target); if (retval != ERROR_OK) { - LOG_ERROR("Failed to enable the FPB"); + LOG_TARGET_ERROR(target, "Failed to enable the FPB"); return retval; } @@ -766,7 +766,7 @@ static int cortex_m_examine_exception_reason(struct target *target) } retval = dap_run(swjdp); if (retval == ERROR_OK) - LOG_DEBUG("%s SHCSR 0x%" PRIx32 ", SR 0x%" PRIx32 + LOG_TARGET_DEBUG(target, "%s SHCSR 0x%" PRIx32 ", SR 0x%" PRIx32 ", CFSR 0x%" PRIx32 ", AR 0x%" PRIx32, armv7m_exception_string(armv7m->exception_number), shcsr, except_sr, cfsr, except_ar); @@ -782,7 +782,7 @@ static int cortex_m_debug_entry(struct target *target) struct arm *arm = &armv7m->arm; struct reg *r; - LOG_DEBUG(" "); + LOG_TARGET_DEBUG(target, " "); /* Do this really early to minimize the window where the MASKINTS erratum * can pile up pending interrupts. */ @@ -815,7 +815,7 @@ static int cortex_m_debug_entry(struct target *target) retval = cortex_m_fast_read_all_regs(target); if (retval == ERROR_TIMEOUT_REACHED) { cortex_m->slow_register_read = true; - LOG_DEBUG("Switched to slow register read"); + LOG_TARGET_DEBUG(target, "Switched to slow register read"); } } @@ -855,7 +855,8 @@ static int cortex_m_debug_entry(struct target *target) if (armv7m->exception_number) cortex_m_examine_exception_reason(target); - LOG_DEBUG("entered debug state in core mode: %s at PC 0x%" PRIx32 ", cpu in %s state, target->state: %s", + LOG_TARGET_DEBUG(target, "entered debug state in core mode: %s at PC 0x%" PRIx32 + ", cpu in %s state, target->state: %s", arm_mode_name(arm->core_mode), buf_get_u32(arm->pc->value, 0, 32), secure_state ? "Secure" : "Non-Secure", @@ -889,8 +890,7 @@ static int cortex_m_poll(struct target *target) * section B1.5.15 "Unrecoverable exception cases". */ if (cortex_m->dcb_dhcsr & S_LOCKUP) { - LOG_ERROR("%s -- clearing lockup after double fault", - target_name(target)); + LOG_TARGET_ERROR(target, "clearing lockup after double fault"); cortex_m_write_debug_halt_mask(target, C_HALT, 0); target->debug_reason = DBG_REASON_DBGRQ; @@ -909,7 +909,7 @@ static int cortex_m_poll(struct target *target) cortex_m->dcb_dhcsr_cumulated_sticky &= ~S_RESET_ST; if (target->state != TARGET_RESET) { target->state = TARGET_RESET; - LOG_INFO("%s: external reset detected", target_name(target)); + LOG_TARGET_INFO(target, "external reset detected"); } return ERROR_OK; } @@ -918,7 +918,7 @@ static int cortex_m_poll(struct target *target) /* Cannot switch context while running so endreset is * called with target->state == TARGET_RESET */ - LOG_DEBUG("Exit from reset with dcb_dhcsr 0x%" PRIx32, + LOG_TARGET_DEBUG(target, "Exit from reset with dcb_dhcsr 0x%" PRIx32, cortex_m->dcb_dhcsr); retval = cortex_m_endreset_event(target); if (retval != ERROR_OK) { @@ -943,7 +943,6 @@ static int cortex_m_poll(struct target *target) target_call_event_callbacks(target, TARGET_EVENT_HALTED); } if (prev_target_state == TARGET_DEBUG_RUNNING) { - LOG_DEBUG(" "); retval = cortex_m_debug_entry(target); if (retval != ERROR_OK) return retval; @@ -971,7 +970,7 @@ static int cortex_m_poll(struct target *target) register_cache_invalidate(armv7m->arm.core_cache); target->state = TARGET_RUNNING; - LOG_WARNING("%s: external resume detected", target_name(target)); + LOG_TARGET_WARNING(target, "external resume detected"); target_call_event_callbacks(target, TARGET_EVENT_RESUMED); retval = ERROR_OK; } @@ -984,20 +983,19 @@ static int cortex_m_poll(struct target *target) static int cortex_m_halt(struct target *target) { - LOG_DEBUG("target->state: %s", - target_state_name(target)); + LOG_TARGET_DEBUG(target, "target->state: %s", target_state_name(target)); if (target->state == TARGET_HALTED) { - LOG_DEBUG("target was already halted"); + LOG_TARGET_DEBUG(target, "target was already halted"); return ERROR_OK; } if (target->state == TARGET_UNKNOWN) - LOG_WARNING("target was in unknown state when halt was requested"); + LOG_TARGET_WARNING(target, "target was in unknown state when halt was requested"); if (target->state == TARGET_RESET) { if ((jtag_get_reset_config() & RESET_SRST_PULLS_TRST) && jtag_get_srst()) { - LOG_ERROR("can't request a halt while in reset if nSRST pulls nTRST"); + LOG_TARGET_ERROR(target, "can't request a halt while in reset if nSRST pulls nTRST"); return ERROR_TARGET_FAILURE; } else { /* we came here in a reset_halt or reset_init sequence @@ -1031,10 +1029,10 @@ static int cortex_m_soft_reset_halt(struct target *target) * can be obtained by using 'reset halt' and 'cortex_m reset_config vectreset'. * As this reset only uses VC_CORERESET it would only ever reset the cortex_m * core, not the peripherals */ - LOG_DEBUG("soft_reset_halt is discouraged, please use 'reset halt' instead."); + LOG_TARGET_DEBUG(target, "soft_reset_halt is discouraged, please use 'reset halt' instead."); if (!cortex_m->vectreset_supported) { - LOG_ERROR("VECTRESET is not supported on this Cortex-M core"); + LOG_TARGET_ERROR(target, "VECTRESET is not supported on this Cortex-M core"); return ERROR_FAIL; } @@ -1068,15 +1066,16 @@ static int cortex_m_soft_reset_halt(struct target *target) return retval; if ((cortex_m->dcb_dhcsr & S_HALT) && (cortex_m->nvic_dfsr & DFSR_VCATCH)) { - LOG_DEBUG("system reset-halted, DHCSR 0x%08" PRIx32 ", DFSR 0x%08" PRIx32, - cortex_m->dcb_dhcsr, cortex_m->nvic_dfsr); + LOG_TARGET_DEBUG(target, "system reset-halted, DHCSR 0x%08" PRIx32 ", DFSR 0x%08" PRIx32, + cortex_m->dcb_dhcsr, cortex_m->nvic_dfsr); cortex_m_poll(target); /* FIXME restore user's vector catch config */ return ERROR_OK; - } else - LOG_DEBUG("waiting for system reset-halt, " + } else { + LOG_TARGET_DEBUG(target, "waiting for system reset-halt, " "DHCSR 0x%08" PRIx32 ", %d ms", cortex_m->dcb_dhcsr, timeout); + } } timeout++; alive_sleep(1); @@ -1091,7 +1090,7 @@ void cortex_m_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (breakpoint) { - if (!breakpoint->set) + if (!breakpoint->is_set) cortex_m_set_breakpoint(target, breakpoint); breakpoint = breakpoint->next; } @@ -1106,7 +1105,7 @@ static int cortex_m_resume(struct target *target, int current, struct reg *r; if (target->state != TARGET_HALTED) { - LOG_WARNING("target not halted"); + LOG_TARGET_WARNING(target, "target not halted"); return ERROR_TARGET_NOT_HALTED; } @@ -1170,7 +1169,7 @@ static int cortex_m_resume(struct target *target, int current, /* Single step past breakpoint at current address */ breakpoint = breakpoint_find(target, resume_pc); if (breakpoint) { - LOG_DEBUG("unset breakpoint at " TARGET_ADDR_FMT " (ID: %" PRIu32 ")", + LOG_TARGET_DEBUG(target, "unset breakpoint at " TARGET_ADDR_FMT " (ID: %" PRIu32 ")", breakpoint->address, breakpoint->unique_id); cortex_m_unset_breakpoint(target, breakpoint); @@ -1191,11 +1190,11 @@ static int cortex_m_resume(struct target *target, int current, if (!debug_execution) { target->state = TARGET_RUNNING; target_call_event_callbacks(target, TARGET_EVENT_RESUMED); - LOG_DEBUG("target resumed at 0x%" PRIx32 "", resume_pc); + LOG_TARGET_DEBUG(target, "target resumed at 0x%" PRIx32 "", resume_pc); } else { target->state = TARGET_DEBUG_RUNNING; target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED); - LOG_DEBUG("target debug resumed at 0x%" PRIx32 "", resume_pc); + LOG_TARGET_DEBUG(target, "target debug resumed at 0x%" PRIx32 "", resume_pc); } return ERROR_OK; @@ -1214,7 +1213,7 @@ static int cortex_m_step(struct target *target, int current, bool isr_timed_out = false; if (target->state != TARGET_HALTED) { - LOG_WARNING("target not halted"); + LOG_TARGET_WARNING(target, "target not halted"); return ERROR_TARGET_NOT_HALTED; } @@ -1287,7 +1286,7 @@ static int cortex_m_step(struct target *target, int current, * */ if ((pc_value & 0x02) && breakpoint_find(target, pc_value & ~0x03)) { - LOG_DEBUG("Stepping over next instruction with interrupts disabled"); + LOG_TARGET_DEBUG(target, "Stepping over next instruction with interrupts disabled"); cortex_m_write_debug_halt_mask(target, C_HALT | C_MASKINTS, 0); cortex_m_write_debug_halt_mask(target, C_STEP, C_HALT); /* Re-enable interrupts if appropriate */ @@ -1318,7 +1317,7 @@ static int cortex_m_step(struct target *target, int current, cortex_m_set_maskints_for_halt(target); } else { /* Start the core */ - LOG_DEBUG("Starting core to serve pending interrupts"); + LOG_TARGET_DEBUG(target, "Starting core to serve pending interrupts"); int64_t t_start = timeval_ms(); cortex_m_set_maskints_for_run(target); cortex_m_write_debug_halt_mask(target, 0, C_HALT | C_STEP); @@ -1342,7 +1341,7 @@ static int cortex_m_step(struct target *target, int current, } if (isr_timed_out) { - LOG_DEBUG("Interrupt handlers didn't complete within time, " + LOG_TARGET_DEBUG(target, "Interrupt handlers didn't complete within time, " "leaving target running"); } else { /* Step over next instruction with interrupts disabled */ @@ -1377,7 +1376,7 @@ static int cortex_m_step(struct target *target, int current, return ERROR_OK; } - LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 + LOG_TARGET_DEBUG(target, "target stepped dcb_dhcsr = 0x%" PRIx32 " nvic_icsr = 0x%" PRIx32, cortex_m->dcb_dhcsr, cortex_m->nvic_icsr); @@ -1386,7 +1385,7 @@ static int cortex_m_step(struct target *target, int current, return retval; target_call_event_callbacks(target, TARGET_EVENT_HALTED); - LOG_DEBUG("target stepped dcb_dhcsr = 0x%" PRIx32 + LOG_TARGET_DEBUG(target, "target stepped dcb_dhcsr = 0x%" PRIx32 " nvic_icsr = 0x%" PRIx32, cortex_m->dcb_dhcsr, cortex_m->nvic_icsr); @@ -1399,7 +1398,7 @@ static int cortex_m_assert_reset(struct target *target) struct armv7m_common *armv7m = &cortex_m->armv7m; enum cortex_m_soft_reset_config reset_config = cortex_m->soft_reset_config; - LOG_DEBUG("target->state: %s", + LOG_TARGET_DEBUG(target, "target->state: %s", target_state_name(target)); enum reset_types jtag_reset_config = jtag_get_reset_config(); @@ -1423,10 +1422,10 @@ static int cortex_m_assert_reset(struct target *target) if (jtag_reset_config & RESET_HAS_SRST) { adapter_assert_reset(); if (target->reset_halt) - LOG_ERROR("Target not examined, will not halt after reset!"); + LOG_TARGET_ERROR(target, "Target not examined, will not halt after reset!"); return ERROR_OK; } else { - LOG_ERROR("Target not examined, reset NOT asserted!"); + LOG_TARGET_ERROR(target, "Target not examined, reset NOT asserted!"); return ERROR_FAIL; } } @@ -1473,7 +1472,7 @@ static int cortex_m_assert_reset(struct target *target) retval2 = mem_ap_write_atomic_u32(armv7m->debug_ap, DCB_DEMCR, TRCENA | VC_HARDERR | VC_BUSERR | VC_CORERESET); if (retval != ERROR_OK || retval2 != ERROR_OK) - LOG_INFO("AP write error, reset will not halt"); + LOG_TARGET_INFO(target, "AP write error, reset will not halt"); } if (jtag_reset_config & RESET_HAS_SRST) { @@ -1493,15 +1492,15 @@ static int cortex_m_assert_reset(struct target *target) if (!cortex_m->vectreset_supported && reset_config == CORTEX_M_RESET_VECTRESET) { reset_config = CORTEX_M_RESET_SYSRESETREQ; - LOG_WARNING("VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ instead."); - LOG_WARNING("Set 'cortex_m reset_config sysresetreq'."); + LOG_TARGET_WARNING(target, "VECTRESET is not supported on this Cortex-M core, using SYSRESETREQ instead."); + LOG_TARGET_WARNING(target, "Set 'cortex_m reset_config sysresetreq'."); } - LOG_DEBUG("Using Cortex-M %s", (reset_config == CORTEX_M_RESET_SYSRESETREQ) + LOG_TARGET_DEBUG(target, "Using Cortex-M %s", (reset_config == CORTEX_M_RESET_SYSRESETREQ) ? "SYSRESETREQ" : "VECTRESET"); if (reset_config == CORTEX_M_RESET_VECTRESET) { - LOG_WARNING("Only resetting the Cortex-M core, use a reset-init event " + LOG_TARGET_WARNING(target, "Only resetting the Cortex-M core, use a reset-init event " "handler to reset any peripherals or configure hardware srst support."); } @@ -1510,11 +1509,11 @@ static int cortex_m_assert_reset(struct target *target) AIRCR_VECTKEY | ((reset_config == CORTEX_M_RESET_SYSRESETREQ) ? AIRCR_SYSRESETREQ : AIRCR_VECTRESET)); if (retval3 != ERROR_OK) - LOG_DEBUG("Ignoring AP write error right after reset"); + LOG_TARGET_DEBUG(target, "Ignoring AP write error right after reset"); retval3 = dap_dp_init_or_reconnect(armv7m->debug_ap->dap); if (retval3 != ERROR_OK) { - LOG_ERROR("DP initialisation failed"); + LOG_TARGET_ERROR(target, "DP initialisation failed"); /* The error return value must not be propagated in this case. * SYSRESETREQ or VECTRESET have been possibly triggered * so reset processing should continue */ @@ -1550,7 +1549,7 @@ static int cortex_m_deassert_reset(struct target *target) { struct armv7m_common *armv7m = &target_to_cm(target)->armv7m; - LOG_DEBUG("target->state: %s", + LOG_TARGET_DEBUG(target, "target->state: %s", target_state_name(target)); /* deassert reset lines */ @@ -1564,7 +1563,7 @@ static int cortex_m_deassert_reset(struct target *target) int retval = dap_dp_init_or_reconnect(armv7m->debug_ap->dap); if (retval != ERROR_OK) { - LOG_ERROR("DP initialisation failed"); + LOG_TARGET_ERROR(target, "DP initialisation failed"); return retval; } } @@ -1579,8 +1578,8 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint struct cortex_m_common *cortex_m = target_to_cm(target); struct cortex_m_fp_comparator *comparator_list = cortex_m->fp_comparator_list; - if (breakpoint->set) { - LOG_WARNING("breakpoint (BPID: %" PRIu32 ") already set", breakpoint->unique_id); + if (breakpoint->is_set) { + LOG_TARGET_WARNING(target, "breakpoint (BPID: %" PRIu32 ") already set", breakpoint->unique_id); return ERROR_OK; } @@ -1589,35 +1588,36 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint while (comparator_list[fp_num].used && (fp_num < cortex_m->fp_num_code)) fp_num++; if (fp_num >= cortex_m->fp_num_code) { - LOG_ERROR("Can not find free FPB Comparator!"); + LOG_TARGET_ERROR(target, "Can not find free FPB Comparator!"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = fp_num + 1; + breakpoint_hw_set(breakpoint, fp_num); fpcr_value = breakpoint->address | 1; if (cortex_m->fp_rev == 0) { if (breakpoint->address > 0x1FFFFFFF) { - LOG_ERROR("Cortex-M Flash Patch Breakpoint rev.1 cannot handle HW breakpoint above address 0x1FFFFFFE"); + LOG_TARGET_ERROR(target, "Cortex-M Flash Patch Breakpoint rev.1 " + "cannot handle HW breakpoint above address 0x1FFFFFFE"); return ERROR_FAIL; } uint32_t hilo; hilo = (breakpoint->address & 0x2) ? FPCR_REPLACE_BKPT_HIGH : FPCR_REPLACE_BKPT_LOW; fpcr_value = (fpcr_value & 0x1FFFFFFC) | hilo | 1; } else if (cortex_m->fp_rev > 1) { - LOG_ERROR("Unhandled Cortex-M Flash Patch Breakpoint architecture revision"); + LOG_TARGET_ERROR(target, "Unhandled Cortex-M Flash Patch Breakpoint architecture revision"); return ERROR_FAIL; } comparator_list[fp_num].used = true; comparator_list[fp_num].fpcr_value = fpcr_value; target_write_u32(target, comparator_list[fp_num].fpcr_address, comparator_list[fp_num].fpcr_value); - LOG_DEBUG("fpc_num %i fpcr_value 0x%" PRIx32 "", + LOG_TARGET_DEBUG(target, "fpc_num %i fpcr_value 0x%" PRIx32 "", fp_num, comparator_list[fp_num].fpcr_value); if (!cortex_m->fpb_enabled) { - LOG_DEBUG("FPB wasn't enabled, do it now"); + LOG_TARGET_DEBUG(target, "FPB wasn't enabled, do it now"); retval = cortex_m_enable_fpb(target); if (retval != ERROR_OK) { - LOG_ERROR("Failed to enable the FPB"); + LOG_TARGET_ERROR(target, "Failed to enable the FPB"); return retval; } @@ -1643,15 +1643,15 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint code); if (retval != ERROR_OK) return retval; - breakpoint->set = true; + breakpoint->is_set = true; } - LOG_DEBUG("BPID: %" PRIu32 ", Type: %d, Address: " TARGET_ADDR_FMT " Length: %d (set=%d)", + LOG_TARGET_DEBUG(target, "BPID: %" PRIu32 ", Type: %d, Address: " TARGET_ADDR_FMT " Length: %d (n=%u)", breakpoint->unique_id, (int)(breakpoint->type), breakpoint->address, breakpoint->length, - breakpoint->set); + (breakpoint->type == BKPT_SOFT) ? 0 : breakpoint->number); return ERROR_OK; } @@ -1662,22 +1662,22 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi struct cortex_m_common *cortex_m = target_to_cm(target); struct cortex_m_fp_comparator *comparator_list = cortex_m->fp_comparator_list; - if (breakpoint->set <= 0) { - LOG_WARNING("breakpoint not set"); + if (!breakpoint->is_set) { + LOG_TARGET_WARNING(target, "breakpoint not set"); return ERROR_OK; } - LOG_DEBUG("BPID: %" PRIu32 ", Type: %d, Address: " TARGET_ADDR_FMT " Length: %d (set=%d)", + LOG_TARGET_DEBUG(target, "BPID: %" PRIu32 ", Type: %d, Address: " TARGET_ADDR_FMT " Length: %d (n=%u)", breakpoint->unique_id, (int)(breakpoint->type), breakpoint->address, breakpoint->length, - breakpoint->set); + (breakpoint->type == BKPT_SOFT) ? 0 : breakpoint->number); if (breakpoint->type == BKPT_HARD) { - unsigned int fp_num = breakpoint->set - 1; + unsigned int fp_num = breakpoint->number; if (fp_num >= cortex_m->fp_num_code) { - LOG_DEBUG("Invalid FP Comparator number in breakpoint"); + LOG_TARGET_DEBUG(target, "Invalid FP Comparator number in breakpoint"); return ERROR_OK; } comparator_list[fp_num].used = false; @@ -1692,7 +1692,7 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi if (retval != ERROR_OK) return retval; } - breakpoint->set = false; + breakpoint->is_set = false; return ERROR_OK; } @@ -1700,12 +1700,12 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint) { if (breakpoint->length == 3) { - LOG_DEBUG("Using a two byte breakpoint for 32bit Thumb-2 request"); + LOG_TARGET_DEBUG(target, "Using a two byte breakpoint for 32bit Thumb-2 request"); breakpoint->length = 2; } if ((breakpoint->length != 2)) { - LOG_INFO("only breakpoints of two bytes length supported"); + LOG_TARGET_INFO(target, "only breakpoints of two bytes length supported"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -1714,7 +1714,7 @@ int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint int cortex_m_remove_breakpoint(struct target *target, struct breakpoint *breakpoint) { - if (!breakpoint->set) + if (!breakpoint->is_set) return ERROR_OK; return cortex_m_unset_breakpoint(target, breakpoint); @@ -1737,11 +1737,11 @@ static int cortex_m_set_watchpoint(struct target *target, struct watchpoint *wat comparator++, dwt_num++) continue; if (dwt_num >= cortex_m->dwt_num_comp) { - LOG_ERROR("Can not find free DWT Comparator"); + LOG_TARGET_ERROR(target, "Can not find free DWT Comparator"); return ERROR_FAIL; } comparator->used = true; - watchpoint->set = dwt_num + 1; + watchpoint_set(watchpoint, dwt_num); comparator->comp = watchpoint->address; target_write_u32(target, comparator->dwt_comparator_address + 0, @@ -1795,7 +1795,7 @@ static int cortex_m_set_watchpoint(struct target *target, struct watchpoint *wat target_write_u32(target, comparator->dwt_comparator_address + 8, comparator->function); - LOG_DEBUG("Watchpoint (ID %d) DWT%d 0x%08x 0x%x 0x%05x", + LOG_TARGET_DEBUG(target, "Watchpoint (ID %d) DWT%d 0x%08x 0x%x 0x%05x", watchpoint->unique_id, dwt_num, (unsigned) comparator->comp, (unsigned) comparator->mask, @@ -1808,20 +1808,20 @@ static int cortex_m_unset_watchpoint(struct target *target, struct watchpoint *w struct cortex_m_common *cortex_m = target_to_cm(target); struct cortex_m_dwt_comparator *comparator; - if (watchpoint->set <= 0) { - LOG_WARNING("watchpoint (wpid: %d) not set", + if (!watchpoint->is_set) { + LOG_TARGET_WARNING(target, "watchpoint (wpid: %d) not set", watchpoint->unique_id); return ERROR_OK; } - unsigned int dwt_num = watchpoint->set - 1; + unsigned int dwt_num = watchpoint->number; - LOG_DEBUG("Watchpoint (ID %d) DWT%d address: 0x%08x clear", + LOG_TARGET_DEBUG(target, "Watchpoint (ID %d) DWT%u address: 0x%08x clear", watchpoint->unique_id, dwt_num, (unsigned) watchpoint->address); if (dwt_num >= cortex_m->dwt_num_comp) { - LOG_DEBUG("Invalid DWT Comparator number in watchpoint"); + LOG_TARGET_DEBUG(target, "Invalid DWT Comparator number in watchpoint"); return ERROR_OK; } @@ -1831,7 +1831,7 @@ static int cortex_m_unset_watchpoint(struct target *target, struct watchpoint *w target_write_u32(target, comparator->dwt_comparator_address + 8, comparator->function); - watchpoint->set = false; + watchpoint->is_set = false; return ERROR_OK; } @@ -1841,13 +1841,13 @@ int cortex_m_add_watchpoint(struct target *target, struct watchpoint *watchpoint struct cortex_m_common *cortex_m = target_to_cm(target); if (cortex_m->dwt_comp_available < 1) { - LOG_DEBUG("no comparators?"); + LOG_TARGET_DEBUG(target, "no comparators?"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } /* hardware doesn't support data value masking */ if (watchpoint->mask != ~(uint32_t)0) { - LOG_DEBUG("watchpoint value masks not supported"); + LOG_TARGET_DEBUG(target, "watchpoint value masks not supported"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -1859,11 +1859,11 @@ int cortex_m_add_watchpoint(struct target *target, struct watchpoint *watchpoint break; } if (mask == 16) { - LOG_DEBUG("unsupported watchpoint length"); + LOG_TARGET_DEBUG(target, "unsupported watchpoint length"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } if (watchpoint->address & ((1 << mask) - 1)) { - LOG_DEBUG("watchpoint address is unaligned"); + LOG_TARGET_DEBUG(target, "watchpoint address is unaligned"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } @@ -1875,12 +1875,12 @@ int cortex_m_add_watchpoint(struct target *target, struct watchpoint *watchpoint * the data, and another comparator (DATAVADDR0) matching addr. */ if (watchpoint->value) { - LOG_DEBUG("data value watchpoint not YET supported"); + LOG_TARGET_DEBUG(target, "data value watchpoint not YET supported"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } cortex_m->dwt_comp_available--; - LOG_DEBUG("dwt_comp_available: %d", cortex_m->dwt_comp_available); + LOG_TARGET_DEBUG(target, "dwt_comp_available: %d", cortex_m->dwt_comp_available); return ERROR_OK; } @@ -1891,15 +1891,15 @@ int cortex_m_remove_watchpoint(struct target *target, struct watchpoint *watchpo /* REVISIT why check? DWT can be updated with core running ... */ if (target->state != TARGET_HALTED) { - LOG_WARNING("target not halted"); + LOG_TARGET_WARNING(target, "target not halted"); return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) cortex_m_unset_watchpoint(target, watchpoint); cortex_m->dwt_comp_available++; - LOG_DEBUG("dwt_comp_available: %d", cortex_m->dwt_comp_available); + LOG_TARGET_DEBUG(target, "dwt_comp_available: %d", cortex_m->dwt_comp_available); return ERROR_OK; } @@ -1912,10 +1912,10 @@ int cortex_m_hit_watchpoint(struct target *target, struct watchpoint **hit_watch struct cortex_m_common *cortex_m = target_to_cm(target); for (struct watchpoint *wp = target->watchpoints; wp; wp = wp->next) { - if (!wp->set) + if (!wp->is_set) continue; - unsigned int dwt_num = wp->set - 1; + unsigned int dwt_num = wp->number; struct cortex_m_dwt_comparator *comparator = cortex_m->dwt_comparator_list + dwt_num; uint32_t dwt_function; @@ -1939,7 +1939,7 @@ void cortex_m_enable_watchpoints(struct target *target) /* set any pending watchpoints */ while (watchpoint) { - if (!watchpoint->set) + if (!watchpoint->is_set) cortex_m_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } @@ -2004,18 +2004,18 @@ int cortex_m_profiling(struct target *target, uint32_t *samples, retval = target_read_u32(target, DWT_PCSR, ®_value); if (retval != ERROR_OK) { - LOG_ERROR("Error while reading PCSR"); + LOG_TARGET_ERROR(target, "Error while reading PCSR"); return retval; } if (reg_value == 0) { - LOG_INFO("PCSR sampling not supported on this processor."); + LOG_TARGET_INFO(target, "PCSR sampling not supported on this processor."); return target_profiling_default(target, samples, max_num_samples, num_samples, seconds); } gettimeofday(&timeout, NULL); timeval_add_time(&timeout, seconds, 0); - LOG_INFO("Starting Cortex-M profiling. Sampling DWT_PCSR as fast as we can..."); + LOG_TARGET_INFO(target, "Starting Cortex-M profiling. Sampling DWT_PCSR as fast as we can..."); /* Make sure the target is running */ target_poll(target); @@ -2023,7 +2023,7 @@ int cortex_m_profiling(struct target *target, uint32_t *samples, retval = target_resume(target, 1, 0, 0, 0); if (retval != ERROR_OK) { - LOG_ERROR("Error while resuming target"); + LOG_TARGET_ERROR(target, "Error while resuming target"); return retval; } @@ -2044,14 +2044,14 @@ int cortex_m_profiling(struct target *target, uint32_t *samples, } if (retval != ERROR_OK) { - LOG_ERROR("Error while reading PCSR"); + LOG_TARGET_ERROR(target, "Error while reading PCSR"); return retval; } gettimeofday(&now, NULL); if (sample_count >= max_num_samples || timeval_compare(&now, &timeout) > 0) { - LOG_INFO("Profiling completed. %" PRIu32 " samples.", sample_count); + LOG_TARGET_INFO(target, "Profiling completed. %" PRIu32 " samples.", sample_count); break; } } @@ -2162,14 +2162,14 @@ static void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target int reg; target_read_u32(target, DWT_CTRL, &dwtcr); - LOG_DEBUG("DWT_CTRL: 0x%" PRIx32, dwtcr); + LOG_TARGET_DEBUG(target, "DWT_CTRL: 0x%" PRIx32, dwtcr); if (!dwtcr) { - LOG_DEBUG("no DWT"); + LOG_TARGET_DEBUG(target, "no DWT"); return; } target_read_u32(target, DWT_DEVARCH, &cm->dwt_devarch); - LOG_DEBUG("DWT_DEVARCH: 0x%" PRIx32, cm->dwt_devarch); + LOG_TARGET_DEBUG(target, "DWT_DEVARCH: 0x%" PRIx32, cm->dwt_devarch); cm->dwt_num_comp = (dwtcr >> 28) & 0xF; cm->dwt_comp_available = cm->dwt_num_comp; @@ -2178,7 +2178,7 @@ static void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target if (!cm->dwt_comparator_list) { fail0: cm->dwt_num_comp = 0; - LOG_ERROR("out of mem"); + LOG_TARGET_ERROR(target, "out of mem"); return; } @@ -2216,7 +2216,7 @@ static void cortex_m_dwt_setup(struct cortex_m_common *cm, struct target *target *register_get_last_cache_p(&target->reg_cache) = cache; cm->dwt_cache = cache; - LOG_DEBUG("DWT dwtcr 0x%" PRIx32 ", comp %d, watch%s", + LOG_TARGET_DEBUG(target, "DWT dwtcr 0x%" PRIx32 ", comp %d, watch%s", dwtcr, cm->dwt_num_comp, (dwtcr & (0xf << 24)) ? " only" : "/trigger"); @@ -2283,7 +2283,7 @@ int cortex_m_examine(struct target *target) /* Search for the MEM-AP */ retval = cortex_m_find_mem_ap(swjdp, &armv7m->debug_ap); if (retval != ERROR_OK) { - LOG_ERROR("Could not find MEM-AP to control the core"); + LOG_TARGET_ERROR(target, "Could not find MEM-AP to control the core"); return retval; } } else { @@ -2316,14 +2316,13 @@ int cortex_m_examine(struct target *target) } if (!cortex_m->core_info) { - LOG_ERROR("Cortex-M PARTNO 0x%x is unrecognized", core_partno); + LOG_TARGET_ERROR(target, "Cortex-M PARTNO 0x%x is unrecognized", core_partno); return ERROR_FAIL; } armv7m->arm.arch = cortex_m->core_info->arch; - LOG_INFO("%s: %s r%" PRId8 "p%" PRId8 " processor detected", - target_name(target), + LOG_TARGET_INFO(target, "%s r%" PRId8 "p%" PRId8 " processor detected", cortex_m->core_info->name, (uint8_t)((cpuid >> 20) & 0xf), (uint8_t)((cpuid >> 0) & 0xf)); @@ -2334,11 +2333,11 @@ int cortex_m_examine(struct target *target) rev = (cpuid >> 20) & 0xf; patch = (cpuid >> 0) & 0xf; if ((rev == 0) && (patch < 2)) { - LOG_WARNING("Silicon bug: single stepping may enter pending exception handler!"); + LOG_TARGET_WARNING(target, "Silicon bug: single stepping may enter pending exception handler!"); cortex_m->maskints_erratum = true; } } - LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid); + LOG_TARGET_DEBUG(target, "cpuid: 0x%8.8" PRIx32 "", cpuid); if (cortex_m->core_info->flags & CORTEX_M_F_HAS_FPV4) { target_read_u32(target, MVFR0, &mvfr0); @@ -2346,7 +2345,7 @@ int cortex_m_examine(struct target *target) /* test for floating point feature on Cortex-M4 */ if ((mvfr0 == MVFR0_DEFAULT_M4) && (mvfr1 == MVFR1_DEFAULT_M4)) { - LOG_DEBUG("%s floating point feature FPv4_SP found", cortex_m->core_info->name); + LOG_TARGET_DEBUG(target, "%s floating point feature FPv4_SP found", cortex_m->core_info->name); armv7m->fp_feature = FPV4_SP; } } else if (cortex_m->core_info->flags & CORTEX_M_F_HAS_FPV5) { @@ -2355,10 +2354,10 @@ int cortex_m_examine(struct target *target) /* test for floating point features on Cortex-M7 */ if ((mvfr0 == MVFR0_DEFAULT_M7_SP) && (mvfr1 == MVFR1_DEFAULT_M7_SP)) { - LOG_DEBUG("%s floating point feature FPv5_SP found", cortex_m->core_info->name); + LOG_TARGET_DEBUG(target, "%s floating point feature FPv5_SP found", cortex_m->core_info->name); armv7m->fp_feature = FPV5_SP; } else if ((mvfr0 == MVFR0_DEFAULT_M7_DP) && (mvfr1 == MVFR1_DEFAULT_M7_DP)) { - LOG_DEBUG("%s floating point feature FPv5_DP found", cortex_m->core_info->name); + LOG_TARGET_DEBUG(target, "%s floating point feature FPv5_DP found", cortex_m->core_info->name); armv7m->fp_feature = FPV5_DP; } } @@ -2428,7 +2427,7 @@ int cortex_m_examine(struct target *target) /* make sure we clear any breakpoints enabled on the target */ target_write_u32(target, cortex_m->fp_comparator_list[i].fpcr_address, 0); } - LOG_DEBUG("FPB fpcr 0x%" PRIx32 ", numcode %i, numlit %i", + LOG_TARGET_DEBUG(target, "FPB fpcr 0x%" PRIx32 ", numcode %i, numlit %i", fpcr, cortex_m->fp_num_code, cortex_m->fp_num_lit); @@ -2438,8 +2437,7 @@ int cortex_m_examine(struct target *target) cortex_m_dwt_setup(cortex_m, target); /* These hardware breakpoints only work for code in flash! */ - LOG_INFO("%s: target has %d breakpoints, %d watchpoints", - target_name(target), + LOG_TARGET_INFO(target, "target has %d breakpoints, %d watchpoints", cortex_m->fp_num_code, cortex_m->dwt_num_comp); } @@ -2462,7 +2460,7 @@ static int cortex_m_dcc_read(struct target *target, uint8_t *value, uint8_t *ctr *ctrl = (uint8_t)dcrdr; *value = (uint8_t)(dcrdr >> 8); - LOG_DEBUG("data 0x%x ctrl 0x%x", *value, *ctrl); + LOG_TARGET_DEBUG(target, "data 0x%x ctrl 0x%x", *value, *ctrl); /* write ack back to software dcc register * signify we have read data */ @@ -2569,7 +2567,7 @@ static int cortex_m_target_create(struct target *target, Jim_Interp *interp) struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common)); if (!cortex_m) { - LOG_ERROR("No memory creating target"); + LOG_TARGET_ERROR(target, "No memory creating target"); return ERROR_FAIL; } @@ -2626,7 +2624,7 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command) return retval; if (!target_was_examined(target)) { - LOG_ERROR("Target not examined yet"); + LOG_TARGET_ERROR(target, "Target not examined yet"); return ERROR_FAIL; } @@ -2655,7 +2653,7 @@ COMMAND_HANDLER(handle_cortex_m_vector_catch_command) break; } if (i == ARRAY_SIZE(vec_ids)) { - LOG_ERROR("No CM3 vector '%s'", CMD_ARGV[CMD_ARGC]); + LOG_TARGET_ERROR(target, "No CM3 vector '%s'", CMD_ARGV[CMD_ARGC]); return ERROR_COMMAND_SYNTAX_ERROR; } } @@ -2745,7 +2743,7 @@ COMMAND_HANDLER(handle_cortex_m_reset_config_command) else if (strcmp(*CMD_ARGV, "vectreset") == 0) { if (target_was_examined(target) && !cortex_m->vectreset_supported) - LOG_WARNING("VECTRESET is not supported on your Cortex-M core!"); + LOG_TARGET_WARNING(target, "VECTRESET is not supported on your Cortex-M core!"); else cortex_m->soft_reset_config = CORTEX_M_RESET_VECTRESET; diff --git a/src/target/cortex_m.h b/src/target/cortex_m.h index c2f836a35..555401416 100644 --- a/src/target/cortex_m.h +++ b/src/target/cortex_m.h @@ -46,6 +46,7 @@ #define ARM_CPUID_PARTNO_MASK (0xFFF << ARM_CPUID_PARTNO_POS) enum cortex_m_partno { + CORTEX_M_PARTNO_INVALID, CORTEX_M0_PARTNO = 0xC20, CORTEX_M1_PARTNO = 0xC21, CORTEX_M3_PARTNO = 0xC23, @@ -247,13 +248,6 @@ struct cortex_m_common { bool maskints_erratum; }; -static inline struct cortex_m_common * -target_to_cm(struct target *target) -{ - return container_of(target->arch_info, - struct cortex_m_common, armv7m); -} - static inline bool is_cortex_m_or_hla(const struct cortex_m_common *cortex_m) { return cortex_m->common_magic == CORTEX_M_COMMON_MAGIC; @@ -267,6 +261,57 @@ static inline bool is_cortex_m_with_dap_access(const struct cortex_m_common *cor return !cortex_m->armv7m.is_hla_target; } +/** + * @returns the pointer to the target specific struct + * without matching a magic number. + * Use in target specific service routines, where the correct + * type of arch_info is certain. + */ +static inline struct cortex_m_common * +target_to_cm(struct target *target) +{ + return container_of(target->arch_info, + struct cortex_m_common, armv7m.arm); +} + +/** + * @returns the pointer to the target specific struct + * or NULL if the magic number does not match. + * Use in a flash driver or any place where mismatch of the arch_info + * type can happen. + */ +static inline struct cortex_m_common * +target_to_cortex_m_safe(struct target *target) +{ + /* Check the parent types first to prevent peeking memory too far + * from arch_info pointer */ + if (!target_to_armv7m_safe(target)) + return NULL; + + struct cortex_m_common *cortex_m = target_to_cm(target); + if (!is_cortex_m_or_hla(cortex_m)) + return NULL; + + return cortex_m; +} + +/** + * @returns cached value of Cortex-M part number + * or CORTEX_M_PARTNO_INVALID if the magic number does not match + * or core_info is not initialised. + */ +static inline enum cortex_m_partno cortex_m_get_partno_safe(struct target *target) +{ + struct cortex_m_common *cortex_m = target_to_cortex_m_safe(target); + if (!cortex_m) + return CORTEX_M_PARTNO_INVALID; + + if (!cortex_m->core_info) + return CORTEX_M_PARTNO_INVALID; + + return cortex_m->core_info->partno; +} + int cortex_m_examine(struct target *target); int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint); int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoint); diff --git a/src/target/esirisc.c b/src/target/esirisc.c index e49f5f659..aadd111ee 100644 --- a/src/target/esirisc.c +++ b/src/target/esirisc.c @@ -504,7 +504,7 @@ static int esirisc_add_breakpoint(struct target *target, struct breakpoint *brea return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = bp_index + 1; + breakpoint_hw_set(breakpoint, bp_index); esirisc->breakpoints_p[bp_index] = breakpoint; /* specify instruction breakpoint address */ @@ -540,7 +540,7 @@ static int esirisc_add_breakpoints(struct target *target) LOG_DEBUG("-"); while (breakpoint) { - if (breakpoint->set == 0) + if (!breakpoint->is_set) esirisc_add_breakpoint(target, breakpoint); breakpoint = breakpoint->next; @@ -553,7 +553,7 @@ static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *b { struct esirisc_common *esirisc = target_to_esirisc(target); struct esirisc_jtag *jtag_info = &esirisc->jtag_info; - int bp_index = breakpoint->set - 1; + unsigned int bp_index = breakpoint->number; uint32_t ibc; int retval; @@ -575,7 +575,7 @@ static int esirisc_remove_breakpoint(struct target *target, struct breakpoint *b } esirisc->breakpoints_p[bp_index] = NULL; - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } @@ -630,7 +630,7 @@ static int esirisc_add_watchpoint(struct target *target, struct watchpoint *watc return ERROR_FAIL; } - watchpoint->set = wp_index + 1; + watchpoint_set(watchpoint, wp_index); esirisc->watchpoints_p[wp_index] = watchpoint; /* specify data breakpoint address */ @@ -724,7 +724,7 @@ static int esirisc_add_watchpoints(struct target *target) LOG_DEBUG("-"); while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->is_set) esirisc_add_watchpoint(target, watchpoint); watchpoint = watchpoint->next; @@ -737,7 +737,7 @@ static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *w { struct esirisc_common *esirisc = target_to_esirisc(target); struct esirisc_jtag *jtag_info = &esirisc->jtag_info; - int wp_index = watchpoint->set - 1; + unsigned int wp_index = watchpoint->number; uint32_t dbc; int retval; @@ -759,7 +759,7 @@ static int esirisc_remove_watchpoint(struct target *target, struct watchpoint *w } esirisc->watchpoints_p[wp_index] = NULL; - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } diff --git a/src/target/image.c b/src/target/image.c index eafa73eaa..130ea6c1f 100644 --- a/src/target/image.c +++ b/src/target/image.c @@ -596,7 +596,7 @@ static int image_elf64_read_headers(struct image *image) image->sections[j].base_address = field64(elf, elf->segments64[i].p_paddr); image->sections[j].private = &elf->segments64[i]; - image->sections[j].flags = field32(elf, elf->segments64[i].p_flags); + image->sections[j].flags = field64(elf, elf->segments64[i].p_flags); j++; } } @@ -1168,7 +1168,7 @@ int image_read_section(struct image *image, return ERROR_OK; } -int image_add_section(struct image *image, target_addr_t base, uint32_t size, int flags, uint8_t const *data) +int image_add_section(struct image *image, target_addr_t base, uint32_t size, uint64_t flags, uint8_t const *data) { struct imagesection *section; diff --git a/src/target/image.h b/src/target/image.h index 5b5d11f6b..bf06064ac 100644 --- a/src/target/image.h +++ b/src/target/image.h @@ -52,7 +52,7 @@ enum image_type { struct imagesection { target_addr_t base_address; uint32_t size; - int flags; + uint64_t flags; void *private; /* private data */ }; @@ -108,7 +108,7 @@ int image_read_section(struct image *image, int section, target_addr_t offset, void image_close(struct image *image); int image_add_section(struct image *image, target_addr_t base, uint32_t size, - int flags, uint8_t const *data); + uint64_t flags, uint8_t const *data); int image_calculate_checksum(const uint8_t *buffer, uint32_t nbytes, uint32_t *checksum); diff --git a/src/target/lakemont.c b/src/target/lakemont.c index e46ee5cf8..230f53fe0 100644 --- a/src/target/lakemont.c +++ b/src/target/lakemont.c @@ -1024,10 +1024,10 @@ int lakemont_resume(struct target *t, int current, target_addr_t address, /* if breakpoints are enabled, we need to redirect these into probe mode */ struct breakpoint *activeswbp = t->breakpoints; - while (activeswbp && activeswbp->set == 0) + while (activeswbp && !activeswbp->is_set) activeswbp = activeswbp->next; struct watchpoint *activehwbp = t->watchpoints; - while (activehwbp && activehwbp->set == 0) + while (activehwbp && !activehwbp->is_set) activehwbp = activehwbp->next; if (activeswbp || activehwbp) buf_set_u32(x86_32->cache->reg_list[PMCR].value, 0, 32, 1); diff --git a/src/target/mips_m4k.c b/src/target/mips_m4k.c index ca4416981..860119345 100644 --- a/src/target/mips_m4k.c +++ b/src/target/mips_m4k.c @@ -128,14 +128,11 @@ static int mips_m4k_debug_entry(struct target *target) static struct target *get_mips_m4k(struct target *target, int32_t coreid) { struct target_list *head; - struct target *curr; - head = target->head; - while (head) { - curr = head->target; + foreach_smp_target(head, target->smp_targets) { + struct target *curr = head->target; if ((curr->coreid == coreid) && (curr->state == TARGET_HALTED)) return curr; - head = head->next; } return target; } @@ -144,11 +141,10 @@ static int mips_m4k_halt_smp(struct target *target) { int retval = ERROR_OK; struct target_list *head; - struct target *curr; - head = target->head; - while (head) { + + foreach_smp_target(head, target->smp_targets) { int ret = ERROR_OK; - curr = head->target; + struct target *curr = head->target; if ((curr != target) && (curr->state != TARGET_HALTED)) ret = mips_m4k_halt(curr); @@ -156,7 +152,6 @@ static int mips_m4k_halt_smp(struct target *target) LOG_ERROR("halt failed target->coreid: %" PRId32, curr->coreid); retval = ret; } - head = head->next; } return retval; } @@ -414,12 +409,10 @@ static int mips_m4k_restore_smp(struct target *target, uint32_t address, int han { int retval = ERROR_OK; struct target_list *head; - struct target *curr; - head = target->head; - while (head) { + foreach_smp_target(head, target->smp_targets) { int ret = ERROR_OK; - curr = head->target; + struct target *curr = head->target; if ((curr != target) && (curr->state != TARGET_RUNNING)) { /* resume current address , not in step mode */ ret = mips_m4k_internal_restore(curr, 1, address, @@ -431,7 +424,6 @@ static int mips_m4k_restore_smp(struct target *target, uint32_t address, int han retval = ret; } } - head = head->next; } return retval; } @@ -601,7 +593,7 @@ static void mips_m4k_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (breakpoint) { - if (breakpoint->set == 0) + if (!breakpoint->is_set) mips_m4k_set_breakpoint(target, breakpoint); breakpoint = breakpoint->next; } @@ -615,7 +607,7 @@ static int mips_m4k_set_breakpoint(struct target *target, struct mips32_comparator *comparator_list = mips32->inst_break_list; int retval; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -630,7 +622,7 @@ static int mips_m4k_set_breakpoint(struct target *target, breakpoint->unique_id); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = bp_num + 1; + breakpoint_hw_set(breakpoint, bp_num); comparator_list[bp_num].used = 1; comparator_list[bp_num].bp_value = breakpoint->address; @@ -732,7 +724,7 @@ static int mips_m4k_set_breakpoint(struct target *target, } } - breakpoint->set = 20; /* Any nice value but 0 */ + breakpoint->is_set = true; } return ERROR_OK; @@ -747,14 +739,14 @@ static int mips_m4k_unset_breakpoint(struct target *target, struct mips32_comparator *comparator_list = mips32->inst_break_list; int retval; - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } if (breakpoint->type == BKPT_HARD) { - int bp_num = breakpoint->set - 1; - if ((bp_num < 0) || (bp_num >= mips32->num_inst_bpoints)) { + int bp_num = breakpoint->number; + if (bp_num >= mips32->num_inst_bpoints) { LOG_DEBUG("Invalid FP Comparator number in breakpoint (bpid: %" PRIu32 ")", breakpoint->unique_id); return ERROR_OK; @@ -821,7 +813,7 @@ static int mips_m4k_unset_breakpoint(struct target *target, } } - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } @@ -859,7 +851,7 @@ static int mips_m4k_remove_breakpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (breakpoint->set) + if (breakpoint->is_set) mips_m4k_unset_breakpoint(target, breakpoint); if (breakpoint->type == BKPT_HARD) @@ -883,7 +875,7 @@ static int mips_m4k_set_watchpoint(struct target *target, int enable = EJTAG_DBCN_NOSB | EJTAG_DBCN_NOLB | EJTAG_DBCN_BE | (0xff << EJTAG_DBCN_BLM_SHIFT); - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return ERROR_OK; } @@ -919,7 +911,7 @@ static int mips_m4k_set_watchpoint(struct target *target, LOG_ERROR("BUG: watchpoint->rw neither read, write nor access"); } - watchpoint->set = wp_num + 1; + watchpoint->number = wp_num; comparator_list[wp_num].used = 1; comparator_list[wp_num].bp_value = watchpoint->address; @@ -954,13 +946,13 @@ static int mips_m4k_unset_watchpoint(struct target *target, struct mips_ejtag *ejtag_info = &mips32->ejtag_info; struct mips32_comparator *comparator_list = mips32->data_break_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - int wp_num = watchpoint->set - 1; - if ((wp_num < 0) || (wp_num >= mips32->num_data_bpoints)) { + int wp_num = watchpoint->number; + if (wp_num >= mips32->num_data_bpoints) { LOG_DEBUG("Invalid FP Comparator number in watchpoint"); return ERROR_OK; } @@ -968,7 +960,7 @@ static int mips_m4k_unset_watchpoint(struct target *target, comparator_list[wp_num].bp_value = 0; target_write_u32(target, comparator_list[wp_num].reg_address + ejtag_info->ejtag_dbc_offs, 0); - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -999,7 +991,7 @@ static int mips_m4k_remove_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) mips_m4k_unset_watchpoint(target, watchpoint); mips32->num_data_bpoints_avail++; @@ -1013,7 +1005,7 @@ static void mips_m4k_enable_watchpoints(struct target *target) /* set any pending watchpoints */ while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->is_set) mips_m4k_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } diff --git a/src/target/mips_mips64.c b/src/target/mips_mips64.c index 56b0194e7..5d821d7cb 100644 --- a/src/target/mips_mips64.c +++ b/src/target/mips_mips64.c @@ -346,7 +346,7 @@ static int mips_mips64_set_breakpoint(struct target *target, { int retval; - if (bp->set) { + if (bp->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -373,7 +373,7 @@ static int mips_mips64_set_breakpoint(struct target *target, return retval; } - bp->set = true; + bp->is_set = true; return ERROR_OK; } @@ -385,7 +385,7 @@ static int mips_mips64_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (bp) { - if (!bp->set) { + if (!bp->is_set) { retval = mips_mips64_set_breakpoint(target, bp); if (retval != ERROR_OK) return retval; @@ -413,7 +413,7 @@ static int mips_mips64_set_watchpoint(struct target *target, int enable = EJTAG_DBCN_NOSB | EJTAG_DBCN_NOLB | EJTAG_DBCN_BE | (0xff << EJTAG_DBCN_BLM_SHIFT); - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return ERROR_OK; } @@ -451,7 +451,7 @@ static int mips_mips64_set_watchpoint(struct target *target, } c = &cl[wp_num]; - watchpoint->set = wp_num + 1; + watchpoint_set(watchpoint, wp_num); c->used = true; c->bp_value = watchpoint->address; @@ -491,7 +491,7 @@ static int mips_mips64_enable_watchpoints(struct target *target) /* set any pending watchpoints */ while (watchpoint) { - if (watchpoint->set == 0) { + if (!watchpoint->is_set) { retval = mips_mips64_set_watchpoint(target, watchpoint); if (retval != ERROR_OK) return retval; @@ -506,11 +506,10 @@ static int mips_mips64_unset_hwbp(struct target *target, struct breakpoint *bp) { struct mips64_common *mips64 = target->arch_info; struct mips64_comparator *comparator_list = mips64->inst_break_list; - int bp_num; - bp_num = bp->set - 1; + int bp_num = bp->number; - if ((bp_num < 0) || (bp_num >= mips64->num_inst_bpoints)) { + if (bp_num >= mips64->num_inst_bpoints) { LOG_DEBUG("Invalid FP Comparator number in breakpoint (bpid: %" PRIu32 ")", bp->unique_id); return ERROR_OK; @@ -568,7 +567,7 @@ static int mips_mips64_unset_breakpoint(struct target *target, /* get pointers to arch-specific information */ int retval; - if (!bp->set) { + if (!bp->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } @@ -594,7 +593,7 @@ static int mips_mips64_unset_breakpoint(struct target *target, return retval; } - bp->set = false; + bp->is_set = false; return ERROR_OK; } @@ -815,7 +814,7 @@ static int mips_mips64_remove_breakpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (bp->set) + if (bp->is_set) retval = mips_mips64_unset_breakpoint(target, bp); if (bp->type == BKPT_HARD) @@ -831,20 +830,20 @@ static int mips_mips64_unset_watchpoint(struct target *target, struct mips64_common *mips64 = target->arch_info; struct mips64_comparator *comparator_list = mips64->data_break_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - int wp_num = watchpoint->set - 1; - if ((wp_num < 0) || (wp_num >= mips64->num_data_bpoints)) { + int wp_num = watchpoint->number; + if (wp_num >= mips64->num_data_bpoints) { LOG_DEBUG("Invalid FP Comparator number in watchpoint"); return ERROR_OK; } comparator_list[wp_num].used = false; comparator_list[wp_num].bp_value = 0; target_write_u64(target, comparator_list[wp_num].reg_address + 0x18, 0); - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -876,7 +875,7 @@ static int mips_mips64_remove_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) retval = mips_mips64_unset_watchpoint(target, watchpoint); mips64->num_data_bpoints_avail++; diff --git a/src/target/nds32.c b/src/target/nds32.c index 12340ac2c..f0fb74d31 100644 --- a/src/target/nds32.c +++ b/src/target/nds32.c @@ -1653,7 +1653,7 @@ int nds32_init_arch_info(struct target *target, struct nds32 *nds32) nds32->syscall_break.asid = 0; nds32->syscall_break.length = 4; - nds32->syscall_break.set = 0; + nds32->syscall_break.is_set = false; nds32->syscall_break.orig_instr = NULL; nds32->syscall_break.next = NULL; nds32->syscall_break.unique_id = 0x515CAll + target->target_number; diff --git a/src/target/nds32_v3_common.c b/src/target/nds32_v3_common.c index b0c3de622..8ff8e30c6 100644 --- a/src/target/nds32_v3_common.c +++ b/src/target/nds32_v3_common.c @@ -78,12 +78,12 @@ static int nds32_v3_debug_entry(struct nds32 *nds32, bool enable_watchpoint) struct breakpoint *syscall_break = &(nds32->syscall_break); if (nds32->virtual_hosting) { - if (syscall_break->set) { + if (syscall_break->is_set) { /** disable virtual hosting */ /* remove breakpoint at syscall entry */ target_remove_breakpoint(nds32->target, syscall_break); - syscall_break->set = 0; + syscall_break->is_set = false; uint32_t value_pc; nds32_get_mapped_reg(nds32, PC, &value_pc); @@ -209,7 +209,7 @@ static int nds32_v3_leave_debug_state(struct nds32 *nds32, bool enable_watchpoin syscall_break->address = syscall_address; syscall_break->type = BKPT_SOFT; - syscall_break->set = 1; + syscall_break->is_set = true; target_add_breakpoint(target, syscall_break); } diff --git a/src/target/openrisc/jsp_server.c b/src/target/openrisc/jsp_server.c index e0a4475cf..54c969424 100644 --- a/src/target/openrisc/jsp_server.c +++ b/src/target/openrisc/jsp_server.c @@ -195,19 +195,22 @@ static int jsp_connection_closed(struct connection *connection) return ERROR_OK; } +static const struct service_driver jsp_service_driver = { + .name = "jsp", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = jsp_new_connection, + .input_handler = jsp_input, + .connection_closed_handler = jsp_connection_closed, + .keep_client_alive_handler = NULL, +}; + int jsp_init(struct or1k_jtag *jtag_info, char *banner) { struct jsp_service *jsp_service = malloc(sizeof(struct jsp_service)); jsp_service->banner = banner; jsp_service->jtag_info = jtag_info; - return add_service("jsp", - jsp_port, - 1, - jsp_new_connection, - jsp_input, - jsp_connection_closed, - jsp_service); + return add_service(&jsp_service_driver, jsp_port, 1, jsp_service); } COMMAND_HANDLER(handle_jsp_port_command) diff --git a/src/target/openrisc/or1k.c b/src/target/openrisc/or1k.c index 8fbcd9620..77fa15d50 100644 --- a/src/target/openrisc/or1k.c +++ b/src/target/openrisc/or1k.c @@ -923,9 +923,9 @@ static int or1k_add_breakpoint(struct target *target, struct or1k_du *du_core = or1k_to_du(or1k); uint8_t data; - LOG_DEBUG("Adding breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, set: %d, id: %" PRIu32, + LOG_DEBUG("Adding breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, id: %" PRIu32, breakpoint->address, breakpoint->length, breakpoint->type, - breakpoint->set, breakpoint->unique_id); + breakpoint->unique_id); /* Only support SW breakpoints for now. */ if (breakpoint->type == BKPT_HARD) @@ -981,9 +981,9 @@ static int or1k_remove_breakpoint(struct target *target, struct or1k_common *or1k = target_to_or1k(target); struct or1k_du *du_core = or1k_to_du(or1k); - LOG_DEBUG("Removing breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, set: %d, id: %" PRIu32, + LOG_DEBUG("Removing breakpoint: addr 0x%08" TARGET_PRIxADDR ", len %d, type %d, id: %" PRIu32, breakpoint->address, breakpoint->length, breakpoint->type, - breakpoint->set, breakpoint->unique_id); + breakpoint->unique_id); /* Only support SW breakpoints for now. */ if (breakpoint->type == BKPT_HARD) diff --git a/src/target/riscv/debug_defines.h b/src/target/riscv/debug_defines.h index 5c9eef4ea..8113d4766 100644 --- a/src/target/riscv/debug_defines.h +++ b/src/target/riscv/debug_defines.h @@ -1,22 +1,21 @@ /* * This file is auto-generated by running 'make debug_defines.h' in - * https://github.com/riscv/riscv-debug-spec/ (3dfe4f7) - * License: Creative Commons Attribution 4.0 International Public License (CC BY 4.0) + * https://github.com/riscv/riscv-debug-spec/ (d749752) */ #define DTM_IDCODE 0x01 /* * Identifies the release version of this part. */ -#define DTM_IDCODE_VERSION_OFFSET 28 +#define DTM_IDCODE_VERSION_OFFSET 0x1c #define DTM_IDCODE_VERSION_LENGTH 4 -#define DTM_IDCODE_VERSION (0xfU << DTM_IDCODE_VERSION_OFFSET) +#define DTM_IDCODE_VERSION 0xf0000000U /* * Identifies the designer's part number of this part. */ -#define DTM_IDCODE_PARTNUMBER_OFFSET 12 -#define DTM_IDCODE_PARTNUMBER_LENGTH 16 -#define DTM_IDCODE_PARTNUMBER (0xffffU << DTM_IDCODE_PARTNUMBER_OFFSET) +#define DTM_IDCODE_PARTNUMBER_OFFSET 0xc +#define DTM_IDCODE_PARTNUMBER_LENGTH 0x10 +#define DTM_IDCODE_PARTNUMBER 0xffff000 /* * Identifies the designer/manufacturer of this part. Bits 6:0 must be * bits 6:0 of the designer/manufacturer's Identification Code as @@ -25,11 +24,11 @@ * Identification Code. */ #define DTM_IDCODE_MANUFID_OFFSET 1 -#define DTM_IDCODE_MANUFID_LENGTH 11 -#define DTM_IDCODE_MANUFID (0x7ffU << DTM_IDCODE_MANUFID_OFFSET) +#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 (0x1U << DTM_IDCODE_1_OFFSET) +#define DTM_IDCODE_1 1 #define DTM_DTMCS 0x10 /* * Writing 1 to this bit does a hard reset of the DTM, @@ -40,16 +39,16 @@ * complete (e.g. a reset condition caused an inflight DMI transaction to * be cancelled). */ -#define DTM_DTMCS_DMIHARDRESET_OFFSET 17 +#define DTM_DTMCS_DMIHARDRESET_OFFSET 0x11 #define DTM_DTMCS_DMIHARDRESET_LENGTH 1 -#define DTM_DTMCS_DMIHARDRESET (0x1U << DTM_DTMCS_DMIHARDRESET_OFFSET) +#define DTM_DTMCS_DMIHARDRESET 0x20000 /* * Writing 1 to this bit clears the sticky error state, but does * not affect outstanding DMI transactions. */ -#define DTM_DTMCS_DMIRESET_OFFSET 16 +#define DTM_DTMCS_DMIRESET_OFFSET 0x10 #define DTM_DTMCS_DMIRESET_LENGTH 1 -#define DTM_DTMCS_DMIRESET (0x1U << DTM_DTMCS_DMIRESET_OFFSET) +#define DTM_DTMCS_DMIRESET 0x10000 /* * This is a hint to the debugger of the minimum number of * cycles a debugger should spend in @@ -65,76 +64,89 @@ * * And so on. */ -#define DTM_DTMCS_IDLE_OFFSET 12 +#define DTM_DTMCS_IDLE_OFFSET 0xc #define DTM_DTMCS_IDLE_LENGTH 3 -#define DTM_DTMCS_IDLE (0x7U << DTM_DTMCS_IDLE_OFFSET) +#define DTM_DTMCS_IDLE 0x7000 /* - * 0: No error. - * - * 1: Reserved. Interpret the same as 2. - * - * 2: An operation failed (resulted in \FdtmDmiOp of 2). - * - * 3: An operation was attempted while a DMI access was still in - * progress (resulted in \FdtmDmiOp of 3). + * Read-only alias of \FdtmDmiOp. */ -#define DTM_DTMCS_DMISTAT_OFFSET 10 +#define DTM_DTMCS_DMISTAT_OFFSET 0xa #define DTM_DTMCS_DMISTAT_LENGTH 2 -#define DTM_DTMCS_DMISTAT (0x3U << DTM_DTMCS_DMISTAT_OFFSET) +#define DTM_DTMCS_DMISTAT 0xc00 /* * The size of \FdmSbaddressZeroAddress in \RdtmDmi. */ #define DTM_DTMCS_ABITS_OFFSET 4 #define DTM_DTMCS_ABITS_LENGTH 6 -#define DTM_DTMCS_ABITS (0x3fU << DTM_DTMCS_ABITS_OFFSET) -/* - * 0: Version described in spec version 0.11. - * - * 1: Version described in spec versions 0.13 and 1.0. - * - * 15: Version not described in any available version of this spec. - */ +#define DTM_DTMCS_ABITS 0x3f0 #define DTM_DTMCS_VERSION_OFFSET 0 #define DTM_DTMCS_VERSION_LENGTH 4 -#define DTM_DTMCS_VERSION (0xfU << DTM_DTMCS_VERSION_OFFSET) +#define DTM_DTMCS_VERSION 0xf +/* + * 0.11: Version described in spec version 0.11. + */ +#define DTM_DTMCS_VERSION_0_11 0 +/* + * 1.0: Version described in spec versions 0.13 and 1.0. + */ +#define DTM_DTMCS_VERSION_1_0 1 +/* + * custom: Version not described in any available version of this spec. + */ +#define DTM_DTMCS_VERSION_CUSTOM 15 #define DTM_DMI 0x11 /* * Address used for DMI access. In Update-DR this value is used * to access the DM over the DMI. */ -#define DTM_DMI_ADDRESS_OFFSET 34 -#define DTM_DMI_ADDRESS_LENGTH abits -#define DTM_DMI_ADDRESS (((1L << abits) - 1) << DTM_DMI_ADDRESS_OFFSET) +#define DTM_DMI_ADDRESS_OFFSET 0x22 +#define DTM_DMI_ADDRESS_LENGTH(abits) abits +#define DTM_DMI_ADDRESS(abits) ((0x400000000ULL * (1ULL<abs_chain_position = abs_chain_position; - dm->current_hartid = -1; + dm->current_hartid = 0; dm->hart_count = -1; INIT_LIST_HEAD(&dm->target_list); list_add(&dm->list, &dm_list); @@ -281,16 +282,21 @@ dm013_info_t *get_dm(struct target *target) return dm; } -static uint32_t set_hartsel(uint32_t initial, uint32_t index) +static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index) { - initial &= ~DM_DMCONTROL_HARTSELLO; - initial &= ~DM_DMCONTROL_HARTSELHI; - - 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; + 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; } @@ -707,8 +713,14 @@ int dmstatus_read_timeout(struct target *target, uint32_t *dmstatus, 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) @@ -748,20 +760,6 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) 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_ERROR("Timed out after %ds waiting for busy to go low (abstractcs=0x%x). " "Increase the timeout with riscv set_command_timeout_sec.", @@ -772,6 +770,12 @@ static int wait_for_idle(struct target *target, uint32_t *abstractcs) } } +static int dm013_select_target(struct target *target) +{ + 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) { RISCV013_INFO(info); @@ -1248,6 +1252,7 @@ static int scratch_write64(struct target *target, scratch_mem_t *scratch, case SPACE_DMI_PROGBUF: dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value); dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); + riscv013_invalidate_cached_debug_buffer(target); break; case SPACE_DMI_RAM: { @@ -1295,8 +1300,7 @@ static bool has_sufficient_progbuf(struct target *target, unsigned size) static int register_write_direct(struct target *target, unsigned number, uint64_t value) { - LOG_DEBUG("{%d} %s <- 0x%" PRIx64, riscv_current_hartid(target), - gdb_regno_name(number), value); + LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(number), value); int result = register_write_abstract(target, number, value, register_size(target, number)); @@ -1322,7 +1326,9 @@ static int register_write_direct(struct target *target, unsigned number, /* 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 (riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0)) + != ERROR_OK) + return ERROR_FAIL; if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) return ERROR_FAIL; @@ -1340,10 +1346,15 @@ static int register_write_direct(struct target *target, unsigned number, } else { 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)); + if (riscv_supports_extension(target, 'D')) { + if (riscv_program_insert(&program, + fmv_d_x(number - GDB_REGNO_FPR0, S0)) != ERROR_OK) + return ERROR_FAIL; + } else { + if (riscv_program_insert(&program, + fmv_w_x(number - GDB_REGNO_FPR0, S0)) != ERROR_OK) + return ERROR_FAIL; + } } else if (number == GDB_REGNO_VTYPE) { if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) return ERROR_FAIL; @@ -1362,7 +1373,8 @@ static int register_write_direct(struct target *target, unsigned number, if (riscv_program_insert(&program, vsetvl(ZERO, S0, S1)) != ERROR_OK) return ERROR_FAIL; } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - riscv_program_csrw(&program, S0, number); + if (riscv_program_csrw(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; } else { LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); return ERROR_FAIL; @@ -1390,6 +1402,9 @@ static int register_write_direct(struct target *target, unsigned number, /** Actually read registers from the target right now. */ static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) { + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + int result = register_read_abstract(target, value, number, register_size(target, number)); @@ -1416,9 +1431,9 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t && 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 (riscv_program_insert(&program, + fsd(number - GDB_REGNO_FPR0, S0, 0)) != ERROR_OK) + return ERROR_FAIL; if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) return ERROR_FAIL; use_scratch = true; @@ -1429,12 +1444,17 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t return ERROR_FAIL; } } else if (riscv_supports_extension(target, 'D')) { - riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)); + if (riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)) != + ERROR_OK) + return ERROR_FAIL; } else { - riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)); + if (riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)) != + ERROR_OK) + return ERROR_FAIL; } } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { - riscv_program_csrr(&program, S0, number); + if (riscv_program_csrr(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; } else { LOG_ERROR("Unsupported register: %s", gdb_regno_name(number)); return ERROR_FAIL; @@ -1459,10 +1479,8 @@ static int register_read_direct(struct target *target, uint64_t *value, uint32_t return ERROR_FAIL; } - if (result == ERROR_OK) { - LOG_DEBUG("{%d} %s = 0x%" PRIx64, riscv_current_hartid(target), - gdb_regno_name(number), *value); - } + if (result == ERROR_OK) + LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(number), *value); return result; } @@ -1501,15 +1519,22 @@ static void deinit_target(struct target *target) info->version_specific = NULL; } -static int set_haltgroup(struct target *target, bool *supported) +typedef enum { + HALTGROUP, + RESUMEGROUP +} grouptype_t; +static int set_group(struct target *target, bool *supported, unsigned 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 == HALTGROUP) ? 0 : 1); + if (dmi_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 (dmi_read(target, &read_val, DM_DMCS2) != ERROR_OK) return ERROR_FAIL; - *supported = get_field(read, DM_DMCS2_GROUP) == (unsigned)target->smp; + *supported = get_field(read_val, DM_DMCS2_GROUP) == group; return ERROR_OK; } @@ -1566,6 +1591,9 @@ static int examine(struct target *target) dmi_write(target, DM_DMCONTROL, 0); dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_DMACTIVE); dm->was_reset = true; + + /* The DM gets reset, so forget any cached progbuf entries. */ + riscv013_invalidate_cached_debug_buffer(target); } dmi_write(target, DM_DMCONTROL, DM_DMCONTROL_HARTSELLO | @@ -1652,8 +1680,7 @@ static int examine(struct target *target) /* 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) + if (dm013_select_hart(target, i) != ERROR_OK) return ERROR_FAIL; uint32_t s; @@ -1665,14 +1692,12 @@ static int examine(struct target *target) if (get_field(s, DM_DMSTATUS_ANYHAVERESET)) dmi_write(target, DM_DMCONTROL, - set_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i)); + set_dmcontrol_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; @@ -1681,14 +1706,15 @@ static int examine(struct target *target) /* 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) + if (dm013_select_hart(target, info->index) != ERROR_OK) return ERROR_FAIL; bool halted = riscv_is_halted(target); if (!halted) { + r->prepped = true; if (riscv013_halt_go(target) != ERROR_OK) { - LOG_ERROR("[%s] Fatal: Hart %d failed to halt during examine()", - target_name(target), r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during examine()", + info->index); return ERROR_FAIL; } } @@ -1707,16 +1733,16 @@ static int examine(struct target *target) * need to take care of this manually. */ uint64_t s0, s1; if (register_read_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) { - LOG_ERROR("Fatal: Failed to read s0 from hart %d.", r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Failed to read s0."); return ERROR_FAIL; } if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) { - LOG_ERROR("Fatal: Failed to read s1 from hart %d.", r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Failed to read s1."); return ERROR_FAIL; } if (register_read_direct(target, &r->misa, GDB_REGNO_MISA)) { - LOG_ERROR("Fatal: Failed to read MISA from hart %d.", r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Failed to read MISA."); return ERROR_FAIL; } @@ -1731,25 +1757,24 @@ static int examine(struct target *target) /* 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); + 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_ERROR("Fatal: Failed to write s0 back to hart %d.", r->current_hartid); + 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_ERROR("Fatal: Failed to write s1 back to hart %d.", r->current_hartid); + LOG_TARGET_ERROR(target, "Fatal: Failed to write back s1."); return ERROR_FAIL; } if (!halted) - riscv013_step_or_resume_current_hart(target, false, false); + riscv013_step_or_resume_current_hart(target, false); if (target->smp) { bool haltgroup_supported; - if (set_haltgroup(target, &haltgroup_supported) != ERROR_OK) + if (set_group(target, &haltgroup_supported, target->smp, HALTGROUP) != ERROR_OK) return ERROR_FAIL; if (haltgroup_supported) LOG_INFO("Core %d made part of halt group %d.", target->coreid, @@ -1762,10 +1787,9 @@ static int examine(struct target *target) /* 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", + LOG_TARGET_INFO(target, "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, " XLEN=%d, misa=0x%" PRIx64, r->xlen, r->misa); return ERROR_OK; } @@ -1943,7 +1967,7 @@ static int riscv013_get_register_buf(struct target *target, { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); - if (riscv_select_current_hart(target) != ERROR_OK) + if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) @@ -1999,7 +2023,7 @@ static int riscv013_set_register_buf(struct target *target, { assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); - if (riscv_select_current_hart(target) != ERROR_OK) + if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) @@ -2265,7 +2289,7 @@ static int init_target(struct command_context *cmd_ctx, 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->select_target = &dm013_select_target; generic_info->is_halted = &riscv013_is_halted; generic_info->resume_go = &riscv013_resume_go; generic_info->step_current_hart = &riscv013_step_current_hart; @@ -2278,6 +2302,7 @@ static int init_target(struct command_context *cmd_ctx, 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->invalidate_cached_debug_buffer = &riscv013_invalidate_cached_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; @@ -2291,9 +2316,11 @@ static int init_target(struct command_context *cmd_ctx, generic_info->hart_count = &riscv013_hart_count; generic_info->data_bits = &riscv013_data_bits; generic_info->print_info = &riscv013_print_info; - generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); - if (!generic_info->version_specific) - return ERROR_FAIL; + if (!generic_info->version_specific) { + generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); + if (!generic_info->version_specific) + return ERROR_FAIL; + } generic_info->sample_memory = sample_memory; riscv013_info_t *info = get_info(target); @@ -2321,7 +2348,7 @@ static int init_target(struct command_context *cmd_ctx, static int assert_reset(struct target *target) { - RISCV_INFO(r); + RISCV013_INFO(info); select_dmi(target); @@ -2339,7 +2366,7 @@ static int assert_reset(struct target *target) /* Set haltreq for each hart. */ uint32_t control = control_base; - control = set_hartsel(control_base, target->coreid); + control = set_dmcontrol_hartsel(control_base, info->index); control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); dmi_write(target, DM_DMCONTROL, control); @@ -2350,7 +2377,7 @@ static int assert_reset(struct target *target) } else { /* Reset just this hart. */ - uint32_t control = set_hartsel(control_base, r->current_hartid); + uint32_t control = set_dmcontrol_hartsel(control_base, info->index); control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); control = set_field(control, DM_DMCONTROL_NDMRESET, 1); @@ -2366,14 +2393,13 @@ static int assert_reset(struct target *target) /* 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)); + riscv013_invalidate_cached_debug_buffer(target); return ERROR_OK; } static int deassert_reset(struct target *target) { - RISCV_INFO(r); RISCV013_INFO(info); select_dmi(target); @@ -2381,22 +2407,21 @@ static int deassert_reset(struct target *target) uint32_t control = 0; control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); - dmi_write(target, DM_DMCONTROL, - set_hartsel(control, r->current_hartid)); + dmi_write(target, DM_DMCONTROL, set_dmcontrol_hartsel(control, info->index)); uint32_t dmstatus; int 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; + for (unsigned int i = 0; i < riscv_count_harts(target); ++i) { + unsigned int index = i; if (target->rtos) { - if (index != target->coreid) + if (index != info->index) continue; dmi_write(target, DM_DMCONTROL, - set_hartsel(control, index)); + set_dmcontrol_hartsel(control, index)); } else { - index = r->current_hartid; + index = info->index; } LOG_DEBUG("Waiting for hart %d to come out of reset.", index); @@ -2427,12 +2452,18 @@ static int deassert_reset(struct target *target) return ERROR_FAIL; } } - target->state = TARGET_HALTED; + 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; + } if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) { /* Ack reset. */ dmi_write(target, DM_DMCONTROL, - set_hartsel(control, index) | + set_dmcontrol_hartsel(control, index) | DM_DMCONTROL_ACKHAVERESET); } @@ -2445,17 +2476,18 @@ static int deassert_reset(struct target *target) 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; - 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"); - } + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_fence_i(&program); + riscv_program_fence(&program); + int result = riscv_program_exec(&program, target); + if (result != ERROR_OK) + LOG_TARGET_DEBUG(target, "Unable to execute pre-fence"); return ERROR_OK; } @@ -2553,9 +2585,9 @@ static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t * *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); @@ -3142,7 +3174,7 @@ static int read_memory_progbuf_inner(struct target *target, target_addr_t addres * dm_data0 contains[read_addr-size*2] */ - struct riscv_batch *batch = riscv_batch_alloc(target, 32, + struct riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE, info->dmi_busy_delay + info->ac_busy_delay); if (!batch) return ERROR_FAIL; @@ -3407,6 +3439,9 @@ static int read_memory_progbuf(struct target *target, target_addr_t 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); memset(buffer, 0, count*size); @@ -3640,7 +3675,7 @@ static int write_memory_bus_v1(struct target *target, target_addr_t address, struct riscv_batch *batch = riscv_batch_alloc( target, - 32, + RISCV_BATCH_ALLOC_SIZE, info->dmi_busy_delay + info->bus_master_write_delay); if (!batch) return ERROR_FAIL; @@ -3825,29 +3860,27 @@ static int write_memory_progbuf(struct target *target, target_addr_t address, riscv_program_write(&program); riscv_addr_t cur_addr = address; - riscv_addr_t fin_addr = address + (count * size); + riscv_addr_t distance = (riscv_addr_t)count * size; bool setup_needed = true; - LOG_DEBUG("writing until final address 0x%016" PRIx64, fin_addr); - while (cur_addr < fin_addr) { + LOG_DEBUG("writing until final address 0x%016" PRIx64, cur_addr + distance); + while (cur_addr - address < distance) { LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64, cur_addr); struct riscv_batch *batch = riscv_batch_alloc( target, - 32, + RISCV_BATCH_ALLOC_SIZE, info->dmi_busy_delay + info->ac_busy_delay); if (!batch) goto error; /* 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; + for (riscv_addr_t offset = cur_addr - address; offset < distance; offset += size) { const uint8_t *t_buffer = buffer + offset; uint64_t value = buf_get_u64(t_buffer, 0, 8 * size); - log_memory_access(address + offset, value, size, false); + log_memory_access(cur_addr, value, size, false); cur_addr += size; if (setup_needed) { @@ -4038,14 +4071,14 @@ static int riscv013_get_register(struct target *target, LOG_DEBUG("[%s] reading register %s", target_name(target), gdb_regno_name(rid)); - if (riscv_select_current_hart(target) != ERROR_OK) + if (dm013_select_target(target) != ERROR_OK) return ERROR_FAIL; int result = ERROR_OK; if (rid == GDB_REGNO_PC) { /* TODO: move this into riscv.c. */ result = register_read_direct(target, value, GDB_REGNO_DPC); - LOG_DEBUG("[%d] read PC from DPC: 0x%" PRIx64, target->coreid, *value); + LOG_TARGET_DEBUG(target, "read PC from DPC: 0x%" PRIx64, *value); } else if (rid == GDB_REGNO_PRIV) { uint64_t dcsr; /* TODO: move this into riscv.c. */ @@ -4063,18 +4096,19 @@ static int riscv013_get_register(struct target *target, 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; + LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s", + value, gdb_regno_name(rid)); 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); + LOG_TARGET_DEBUG(target, "writing PC to DPC: 0x%" PRIx64, 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); + LOG_TARGET_DEBUG(target, " actual DPC written: 0x%016" PRIx64, actual_value); if (value != actual_value) { LOG_ERROR("Written PC (0x%" PRIx64 ") does not match read back " "value (0x%" PRIx64 ")", value, actual_value); @@ -4093,37 +4127,36 @@ static int riscv013_set_register(struct target *target, int rid, uint64_t value) return ERROR_OK; } -static int riscv013_select_current_hart(struct target *target) +static int dm013_select_hart(struct target *target, int hart_index) { - RISCV_INFO(r); - 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) + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index); + if (dmi_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) { + /* Who knows what the state is? */ + dm->current_hartid = -2; 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; + dm013_select_target(target); return ERROR_OK; } @@ -4135,27 +4168,33 @@ static int select_prepped_harts(struct target *target, bool *use_hasel) 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; - riscv_info_t *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) { + riscv_info_t *info = riscv_info(t); + riscv013_info_t *info_013 = get_info(t); + unsigned index = info_013->index; + LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, 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!"); + return ERROR_FAIL; + } else if (total_selected == 1) { + /* Don't use hasel if we only need to talk to one hart. */ + return dm013_select_hart(target, selected_index); } + if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) + return ERROR_FAIL; + for (unsigned i = 0; i < hawindow_count; i++) { if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) return ERROR_FAIL; @@ -4163,7 +4202,6 @@ static int select_prepped_harts(struct target *target, bool *use_hasel) return ERROR_FAIL; } - *use_hasel = true; return ERROR_OK; } @@ -4174,43 +4212,45 @@ 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"); /* 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); + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->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; + uint32_t dmstatus; + for (size_t i = 0; i < 256; ++i) { if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) return ERROR_FAIL; + /* When no harts are running, there's no point in continuing this loop. */ + if (!get_field(dmstatus, DM_DMSTATUS_ALLRUNNING)) + 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 (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) return ERROR_FAIL; - LOG_ERROR("[%s] Unable to halt hart %d. dmcontrol=0x%08x, dmstatus=0x%08x", - target_name(target), r->current_hartid, dmcontrol, 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); - 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; @@ -4225,16 +4265,15 @@ static int riscv013_halt_go(struct target *target) 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) @@ -4254,20 +4293,23 @@ static int riscv013_on_halt(struct target *target) static bool riscv013_is_halted(struct target *target) { + RISCV013_INFO(info); + uint32_t dmstatus; + if (dm013_select_target(target) != ERROR_OK) + return false; 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)); + LOG_TARGET_ERROR(target, "Hart is unavailable."); if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT)) - LOG_ERROR("Hart %d doesn't exist.", riscv_current_hartid(target)); + LOG_TARGET_ERROR(target, "Hart doesn't exist."); if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { - int hartid = riscv_current_hartid(target); - LOG_INFO("Hart %d unexpectedly reset!", hartid); + LOG_TARGET_INFO(target, "Hart unexpectedly reset!"); /* 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); + 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 @@ -4287,10 +4329,10 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) if (result != ERROR_OK) return RISCV_HALT_UNKNOWN; - LOG_DEBUG("dcsr.cause: 0x%" PRIx64, get_field(dcsr, CSR_DCSR_CAUSE)); + 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: + case CSR_DCSR_CAUSE_EBREAK: return RISCV_HALT_BREAKPOINT; case CSR_DCSR_CAUSE_TRIGGER: /* We could get here before triggers are enumerated if a trigger was @@ -4301,8 +4343,8 @@ static enum riscv_halt_reason riscv013_halt_reason(struct target *target) 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; @@ -4335,6 +4377,18 @@ riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) return value; } +int riscv013_invalidate_cached_debug_buffer(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + 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) { uint32_t run_program = 0; @@ -4758,26 +4812,23 @@ 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) + 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); + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); dmi_write(target, DM_DMCONTROL, dmcontrol); - dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, 0); dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); uint32_t dmstatus; @@ -4796,10 +4847,10 @@ static int riscv013_step_or_resume_current_hart(struct target *target, dmi_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"); diff --git a/src/target/riscv/riscv.c b/src/target/riscv/riscv.c old mode 100644 new mode 100755 index 09c1939fe..e5df47718 --- a/src/target/riscv/riscv.c +++ b/src/target/riscv/riscv.c @@ -13,6 +13,7 @@ #include "target/target.h" #include "target/algorithm.h" #include "target/target_type.h" +#include #include "jtag/jtag.h" #include "target/register.h" #include "target/breakpoints.h" @@ -27,88 +28,12 @@ #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 - /*** 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 uint8_t ir_dtmcontrol[4] = {DTMCONTROL}; struct scan_field select_dtmcontrol = { @@ -128,6 +53,7 @@ struct scan_field select_idcode = { bscan_tunnel_type_t bscan_tunnel_type; int bscan_tunnel_ir_width; /* if zero, then tunneling is not present/active */ +static 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}; @@ -449,8 +375,12 @@ static int riscv_init_target(struct command_context *cmd_ctx, select_idcode.num_bits = target->tap->ir_length; if (bscan_tunnel_ir_width != 0) { - assert(target->tap->ir_length >= 6); - uint32_t ir_user4_raw = 0x23 << (target->tap->ir_length - 6); + 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); + } ir_user4[0] = (uint8_t)ir_user4_raw; ir_user4[1] = (uint8_t)(ir_user4_raw >>= 8); ir_user4[2] = (uint8_t)(ir_user4_raw >>= 8); @@ -591,28 +521,28 @@ static int maybe_add_trigger_t2(struct target *target, RISCV_INFO(r); /* tselect is already set */ - if (tdata1 & (MCONTROL_EXECUTE | MCONTROL_STORE | MCONTROL_LOAD)) { + if (tdata1 & (CSR_MCONTROL_EXECUTE | CSR_MCONTROL_STORE | CSR_MCONTROL_LOAD)) { /* Trigger is already in use, presumably by user code. */ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } /* address/data match trigger */ - tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); - tdata1 = set_field(tdata1, MCONTROL_ACTION, - MCONTROL_ACTION_DEBUG_MODE); - tdata1 = set_field(tdata1, MCONTROL_MATCH, MCONTROL_MATCH_EQUAL); - tdata1 |= MCONTROL_M; + tdata1 |= CSR_MCONTROL_DMODE(riscv_xlen(target)); + tdata1 = set_field(tdata1, CSR_MCONTROL_ACTION, + CSR_MCONTROL_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_EQUAL); + tdata1 |= CSR_MCONTROL_M; if (r->misa & (1 << ('S' - 'A'))) - tdata1 |= MCONTROL_S; + tdata1 |= CSR_MCONTROL_S; if (r->misa & (1 << ('U' - 'A'))) - tdata1 |= MCONTROL_U; + tdata1 |= CSR_MCONTROL_U; if (trigger->execute) - tdata1 |= MCONTROL_EXECUTE; + tdata1 |= CSR_MCONTROL_EXECUTE; if (trigger->read) - tdata1 |= MCONTROL_LOAD; + tdata1 |= CSR_MCONTROL_LOAD; if (trigger->write) - tdata1 |= MCONTROL_STORE; + tdata1 |= CSR_MCONTROL_STORE; riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); @@ -647,10 +577,10 @@ static int maybe_add_trigger_t6(struct target *target, } /* address/data match trigger */ - tdata1 |= MCONTROL_DMODE(riscv_xlen(target)); + tdata1 |= CSR_MCONTROL6_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); + CSR_MCONTROL6_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, CSR_MCONTROL6_MATCH_EQUAL); tdata1 |= CSR_MCONTROL6_M; if (r->misa & (1 << ('H' - 'A'))) tdata1 |= CSR_MCONTROL6_VS | CSR_MCONTROL6_VU; @@ -709,8 +639,7 @@ static int add_trigger(struct target *target, struct trigger *trigger) 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))); - + int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); result = ERROR_OK; switch (type) { case 1: @@ -866,7 +795,7 @@ int riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_ 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 */ @@ -908,7 +837,7 @@ int riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = true; + breakpoint->is_set = true; return ERROR_OK; } @@ -927,7 +856,7 @@ static int remove_trigger(struct target *target, struct trigger *trigger) if (i >= r->trigger_count) { LOG_ERROR("Couldn't find the hardware resources used by hardware " "trigger."); - return ERROR_FAIL; + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } LOG_DEBUG("[%d] Stop using resource %d for bp %d", target->coreid, i, trigger->unique_id); @@ -968,7 +897,7 @@ int riscv_remove_breakpoint(struct target *target, return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = false; + breakpoint->is_set = false; return ERROR_OK; } @@ -995,7 +924,7 @@ int riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) int result = add_trigger(target, &trigger); if (result != ERROR_OK) return result; - watchpoint->set = true; + watchpoint->is_set = true; return ERROR_OK; } @@ -1011,7 +940,68 @@ int riscv_remove_watchpoint(struct target *target, int result = remove_trigger(target, &trigger); if (result != ERROR_OK) return result; - watchpoint->set = false; + watchpoint->is_set = false; + + 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 1: + /* Doesn't support hit bit. */ + break; + case 2: + hit_mask = CSR_MCONTROL_HIT; + break; + case 6: + hit_mask = CSR_MCONTROL6_HIT; + break; + default: + LOG_DEBUG("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_DEBUG("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; } @@ -1024,14 +1014,19 @@ int riscv_remove_watchpoint(struct target *target, * and new value. */ 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); const uint8_t length = 4; @@ -1080,6 +1075,7 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi return ERROR_FAIL; } + struct watchpoint *wp = target->watchpoints; while (wp) { /*TODO support length/mask */ if (wp->address == mem_addr) { @@ -1098,7 +1094,6 @@ int riscv_hit_watchpoint(struct target *target, struct watchpoint **hit_watchpoi return ERROR_FAIL; } - static int oldriscv_step(struct target *target, int current, uint32_t address, int handle_breakpoints) { @@ -1159,11 +1154,6 @@ static int old_or_new_riscv_poll(struct target *target) return riscv_openocd_poll(target); } -int riscv_select_current_hart(struct target *target) -{ - return riscv_set_current_hartid(target, target->coreid); -} - int riscv_flush_registers(struct target *target) { RISCV_INFO(r); @@ -1192,12 +1182,21 @@ int riscv_flush_registers(struct target *target) /* Convert: RISC-V hart's halt reason --> OpenOCD's generic debug reason */ 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_BREAKPOINT: 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: @@ -1223,14 +1222,11 @@ int halt_prep(struct target *target) 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 (debug_reason=%d).", target_name(target), target->debug_reason); if (target->debug_reason == DBG_REASON_NOTHALTED) { - enum riscv_halt_reason halt_reason = - riscv_halt_reason(target, r->current_hartid); + enum riscv_halt_reason halt_reason = riscv_halt_reason(target); if (set_debug_reason(target, halt_reason) != ERROR_OK) return ERROR_FAIL; } @@ -1247,8 +1243,6 @@ int riscv_halt_go_all_harts(struct target *target) { RISCV_INFO(r); - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; if (riscv_is_halted(target)) { LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); } else { @@ -1292,17 +1286,18 @@ int riscv_halt(struct target *target) 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) { - for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + struct target_list *tlist; + foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; if (halt_prep(t) != ERROR_OK) result = ERROR_FAIL; } - for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; riscv_info_t *i = riscv_info(t); if (i->prepped) { @@ -1311,7 +1306,7 @@ int riscv_halt(struct target *target) } } - for (struct target_list *tlist = target->head; tlist; tlist = tlist->next) { + foreach_smp_target(tlist, target->smp_targets) { struct target *t = tlist->target; if (halt_finish(t) != ERROR_OK) return ERROR_FAIL; @@ -1365,7 +1360,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; @@ -1379,9 +1374,9 @@ 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->set); - state[i] = watchpoint->set; - if (watchpoint->set) { + LOG_DEBUG("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) return ERROR_FAIL; } @@ -1437,7 +1432,7 @@ static int resume_prep(struct target *target, int current, target_addr_t address, int handle_breakpoints, int debug_execution) { RISCV_INFO(r); - LOG_DEBUG("[%d]", target->coreid); + LOG_TARGET_DEBUG(target, "target->state=%d", target->state); if (!current) riscv_set_register(target, GDB_REGNO_PC, address); @@ -1458,8 +1453,6 @@ static int resume_prep(struct target *target, int current, } if (r->is_halted) { - if (riscv_select_current_hart(target) != ERROR_OK) - return ERROR_FAIL; if (r->resume_prep(target) != ERROR_OK) return ERROR_FAIL; } @@ -1490,39 +1483,14 @@ 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) { 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 a newly allocated target list, that contains the same targets as in - * tlist bit in the opposite order. */ -static struct target_list *tlist_reverse(struct target_list *tlist) -{ - struct target_list *previous = NULL; - struct target_list *reversed = NULL; - for (struct target_list *node = tlist; node; node = node->next) { - reversed = calloc(1, sizeof(struct target_list)); - reversed->target = node->target; - reversed->next = previous; - previous = reversed; - } - return reversed; -} - -/* Free a target list, but not the targets that are referenced. */ -static void tlist_free(struct target_list *tlist) -{ - struct target_list *node = tlist; - while (node) { - struct target_list *previous = node; - node = node->next; - free(previous); - } + return target_call_event_callbacks(target, + debug_execution ? TARGET_EVENT_DEBUG_RESUMED : TARGET_EVENT_RESUMED); } /** @@ -1540,21 +1508,17 @@ int riscv_resume( LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); int result = ERROR_OK; if (target->smp && !single_hart) { - struct target_list *ordered_tlist; - - if (resume_order == RO_REVERSED) - ordered_tlist = tlist_reverse(target->head); - else - ordered_tlist = target->head; - - for (struct target_list *tlist = ordered_tlist; tlist; tlist = tlist->next) { + 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; } - for (struct target_list *tlist = ordered_tlist; tlist; tlist = tlist->next) { + foreach_smp_target_direction(resume_order == RO_NORMAL, + tlist, target->smp_targets) { struct target *t = tlist->target; riscv_info_t *i = riscv_info(t); if (i->prepped) { @@ -1564,15 +1528,13 @@ int riscv_resume( } } - for (struct target_list *tlist = ordered_tlist; tlist; tlist = tlist->next) { + foreach_smp_target_direction(resume_order == RO_NORMAL, + tlist, target->smp_targets) { struct target *t = tlist->target; - if (resume_finish(t) != ERROR_OK) + if (resume_finish(t, debug_execution) != ERROR_OK) result = ERROR_FAIL; } - if (resume_order == RO_REVERSED) - tlist_free(ordered_tlist); - } else { if (resume_prep(target, current, address, handle_breakpoints, debug_execution) != ERROR_OK) @@ -1580,7 +1542,7 @@ int riscv_resume( if (resume_go(target, current, address, handle_breakpoints, debug_execution) != ERROR_OK) result = ERROR_FAIL; - if (resume_finish(target) != ERROR_OK) + if (resume_finish(target, debug_execution) != ERROR_OK) return ERROR_FAIL; } @@ -1763,8 +1725,6 @@ static int riscv_read_phys_memory(struct target *target, target_addr_t phys_addr 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); } @@ -1776,9 +1736,6 @@ static int riscv_read_memory(struct target *target, target_addr_t 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; @@ -1790,8 +1747,6 @@ static int riscv_read_memory(struct target *target, target_addr_t address, 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); return tt->write_memory(target, phys_address, size, count, buffer); } @@ -1804,9 +1759,6 @@ static int riscv_write_memory(struct target *target, target_addr_t 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; @@ -1831,18 +1783,13 @@ 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) { - 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, read); if (!target->reg_cache) { LOG_ERROR("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; @@ -1952,30 +1899,15 @@ 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, ¤t_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) + if (riscv_resume(target, 0, entry_point, 0, 1, true) != ERROR_OK) return ERROR_FAIL; int64_t start = timeval_ms(); @@ -2013,9 +1945,9 @@ 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; @@ -2027,9 +1959,8 @@ static int riscv_run_algorithm(struct target *target, int num_mem_params, } /* Restore Interrupts */ - LOG_DEBUG("Restoring Interrupts"); - buf_set_u64(mstatus_bytes, 0, info->xlen, 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 }; @@ -2156,11 +2087,8 @@ enum riscv_poll_hart { static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) { RISCV_INFO(r); - if (riscv_set_current_hartid(target, hartid) != ERROR_OK) - return RPH_ERROR; - LOG_DEBUG("[%s] polling hart %d, target->state=%d", target_name(target), - hartid, target->state); + LOG_TARGET_DEBUG(target, "polling, target->state=%d", target->state); /* If OpenOCD thinks we're running but this hart is halted then it's time * to raise an event. */ @@ -2177,7 +2105,7 @@ static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) LOG_DEBUG(" triggered a halt"); r->on_halt(target); return RPH_DISCOVERED_HALTED; - } else if (target->state != TARGET_RUNNING && !halted) { + } else if (target->state != TARGET_RUNNING && target->state != TARGET_DEBUG_RUNNING && !halted) { LOG_DEBUG(" triggered running"); target->state = TARGET_RUNNING; target->debug_reason = DBG_REASON_NOTHALTED; @@ -2238,20 +2166,17 @@ int sample_memory(struct target *target) int riscv_openocd_poll(struct target *target) { LOG_DEBUG("polling all harts"); - int halted_hart = -1; + enum target_state old_state = target->state; if (target->smp) { - unsigned halts_discovered = 0; unsigned should_remain_halted = 0; unsigned should_resume = 0; - unsigned i = 0; - for (struct target_list *list = target->head; list; - list = list->next, i++) { + struct target_list *list; + foreach_smp_target(list, target->smp_targets) { struct target *t = list->target; if (!target_was_examined(t)) continue; - riscv_info_t *r = riscv_info(t); - enum riscv_poll_hart out = riscv_poll_hart(t, r->current_hartid); + enum riscv_poll_hart out = riscv_poll_hart(t, t->coreid); switch (out) { case RPH_NO_CHANGE: break; @@ -2260,10 +2185,9 @@ int riscv_openocd_poll(struct target *target) t->debug_reason = DBG_REASON_NOTHALTED; break; case RPH_DISCOVERED_HALTED: - halts_discovered++; t->state = TARGET_HALTED; enum riscv_halt_reason halt_reason = - riscv_halt_reason(t, r->current_hartid); + riscv_halt_reason(t); if (set_debug_reason(t, halt_reason) != ERROR_OK) return ERROR_FAIL; @@ -2308,8 +2232,7 @@ int riscv_openocd_poll(struct target *target) } /* Sample memory if any target is running. */ - for (struct target_list *list = target->head; list; - list = list->next, i++) { + foreach_smp_target(list, target->smp_targets) { struct target *t = list->target; if (t->state == TARGET_RUNNING) { sample_memory(target); @@ -2320,8 +2243,7 @@ int riscv_openocd_poll(struct target *target) return ERROR_OK; } else { - enum riscv_poll_hart out = riscv_poll_hart(target, - riscv_current_hartid(target)); + enum riscv_poll_hart out = riscv_poll_hart(target, target->coreid); if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) { if (target->state == TARGET_RUNNING) sample_memory(target); @@ -2330,10 +2252,10 @@ int riscv_openocd_poll(struct target *target) return ERROR_FAIL; } - halted_hart = riscv_current_hartid(target); - LOG_DEBUG(" hart %d halted", halted_hart); + LOG_TARGET_DEBUG(target, "hart halted"); - enum riscv_halt_reason halt_reason = riscv_halt_reason(target, halted_hart); + 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; target->state = TARGET_HALTED; @@ -2354,41 +2276,88 @@ int riscv_openocd_poll(struct target *target) return retval; } } else { - target_call_event_callbacks(target, TARGET_EVENT_HALTED); + if (old_state == TARGET_DEBUG_RUNNING) + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + 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) + target_addr_t address, int handle_breakpoints) { - LOG_DEBUG("stepping rtos hart"); + LOG_TARGET_DEBUG(target, "stepping hart"); - if (!current) - riscv_set_register(target, GDB_REGNO_PC, address); + if (!current) { + if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK) + return ERROR_FAIL; + } + + 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) { + 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, + ¤t_mstatus) != ERROR_OK) { + success = false; + LOG_ERROR("unable to disable interrupts"); + goto _exit; + } + } + + if (riscv_step_rtos_hart(target) != ERROR_OK) { + success = false; LOG_ERROR("unable to step rtos hart"); - return out; } 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_ERROR("unable to restore interrupts"); + } - 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; +_exit: + if (enable_triggers(target, trigger_state) != ERROR_OK) { + success = false; + LOG_ERROR("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; + 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 success ? ERROR_OK : ERROR_FAIL; } /* Command Handlers */ @@ -2795,11 +2764,26 @@ COMMAND_HANDLER(riscv_dmi_write) 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; + /* Perform the DMI write */ + int retval = r->dmi_write(target, 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. + */ + bool progbufTouched = (address >= DM_PROGBUF0 && address <= DM_PROGBUF15); + bool dmDeactivated = (address == DM_DMCONTROL && (value & DM_DMCONTROL_DMACTIVE) == 0); + if (progbufTouched || dmDeactivated) { + if (r->invalidate_cached_debug_buffer) + r->invalidate_cached_debug_buffer(target); + } + + return retval; } + + LOG_ERROR("dmi_write is not implemented for this target."); + return ERROR_FAIL; } COMMAND_HANDLER(riscv_test_sba_config_reg) @@ -2916,6 +2900,49 @@ 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) { @@ -3325,6 +3352,22 @@ static const struct command_registration riscv_exec_command_handlers[] = { "(optional) to indicate Bscan Tunnel Type {0:(default) NESTED_TAP , " "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, @@ -3461,14 +3504,14 @@ void riscv_info_init(struct target *target, riscv_info_t *r) { memset(r, 0, sizeof(*r)); r->dtm_version = 1; - r->registers_initialized = false; - r->current_hartid = target->coreid; r->version_specific = NULL; memset(r->trigger_unique_id, 0xff, sizeof(r->trigger_unique_id)); 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; @@ -3485,9 +3528,7 @@ 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; + LOG_TARGET_DEBUG(target, "resuming hart, state=%d", target->state); if (riscv_is_halted(target)) { if (r->resume_go(target) != ERROR_OK) return ERROR_FAIL; @@ -3500,11 +3541,55 @@ static int riscv_resume_go_all_harts(struct target *target) return ERROR_OK; } +int riscv_interrupts_disable(struct target *target, uint64_t irq_mask, uint64_t *old_mstatus) +{ + 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!"); + 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_DEBUG("Restore Interrupts"); + struct reg *reg_mstatus = register_get_by_name(target->reg_cache, + "mstatus", true); + if (!reg_mstatus) { + LOG_ERROR("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); +} + 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)); if (!riscv_is_halted(target)) { @@ -3541,24 +3626,12 @@ unsigned riscv_xlen(const struct target *target) return r->xlen; } -int riscv_set_current_hartid(struct target *target, int hartid) -{ - 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; -} - void riscv_invalidate_register_cache(struct target *target) { - RISCV_INFO(r); + /* 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; LOG_DEBUG("[%d]", target->coreid); register_cache_invalidate(target->reg_cache); @@ -3566,17 +3639,10 @@ void riscv_invalidate_register_cache(struct target *target) struct reg *reg = &target->reg_cache->reg_list[i]; reg->valid = false; } - - r->registers_initialized = true; } -int riscv_current_hartid(const struct target *target) -{ - RISCV_INFO(r); - return r->current_hartid; -} -int riscv_count_harts(struct target *target) +unsigned int riscv_count_harts(struct target *target) { if (!target) return 1; @@ -3743,12 +3809,10 @@ bool riscv_is_halted(struct target *target) return r->is_halted(target); } -enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid) +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)) { + if (target->state != TARGET_HALTED) { LOG_ERROR("Hart is not halted!"); return RISCV_HALT_UNKNOWN; } @@ -3851,8 +3915,8 @@ int riscv_enumerate_triggers(struct target *target) 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))); + + int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); if (type == 0) break; switch (type) { @@ -3862,11 +3926,11 @@ int riscv_enumerate_triggers(struct target *target) riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; case 2: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) + if (tdata1 & CSR_MCONTROL_DMODE(riscv_xlen(target))) riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; case 6: - if (tdata1 & MCONTROL_DMODE(riscv_xlen(target))) + if (tdata1 & CSR_MCONTROL6_DMODE(riscv_xlen(target))) riscv_set_register(target, GDB_REGNO_TDATA1, 0); break; } @@ -4157,7 +4221,7 @@ static int cmp_csr_info(const void *p1, const void *p2) int riscv_init_registers(struct target *target) { RISCV_INFO(info); - + riscv_free_registers(target); target->reg_cache = calloc(1, sizeof(*target->reg_cache)); diff --git a/src/target/riscv/riscv.h b/src/target/riscv/riscv.h index 531c896b9..74eff7007 100644 --- a/src/target/riscv/riscv.h +++ b/src/target/riscv/riscv.h @@ -29,6 +29,8 @@ struct riscv_program; #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; @@ -56,6 +58,13 @@ 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, +}; + typedef struct { struct target *target; unsigned custom_number; @@ -90,12 +99,6 @@ typedef struct { 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; @@ -115,12 +118,13 @@ typedef struct { * target controls, while otherwise only a single hart is controlled. */ int trigger_unique_id[RISCV_MAX_HWBPS]; + /* 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 debug buffer. */ int debug_buffer_size; - /* This avoids invalidating the register cache too often. */ - bool registers_initialized; - /* This hart contains an implicit ebreak at the end of the program buffer. */ bool impebreak; @@ -135,6 +139,8 @@ typedef struct { /* This target was selected using hasel. */ bool selected; + 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); @@ -142,7 +148,7 @@ typedef struct { int (*get_register_buf)(struct target *target, uint8_t *buf, int regno); int (*set_register_buf)(struct target *target, int regno, const uint8_t *buf); - int (*select_current_hart)(struct target *target); + int (*select_target)(struct target *target); bool (*is_halted)(struct target *target); /* Resume this target, as well as every other prepped target that can be * resumed near-simultaneously. Clear the prepped flag on any target that @@ -161,6 +167,7 @@ typedef struct { riscv_insn_t d); riscv_insn_t (*read_debug_buffer)(struct target *target, unsigned index); int (*execute_debug_buffer)(struct target *target); + int (*invalidate_cached_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); @@ -204,7 +211,7 @@ typedef struct { 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; @@ -341,7 +348,7 @@ int riscv_current_hartid(const struct target *target); /* 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); +unsigned int riscv_count_harts(struct target *target); /** Set register, updating the cache. */ int riscv_set_register(struct target *target, enum gdb_regno i, riscv_reg_t v); @@ -357,7 +364,7 @@ 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); -enum riscv_halt_reason riscv_halt_reason(struct target *target, int hartid); +enum riscv_halt_reason riscv_halt_reason(struct target *target); /* These helper functions let the generic program interface get target-specific * information. */ @@ -402,4 +409,7 @@ void riscv_add_bscan_tunneled_scan(struct target *target, struct scan_field *fie 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 b347212d3..2c53813ee 100644 --- a/src/target/riscv/riscv_semihosting.c +++ b/src/target/riscv/riscv_semihosting.c @@ -140,7 +140,9 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval) semihosting->word_size_bytes = riscv_xlen(target) / 8; /* Check for ARM operation numbers. */ - if (semihosting->op >= 0 && semihosting->op <= 0x31) { + if ((semihosting->op >= 0 && semihosting->op <= 0x31) || + (semihosting->op >= 0x100 && semihosting->op <= 0x107)) { + *retval = semihosting_common(target); if (*retval != ERROR_OK) { LOG_ERROR("Failed semihosting operation (0x%02X)", semihosting->op); @@ -153,16 +155,16 @@ semihosting_result_t riscv_semihosting(struct target *target, int *retval) } } + /* Resume right after the EBREAK 4 bytes instruction. */ + *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4); + if (*retval != ERROR_OK) + return SEMI_ERROR; + /* * Resume target if we are not waiting on a fileio * operation to complete. */ if (semihosting->is_resumable && !semihosting->hit_fileio) { - /* Resume right after the EBREAK 4 bytes instruction. */ - *retval = riscv_set_register(target, GDB_REGNO_PC, pc + 4); - if (*retval != ERROR_OK) - return SEMI_ERROR; - LOG_DEBUG(" -> HANDLED"); return SEMI_HANDLED; } diff --git a/src/target/rtt.c b/src/target/rtt.c index 7e556e1cb..41830213d 100644 --- a/src/target/rtt.c +++ b/src/target/rtt.c @@ -15,6 +15,10 @@ * along with this program. If not, see . */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include diff --git a/src/target/semihosting_common.c b/src/target/semihosting_common.c index 1b65e126c..bc1f417ef 100644 --- a/src/target/semihosting_common.c +++ b/src/target/semihosting_common.c @@ -52,19 +52,50 @@ #include #include -static const int open_modeflags[12] = { +/** + * It is not possible to use O_... flags defined in sys/stat.h because they + * are not guaranteed to match the values defined by the GDB Remote Protocol. + * See https://sourceware.org/gdb/onlinedocs/gdb/Open-Flags.html#Open-Flags + */ +enum { + TARGET_O_RDONLY = 0x000, + TARGET_O_WRONLY = 0x001, + TARGET_O_RDWR = 0x002, + TARGET_O_APPEND = 0x008, + TARGET_O_CREAT = 0x200, + TARGET_O_TRUNC = 0x400, + /* O_EXCL=0x800 is not required in this implementation. */ +}; + +/* GDB remote protocol does not differentiate between text and binary open modes. */ +static const int open_gdb_modeflags[12] = { + TARGET_O_RDONLY, + TARGET_O_RDONLY, + TARGET_O_RDWR, + TARGET_O_RDWR, + TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC, + TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_TRUNC, + TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC, + TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_TRUNC, + TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND, + TARGET_O_WRONLY | TARGET_O_CREAT | TARGET_O_APPEND, + TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND, + TARGET_O_RDWR | TARGET_O_CREAT | TARGET_O_APPEND +}; + +static const int open_host_modeflags[12] = { O_RDONLY, O_RDONLY | O_BINARY, O_RDWR, - O_RDWR | O_BINARY, + O_RDWR | O_BINARY, O_WRONLY | O_CREAT | O_TRUNC, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - O_RDWR | O_CREAT | O_TRUNC, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY, + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, + O_RDWR | O_CREAT | O_TRUNC, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY, O_WRONLY | O_CREAT | O_APPEND, O_WRONLY | O_CREAT | O_APPEND | O_BINARY, - O_RDWR | O_CREAT | O_APPEND, - O_RDWR | O_CREAT | O_APPEND | O_BINARY + O_RDWR | O_CREAT | O_APPEND, + O_RDWR | O_CREAT | O_APPEND | O_BINARY }; static int semihosting_common_fileio_info(struct target *target, @@ -113,6 +144,11 @@ int semihosting_common_init(struct target *target, void *setup, } semihosting->is_active = false; + semihosting->redirect_cfg = SEMIHOSTING_REDIRECT_CFG_NONE; + semihosting->tcp_connection = NULL; + semihosting->stdin_fd = -1; + semihosting->stdout_fd = -1; + semihosting->stderr_fd = -1; semihosting->is_fileio = false; semihosting->hit_fileio = false; semihosting->is_resumable = false; @@ -138,6 +174,147 @@ int semihosting_common_init(struct target *target, void *setup, return ERROR_OK; } +struct semihosting_tcp_service { + struct semihosting *semihosting; + char *name; + int error; +}; + +static bool semihosting_is_redirected(struct semihosting *semihosting, int fd) +{ + if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_NONE) + return false; + + bool is_read_op = false; + + switch (semihosting->op) { + /* check debug semihosting operations: READC, WRITEC and WRITE0 */ + case SEMIHOSTING_SYS_READC: + is_read_op = true; + /* fall through */ + case SEMIHOSTING_SYS_WRITEC: + case SEMIHOSTING_SYS_WRITE0: + /* debug operations are redirected when CFG is either DEBUG or ALL */ + if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_STDIO) + return false; + break; + + /* check stdio semihosting operations: READ and WRITE */ + case SEMIHOSTING_SYS_READ: + is_read_op = true; + /* fall through */ + case SEMIHOSTING_SYS_WRITE: + /* stdio operations are redirected when CFG is either STDIO or ALL */ + if (semihosting->redirect_cfg == SEMIHOSTING_REDIRECT_CFG_DEBUG) + return false; + break; + + default: + return false; + } + + if (is_read_op) + return fd == semihosting->stdin_fd; + + /* write operation */ + return fd == semihosting->stdout_fd || fd == semihosting->stderr_fd; +} + +static ssize_t semihosting_redirect_write(struct semihosting *semihosting, void *buf, int size) +{ + if (!semihosting->tcp_connection) { + LOG_ERROR("No connected TCP client for semihosting"); + semihosting->sys_errno = EBADF; /* Bad file number */ + return -1; + } + + struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv; + + int retval = connection_write(semihosting->tcp_connection, buf, size); + + if (retval < 0) + log_socket_error(service->name); + + return retval; +} + +static ssize_t semihosting_write(struct semihosting *semihosting, int fd, void *buf, int size) +{ + if (semihosting_is_redirected(semihosting, fd)) + return semihosting_redirect_write(semihosting, buf, size); + + /* default write */ + return write(fd, buf, size); +} + +static ssize_t semihosting_redirect_read(struct semihosting *semihosting, void *buf, int size) +{ + if (!semihosting->tcp_connection) { + LOG_ERROR("No connected TCP client for semihosting"); + semihosting->sys_errno = EBADF; /* Bad file number */ + return -1; + } + + struct semihosting_tcp_service *service = semihosting->tcp_connection->service->priv; + + service->error = ERROR_OK; + semihosting->tcp_connection->input_pending = true; + + int retval = connection_read(semihosting->tcp_connection, buf, size); + + if (retval <= 0) + service->error = ERROR_SERVER_REMOTE_CLOSED; + + if (retval < 0) + log_socket_error(service->name); + + semihosting->tcp_connection->input_pending = false; + + return retval; +} + +static inline int semihosting_putchar(struct semihosting *semihosting, int fd, int c) +{ + if (semihosting_is_redirected(semihosting, fd)) + return semihosting_redirect_write(semihosting, &c, 1); + + /* default putchar */ + return putchar(c); +} + +static inline ssize_t semihosting_read(struct semihosting *semihosting, int fd, void *buf, int size) +{ + if (semihosting_is_redirected(semihosting, fd)) + return semihosting_redirect_read(semihosting, buf, size); + + /* default read */ + ssize_t result = read(fd, buf, size); + semihosting->sys_errno = errno; + + return result; +} + +static inline int semihosting_getchar(struct semihosting *semihosting, int fd) +{ + if (semihosting_is_redirected(semihosting, fd)) { + unsigned char c; + + if (semihosting_redirect_read(semihosting, &c, 1) > 0) + return c; + + return EOF; + } + + /* default getchar */ + return getchar(); +} + +/** + * User operation parameter string storage buffer. Contains valid data when the + * TARGET_EVENT_SEMIHOSTING_USER_CMD_xxxxx event callbacks are running. + */ +static char *semihosting_user_op_params; + /** * Portable implementation of ARM semihosting calls. * Performs the currently pending semihosting operation @@ -167,7 +344,7 @@ int semihosting_common(struct target *target) /* Enough space to hold 4 long words. */ uint8_t fields[4*8]; - LOG_DEBUG("op=0x%x, param=0x%" PRIx64, (int)semihosting->op, + LOG_DEBUG("op=0x%x, param=0x%" PRIx64, semihosting->op, semihosting->param); switch (semihosting->op) { @@ -724,7 +901,7 @@ int semihosting_common(struct target *target) fileio_info->identifier = "open"; fileio_info->param_1 = addr; fileio_info->param_2 = len; - fileio_info->param_3 = open_modeflags[mode]; + fileio_info->param_3 = open_gdb_modeflags[mode]; fileio_info->param_4 = 0644; } } else { @@ -734,20 +911,23 @@ int semihosting_common(struct target *target) * - 4-7 ("w") for stdout, * - 8-11 ("a") for stderr */ if (mode < 4) { - semihosting->result = dup( - STDIN_FILENO); + int fd = dup(STDIN_FILENO); + semihosting->result = fd; + semihosting->stdin_fd = fd; semihosting->sys_errno = errno; LOG_DEBUG("dup(STDIN)=%d", (int)semihosting->result); } else if (mode < 8) { - semihosting->result = dup( - STDOUT_FILENO); + int fd = dup(STDOUT_FILENO); + semihosting->result = fd; + semihosting->stdout_fd = fd; semihosting->sys_errno = errno; LOG_DEBUG("dup(STDOUT)=%d", (int)semihosting->result); } else { - semihosting->result = dup( - STDERR_FILENO); + int fd = dup(STDERR_FILENO); + semihosting->result = fd; + semihosting->stderr_fd = fd; semihosting->sys_errno = errno; LOG_DEBUG("dup(STDERR)=%d", (int)semihosting->result); @@ -757,7 +937,7 @@ int semihosting_common(struct target *target) * otherwise it will fail to reopen a previously * written file */ semihosting->result = open((char *)fn, - open_modeflags[mode], + open_host_modeflags[mode], 0644); semihosting->sys_errno = errno; LOG_DEBUG("open('%s')=%d", fn, @@ -823,8 +1003,7 @@ int semihosting_common(struct target *target) semihosting->result = -1; semihosting->sys_errno = ENOMEM; } else { - semihosting->result = read(fd, buf, len); - semihosting->sys_errno = errno; + semihosting->result = semihosting_read(semihosting, fd, buf, len); LOG_DEBUG("read(%d, 0x%" PRIx64 ", %zu)=%d", fd, addr, @@ -864,7 +1043,7 @@ int semihosting_common(struct target *target) LOG_ERROR("SYS_READC not supported by semihosting fileio"); return ERROR_FAIL; } - semihosting->result = getchar(); + semihosting->result = semihosting_getchar(semihosting, semihosting->stdin_fd); LOG_DEBUG("getchar()=%d", (int)semihosting->result); break; @@ -1167,7 +1346,7 @@ int semihosting_common(struct target *target) free(buf); return retval; } - semihosting->result = write(fd, buf, len); + semihosting->result = semihosting_write(semihosting, fd, buf, len); semihosting->sys_errno = errno; LOG_DEBUG("write(%d, 0x%" PRIx64 ", %zu)=%d", fd, @@ -1212,7 +1391,7 @@ int semihosting_common(struct target *target) retval = target_read_memory(target, addr, 1, 1, &c); if (retval != ERROR_OK) return retval; - putchar(c); + semihosting_putchar(semihosting, semihosting->stdout_fd, c); semihosting->result = 0; } break; @@ -1256,12 +1435,77 @@ int semihosting_common(struct target *target) return retval; if (!c) break; - putchar(c); + semihosting_putchar(semihosting, semihosting->stdout_fd, c); } while (1); semihosting->result = 0; } break; + case SEMIHOSTING_USER_CMD_0x100 ... SEMIHOSTING_USER_CMD_0x107: + /** + * This is a user defined operation (while user cmds 0x100-0x1ff + * are possible, only 0x100-0x107 are currently implemented). + * + * Reads the user operation parameters from target, then fires the + * corresponding target event. When the target callbacks returned, + * cleans up the command parameter buffer. + * + * Entry + * On entry, the PARAMETER REGISTER contains a pointer to a + * two-field data block: + * - field 1 Contains a pointer to the bound command parameter + * string + * - field 2 Contains the command parameter string length + * + * Return + * On exit, the RETURN REGISTER contains the return status. + */ + { + assert(!semihosting_user_op_params); + + retval = semihosting_read_fields(target, 2, fields); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read fields for user defined command" + " op=0x%x", semihosting->op); + return retval; + } + + uint64_t addr = semihosting_get_field(target, 0, fields); + + size_t len = semihosting_get_field(target, 1, fields); + if (len > SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH) { + LOG_ERROR("The maximum length for user defined command " + "parameter is %u, received length is %zu (op=0x%x)", + SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH, + len, + semihosting->op); + return ERROR_FAIL; + } + + semihosting_user_op_params = malloc(len + 1); + if (!semihosting_user_op_params) + return ERROR_FAIL; + semihosting_user_op_params[len] = 0; + + retval = target_read_buffer(target, addr, len, + (uint8_t *)(semihosting_user_op_params)); + if (retval != ERROR_OK) { + LOG_ERROR("Failed to read from target, semihosting op=0x%x", + semihosting->op); + free(semihosting_user_op_params); + semihosting_user_op_params = NULL; + return retval; + } + + target_handle_event(target, semihosting->op); + free(semihosting_user_op_params); + semihosting_user_op_params = NULL; + + semihosting->result = 0; + break; + } + + case SEMIHOSTING_SYS_ELAPSED: /* 0x30 */ /* * Returns the number of elapsed target ticks since execution @@ -1470,6 +1714,70 @@ static void semihosting_set_field(struct target *target, uint64_t value, target_buffer_set_u32(target, fields + (index * 4), value); } +/* ------------------------------------------------------------------------- + * Semihosting redirect over TCP structs and functions */ + +static int semihosting_service_new_connection_handler(struct connection *connection) +{ + struct semihosting_tcp_service *service = connection->service->priv; + service->semihosting->tcp_connection = connection; + + return ERROR_OK; +} + +static int semihosting_service_input_handler(struct connection *connection) +{ + struct semihosting_tcp_service *service = connection->service->priv; + + if (!connection->input_pending) { + /* consume received data, not for semihosting IO */ + const int buf_len = 100; + char buf[buf_len]; + int bytes_read = connection_read(connection, buf, buf_len); + + if (bytes_read == 0) { + return ERROR_SERVER_REMOTE_CLOSED; + } else if (bytes_read == -1) { + LOG_ERROR("error during read: %s", strerror(errno)); + return ERROR_SERVER_REMOTE_CLOSED; + } + } else if (service->error != ERROR_OK) { + return ERROR_SERVER_REMOTE_CLOSED; + } + + return ERROR_OK; +} + +static int semihosting_service_connection_closed_handler(struct connection *connection) +{ + struct semihosting_tcp_service *service = connection->service->priv; + if (service) { + free(service->name); + free(service); + } + + return ERROR_OK; +} + +static void semihosting_tcp_close_cnx(struct semihosting *semihosting) +{ + if (!semihosting->tcp_connection) + return; + + struct service *service = semihosting->tcp_connection->service; + remove_service(service->name, service->port); + semihosting->tcp_connection = NULL; + +} + +static const struct service_driver semihosting_service_driver = { + .name = "semihosting", + .new_connection_during_keep_alive_handler = NULL, + .new_connection_handler = semihosting_service_new_connection_handler, + .input_handler = semihosting_service_input_handler, + .connection_closed_handler = semihosting_service_connection_closed_handler, + .keep_client_alive_handler = NULL, +}; /* ------------------------------------------------------------------------- * Common semihosting commands handlers. */ @@ -1515,6 +1823,91 @@ COMMAND_HANDLER(handle_common_semihosting_command) return ERROR_OK; } +COMMAND_HANDLER(handle_common_semihosting_redirect_command) +{ + struct target *target = get_current_target(CMD_CTX); + + if (target == NULL) { + LOG_ERROR("No target selected"); + return ERROR_FAIL; + } + + struct semihosting *semihosting = target->semihosting; + if (!semihosting) { + command_print(CMD, "semihosting not supported for current target"); + return ERROR_FAIL; + } + + if (!semihosting->is_active) { + command_print(CMD, "semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + enum semihosting_redirect_config cfg; + const char *port; + + if (CMD_ARGC < 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (strcmp(CMD_ARGV[0], "disable") == 0) { + cfg = SEMIHOSTING_REDIRECT_CFG_NONE; + if (CMD_ARGC > 1) + return ERROR_COMMAND_SYNTAX_ERROR; + } else if (strcmp(CMD_ARGV[0], "tcp") == 0) { + if (CMD_ARGC < 2 || CMD_ARGC > 3) + return ERROR_COMMAND_SYNTAX_ERROR; + + port = CMD_ARGV[1]; + + cfg = SEMIHOSTING_REDIRECT_CFG_ALL; + if (CMD_ARGC == 3) { + if (strcmp(CMD_ARGV[2], "debug") == 0) + cfg = SEMIHOSTING_REDIRECT_CFG_DEBUG; + else if (strcmp(CMD_ARGV[2], "stdio") == 0) + cfg = SEMIHOSTING_REDIRECT_CFG_STDIO; + else if (strcmp(CMD_ARGV[2], "all") != 0) + return ERROR_COMMAND_SYNTAX_ERROR; + } + } else { + return ERROR_COMMAND_SYNTAX_ERROR; + } + + semihosting_tcp_close_cnx(semihosting); + semihosting->redirect_cfg = SEMIHOSTING_REDIRECT_CFG_NONE; + + if (cfg != SEMIHOSTING_REDIRECT_CFG_NONE) { + struct semihosting_tcp_service *service = + calloc(1, sizeof(struct semihosting_tcp_service)); + if (!service) { + LOG_ERROR("Failed to allocate semihosting TCP service."); + return ERROR_FAIL; + } + + service->semihosting = semihosting; + + service->name = alloc_printf("%s semihosting service", target_name(target)); + if (!service->name) { + LOG_ERROR("Out of memory"); + free(service); + return ERROR_FAIL; + } + + int ret = add_service(&semihosting_service_driver, + port, 1, service); + + if (ret != ERROR_OK) { + LOG_ERROR("failed to initialize %s", service->name); + free(service->name); + free(service); + return ERROR_FAIL; + } + } + + semihosting->redirect_cfg = cfg; + + return ERROR_OK; +} + COMMAND_HANDLER(handle_common_semihosting_fileio_command) { struct target *target = get_current_target(CMD_CTX); @@ -1608,34 +2001,72 @@ COMMAND_HANDLER(handle_common_semihosting_resumable_exit_command) return ERROR_OK; } +COMMAND_HANDLER(handle_common_semihosting_read_user_param_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct semihosting *semihosting = target->semihosting; + + if (CMD_ARGC) + return ERROR_COMMAND_SYNTAX_ERROR; + + if (!semihosting->is_active) { + LOG_ERROR("semihosting not yet enabled for current target"); + return ERROR_FAIL; + } + + if (!semihosting_user_op_params) { + LOG_ERROR("This command is usable only from a registered user " + "semihosting event callback."); + return ERROR_FAIL; + } + + command_print_sameline(CMD, "%s", semihosting_user_op_params); + + return ERROR_OK; +} + const struct command_registration semihosting_common_handlers[] = { { - "semihosting", + .name = "semihosting", .handler = handle_common_semihosting_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting operations", }, { - "semihosting_cmdline", + .name = "semihosting_redirect", + .handler = handle_common_semihosting_redirect_command, + .mode = COMMAND_EXEC, + .usage = "(disable | tcp ['debug'|'stdio'|'all'])", + .help = "redirect semihosting IO", + }, + { + .name = "semihosting_cmdline", .handler = handle_common_semihosting_cmdline, .mode = COMMAND_EXEC, .usage = "arguments", .help = "command line arguments to be passed to program", }, { - "semihosting_fileio", + .name = "semihosting_fileio", .handler = handle_common_semihosting_fileio_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting fileio operations", }, { - "semihosting_resexit", + .name = "semihosting_resexit", .handler = handle_common_semihosting_resumable_exit_command, .mode = COMMAND_EXEC, .usage = "['enable'|'disable']", .help = "activate support for semihosting resumable exit", }, + { + .name = "semihosting_read_user_param", + .handler = handle_common_semihosting_read_user_param_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "read parameters in semihosting-user-cmd-0x10X callbacks", + }, COMMAND_REGISTRATION_DONE }; diff --git a/src/target/semihosting_common.h b/src/target/semihosting_common.h index b83464ed5..459faf656 100644 --- a/src/target/semihosting_common.h +++ b/src/target/semihosting_common.h @@ -26,6 +26,7 @@ #include #include #include "helper/replacements.h" +#include /* * According to: @@ -75,8 +76,14 @@ enum semihosting_operation_numbers { SEMIHOSTING_SYS_WRITE = 0x05, SEMIHOSTING_SYS_WRITEC = 0x03, SEMIHOSTING_SYS_WRITE0 = 0x04, + SEMIHOSTING_USER_CMD_0x100 = 0x100, /* First user cmd op code */ + SEMIHOSTING_USER_CMD_0x107 = 0x107, /* Last supported user cmd op code */ + SEMIHOSTING_USER_CMD_0x1FF = 0x1FF, /* Last user cmd op code */ }; +/** Maximum allowed Tcl command segment length in bytes*/ +#define SEMIHOSTING_MAX_TCL_COMMAND_FIELD_LENGTH (1024 * 1024) + /* * Codes used by SEMIHOSTING_SYS_EXIT (formerly * SEMIHOSTING_REPORT_EXCEPTION). @@ -89,6 +96,13 @@ enum semihosting_reported_exceptions { ADP_STOPPED_RUN_TIME_ERROR = ((2 << 16) + 35), }; +enum semihosting_redirect_config { + SEMIHOSTING_REDIRECT_CFG_NONE, + SEMIHOSTING_REDIRECT_CFG_DEBUG, + SEMIHOSTING_REDIRECT_CFG_STDIO, + SEMIHOSTING_REDIRECT_CFG_ALL, +}; + struct target; /* @@ -99,6 +113,15 @@ struct semihosting { /** A flag reporting whether semihosting is active. */ bool is_active; + /** Semihosting STDIO file descriptors */ + int stdin_fd, stdout_fd, stderr_fd; + + /** redirection configuration, NONE by default */ + enum semihosting_redirect_config redirect_cfg; + + /** Handle to redirect semihosting print via tcp */ + struct connection *tcp_connection; + /** A flag reporting whether semihosting fileio is active. */ bool is_fileio; diff --git a/src/target/smp.c b/src/target/smp.c index 518f6e458..3e1ded8bc 100644 --- a/src/target/smp.c +++ b/src/target/smp.c @@ -111,18 +111,18 @@ COMMAND_HANDLER(default_handle_smp_command) } if (!strcmp(CMD_ARGV[0], "on")) { - foreach_smp_target(head, target->head) + foreach_smp_target(head, target->smp_targets) head->target->smp = 1; return ERROR_OK; } if (!strcmp(CMD_ARGV[0], "off")) { - foreach_smp_target(head, target->head) + foreach_smp_target(head, target->smp_targets) head->target->smp = 0; /* fixes the target display to the debugger */ - if (target->head) + if (!list_empty(target->smp_targets)) target->gdb_service->target = target; return ERROR_OK; @@ -135,9 +135,7 @@ COMMAND_HANDLER(handle_smp_gdb_command) { struct target *target = get_current_target(CMD_CTX); int retval = ERROR_OK; - struct target_list *head; - head = target->head; - if (head) { + if (!list_empty(target->smp_targets)) { if (CMD_ARGC == 1) { int coreid = 0; COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], coreid); diff --git a/src/target/smp.h b/src/target/smp.h index 3338240ad..490a49310 100644 --- a/src/target/smp.h +++ b/src/target/smp.h @@ -19,10 +19,14 @@ #ifndef OPENOCD_TARGET_SMP_H #define OPENOCD_TARGET_SMP_H +#include #include "server/server.h" #define foreach_smp_target(pos, head) \ - for (pos = head; (pos); pos = pos->next) + list_for_each_entry(pos, head, lh) + +#define foreach_smp_target_direction(forward, pos, head) \ + list_for_each_entry_direction(forward, pos, head, lh) extern const struct command_registration smp_command_handlers[]; diff --git a/src/target/startup.tcl b/src/target/startup.tcl index cd98d68e4..0e46992b7 100644 --- a/src/target/startup.tcl +++ b/src/target/startup.tcl @@ -206,6 +206,32 @@ proc init_target_events {} { proc init_board {} { } +proc mem2array {arrayname bitwidth address count {phys ""}} { + echo "DEPRECATED! use 'read_memory' not 'mem2array'" + + upvar $arrayname $arrayname + set $arrayname "" + set i 0 + + foreach elem [read_memory $address $bitwidth $count {*}$phys] { + set ${arrayname}($i) $elem + incr i + } +} + +proc array2mem {arrayname bitwidth address count {phys ""}} { + echo "DEPRECATED! use 'write_memory' not 'array2mem'" + + upvar $arrayname $arrayname + set data "" + + for {set i 0} {$i < $count} {incr i} { + lappend data [expr $${arrayname}($i)] + } + + write_memory $address $bitwidth $data {*}$phys +} + # smp_on/smp_off were already DEPRECATED in v0.11.0 through http://openocd.zylin.com/4615 lappend _telnet_autocomplete_skip "aarch64 smp_on" proc "aarch64 smp_on" {args} { diff --git a/src/target/stm8.c b/src/target/stm8.c index 21fc8c54f..4102082ff 100644 --- a/src/target/stm8.c +++ b/src/target/stm8.c @@ -1370,7 +1370,7 @@ static void stm8_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (breakpoint) { - if (breakpoint->set == 0) + if (!breakpoint->is_set) stm8_set_breakpoint(target, breakpoint); breakpoint = breakpoint->next; } @@ -1383,7 +1383,7 @@ static int stm8_set_breakpoint(struct target *target, struct stm8_comparator *comparator_list = stm8->hw_break_list; int retval; - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -1398,7 +1398,7 @@ static int stm8_set_breakpoint(struct target *target, breakpoint->unique_id); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = bp_num + 1; + breakpoint_hw_set(breakpoint, bp_num); comparator_list[bp_num].used = true; comparator_list[bp_num].bp_value = breakpoint->address; comparator_list[bp_num].type = HWBRK_EXEC; @@ -1435,7 +1435,7 @@ static int stm8_set_breakpoint(struct target *target, } else { return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; } - breakpoint->set = 1; /* Any nice value but 0 */ + breakpoint->is_set = true; } return ERROR_OK; @@ -1476,14 +1476,14 @@ static int stm8_unset_breakpoint(struct target *target, struct stm8_comparator *comparator_list = stm8->hw_break_list; int retval; - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } if (breakpoint->type == BKPT_HARD) { - int bp_num = breakpoint->set - 1; - if ((bp_num < 0) || (bp_num >= stm8->num_hw_bpoints)) { + int bp_num = breakpoint->number; + if (bp_num >= stm8->num_hw_bpoints) { LOG_DEBUG("Invalid comparator number in breakpoint (bpid: %" PRIu32 ")", breakpoint->unique_id); return ERROR_OK; @@ -1517,7 +1517,7 @@ static int stm8_unset_breakpoint(struct target *target, } else return ERROR_FAIL; } - breakpoint->set = 0; + breakpoint->is_set = false; return ERROR_OK; } @@ -1533,7 +1533,7 @@ static int stm8_remove_breakpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (breakpoint->set) + if (breakpoint->is_set) stm8_unset_breakpoint(target, breakpoint); if (breakpoint->type == BKPT_HARD) @@ -1550,7 +1550,7 @@ static int stm8_set_watchpoint(struct target *target, int wp_num = 0; int ret; - if (watchpoint->set) { + if (watchpoint->is_set) { LOG_WARNING("watchpoint already set"); return ERROR_OK; } @@ -1593,7 +1593,7 @@ static int stm8_set_watchpoint(struct target *target, return ret; } - watchpoint->set = wp_num + 1; + watchpoint_set(watchpoint, wp_num); LOG_DEBUG("wp_num %i bp_value 0x%" PRIx32 "", wp_num, @@ -1627,7 +1627,7 @@ static void stm8_enable_watchpoints(struct target *target) /* set any pending watchpoints */ while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->is_set) stm8_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } @@ -1640,18 +1640,18 @@ static int stm8_unset_watchpoint(struct target *target, struct stm8_common *stm8 = target_to_stm8(target); struct stm8_comparator *comparator_list = stm8->hw_break_list; - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - int wp_num = watchpoint->set - 1; - if ((wp_num < 0) || (wp_num >= stm8->num_hw_bpoints)) { + int wp_num = watchpoint->number; + if (wp_num >= stm8->num_hw_bpoints) { LOG_DEBUG("Invalid hw comparator number in watchpoint"); return ERROR_OK; } comparator_list[wp_num].used = false; - watchpoint->set = 0; + watchpoint->is_set = false; stm8_set_hwbreak(target, comparator_list); @@ -1669,7 +1669,7 @@ static int stm8_remove_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) stm8_unset_watchpoint(target, watchpoint); stm8->num_hw_bpoints_avail++; diff --git a/src/target/target.c b/src/target/target.c old mode 100644 new mode 100755 index 0fa8cd24f..9c07e593b --- a/src/target/target.c +++ b/src/target/target.c @@ -56,6 +56,7 @@ #include "rtos/rtos.h" #include "transport/transport.h" #include "arm_cti.h" +#include "smp.h" /* default halt wait timeout (ms) */ #define DEFAULT_HALT_TIMEOUT 5000 @@ -111,7 +112,7 @@ extern struct target_type riscv_target; extern struct target_type mem_ap_target; extern struct target_type esirisc_target; extern struct target_type arcv2_target; - +extern struct target_type wch_riscv_target; static struct target_type *target_types[] = { &arm7tdmi_target, &arm9tdmi_target, @@ -149,6 +150,7 @@ static struct target_type *target_types[] = { &arcv2_target, &aarch64_target, &mips_mips64_target, + &wch_riscv_target, NULL, }; @@ -158,7 +160,8 @@ 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); static const struct jim_nvp nvp_assert[] = { { .name = "assert", NVP_ASSERT }, @@ -236,6 +239,15 @@ static const struct jim_nvp nvp_target_event[] = { { .value = TARGET_EVENT_TRACE_CONFIG, .name = "trace-config" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x100, .name = "semihosting-user-cmd-0x100" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x101, .name = "semihosting-user-cmd-0x101" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x102, .name = "semihosting-user-cmd-0x102" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x103, .name = "semihosting-user-cmd-0x103" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x104, .name = "semihosting-user-cmd-0x104" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x105, .name = "semihosting-user-cmd-0x105" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x106, .name = "semihosting-user-cmd-0x106" }, + { .value = TARGET_EVENT_SEMIHOSTING_USER_CMD_0x107, .name = "semihosting-user-cmd-0x107" }, + { .name = NULL, .value = -1 } }; @@ -813,7 +825,7 @@ const char *target_type_name(struct target *target) return target->type->name; } -static int target_soft_reset_halt(struct target *target) + int target_soft_reset_halt(struct target *target) { if (!target_was_examined(target)) { LOG_ERROR("Target not examined yet"); @@ -852,7 +864,7 @@ int target_run_algorithm(struct target *target, int timeout_ms, void *arch_info) { int retval = ERROR_FAIL; - + if (!target_was_examined(target)) { LOG_ERROR("Target not examined yet"); goto done; @@ -862,7 +874,7 @@ int target_run_algorithm(struct target *target, target_type_name(target), __func__); goto done; } - + target->running_alg = true; retval = target->type->run_algorithm(target, num_mem_params, mem_params, @@ -1843,7 +1855,7 @@ int target_call_event_callbacks(struct target *target, enum target_event event) } LOG_DEBUG("target event %i (%s) for core %s", event, - jim_nvp_value2name_simple(nvp_target_event, event)->name, + target_event_name(event), target_name(target)); target_handle_event(target, event); @@ -2272,13 +2284,15 @@ static void target_destroy(struct target *target) /* release the targets SMP list */ if (target->smp) { - struct target_list *head = target->head; - while (head) { - struct target_list *pos = head->next; + struct target_list *head, *tmp; + + list_for_each_entry_safe(head, tmp, target->smp_targets, lh) { + list_del(&head->lh); head->target->smp = 0; free(head); - head = pos; } + if (target->smp_targets != &empty_smp_targets) + free(target->smp_targets); target->smp = 0; } @@ -3033,44 +3047,40 @@ static int handle_target(void *priv) is_jtag_poll_safe() && target; target = target->next) { - 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)); + /* 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); } - 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 = MIN(target->backoff.times * 2 + 1, - 5000 / polling_interval); - - /* 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 && examine_attempted) { - LOG_DEBUG("[%s] Polling failed, trying to reexamine", target_name(target)); - target_reset_examined(target); - retval = target_examine_one(target); - if (retval != ERROR_OK) { - LOG_DEBUG("[%s] Examination failed, GDB will be halted. Polling again in %dms", - target_name(target), target->backoff.times * polling_interval); - return retval; - } + LOG_TARGET_DEBUG(target, "Examination failed, GDB will be halted. " + "Polling again in %dms", + target->backoff.interval); + return retval; } - - /* Since we succeeded, we reset backoff count */ - target->backoff.times = 0; } } @@ -3087,13 +3097,12 @@ COMMAND_HANDLER(handle_reg_command) /* list all available registers for the current target */ if (CMD_ARGC == 0) { struct reg_cache *cache = target->reg_cache; - unsigned int count = 0; while (cache) { unsigned i; command_print(CMD, "===== %s", cache->name); - + for (i = 0, reg = cache->reg_list; i < cache->num_regs; i++, reg++, count++) { @@ -3346,7 +3355,12 @@ COMMAND_HANDLER(handle_reset_command) /* reset *all* targets */ return target_process_reset(CMD, reset_mode); } - +extern void wlink_softreset(void); +COMMAND_HANDLER(handle_wlink_reset_resume_command) +{ + + wlink_softreset(); +} COMMAND_HANDLER(handle_resume_command) { @@ -3959,26 +3973,26 @@ static int handle_bp_command_list(struct command_invocation *cmd) if (breakpoint->type == BKPT_SOFT) { char *buf = buf_to_hex_str(breakpoint->orig_instr, breakpoint->length); - command_print(cmd, "IVA breakpoint: " TARGET_ADDR_FMT ", 0x%x, %i, 0x%s", + command_print(cmd, "IVA breakpoint: " TARGET_ADDR_FMT ", 0x%x, 0x%s", breakpoint->address, breakpoint->length, - breakpoint->set, buf); + buf); free(buf); } else { if ((breakpoint->address == 0) && (breakpoint->asid != 0)) - command_print(cmd, "Context breakpoint: 0x%8.8" PRIx32 ", 0x%x, %i", + command_print(cmd, "Context breakpoint: 0x%8.8" PRIx32 ", 0x%x, %u", breakpoint->asid, - breakpoint->length, breakpoint->set); + breakpoint->length, breakpoint->number); else if ((breakpoint->address != 0) && (breakpoint->asid != 0)) { - command_print(cmd, "Hybrid breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %i", + command_print(cmd, "Hybrid breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %u", breakpoint->address, - breakpoint->length, breakpoint->set); + breakpoint->length, breakpoint->number); command_print(cmd, "\t|--->linked with ContextID: 0x%8.8" PRIx32, breakpoint->asid); } else - command_print(cmd, "Breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %i", + command_print(cmd, "Breakpoint(IVA): " TARGET_ADDR_FMT ", 0x%x, %u", breakpoint->address, - breakpoint->length, breakpoint->set); + breakpoint->length, breakpoint->number); } breakpoint = breakpoint->next; @@ -4395,6 +4409,7 @@ COMMAND_HANDLER(handle_profile_command) return retval; } + static int new_u64_array_element(Jim_Interp *interp, const char *varname, int idx, uint64_t val) { char *namebuf; @@ -4423,27 +4438,12 @@ static int new_u64_array_element(Jim_Interp *interp, const char *varname, int id return result; } -static int jim_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - struct command_context *context; - struct target *target; - - context = current_command_context(interp); - assert(context); - - target = get_current_target(context); - if (!target) { - LOG_ERROR("mem2array: no current target"); - return JIM_ERR; - } - - return target_mem2array(interp, target, argc - 1, argv + 1); -} - static int target_mem2array(Jim_Interp *interp, struct target *target, int argc, Jim_Obj *const *argv) { int e; + LOG_WARNING("DEPRECATED! use 'read_memory' not 'mem2array'"); + /* argv[0] = name of array to receive the data * argv[1] = desired element width in bits * argv[2] = memory address @@ -4596,6 +4596,161 @@ static int target_mem2array(Jim_Interp *interp, struct target *target, int argc, return e; } +static int target_jim_read_memory(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + /* + * argv[1] = memory address + * argv[2] = desired element width in bits + * argv[3] = number of elements to read + * argv[4] = optional "phys" + */ + + if (argc < 4 || argc > 5) { + Jim_WrongNumArgs(interp, 1, argv, "address width count ['phys']"); + return JIM_ERR; + } + + /* Arg 1: Memory address. */ + jim_wide wide_addr; + int e; + e = Jim_GetWide(interp, argv[1], &wide_addr); + + if (e != JIM_OK) + return e; + + target_addr_t addr = (target_addr_t)wide_addr; + + /* Arg 2: Bit width of one element. */ + long l; + e = Jim_GetLong(interp, argv[2], &l); + + if (e != JIM_OK) + return e; + + const unsigned int width_bits = l; + + /* Arg 3: Number of elements to read. */ + e = Jim_GetLong(interp, argv[3], &l); + + if (e != JIM_OK) + return e; + + size_t count = l; + + /* Arg 4: Optional 'phys'. */ + bool is_phys = false; + + if (argc > 4) { + const char *phys = Jim_GetString(argv[4], NULL); + + if (strcmp(phys, "phys")) { + Jim_SetResultFormatted(interp, "invalid argument '%s', must be 'phys'", phys); + return JIM_ERR; + } + + is_phys = true; + } + + switch (width_bits) { + case 8: + case 16: + case 32: + case 64: + break; + default: + Jim_SetResultString(interp, "invalid width, must be 8, 16, 32 or 64", -1); + return JIM_ERR; + } + + const unsigned int width = width_bits / 8; + + if ((addr + (count * width)) < addr) { + Jim_SetResultString(interp, "read_memory: addr + count wraps to zero", -1); + return JIM_ERR; + } + + if (count > 65536) { + Jim_SetResultString(interp, "read_memory: too large read request, exeeds 64K elements", -1); + return JIM_ERR; + } + + struct command_context *cmd_ctx = current_command_context(interp); + assert(cmd_ctx != NULL); + struct target *target = get_current_target(cmd_ctx); + + const size_t buffersize = 4096; + uint8_t *buffer = malloc(buffersize); + + if (!buffer) { + LOG_ERROR("Failed to allocate memory"); + return JIM_ERR; + } + + Jim_Obj *result_list = Jim_NewListObj(interp, NULL, 0); + Jim_IncrRefCount(result_list); + + while (count > 0) { + const unsigned int max_chunk_len = buffersize / width; + const size_t chunk_len = MIN(count, max_chunk_len); + + int retval; + + if (is_phys) + retval = target_read_phys_memory(target, addr, width, chunk_len, buffer); + else + retval = target_read_memory(target, addr, width, chunk_len, buffer); + + if (retval != ERROR_OK) { + LOG_ERROR("read_memory: read at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", + addr, width_bits, chunk_len); + Jim_SetResultString(interp, "read_memory: failed to read memory", -1); + e = JIM_ERR; + break; + } + + for (size_t i = 0; i < chunk_len ; i++) { + uint64_t v = 0; + + switch (width) { + case 8: + v = target_buffer_get_u64(target, &buffer[i * width]); + break; + case 4: + v = target_buffer_get_u32(target, &buffer[i * width]); + break; + case 2: + v = target_buffer_get_u16(target, &buffer[i * width]); + break; + case 1: + v = buffer[i]; + break; + } + + char value_buf[11]; + snprintf(value_buf, sizeof(value_buf), "0x%" PRIx64, v); + + Jim_ListAppendElement(interp, result_list, + Jim_NewStringObj(interp, value_buf, -1)); + } + + count -= chunk_len; + addr += chunk_len * width; + } + + free(buffer); + + if (e != JIM_OK) { + Jim_DecrRefCount(interp, result_list); + return e; + } + + Jim_SetResult(interp, result_list); + Jim_DecrRefCount(interp, result_list); + + return JIM_OK; +} + static int get_u64_array_element(Jim_Interp *interp, const char *varname, size_t idx, uint64_t *val) { char *namebuf = alloc_printf("%s(%zu)", varname, idx); @@ -4621,28 +4776,13 @@ static int get_u64_array_element(Jim_Interp *interp, const char *varname, size_t return result; } -static int jim_array2mem(Jim_Interp *interp, int argc, Jim_Obj *const *argv) -{ - struct command_context *context; - struct target *target; - - context = current_command_context(interp); - assert(context); - - target = get_current_target(context); - if (!target) { - LOG_ERROR("array2mem: no current target"); - return JIM_ERR; - } - - return target_array2mem(interp, target, argc-1, argv + 1); -} - static int target_array2mem(Jim_Interp *interp, struct target *target, int argc, Jim_Obj *const *argv) { int e; + LOG_WARNING("DEPRECATED! use 'write_memory' not 'array2mem'"); + /* argv[0] = name of array from which to read the data * argv[1] = desired element width in bits * argv[2] = memory address @@ -4806,6 +4946,144 @@ static int target_array2mem(Jim_Interp *interp, struct target *target, return e; } +static int target_jim_write_memory(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + /* + * argv[1] = memory address + * argv[2] = desired element width in bits + * argv[3] = list of data to write + * argv[4] = optional "phys" + */ + + if (argc < 4 || argc > 5) { + Jim_WrongNumArgs(interp, 1, argv, "address width data ['phys']"); + return JIM_ERR; + } + + /* Arg 1: Memory address. */ + int e; + jim_wide wide_addr; + e = Jim_GetWide(interp, argv[1], &wide_addr); + + if (e != JIM_OK) + return e; + + target_addr_t addr = (target_addr_t)wide_addr; + + /* Arg 2: Bit width of one element. */ + long l; + e = Jim_GetLong(interp, argv[2], &l); + + if (e != JIM_OK) + return e; + + const unsigned int width_bits = l; + size_t count = Jim_ListLength(interp, argv[3]); + + /* Arg 4: Optional 'phys'. */ + bool is_phys = false; + + if (argc > 4) { + const char *phys = Jim_GetString(argv[4], NULL); + + if (strcmp(phys, "phys")) { + Jim_SetResultFormatted(interp, "invalid argument '%s', must be 'phys'", phys); + return JIM_ERR; + } + + is_phys = true; + } + + switch (width_bits) { + case 8: + case 16: + case 32: + case 64: + break; + default: + Jim_SetResultString(interp, "invalid width, must be 8, 16, 32 or 64", -1); + return JIM_ERR; + } + + const unsigned int width = width_bits / 8; + + if ((addr + (count * width)) < addr) { + Jim_SetResultString(interp, "write_memory: addr + len wraps to zero", -1); + return JIM_ERR; + } + + if (count > 65536) { + Jim_SetResultString(interp, "write_memory: too large memory write request, exceeds 64K elements", -1); + return JIM_ERR; + } + + struct command_context *cmd_ctx = current_command_context(interp); + assert(cmd_ctx != NULL); + struct target *target = get_current_target(cmd_ctx); + + const size_t buffersize = 4096; + uint8_t *buffer = malloc(buffersize); + + if (!buffer) { + LOG_ERROR("Failed to allocate memory"); + return JIM_ERR; + } + + size_t j = 0; + + while (count > 0) { + const unsigned int max_chunk_len = buffersize / width; + const size_t chunk_len = MIN(count, max_chunk_len); + + for (size_t i = 0; i < chunk_len; i++, j++) { + Jim_Obj *tmp = Jim_ListGetIndex(interp, argv[3], j); + jim_wide element_wide; + Jim_GetWide(interp, tmp, &element_wide); + + const uint64_t v = element_wide; + + switch (width) { + case 8: + target_buffer_set_u64(target, &buffer[i * width], v); + break; + case 4: + target_buffer_set_u32(target, &buffer[i * width], v); + break; + case 2: + target_buffer_set_u16(target, &buffer[i * width], v); + break; + case 1: + buffer[i] = v & 0x0ff; + break; + } + } + + count -= chunk_len; + + int retval; + + if (is_phys) + retval = target_write_phys_memory(target, addr, width, chunk_len, buffer); + else + retval = target_write_memory(target, addr, width, chunk_len, buffer); + + if (retval != ERROR_OK) { + LOG_ERROR("write_memory: write at " TARGET_ADDR_FMT " with width=%u and count=%zu failed", + addr, width_bits, chunk_len); + Jim_SetResultString(interp, "write_memory: failed to write memory", -1); + e = JIM_ERR; + break; + } + + addr += chunk_len * width; + } + + free(buffer); + + return e; +} + /* FIX? should we propagate errors here rather than printing them * and continuing? */ @@ -4821,7 +5099,7 @@ void target_handle_event(struct target *target, enum target_event e) target_name(target), target_type_name(target), e, - jim_nvp_value2name_simple(nvp_target_event, e)->name, + target_event_name(e), Jim_GetString(teap->body, NULL)); /* Override current target by the target an event @@ -4845,7 +5123,7 @@ void target_handle_event(struct target *target, enum target_event e) if (retval != JIM_OK) { Jim_MakeErrorMessage(teap->interp); LOG_USER("Error executing event %s on target %s:\n%s", - jim_nvp_value2name_simple(nvp_target_event, e)->name, + target_event_name(e), target_name(target), Jim_GetString(Jim_GetResult(teap->interp), NULL)); /* clean both error code and stacktrace before return */ @@ -4855,6 +5133,144 @@ void target_handle_event(struct target *target, enum target_event e) } } +static int target_jim_get_reg(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + bool force = false; + + if (argc == 3) { + const char *option = Jim_GetString(argv[1], NULL); + + if (!strcmp(option, "-force")) { + argc--; + argv++; + force = true; + } else { + Jim_SetResultFormatted(interp, "invalid option '%s'", option); + return JIM_ERR; + } + } + + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "[-force] list"); + return JIM_ERR; + } + + const int length = Jim_ListLength(interp, argv[1]); + + Jim_Obj *result_dict = Jim_NewDictObj(interp, NULL, 0); + + if (!result_dict) + return JIM_ERR; + + struct command_context *cmd_ctx = current_command_context(interp); + assert(cmd_ctx != NULL); + const struct target *target = get_current_target(cmd_ctx); + + for (int i = 0; i < length; i++) { + Jim_Obj *elem = Jim_ListGetIndex(interp, argv[1], i); + + if (!elem) + return JIM_ERR; + + const char *reg_name = Jim_String(elem); + + struct reg *reg = register_get_by_name(target->reg_cache, reg_name, + false); + + if (!reg || !reg->exist) { + Jim_SetResultFormatted(interp, "unknown register '%s'", reg_name); + return JIM_ERR; + } + + if (force) { + int retval = reg->type->get(reg); + + if (retval != ERROR_OK) { + Jim_SetResultFormatted(interp, "failed to read register '%s'", + reg_name); + return JIM_ERR; + } + } + + char *reg_value = buf_to_hex_str(reg->value, reg->size); + + if (!reg_value) { + LOG_ERROR("Failed to allocate memory"); + return JIM_ERR; + } + + char *tmp = alloc_printf("0x%s", reg_value); + + free(reg_value); + + if (!tmp) { + LOG_ERROR("Failed to allocate memory"); + return JIM_ERR; + } + + Jim_DictAddElement(interp, result_dict, elem, + Jim_NewStringObj(interp, tmp, -1)); + + free(tmp); + } + + Jim_SetResult(interp, result_dict); + + return JIM_OK; +} + +static int target_jim_set_reg(Jim_Interp *interp, int argc, + Jim_Obj * const *argv) +{ + if (argc != 2) { + Jim_WrongNumArgs(interp, 1, argv, "dict"); + return JIM_ERR; + } + + int tmp; + Jim_Obj **dict = Jim_DictPairs(interp, argv[1], &tmp); + + if (!dict) + return JIM_ERR; + + const unsigned int length = tmp; + struct command_context *cmd_ctx = current_command_context(interp); + assert(cmd_ctx); + const struct target *target = get_current_target(cmd_ctx); + + for (unsigned int i = 0; i < length; i += 2) { + const char *reg_name = Jim_String(dict[i]); + const char *reg_value = Jim_String(dict[i + 1]); + struct reg *reg = register_get_by_name(target->reg_cache, reg_name, + false); + + if (!reg || !reg->exist) { + Jim_SetResultFormatted(interp, "unknown register '%s'", reg_name); + return JIM_ERR; + } + + uint8_t *buf = malloc(DIV_ROUND_UP(reg->size, 8)); + + if (!buf) { + LOG_ERROR("Failed to allocate memory"); + return JIM_ERR; + } + + str_to_buf(reg_value, strlen(reg_value), buf, reg->size, 0); + int retval = reg->type->set(reg, buf); + free(buf); + + if (retval != ERROR_OK) { + Jim_SetResultFormatted(interp, "failed to set '%s' to register '%s'", + reg_value, reg_name); + return JIM_ERR; + } + } + + return JIM_OK; +} + /** * Returns true only if the target has a handler for the specified event. */ @@ -5508,9 +5924,9 @@ COMMAND_HANDLER(handle_target_event_list) command_print(CMD, "------------------------- | " "----------------------------------------"); while (teap) { - struct jim_nvp *opt = jim_nvp_value2name_simple(nvp_target_event, teap->event); command_print(CMD, "%-25s | %s", - opt->name, Jim_GetString(teap->body, NULL)); + target_event_name(teap->event), + Jim_GetString(teap->body, NULL)); teap = teap->next; } command_print(CMD, "***END***"); @@ -5637,6 +6053,34 @@ static const struct command_registration target_instance_command_handlers[] = { "from target memory", .usage = "arrayname bitwidth address count", }, + { + .name = "get_reg", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_get_reg, + .help = "Get register values from the target", + .usage = "list", + }, + { + .name = "set_reg", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_set_reg, + .help = "Set target register values", + .usage = "dict", + }, + { + .name = "read_memory", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_read_memory, + .help = "Read Tcl list of 8/16/32/64 bit numbers from target memory", + .usage = "address width count ['phys']", + }, + { + .name = "write_memory", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_write_memory, + .help = "Write Tcl list of 8/16/32/64 bit numbers to target memory", + .usage = "address width data ['phys']", + }, { .name = "eventlist", .handler = handle_target_event_list, @@ -5782,6 +6226,9 @@ static int target_create(struct jim_getopt_info *goi) return JIM_ERR; } + /* set empty smp cluster */ + target->smp_targets = &empty_smp_targets; + /* set target number */ target->target_number = new_target_number(); @@ -5995,9 +6442,7 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) int retval, len; static int smp_group = 1; struct target *target = NULL; - struct target_list *head, *curr, *new; - curr = NULL; - head = NULL; + struct target_list *head, *new; retval = 0; LOG_DEBUG("%d", argc); @@ -6006,6 +6451,13 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) * argv[3] ... */ + struct list_head *lh = malloc(sizeof(*lh)); + if (!lh) { + LOG_ERROR("Out of memory"); + return JIM_ERR; + } + INIT_LIST_HEAD(lh); + for (i = 1; i < argc; i++) { targetname = Jim_GetString(argv[i], &len); @@ -6014,24 +6466,14 @@ static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv) if (target) { new = malloc(sizeof(struct target_list)); new->target = target; - new->next = NULL; - if (!head) { - head = new; - curr = head; - } else { - curr->next = new; - curr = new; - } + list_add_tail(&new->lh, lh); } } /* now parse the list of cpu and put the target in smp mode*/ - curr = head; - - while (curr) { - target = curr->target; + foreach_smp_target(head, lh) { + target = head->target; target->smp = smp_group; - target->head = head; - curr = curr->next; + target->smp_targets = lh; } smp_group++; @@ -6578,6 +7020,14 @@ static const struct command_registration target_exec_command_handlers[] = { .help = "Reset all targets into the specified mode. " "Default reset mode is run, if not given.", }, + { + .name = "wlink_reset_resume", + .handler = handle_wlink_reset_resume_command, + .mode = COMMAND_EXEC, + .usage = "", + .help = "Reset && resume. ", + }, + { .name = "soft_reset_halt", .handler = handle_soft_reset_halt_command, @@ -6708,20 +7158,32 @@ static const struct command_registration target_exec_command_handlers[] = { .usage = "filename [offset [type]]", }, { - .name = "mem2array", + .name = "get_reg", .mode = COMMAND_EXEC, - .jim_handler = jim_mem2array, - .help = "read 8/16/32 bit memory and return as a TCL array " - "for script processing", - .usage = "arrayname bitwidth address count", + .jim_handler = target_jim_get_reg, + .help = "Get register values from the target", + .usage = "list", }, { - .name = "array2mem", + .name = "set_reg", .mode = COMMAND_EXEC, - .jim_handler = jim_array2mem, - .help = "convert a TCL array to memory locations " - "and write the 8/16/32 bit values", - .usage = "arrayname bitwidth address count", + .jim_handler = target_jim_set_reg, + .help = "Set target register values", + .usage = "dict", + }, + { + .name = "read_memory", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_read_memory, + .help = "Read Tcl list of 8/16/32/64 bit numbers from target memory", + .usage = "address width count ['phys']", + }, + { + .name = "write_memory", + .mode = COMMAND_EXEC, + .jim_handler = target_jim_write_memory, + .help = "Write Tcl list of 8/16/32/64 bit numbers to target memory", + .usage = "address width data ['phys']", }, { .name = "reset_nag", diff --git a/src/target/target.h b/src/target/target.h index 6903b5ffa..726a14d3f 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -117,8 +117,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 */ @@ -199,9 +199,14 @@ 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; /* add some target attributes for smp support */ - struct target_list *head; + struct list_head *smp_targets; /* list all targets in this smp group/cluster + * The head of the list is shared between the + * cluster, thus here there is a pointer */ /* the gdb service is there in case of smp, we have only one gdb server * for all smp target * the target attached to the gdb is changing dynamically by changing @@ -220,8 +225,8 @@ struct target { }; struct target_list { + struct list_head lh; struct target *target; - struct target_list *next; }; struct gdb_fileio_info { @@ -294,6 +299,15 @@ enum target_event { TARGET_EVENT_GDB_FLASH_WRITE_END, TARGET_EVENT_TRACE_CONFIG, + + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x100 = 0x100, /* semihosting allows user cmds from 0x100 to 0x1ff */ + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x101 = 0x101, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x102 = 0x102, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x103 = 0x103, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x104 = 0x104, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x105 = 0x105, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x106 = 0x106, + TARGET_EVENT_SEMIHOSTING_USER_CMD_0x107 = 0x107, }; struct target_event_action { diff --git a/src/target/wch_riscv-013.c b/src/target/wch_riscv-013.c new file mode 100755 index 000000000..e1e395f45 --- /dev/null +++ b/src/target/wch_riscv-013.c @@ -0,0 +1,4300 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Support for RISC-V, debug version 0.13, which is currently (2/4/17) the + * latest draft. + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "target/target.h" +#include "target/algorithm.h" +#include "target/target_type.h" +#include +#include "jtag/jtag.h" +#include "target/register.h" +#include "target/breakpoints.h" +#include "helper/time_support.h" +#include "helper/list.h" +#include "target/riscv/riscv.h" +#include "target/riscv/debug_defines.h" +#include "rtos/rtos.h" +#include "target/riscv/program.h" +#include "target/riscv/asm.h" +#include "target/riscv/batch.h" +#include "jtag/sdi.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); +static void riscv013_clear_abstract_error(struct target *target); + +/* Implementations of the functions in riscv_info_t. */ +static int riscv013_get_register(struct target *target, + riscv_reg_t *value, int rid); +static int riscv013_set_register(struct target *target, int regid, uint64_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, + riscv_insn_t d); +static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned + index); +static int riscv013_invalidate_cached_debug_buffer(struct target *target); +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_direct(struct target *target, uint64_t *value, uint32_t number); +int register_write_direct(struct target *target, unsigned number, + uint64_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); +static int riscv013_test_sba_config_reg(struct target *target, target_addr_t legal_address, + uint32_t num_words, target_addr_t illegal_address, bool run_sbbusyerror_test); +static void write_memory_sba_simple(struct target *target, target_addr_t addr, uint32_t *write_data, + uint32_t write_size, uint32_t sbcs); +static void read_memory_sba_simple(struct target *target, target_addr_t addr, + uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs); + +/** + * Since almost everything can be accomplish by scanning the dbus register, all + * functions here assume dbus is already selected. The exception are functions + * called directly by OpenOCD, which can't assume anything about what's + * 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 RISCV013_INFO(r) riscv013_info_t *r = get_info(target) +#define WCH_DMOD 0x80 +/*** JTAG registers. ***/ + +typedef enum { + DMI_OP_NOP = 0, + DMI_OP_READ = 1, + DMI_OP_WRITE = 2 +} dmi_op_t; +typedef enum { + DMI_STATUS_SUCCESS = 0, + DMI_STATUS_FAILED = 2, + DMI_STATUS_BUSY = 3 +} dmi_status_t; + +// typedef enum slot { +// SLOT0, +// SLOT1, +// SLOT_LAST, +// } slot_t; + +/*** 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 + +/*** 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 + +typedef struct { + struct list_head list; + int abs_chain_position; + + /* The number of harts connected to this DM. */ + int hart_count; + /* 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; + /* 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]; +} dm013_info_t; + +extern int WriteNonFullPage(unsigned long iaddr, unsigned char *ibuff,unsigned long ilen); +static int execute_fence(struct target *target); +typedef struct { + struct list_head list; + struct target *target; +} target_list_t; + +typedef struct { + /* The indexed used to address this hart in its DM. */ + unsigned index; + /* Number of address bits in the dbus register. */ + unsigned abits; + /* Number of abstract command data registers. */ + unsigned datacount; + /* Number of words in the Program Buffer. */ + unsigned progbufsize; + + /* We cache the read-only bits of sbcs here. */ + uint32_t sbcs; + + yes_no_maybe_t progbuf_writable; + /* We only need the address so that we know the alignment of the buffer. */ + riscv_addr_t progbuf_address; + + /* Number of run-test/idle cycles the target requests we do after each dbus + * access. */ + unsigned int dtmcs_idle; + + /* This value is incremented every time a dbus access comes back as "busy". + * It's used to determine how many run-test/idle cycles to feed the target + * in between accesses. */ + unsigned int dmi_busy_delay; + + /* Number of run-test/idle cycles to add between consecutive bus master + * reads/writes respectively. */ + unsigned int bus_master_write_delay, bus_master_read_delay; + + /* This value is increased every time we tried to execute two commands + * consecutively, and the second one failed because the previous hadn't + * completed yet. It's used to add extra run-test/idle cycles after + * starting a command, so we don't have to waste time checking for busy to + * go low. */ + unsigned int ac_busy_delay; + + bool abstract_read_csr_supported; + bool abstract_write_csr_supported; + bool abstract_read_fpr_supported; + bool abstract_write_fpr_supported; + + yes_no_maybe_t has_aampostincrement; + + /* When a function returns some error due to a failure indicated by the + * target in cmderr, the caller can look here to see what that error was. + * (Compare with errno.) */ + 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; +} riscv013_info_t; + +static LIST_HEAD(dm_list); + +static riscv013_info_t *get_info(const struct target *target) +{ + riscv_info_t *info = (riscv_info_t *) target->arch_info; + assert(info); + assert(info->version_specific); + return (riscv013_info_t *) info->version_specific; +} + +/** + * Return the DM structure for this target. If there isn't one, find it in the + * global list of DMs. If it's not in there, then create one and initialize it + * to 0. + */ +static dm013_info_t *get_dm(struct target *target) +{ + RISCV013_INFO(info); + if (info->dm) + return info->dm; + + int abs_chain_position = target->tap->abs_chain_position; + + 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) { + dm = entry; + break; + } + } + if (!dm) { + LOG_DEBUG("[%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 = 0; + dm->hart_count = -1; + INIT_LIST_HEAD(&dm->target_list); + list_add(&dm->list, &dm_list); + } + + info->dm = dm; + target_list_t *target_entry; + list_for_each_entry(target_entry, &dm->target_list, list) { + if (target_entry->target == target) + return dm; + } + target_entry = calloc(1, sizeof(*target_entry)); + if (!target_entry) { + info->dm = NULL; + return NULL; + } + target_entry->target = target; + list_add(&target_entry->list, &dm->target_list); + + return dm; +} + +static uint32_t set_dmcontrol_hartsel(uint32_t initial, int hart_index) +{ + 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; +} +int write_flash_data(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer) +{ + + execute_fence(target); + int ret =WriteNonFullPage(address,buffer,size); + return ret; +} + + + +/** + * @param target + * @param data_in The data we received from the target. + * @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 address The address argument to that operation. + * @param data_out The data to send to the target. + * @param timeout_sec + * @param exec When true, this scan will execute something, so extra RTI + * cycles may be added. + * @param ensure_success + * Scan a nop after the requested operation, ensuring the + * 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, + uint32_t data_out, int timeout_sec, bool exec, bool ensure_success) +{ + dmi_status_t status; + uint32_t address_in; + uint8_t recvOP; + uint32_t recvData; + if (dmi_busy_encountered) + *dmi_busy_encountered = false; + + const char *op_name; + switch (dmi_op) { + case DMI_OP_NOP: + op_name = "nop"; + break; + case DMI_OP_READ: + op_name = "read"; + break; + case DMI_OP_WRITE: + op_name = "write"; + break; + default: + LOG_ERROR("Invalid DMI operation: %d", dmi_op); + return ERROR_FAIL; + } + + time_t start = time(NULL); + int result=0; + /* 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) { + if(dmi_op == DMI_OP_READ) + { + result=transfer(0, (unsigned char )address, 0, (unsigned char )dmi_op, &address_in, data_in,&recvOP); + if(!result){ + LOG_ERROR("failed %s at 0x%x, status=%d", op_name, address, status); + LOG_ERROR("Maybe the device has been removed"); + server_quit(); + return ERROR_FAIL; + } + }else{ + transfer(0, (unsigned char )address, data_out, (unsigned char )dmi_op, &address_in, &recvData,&recvOP); + } + status = recvOP; + if (status == DMI_STATUS_BUSY) { + if (dmi_busy_encountered) + *dmi_busy_encountered = true; + transfer(0, (unsigned char )DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR, (unsigned char )DMI_OP_WRITE, &address_in, &recvData,&recvOP); + } else if (status == DMI_STATUS_SUCCESS) { + break; + } else { + return ERROR_FAIL; + } + 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); + return ERROR_FAIL; + } + return ERROR_OK; + +} + +static int dmi_op(struct target *target, uint32_t *data_in, + bool *dmi_busy_encountered, int dmi_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, + address, data_out, riscv_command_timeout_sec, exec, ensure_success); + if (result == ERROR_TIMEOUT_REACHED) { + LOG_ERROR("[%s] 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.", + target_name(target), riscv_command_timeout_sec); + return ERROR_FAIL; + } + return result; + +} + +static int dmi_read(struct target *target, uint32_t *value, uint32_t address) +{ + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, false, true); +} + +static int dmi_read_exec(struct target *target, uint32_t *value, uint32_t address) +{ + return dmi_op(target, value, NULL, DMI_OP_READ, address, 0, true, true); +} + +static int dmi_write(struct target *target, uint32_t address, uint32_t value) +{ + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, false, true); +} + +static int dmi_write_exec(struct target *target, uint32_t address, + uint32_t value, bool ensure_success) +{ + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, address, value, true, ensure_success); +} + +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, + 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 " + "signal issue. Try reducing the JTAG clock speed.", + get_field(*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 " + "`riscv authdata_write` commands to authenticate.", *dmstatus); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int dmstatus_read(struct target *target, uint32_t *dmstatus, + bool 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", + info->dtmcs_idle, info->dmi_busy_delay, + info->ac_busy_delay); +} + +static uint32_t abstract_register_size(unsigned width) +{ + switch (width) { + case 32: + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 2); + case 64: + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 3); + case 128: + return set_field(0, AC_ACCESS_REGISTER_AARSIZE, 4); + default: + LOG_ERROR("Unsupported register width: %d", width); + return 0; + } +} + +static int wait_for_idle(struct target *target, uint32_t *abstractcs) +{ + RISCV013_INFO(info); + time_t start = time(NULL); + while (1) { + if (dmi_read(target, abstractcs, DM_ABSTRACTCS) != ERROR_OK) + return ERROR_FAIL; + + if (get_field(*abstractcs, DM_ABSTRACTCS_BUSY) == 0) + return ERROR_OK; + + if (time(NULL) - start > riscv_command_timeout_sec) { + info->cmderr = get_field(*abstractcs, DM_ABSTRACTCS_CMDERR); + + 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; + } + } +} + +static int dm013_select_target(struct target *target) +{ + 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) +{ + RISCV013_INFO(info); + 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)); + break; + default: + LOG_DEBUG("command=0x%x", command); + break; + } + } + + if (dmi_write_exec(target, DM_COMMAND, command, false) != 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); + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static riscv_reg_t read_abstract_arg(struct target *target, unsigned index, + unsigned size_bits) +{ + 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; + } + return value; +} + +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); + return ERROR_FAIL; + case 64: + dmi_write(target, DM_DATA0 + offset + 1, value >> 32); + /* falls through */ + case 32: + dmi_write(target, DM_DATA0 + offset, value); + } + return ERROR_OK; +} + +/** + * @par size in bits + */ +static uint32_t access_register_command(struct target *target, uint32_t number, + unsigned size, uint32_t flags) +{ + uint32_t command = set_field(0, DM_COMMAND_CMDTYPE, 0); + switch (size) { + case 32: + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 2); + break; + case 64: + command = set_field(command, AC_ACCESS_REGISTER_AARSIZE, 3); + break; + default: + LOG_ERROR("[%s] %d-bit register %s not supported.", + target_name(target), size, gdb_regno_name(number)); + assert(0); + } + + if (number <= GDB_REGNO_XPR31) { + command = set_field(command, AC_ACCESS_REGISTER_REGNO, + 0x1000 + number - GDB_REGNO_ZERO); + } else if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + command = set_field(command, AC_ACCESS_REGISTER_REGNO, + 0x1020 + number - GDB_REGNO_FPR0); + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + command = set_field(command, AC_ACCESS_REGISTER_REGNO, + number - GDB_REGNO_CSR0); + } else if (number >= GDB_REGNO_COUNT) { + /* Custom register. */ + assert(target->reg_cache->reg_list[number].arch_info); + riscv_reg_info_t *reg_info = target->reg_cache->reg_list[number].arch_info; + assert(reg_info); + command = set_field(command, AC_ACCESS_REGISTER_REGNO, + 0xc000 + reg_info->custom_number); + } else { + assert(0); + } + + command |= flags; + + return command; +} + +static int register_read_abstract(struct target *target, uint64_t *value, + uint32_t number, unsigned size) +{ + RISCV013_INFO(info); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + !info->abstract_read_fpr_supported) + return ERROR_FAIL; + if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && + !info->abstract_read_csr_supported) + return ERROR_FAIL; + /* The spec doesn't define abstract register numbers for vector registers. */ + if (number >= GDB_REGNO_V0 && number <= GDB_REGNO_V31) + return ERROR_FAIL; + + uint32_t command = access_register_command(target, number, size, + AC_ACCESS_REGISTER_TRANSFER); + + int result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + if (info->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."); + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + info->abstract_read_csr_supported = false; + LOG_INFO("Disabling abstract command reads from CSRs."); + } + } + return result; + } + + if (value) + *value = read_abstract_arg(target, 0, size); + + return ERROR_OK; +} + +static int register_write_abstract(struct target *target, uint32_t number, + uint64_t value, unsigned size) +{ + RISCV013_INFO(info); + + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31 && + !info->abstract_write_fpr_supported) + return ERROR_FAIL; + if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095 && + !info->abstract_write_csr_supported) + return ERROR_FAIL; + + uint32_t command = access_register_command(target, number, size, + AC_ACCESS_REGISTER_TRANSFER | + AC_ACCESS_REGISTER_WRITE); + + if (write_abstract_arg(target, 0, value, size) != ERROR_OK) + return ERROR_FAIL; + + int result = execute_abstract_command(target, command); + if (result != ERROR_OK) { + if (info->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."); + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + info->abstract_write_csr_supported = false; + LOG_INFO("Disabling abstract command writes to CSRs."); + } + } + return result; + } + + return ERROR_OK; +} + +/* + * Sets the AAMSIZE field of a memory access abstract command based on + * the width (bits). + */ +static uint32_t abstract_memory_size(unsigned width) +{ + switch (width) { + case 8: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 0); + case 16: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 1); + case 32: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 2); + case 64: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 3); + case 128: + return set_field(0, AC_ACCESS_MEMORY_AAMSIZE, 4); + default: + LOG_ERROR("Unsupported memory width: %d", width); + return 0; + } +} + +/* + * Creates a memory access abstract command. + */ +static uint32_t access_memory_command(struct target *target, bool virtual, + unsigned width, bool postincrement, bool 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); + + return command; +} + +static int examine_progbuf(struct target *target) +{ + riscv013_info_t *info = get_info(target); + + if (info->progbuf_writable != YNM_MAYBE) + return ERROR_OK; + + /* Figure out if progbuf is writable. */ + + if (info->progbufsize < 1) { + info->progbuf_writable = YNM_NO; + LOG_INFO("No program buffer present."); + return ERROR_OK; + } + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + struct riscv_program program; + riscv_program_init(&program, target); + riscv_program_insert(&program, auipc(S0)); + if (riscv_program_exec(&program, target) != ERROR_OK) + return ERROR_FAIL; + + if (register_read_direct(target, &info->progbuf_address, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + riscv_program_init(&program, target); + riscv_program_insert(&program, sw(S0, S0, 0)); + int result = riscv_program_exec(&program, target); + + if (result != ERROR_OK) { + /* This program might have failed if the program buffer is not + * writable. */ + info->progbuf_writable = YNM_NO; + return ERROR_OK; + } + + uint32_t written; + if (dmi_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, + info->progbuf_address); + info->progbuf_writable = YNM_YES; + + } else { + LOG_INFO("progbuf is not writeable at 0x%" PRIx64, + info->progbuf_address); + info->progbuf_writable = YNM_NO; + } + + return ERROR_OK; +} + +static int is_fpu_reg(uint32_t gdb_regno) +{ + return (gdb_regno >= GDB_REGNO_FPR0 && gdb_regno <= GDB_REGNO_FPR31) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FFLAGS) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FRM) || + (gdb_regno == GDB_REGNO_CSR0 + CSR_FCSR); +} + +static int is_vector_reg(uint32_t 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_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) +{ + if (is_fpu_reg(regno) || is_vector_reg(regno)) { + if (register_read_direct(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; + } + return ERROR_OK; +} + +static int cleanup_after_register_access(struct target *target, + uint64_t mstatus, int 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; +} + +typedef enum { + SPACE_DM_DATA, + SPACE_DMI_PROGBUF, + SPACE_DMI_RAM +} memory_space_t; + +typedef struct { + /* How can the debugger access this memory? */ + memory_space_t memory_space; + /* Memory address to access the scratch memory from the hart. */ + riscv_addr_t hart_address; + /* Memory address to access the scratch memory from the debugger. */ + riscv_addr_t debug_address; + struct working_area *area; +} scratch_mem_t; + +/** + * Find some scratch memory to be used with the given program. + */ +static int scratch_reserve(struct target *target, + scratch_mem_t *scratch, + struct riscv_program *program, + unsigned size_bytes) +{ + riscv_addr_t alignment = 1; + while (alignment < size_bytes) + alignment *= 2; + + scratch->area = NULL; + + riscv013_info_t *info = get_info(target); + + /* Option 1: See if data# registers can be used as the scratch memory */ + if (info->dataaccess == 1) { + /* Sign extend dataaddr. */ + scratch->hart_address = info->dataaddr; + if (info->dataaddr & (1<<11)) + scratch->hart_address |= 0xfffffffffffff000ULL; + /* Align. */ + scratch->hart_address = (scratch->hart_address + alignment - 1) & ~(alignment - 1); + + if ((size_bytes + scratch->hart_address - info->dataaddr + 3) / 4 >= + info->datasize) { + scratch->memory_space = SPACE_DM_DATA; + scratch->debug_address = (scratch->hart_address - info->dataaddr) / 4; + return ERROR_OK; + } + } + + /* Option 2: See if progbuf can be used as the scratch memory */ + if (examine_progbuf(target) != ERROR_OK) + return ERROR_FAIL; + + /* Allow for ebreak at the end of the program. */ + unsigned program_size = (program->instruction_count + 1) * 4; + scratch->hart_address = (info->progbuf_address + program_size + alignment - 1) & + ~(alignment - 1); + if ((info->progbuf_writable == YNM_YES) && + ((size_bytes + scratch->hart_address - info->progbuf_address + 3) / 4 >= + info->progbufsize)) { + scratch->memory_space = SPACE_DMI_PROGBUF; + scratch->debug_address = (scratch->hart_address - info->progbuf_address) / 4; + return ERROR_OK; + } + + /* Option 3: User-configured memory area as scratch RAM */ + if (target_alloc_working_area(target, size_bytes + alignment - 1, + &scratch->area) == ERROR_OK) { + scratch->hart_address = (scratch->area->address + alignment - 1) & + ~(alignment - 1); + scratch->memory_space = SPACE_DMI_RAM; + scratch->debug_address = scratch->hart_address; + return ERROR_OK; + } + + LOG_ERROR("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; +} + +static int scratch_release(struct target *target, + scratch_mem_t *scratch) +{ + return target_free_working_area(target, scratch->area); +} + +static int scratch_read64(struct target *target, scratch_mem_t *scratch, + uint64_t *value) +{ + uint32_t v; + switch (scratch->memory_space) { + case SPACE_DM_DATA: + if (dmi_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) + 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) + return ERROR_FAIL; + *value = v; + if (dmi_read(target, &v, DM_PROGBUF1 + scratch->debug_address) != ERROR_OK) + return ERROR_FAIL; + *value |= ((uint64_t) v) << 32; + break; + case SPACE_DMI_RAM: + { + uint8_t buffer[8] = {0}; + if (read_memory(target, scratch->debug_address, 4, 2, buffer, 4) != ERROR_OK) + return ERROR_FAIL; + *value = buffer[0] | + (((uint64_t) buffer[1]) << 8) | + (((uint64_t) buffer[2]) << 16) | + (((uint64_t) buffer[3]) << 24) | + (((uint64_t) buffer[4]) << 32) | + (((uint64_t) buffer[5]) << 40) | + (((uint64_t) buffer[6]) << 48) | + (((uint64_t) buffer[7]) << 56); + } + break; + } + return ERROR_OK; +} + +static int scratch_write64(struct target *target, scratch_mem_t *scratch, + uint64_t value) +{ + 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); + break; + case SPACE_DMI_PROGBUF: + dmi_write(target, DM_PROGBUF0 + scratch->debug_address, value); + dmi_write(target, DM_PROGBUF1 + scratch->debug_address, value >> 32); + riscv013_invalidate_cached_debug_buffer(target); + break; + case SPACE_DMI_RAM: + { + uint8_t buffer[8] = { + value, + value >> 8, + value >> 16, + value >> 24, + value >> 32, + value >> 40, + value >> 48, + value >> 56 + }; + if (write_memory(target, scratch->debug_address, 4, 2, buffer) != ERROR_OK) + return ERROR_FAIL; + } + break; + } + return ERROR_OK; +} + +/** Return register size in bits. */ +static unsigned register_size(struct target *target, unsigned number) +{ + /* If reg_cache hasn't been initialized yet, make a guess. We need this for + * when this function is called during examine(). */ + if (target->reg_cache) + return target->reg_cache->reg_list[number].size; + else + return riscv_xlen(target); +} + +static bool has_sufficient_progbuf(struct target *target, unsigned size) +{ + RISCV013_INFO(info); + RISCV_INFO(r); + + return info->progbufsize + r->impebreak >= size; +} + +/** + * Immediately write the new value to the requested register. This mechanism + * bypasses any caches. + */ + int register_write_direct(struct target *target, unsigned number, + uint64_t value) +{ + LOG_TARGET_DEBUG(target, "%s <- 0x%" PRIx64, gdb_regno_name(number), value); + + 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; + + struct riscv_program program; + riscv_program_init(&program, target); + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + uint64_t mstatus; + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; + + 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; + if (riscv_program_insert(&program, fld(number - GDB_REGNO_FPR0, S0, 0)) + != ERROR_OK) + return ERROR_FAIL; + + 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_write64(target, &scratch, value) != ERROR_OK) { + scratch_release(target, &scratch); + return ERROR_FAIL; + } + + } else { + if (number >= GDB_REGNO_FPR0 && number <= GDB_REGNO_FPR31) { + if (riscv_supports_extension(target, 'D')) { + if (riscv_program_insert(&program, + fmv_d_x(number - GDB_REGNO_FPR0, S0)) != ERROR_OK) + return ERROR_FAIL; + } else { + if (riscv_program_insert(&program, + fmv_w_x(number - GDB_REGNO_FPR0, S0)) != ERROR_OK) + return ERROR_FAIL; + } + } else if (number == GDB_REGNO_VTYPE) { + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + 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; + } 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." */ + if (riscv_save_register(target, GDB_REGNO_S1) != ERROR_OK) + return ERROR_FAIL; + 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; + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + if (riscv_program_csrw(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; + } else { + LOG_ERROR("Unsupported register (enum gdb_regno)(%d)", number); + return ERROR_FAIL; + } + if (register_write_direct(target, GDB_REGNO_S0, value) != ERROR_OK) + return ERROR_FAIL; + } + + 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 (use_scratch) + scratch_release(target, &scratch); + + if (cleanup_after_register_access(target, mstatus, number) != ERROR_OK) + return ERROR_FAIL; + + return exec_out; +} + +/** Actually read registers from the target right now. */ +static int register_read_direct(struct target *target, uint64_t *value, uint32_t number) +{ + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + int result = register_read_abstract(target, value, number, + register_size(target, number)); + + if (result != ERROR_OK && + has_sufficient_progbuf(target, 2) && + number > GDB_REGNO_XPR31) { + struct riscv_program program; + riscv_program_init(&program, target); + + scratch_mem_t scratch; + bool use_scratch = false; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + /* Write program to move data into s0. */ + + uint64_t mstatus; + if (prep_for_register_access(target, &mstatus, number) != ERROR_OK) + return ERROR_FAIL; + + 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. */ + if (riscv_program_insert(&program, + fsd(number - GDB_REGNO_FPR0, S0, 0)) != ERROR_OK) + return ERROR_FAIL; + if (scratch_reserve(target, &scratch, &program, 8) != ERROR_OK) + return ERROR_FAIL; + use_scratch = true; + + 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')) { + if (riscv_program_insert(&program, fmv_x_d(S0, number - GDB_REGNO_FPR0)) != + ERROR_OK) + return ERROR_FAIL; + } else { + if (riscv_program_insert(&program, fmv_x_w(S0, number - GDB_REGNO_FPR0)) != + ERROR_OK) + return ERROR_FAIL; + } + } else if (number >= GDB_REGNO_CSR0 && number <= GDB_REGNO_CSR4095) { + if (riscv_program_csrr(&program, S0, number) != ERROR_OK) + return ERROR_FAIL; + } else { + LOG_ERROR("Unsupported register: %s", gdb_regno_name(number)); + return ERROR_FAIL; + } + + /* Execute program. */ + result = riscv_program_exec(&program, target); + /* Don't message on error. Probably the register doesn't exist. */ + + 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 (result == ERROR_OK) + LOG_TARGET_DEBUG(target, "%s = 0x%" PRIx64, gdb_regno_name(number), *value); + + return result; +} + +static int wait_for_authbusy(struct target *target, uint32_t *dmstatus) +{ + time_t start = time(NULL); + while (1) { + uint32_t value; + if (dmstatus_read(target, &value, false) != ERROR_OK) + return ERROR_FAIL; + if (dmstatus) + *dmstatus = value; + 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). " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec, + value); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +/*** OpenOCD target functions. ***/ + +static void deinit_target(struct target *target) +{ + LOG_DEBUG("riscv_deinit_target()"); + riscv_info_t *info = (riscv_info_t *) target->arch_info; + free(info->version_specific); + /* TODO: free register arch_info */ + info->version_specific = NULL; +} + +typedef enum { + HALTGROUP, + RESUMEGROUP +} grouptype_t; +static int set_group(struct target *target, bool *supported, unsigned group, grouptype_t grouptype) +{ + 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 == HALTGROUP) ? 0 : 1); + if (dmi_write(target, DM_DMCS2, write_val) != ERROR_OK) + return ERROR_FAIL; + uint32_t read_val; + if (dmi_read(target, &read_val, DM_DMCS2) != ERROR_OK) + return ERROR_FAIL; + *supported = get_field(read_val, DM_DMCS2_GROUP) == group; + return ERROR_OK; +} + +static int discover_vlenb(struct target *target) +{ + RISCV_INFO(r); + riscv_reg_t vlenb; + + if (register_read_direct(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; + return ERROR_OK; + } + r->vlenb = vlenb; + + LOG_INFO("Vector support with vlenb=%d", r->vlenb); + + return ERROR_OK; +} + +static int examine(struct target *target) +{ + /* Don't need to select dbus, since the first thing we do is read dtmcontrol. */ + + 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) + 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; + + /* The DM gets reset, so forget any cached progbuf entries. */ + riscv013_invalidate_cached_debug_buffer(target); + } + + // 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; + + if (!get_field(dmcontrol, DM_DMCONTROL_DMACTIVE)) { + LOG_ERROR("Debug Module did not become active. dmcontrol=0x%x", + dmcontrol); + return ERROR_FAIL; + } + + dm->hasel_supported = get_field(dmcontrol, DM_DMCONTROL_HASEL); + + uint32_t dmstatus; + if (dmstatus_read(target, &dmstatus, false) != ERROR_OK) + return ERROR_FAIL; + LOG_DEBUG("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) + return ERROR_FAIL; + + info->datasize = get_field(hartinfo, DM_HARTINFO_DATASIZE); + info->dataaccess = get_field(hartinfo, DM_HARTINFO_DATAACCESS); + 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. " + "(dmstatus=0x%x). Use `riscv authdata_read` and " + "`riscv authdata_write` commands to authenticate.", dmstatus); + return ERROR_FAIL; + } + + if (dmi_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) + return ERROR_FAIL; + info->datacount = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT); + info->progbufsize = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE); + + LOG_INFO("[%s] datacount=%d progbufsize=%d", target_name(target), + 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 " + "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 " + "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) { + if (dm013_select_hart(target, i) != 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_dmcontrol_hartsel(DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_ACKHAVERESET, i)); + } + + LOG_DEBUG("Detected %d harts.", dm->hart_count); + } + + 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 (dm013_select_hart(target, info->index) != ERROR_OK) + return ERROR_FAIL; + + bool halted = riscv_is_halted(target); + if (!halted) { + r->prepped = true; + if (riscv013_halt_go(target) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Fatal: Hart %d failed to halt during examine()", + info->index); + return ERROR_FAIL; + } + } + + /* Without knowing anything else we can at least mess with the + * program buffer. */ + r->debug_buffer_size = info->progbufsize; + + int result = register_read_abstract(target, NULL, GDB_REGNO_S0, 64); + + if (result == ERROR_OK) + r->xlen = 64; + else + r->xlen = 32; + + /* 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_direct(target, &s0, GDB_REGNO_S0) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Fatal: Failed to read s0."); + return ERROR_FAIL; + } + if (register_read_direct(target, &s1, GDB_REGNO_S1) != ERROR_OK) { + LOG_TARGET_ERROR(target, "Fatal: Failed to read s1."); + 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; + } + + if (riscv_supports_extension(target, 'V')) { + if (discover_vlenb(target) != ERROR_OK) + return ERROR_FAIL; + } + + /* Now init registers based on what we discovered. */ + if (riscv_init_registers(target) != ERROR_OK) + return ERROR_FAIL; + + /* Display this as early as possible to help people who are using + * really slow simulators. */ + LOG_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; + } + + if (!halted) + riscv013_step_or_resume_current_hart(target, false); + + if (target->smp) { + bool haltgroup_supported; + if (set_group(target, &haltgroup_supported, target->smp, HALTGROUP) != ERROR_OK) + return ERROR_FAIL; + if (haltgroup_supported) + LOG_INFO("Core %d made part of halt group %d.", target->coreid, + target->smp); + else + LOG_INFO("Core %d could not be made part of halt group %d.", + target->coreid, 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_TARGET_INFO(target, "Examined RISC-V core; found %d harts", + riscv_count_harts(target)); + 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."); + return ERROR_FAIL; + } + + if (wait_for_authbusy(target, NULL) != ERROR_OK) + return ERROR_FAIL; + + return dmi_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."); + return ERROR_FAIL; + } + + uint32_t before, after; + if (wait_for_authbusy(target, &before) != ERROR_OK) + return ERROR_FAIL; + + dmi_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"); + 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 (target_examine_one(entry->target) != ERROR_OK) + result = ERROR_FAIL; + } + return result; + } + + 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) +{ + RISCV013_INFO(info); + RISCV_INFO(r); + + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; + + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (has_sufficient_progbuf(target, 3)) + return riscv_xlen(target); + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + if (get_field(info->sbcs, DM_SBCS_SBACCESS128)) + return 128; + if (get_field(info->sbcs, DM_SBCS_SBACCESS64)) + return 64; + if (get_field(info->sbcs, DM_SBCS_SBACCESS32)) + return 32; + if (get_field(info->sbcs, DM_SBCS_SBACCESS16)) + return 16; + if (get_field(info->sbcs, DM_SBCS_SBACCESS8)) + return 8; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + /* TODO: Once there is a spec for discovering abstract commands, we can + * take those into account as well. For now we assume abstract commands + * support XLEN-wide accesses. */ + return riscv_xlen(target); + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + } + LOG_ERROR("Unable to determine supported data bits on this target. Assuming 32 bits."); + return 32; +} + +COMMAND_HELPER(wch_riscv013_print_info, struct target *target) +{ + RISCV013_INFO(info); + + /* Abstract description. */ + riscv_print_info_line(CMD, "target", "memory.read_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8)); + riscv_print_info_line(CMD, "target", "memory.write_while_running8", get_field(info->sbcs, DM_SBCS_SBACCESS8)); + riscv_print_info_line(CMD, "target", "memory.read_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16)); + riscv_print_info_line(CMD, "target", "memory.write_while_running16", get_field(info->sbcs, DM_SBCS_SBACCESS16)); + riscv_print_info_line(CMD, "target", "memory.read_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32)); + riscv_print_info_line(CMD, "target", "memory.write_while_running32", get_field(info->sbcs, DM_SBCS_SBACCESS32)); + riscv_print_info_line(CMD, "target", "memory.read_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64)); + riscv_print_info_line(CMD, "target", "memory.write_while_running64", get_field(info->sbcs, DM_SBCS_SBACCESS64)); + riscv_print_info_line(CMD, "target", "memory.read_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128)); + riscv_print_info_line(CMD, "target", "memory.write_while_running128", get_field(info->sbcs, DM_SBCS_SBACCESS128)); + + uint32_t dmstatus; + if (dmstatus_read(target, &dmstatus, false) == ERROR_OK) + riscv_print_info_line(CMD, "dm", "authenticated", get_field(dmstatus, DM_DMSTATUS_AUTHENTICATED)); + + return 0; +} + +static int prep_for_vector_access(struct target *target, uint64_t *vtype, + uint64_t *vl, unsigned *debug_vl) +{ + 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)); + return ERROR_FAIL; + } + + /* Save vtype and vl. */ + if (register_read_direct(target, vtype, GDB_REGNO_VTYPE) != ERROR_OK) + return ERROR_FAIL; + if (register_read_direct(target, vl, GDB_REGNO_VL) != ERROR_OK) + return ERROR_FAIL; + + if (register_write_direct(target, GDB_REGNO_VTYPE, encoded_vsew << 3) != 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) + return ERROR_FAIL; + + return ERROR_OK; +} + +static int cleanup_after_vector_access(struct target *target, uint64_t vtype, + uint64_t vl) +{ + /* Restore vtype and vl. */ + if (register_write_direct(target, GDB_REGNO_VTYPE, vtype) != ERROR_OK) + return ERROR_FAIL; + if (register_write_direct(target, GDB_REGNO_VL, vl) != ERROR_OK) + return ERROR_FAIL; + return ERROR_OK; +} + +static int riscv013_get_register_buf(struct target *target, + uint8_t *value, int regno) +{ + assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + uint64_t mstatus; + if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) + return ERROR_FAIL; + + uint64_t vtype, vl; + unsigned debug_vl; + if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != 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)); + + int result = ERROR_OK; + for (unsigned i = 0; i < debug_vl; i++) { + /* 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 + * vector register we tried to read already. + * For other failures, we just return error because things are probably + * 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; + if (register_read_direct(target, &v, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + buf_set_u64(value, xlen * i, xlen, v); + } else { + 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; + + return result; +} + +static int riscv013_set_register_buf(struct target *target, + int regno, const uint8_t *value) +{ + assert(regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31); + + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + return ERROR_FAIL; + + uint64_t mstatus; + if (prep_for_register_access(target, &mstatus, regno) != ERROR_OK) + return ERROR_FAIL; + + uint64_t vtype, vl; + unsigned debug_vl; + if (prep_for_vector_access(target, &vtype, &vl, &debug_vl) != 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, vslide1down_vx(vnum, vnum, S0, true)); + int result = ERROR_OK; + for (unsigned i = 0; i < debug_vl; i++) { + if (register_write_direct(target, GDB_REGNO_S0, + buf_get_u64(value, xlen * i, xlen)) != 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; + + return result; +} + +static uint32_t sb_sbaccess(unsigned int size_bytes) +{ + switch (size_bytes) { + case 1: + return set_field(0, DM_SBCS_SBACCESS, 0); + case 2: + return set_field(0, DM_SBCS_SBACCESS, 1); + case 4: + return set_field(0, DM_SBCS_SBACCESS, 2); + case 8: + return set_field(0, DM_SBCS_SBACCESS, 3); + case 16: + return set_field(0, DM_SBCS_SBACCESS, 4); + } + assert(0); + return 0; +} + +static int sb_write_address(struct target *target, target_addr_t address, + bool ensure_success) +{ + RISCV013_INFO(info); + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + /* There currently is no support for >64-bit addresses in OpenOCD. */ + if (sbasize > 96) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS3, 0, false, false); + if (sbasize > 64) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS2, 0, false, false); + if (sbasize > 32) + dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS1, address >> 32, false, false); + return dmi_op(target, NULL, NULL, DMI_OP_WRITE, DM_SBADDRESS0, address, + false, ensure_success); +} + +static int batch_run(const struct target *target, struct riscv_batch *batch) +{ + RISCV013_INFO(info); + RISCV_INFO(r); + if (r->reset_delays_wait >= 0) { + r->reset_delays_wait -= batch->used_scans; + if (r->reset_delays_wait <= 0) { + batch->idle_count = 0; + info->dmi_busy_delay = 0; + info->ac_busy_delay = 0; + } + } + return riscv_batch_run(batch); +} + +static int sba_supports_access(struct target *target, unsigned int size_bytes) +{ + RISCV013_INFO(info); + switch (size_bytes) { + case 1: + return get_field(info->sbcs, DM_SBCS_SBACCESS8); + case 2: + return get_field(info->sbcs, DM_SBCS_SBACCESS16); + case 4: + return get_field(info->sbcs, DM_SBCS_SBACCESS32); + case 8: + return get_field(info->sbcs, DM_SBCS_SBACCESS64); + case 16: + return get_field(info->sbcs, DM_SBCS_SBACCESS128); + default: + return 0; + } +} + +static int sample_memory_bus_v1(struct target *target, + struct riscv_sample_buf *buf, + const riscv_sample_config_t *config, + int64_t until_ms) +{ + RISCV013_INFO(info); + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + if (sbasize > 64) { + LOG_ERROR("Memory sampling is only implemented for sbasize <= 64."); + return ERROR_NOT_IMPLEMENTED; + } + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) != 1) { + LOG_ERROR("Memory sampling is only implemented for SBA version 1."); + return ERROR_NOT_IMPLEMENTED; + } + + uint32_t sbcs = 0; + uint32_t sbcs_valid = false; + + uint32_t sbaddress0 = 0; + bool sbaddress0_valid = false; + uint32_t sbaddress1 = 0; + bool sbaddress1_valid = false; + + /* How often to read each value in a batch. */ + const unsigned int repeat = 5; + + unsigned int enabled_count = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) + enabled_count++; + } + + while (timeval_ms() < until_ms) { + /* + * batch_run() adds to the batch, so we can't simply reuse the same + * batch over and over. So we create a new one every time through the + * loop. + */ + struct riscv_batch *batch = riscv_batch_alloc( + target, 1 + enabled_count * 5 * repeat, + info->dmi_busy_delay + info->bus_master_read_delay); + if (!batch) + return ERROR_FAIL; + + unsigned int result_bytes = 0; + for (unsigned int n = 0; n < repeat; n++) { + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) { + if (!sba_supports_access(target, config->bucket[i].size_bytes)) { + LOG_ERROR("Hardware does not support SBA access for %d-byte memory sampling.", + config->bucket[i].size_bytes); + return ERROR_NOT_IMPLEMENTED; + } + + uint32_t sbcs_write = DM_SBCS_SBREADONADDR; + if (enabled_count == 1) + sbcs_write |= DM_SBCS_SBREADONDATA; + sbcs_write |= sb_sbaccess(config->bucket[i].size_bytes); + if (!sbcs_valid || sbcs_write != sbcs) { + riscv_batch_add_dmi_write(batch, DM_SBCS, sbcs_write); + sbcs = sbcs_write; + sbcs_valid = true; + } + + if (sbasize > 32 && + (!sbaddress1_valid || + sbaddress1 != config->bucket[i].address >> 32)) { + sbaddress1 = config->bucket[i].address >> 32; + riscv_batch_add_dmi_write(batch, DM_SBADDRESS1, sbaddress1); + sbaddress1_valid = true; + } + if (!sbaddress0_valid || + sbaddress0 != (config->bucket[i].address & 0xffffffff)) { + sbaddress0 = config->bucket[i].address; + riscv_batch_add_dmi_write(batch, DM_SBADDRESS0, sbaddress0); + sbaddress0_valid = true; + } + if (config->bucket[i].size_bytes > 4) + riscv_batch_add_dmi_read(batch, DM_SBDATA1); + riscv_batch_add_dmi_read(batch, DM_SBDATA0); + result_bytes += 1 + config->bucket[i].size_bytes; + } + } + } + + if (buf->used + result_bytes >= buf->size) { + riscv_batch_free(batch); + break; + } + + size_t sbcs_key = riscv_batch_add_dmi_read(batch, DM_SBCS); + + int result = batch_run(target, batch); + if (result != ERROR_OK) + return result; + + uint32_t sbcs_read = riscv_batch_get_dmi_read_data(batch, sbcs_key); + if (get_field(sbcs_read, DM_SBCS_SBBUSYERROR)) { + /* Discard this batch (too much hassle to try to recover partial + * data) and try again with a larger delay. */ + info->bus_master_read_delay += info->bus_master_read_delay / 10 + 1; + dmi_write(target, DM_SBCS, sbcs_read | DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + riscv_batch_free(batch); + continue; + } + if (get_field(sbcs_read, DM_SBCS_SBERROR)) { + /* The memory we're sampling was unreadable, somehow. Give up. */ + dmi_write(target, DM_SBCS, DM_SBCS_SBBUSYERROR | DM_SBCS_SBERROR); + riscv_batch_free(batch); + return ERROR_FAIL; + } + + unsigned int read = 0; + for (unsigned int n = 0; n < repeat; n++) { + for (unsigned int i = 0; i < ARRAY_SIZE(config->bucket); i++) { + if (config->bucket[i].enabled) { + assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); + uint64_t value = 0; + if (config->bucket[i].size_bytes > 4) + value = ((uint64_t)riscv_batch_get_dmi_read_data(batch, read++)) << 32; + value |= riscv_batch_get_dmi_read_data(batch, read++); + + buf->buf[buf->used] = i; + buf_set_u64(buf->buf + buf->used + 1, 0, config->bucket[i].size_bytes * 8, value); + buf->used += 1 + config->bucket[i].size_bytes; + } + } + } + + riscv_batch_free(batch); + } + + return ERROR_OK; +} + +static int sample_memory(struct target *target, + struct riscv_sample_buf *buf, + riscv_sample_config_t *config, + int64_t until_ms) +{ + if (!config->enabled) + return ERROR_OK; + + return sample_memory_bus_v1(target, buf, config, until_ms); +} + +static int init_target(struct command_context *cmd_ctx, + struct target *target) +{ + LOG_DEBUG("init"); + RISCV_INFO(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_target = &dm013_select_target; + generic_info->is_halted = &riscv013_is_halted; + 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->invalidate_cached_debug_buffer = &riscv013_invalidate_cached_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->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->read_memory = read_memory; + generic_info->test_sba_config_reg = &riscv013_test_sba_config_reg; + generic_info->hart_count = &riscv013_hart_count; + generic_info->data_bits = &riscv013_data_bits; + generic_info->print_info = &wch_riscv013_print_info; + if (!generic_info->version_specific) { + generic_info->version_specific = calloc(1, sizeof(riscv013_info_t)); + if (!generic_info->version_specific) + return ERROR_FAIL; + } + + riscv013_info_t *info = get_info(target); + + info->progbufsize = -1; + + info->dmi_busy_delay = 0; + info->bus_master_read_delay = 0; + info->bus_master_write_delay = 0; + info->ac_busy_delay = 0; + + /* Assume all these abstract commands are supported until we learn + * otherwise. + * TODO: The spec allows eg. one CSR to be able to be accessed abstractly + * while another one isn't. We don't track that this closely here, but in + * the future we probably should. */ + info->abstract_read_csr_supported = true; + info->abstract_write_csr_supported = true; + info->abstract_read_fpr_supported = true; + info->abstract_write_fpr_supported = true; + + info->has_aampostincrement = YNM_MAYBE; + + return ERROR_OK; +} + +static int assert_reset(struct target *target) +{ + RISCV013_INFO(info); + + target->reset_halt=1; + + 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 = control_base; + + control = set_dmcontrol_hartsel(control_base, info->index); + 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_dmcontrol_hartsel(control_base, 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); + } + + 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. */ + riscv013_invalidate_cached_debug_buffer(target); + + return ERROR_OK; +} + +static int deassert_reset(struct target *target) +{ + RISCV013_INFO(info); + target->reset_halt=1; + + /* Clear the reset, but make sure haltreq is still set */ + uint32_t control = 0; + control = set_field(control, DM_DMCONTROL_HALTREQ, target->reset_halt ? 1 : 0); + control = set_field(control, DM_DMCONTROL_DMACTIVE, 1); + dmi_write(target, DM_DMCONTROL, set_dmcontrol_hartsel(control, info->index)); + + uint32_t dmstatus; + int dmi_busy_delay = info->dmi_busy_delay; + time_t start = time(NULL); + + for (unsigned int i = 0; i < riscv_count_harts(target); ++i) { + unsigned int index = i; + if (target->rtos) { + if (index != info->index) + continue; + dmi_write(target, DM_DMCONTROL, + set_dmcontrol_hartsel(control, index)); + } else { + index = info->index; + } + + LOG_DEBUG("Waiting for hart %d to come out of reset.", index); + while (1) { + int result = dmstatus_read_timeout(target, &dmstatus, true, + riscv_reset_timeout_sec); + 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); + break; + } + 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; + } + + if (get_field(dmstatus, DM_DMSTATUS_ALLHAVERESET)) { + /* Ack reset. */ + dmi_write(target, DM_DMCONTROL, + set_dmcontrol_hartsel(control, index) | + DM_DMCONTROL_ACKHAVERESET); + } + + if (!target->rtos) + break; + } + info->dmi_busy_delay = dmi_busy_delay; + uint64_t tmpDcsr; + int res = register_read_direct(target, &tmpDcsr, GDB_REGNO_DCSR); + if(res != ERROR_OK){ + LOG_DEBUG("[wch] dcsr read fail!"); + } + else{ + LOG_DEBUG("[wch] read dcsr value is 0x%x", tmpDcsr); + //enable ebreak in m&u mode + tmpDcsr = set_field(tmpDcsr, CSR_DCSR_EBREAKM, 1); + tmpDcsr = set_field(tmpDcsr, CSR_DCSR_EBREAKU, 1); + res = register_write_direct(target, GDB_REGNO_DCSR, tmpDcsr); + register_read_direct(target, &tmpDcsr, GDB_REGNO_DCSR); + if(res == ERROR_OK){ + } + else{ + LOG_DEBUG("[wch] dcsr write fail"); + assert(false); + } + } + + return ERROR_OK; +} + +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; + 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_TARGET_DEBUG(target, "Unable to execute pre-fence"); + + return ERROR_OK; +} + +static void log_memory_access(target_addr_t address, uint64_t value, + unsigned size_bytes, bool 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); + switch (size_bytes) { + case 1: + value &= 0xff; + break; + case 2: + value &= 0xffff; + break; + case 4: + value &= 0xffffffffUL; + break; + case 8: + break; + default: + assert(false); + } + LOG_DEBUG(fmt, value); +} + +/* 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; + 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); + 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); + } + return ERROR_OK; +} + +static target_addr_t sb_read_address(struct target *target) +{ + RISCV013_INFO(info); + unsigned sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + target_addr_t address = 0; + uint32_t v; + if (sbasize > 32) { + dmi_read(target, &v, DM_SBADDRESS1); + address |= v; + address <<= 32; + } + dmi_read(target, &v, DM_SBADDRESS0); + address |= v; + return address; +} + +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) + 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). " + "Increase the timeout with riscv set_command_timeout_sec.", + riscv_command_timeout_sec, *sbcs); + return ERROR_FAIL; + } + } +} + +static int modify_privilege(struct target *target, uint64_t *mstatus, uint64_t *mstatus_old) +{ + if (riscv_enable_virtual && has_sufficient_progbuf(target, 5)) { + /* Read DCSR */ + uint64_t dcsr; + if (register_read_direct(target, &dcsr, GDB_REGNO_DCSR) != ERROR_OK) + return ERROR_FAIL; + + /* Read and save MSTATUS */ + 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, CSR_DCSR_PRV) < 3) { + /* MPP = PRIV */ + *mstatus = set_field(*mstatus, MSTATUS_MPP, get_field(dcsr, CSR_DCSR_PRV)); + + /* MPRV = 1 */ + *mstatus = set_field(*mstatus, MSTATUS_MPRV, 1); + + /* Write MSTATUS */ + if (*mstatus != *mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, *mstatus) != ERROR_OK) + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +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"); + return ERROR_NOT_IMPLEMENTED; + } + + LOG_DEBUG("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; + riscv_addr_t fin_addr = address + (count * size); + uint32_t access = 0; + + const int DM_SBCS_SBSINGLEREAD_OFFSET = 20; + const uint32_t DM_SBCS_SBSINGLEREAD = (0x1U << DM_SBCS_SBSINGLEREAD_OFFSET); + + const int DM_SBCS_SBAUTOREAD_OFFSET = 15; + const uint32_t DM_SBCS_SBAUTOREAD = (0x1U << DM_SBCS_SBAUTOREAD_OFFSET); + + /* 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) + return ERROR_FAIL; + dmi_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); + /* 3) read */ + uint32_t value; + if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + return ERROR_FAIL; + LOG_DEBUG("\r\nread_memory: sab: value: 0x%08x", value); + buf_set_u32(t_buffer, 0, 8 * size, value); + t_buffer += size; + cur_addr += size; + } + return ERROR_OK; + } + + /* 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) + return ERROR_FAIL; + /* set current address */ + dmi_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); + + while (cur_addr < fin_addr) { + LOG_DEBUG("\r\nsab:autoincrement: \r\n size: %d\tcount:%d\taddress: 0x%08" + PRIx64, size, count, cur_addr); + /* read */ + uint32_t value; + if (dmi_read(target, &value, DM_SBDATA0) != ERROR_OK) + return ERROR_FAIL; + buf_set_u32(t_buffer, 0, 8 * size, value); + cur_addr += size; + t_buffer += size; + + /* 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) + return ERROR_FAIL; + buf_set_u32(t_buffer, 0, 8 * size, value); + } + } + + uint32_t sbcs; + if (dmi_read(target, &sbcs, DM_SBCS) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + + +static void log_mem_access_result(struct target *target, bool success, int method, bool read) +{ + RISCV_INFO(r); + bool warn = false; + char msg[60]; + + /* Compose the message */ + snprintf(msg, 60, "%s to %s memory via %s.", + success ? "Succeeded" : "Failed", + read ? "read" : "write", + (method == RISCV_MEM_ACCESS_PROGBUF) ? "program buffer" : + (method == RISCV_MEM_ACCESS_SYSBUS) ? "system bus" : "abstract access"); + + /* Determine the log message severity. Show warnings only once. */ + if (!success) { + if (method == RISCV_MEM_ACCESS_PROGBUF) { + warn = r->mem_access_progbuf_warn; + r->mem_access_progbuf_warn = false; + } + if (method == RISCV_MEM_ACCESS_SYSBUS) { + warn = r->mem_access_sysbus_warn; + r->mem_access_sysbus_warn = false; + } + if (method == RISCV_MEM_ACCESS_ABSTRACT) { + warn = r->mem_access_abstract_warn; + r->mem_access_abstract_warn = false; + } + } + + if (warn) + LOG_WARNING("%s", msg); + else + LOG_DEBUG("%s", msg); +} + +static bool mem_should_skip_progbuf(struct target *target, target_addr_t address, + uint32_t size, bool read, char **skip_reason) +{ + assert(skip_reason); + + if (!has_sufficient_progbuf(target, 3)) { + LOG_DEBUG("Skipping mem %s via progbuf - insufficient progbuf size.", + read ? "read" : "write"); + *skip_reason = "skipped (insufficient progbuf)"; + return true; + } + if (target->state != TARGET_HALTED) { + LOG_DEBUG("Skipping mem %s via progbuf - target not halted.", + read ? "read" : "write"); + *skip_reason = "skipped (target not halted)"; + return true; + } + if (riscv_xlen(target) < size * 8) { + LOG_DEBUG("Skipping mem %s via progbuf - XLEN (%d) is too short for %d-bit memory access.", + read ? "read" : "write", riscv_xlen(target), size * 8); + *skip_reason = "skipped (XLEN too short)"; + return true; + } + if (size > 8) { + LOG_DEBUG("Skipping mem %s via progbuf - unsupported size.", + read ? "read" : "write"); + *skip_reason = "skipped (unsupported size)"; + return true; + } + if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { + LOG_DEBUG("Skipping mem %s via progbuf - progbuf only supports %u-bit address.", + read ? "read" : "write", riscv_xlen(target)); + *skip_reason = "skipped (too large address)"; + return true; + } + + return false; +} + +static bool mem_should_skip_sysbus(struct target *target, target_addr_t address, + uint32_t size, uint32_t increment, bool read, char **skip_reason) +{ + assert(skip_reason); + + RISCV013_INFO(info); + if (!sba_supports_access(target, size)) { + LOG_DEBUG("Skipping mem %s via system bus - unsupported size.", + read ? "read" : "write"); + *skip_reason = "skipped (unsupported size)"; + return true; + } + unsigned int sbasize = get_field(info->sbcs, DM_SBCS_SBASIZE); + if ((sizeof(address) * 8 > sbasize) && (address >> sbasize)) { + LOG_DEBUG("Skipping mem %s via system bus - sba only supports %u-bit address.", + read ? "read" : "write", sbasize); + *skip_reason = "skipped (too large address)"; + return true; + } + if (read && increment != size && (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0 || increment != 0)) { + LOG_DEBUG("Skipping mem read via system bus - " + "sba reads only support size==increment or also size==0 for sba v1."); + *skip_reason = "skipped (unsupported increment)"; + return true; + } + + return false; +} + +static bool mem_should_skip_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t increment, bool read, char **skip_reason) +{ + assert(skip_reason); + + if (size > 8) { + /* TODO: Add 128b support if it's ever used. Involves modifying + read/write_abstract_arg() to work on two 64b values. */ + LOG_DEBUG("Skipping mem %s via abstract access - unsupported size: %d bits", + read ? "read" : "write", size * 8); + *skip_reason = "skipped (unsupported size)"; + return true; + } + if ((sizeof(address) * 8 > riscv_xlen(target)) && (address >> riscv_xlen(target))) { + LOG_DEBUG("Skipping mem %s via abstract access - abstract access only supports %u-bit address.", + read ? "read" : "write", riscv_xlen(target)); + *skip_reason = "skipped (too large address)"; + return true; + } + if (read && size != increment) { + LOG_ERROR("Skipping mem read via abstract access - " + "abstract command reads only support size==increment."); + *skip_reason = "skipped (unsupported increment)"; + return true; + } + + return false; +} + +/* + * Performs a memory read using memory access abstract commands. The read sizes + * supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 byte + * aamsize fields in the memory access abstract command. + */ +static int read_memory_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +{ + RISCV013_INFO(info); + + int result = ERROR_OK; + bool use_aampostincrement = info->has_aampostincrement != YNM_NO; + + LOG_DEBUG("reading %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + memset(buffer, 0, count * size); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + + /* Create the command (physical address, postincrement, read) */ + uint32_t command = access_memory_command(target, false, width, use_aampostincrement, false); + + /* Execute the reads */ + uint8_t *p = buffer; + bool updateaddr = true; + unsigned int width32 = (width < 32) ? 32 : width; + for (uint32_t c = 0; c < count; c++) { + /* Update the address if it is the first time or aampostincrement is not supported by the target. */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during read_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + + if (info->has_aampostincrement == YNM_MAYBE) { + if (result == ERROR_OK) { + /* Safety: double-check that the address was really auto-incremented */ + riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); + if (new_address == address + size) { + LOG_DEBUG("aampostincrement is supported on this target."); + info->has_aampostincrement = YNM_YES; + } else { + LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + info->has_aampostincrement = YNM_NO; + } + } else { + /* Try the same access but with postincrement disabled. */ + command = access_memory_command(target, false, width, false, false); + result = execute_abstract_command(target, command); + if (result == ERROR_OK) { + LOG_DEBUG("aampostincrement is not supported on this target."); + info->has_aampostincrement = YNM_NO; + } + } + } + + if (result != ERROR_OK) + return result; + + /* Copy arg0 to buffer (rounded width up to nearest 32) */ + riscv_reg_t value = read_abstract_arg(target, 0, width32); + buf_set_u64(p, 0, 8 * size, value); + + if (info->has_aampostincrement == YNM_YES) + updateaddr = false; + p += size; + } + + return result; +} + +/* + * Performs a memory write using memory access abstract commands. The write + * sizes supported are 1, 2, and 4 bytes despite the spec's support of 8 and 16 + * byte aamsize fields in the memory access abstract command. + */ +static int write_memory_abstract(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + RISCV013_INFO(info); + int result = ERROR_OK; + bool use_aampostincrement = info->has_aampostincrement != YNM_NO; + + LOG_DEBUG("writing %d words of %d bytes from 0x%" TARGET_PRIxADDR, count, + size, address); + + /* Convert the size (bytes) to width (bits) */ + unsigned width = size << 3; + + /* Create the command (physical address, postincrement, write) */ + uint32_t command = access_memory_command(target, false, width, use_aampostincrement, true); + + /* Execute the writes */ + const uint8_t *p = buffer; + bool updateaddr = true; + for (uint32_t c = 0; c < count; c++) { + /* Move data to arg0 */ + 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()."); + return result; + } + + /* Update the address if it is the first time or aampostincrement is not supported by the target. */ + if (updateaddr) { + /* Set arg1 to the address: address + c * size */ + result = write_abstract_arg(target, 1, address + c * size, riscv_xlen(target)); + if (result != ERROR_OK) { + LOG_ERROR("Failed to write arg1 during write_memory_abstract()."); + return result; + } + } + + /* Execute the command */ + result = execute_abstract_command(target, command); + + if (info->has_aampostincrement == YNM_MAYBE) { + if (result == ERROR_OK) { + /* Safety: double-check that the address was really auto-incremented */ + riscv_reg_t new_address = read_abstract_arg(target, 1, riscv_xlen(target)); + if (new_address == address + size) { + LOG_DEBUG("aampostincrement is supported on this target."); + info->has_aampostincrement = YNM_YES; + } else { + LOG_WARNING("Buggy aampostincrement! Address not incremented correctly."); + info->has_aampostincrement = YNM_NO; + } + } else { + /* Try the same access but with postincrement disabled. */ + command = access_memory_command(target, false, width, false, true); + result = execute_abstract_command(target, command); + if (result == ERROR_OK) { + LOG_DEBUG("aampostincrement is not supported on this target."); + info->has_aampostincrement = YNM_NO; + } + } + } + + if (result != ERROR_OK) + return result; + + if (info->has_aampostincrement == YNM_YES) + updateaddr = false; + p += size; + } + + return result; +} + +/** + * Read the requested memory, taking care to execute every read exactly once, + * even if cmderr=busy is encountered. + */ +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) +{ + 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; + + if (increment == 0 && + register_write_direct(target, GDB_REGNO_S2, 0) != ERROR_OK) + return ERROR_FAIL; + + uint32_t 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) + return ERROR_FAIL; + + /* 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); + return ERROR_OK; + } + + if (dmi_write(target, DM_ABSTRACTAUTO, + 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET) != ERROR_OK) + goto error; + result=ERROR_FAIL; + 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 riscv_batch *batch = riscv_batch_alloc(target, RISCV_BATCH_ALLOC_SIZE, + info->dmi_busy_delay + info->ac_busy_delay); + if (!batch) + return ERROR_FAIL; + + 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); + + reads++; + if (riscv_batch_full(batch)) + break; + } + + batch_run(target, batch); + + /* 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) + 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"); + + increase_ac_busy_delay(target); + riscv013_clear_abstract_error(target); + + dmi_write(target, DM_ABSTRACTAUTO, 0); + + 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; + } + + /* 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; + } + + 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); + + /* 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++; + + dmi_write(target, DM_ABSTRACTAUTO, + 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET); + + ignore_last = 1; + + 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; + } + + /* 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; + + 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); + } + + index = next_index; + + riscv_batch_free(batch); + } + + dmi_write(target, DM_ABSTRACTAUTO, 0); + + 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); + } + + /* 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); + + return ERROR_OK; + +error: + dmi_write(target, DM_ABSTRACTAUTO, 0); + + 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) +{ + uint64_t mstatus = 0; + uint64_t mstatus_old = 0; + if (modify_privilege(target, &mstatus, &mstatus_old) != ERROR_OK) + return ERROR_FAIL; + + int result = ERROR_FAIL; + + if (riscv_save_register(target, GDB_REGNO_S0) != ERROR_OK) + goto restore_mstatus; + + /* 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_enable_virtual && has_sufficient_progbuf(target, 5) && get_field(mstatus, MSTATUS_MPRV)) + riscv_program_csrrci(&program, GDB_REGNO_ZERO, CSR_DCSR_MPRVEN, GDB_REGNO_DCSR); + + if (riscv_program_ebreak(&program) != ERROR_OK) + goto restore_mstatus; + if (riscv_program_write(&program) != ERROR_OK) + goto restore_mstatus; + + /* 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, + 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_mstatus; + + uint64_t value; + if (register_read_direct(target, &value, GDB_REGNO_S0) != ERROR_OK) + goto restore_mstatus; + buf_set_u64(buffer, 0, 8 * size, value); + log_memory_access(address, value, size, true); + result = ERROR_OK; + +restore_mstatus: + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + result = ERROR_FAIL; + + return result; +} + +/** + * Read the requested memory, silently handling memory access errors. + */ +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); + return ERROR_FAIL; + } + + int result = ERROR_OK; + + 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); + + memset(buffer, 0, count*size); + + 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 + */ + 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 (increment == 0 && riscv_save_register(target, 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); + + if (riscv_program_ebreak(&program) != ERROR_OK) + return ERROR_FAIL; + if (riscv_program_write(&program) != 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; + } + + /* Restore MSTATUS */ + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + return ERROR_FAIL; + + return result; +} + +static int read_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, uint8_t *buffer, uint32_t increment) +{ + + if (count == 0) + return ERROR_OK; + + if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { + LOG_ERROR("BUG: Unsupported size for memory read: %d", size); + return ERROR_FAIL; + } + + int ret = ERROR_FAIL; + RISCV_INFO(r); + RISCV013_INFO(info); + + char *progbuf_result = "disabled"; + char *sysbus_result = "disabled"; + char *abstract_result = "disabled"; + + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; + + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (mem_should_skip_progbuf(target, address, size, true, &progbuf_result)) + continue; + + ret = read_memory_progbuf(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + progbuf_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + if (mem_should_skip_sysbus(target, address, size, increment, true, &sysbus_result)) + continue; + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) + ret = read_memory_bus_v0(target, address, size, count, buffer, increment); + // else if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 1) + // ret = read_memory_bus_v1(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + sysbus_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + if (mem_should_skip_abstract(target, address, size, increment, true, &abstract_result)) + continue; + + ret = read_memory_abstract(target, address, size, count, buffer, increment); + + if (ret != ERROR_OK) + abstract_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + + log_mem_access_result(target, ret == ERROR_OK, method, true); + + if (ret == ERROR_OK) + return ret; + } + + LOG_ERROR("Target %s: Failed to read memory (addr=0x%" PRIx64 ")", target_name(target), address); + LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + return ret; +} + +static int write_memory_bus_v0(struct target *target, target_addr_t address, + 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" + TARGET_PRIxADDR, size, count, address); + dmi_write(target, DM_SBADDRESS0, address); + int64_t value = 0; + int64_t access = 0; + riscv_addr_t offset = 0; + riscv_addr_t t_addr = 0; + const uint8_t *t_buffer = buffer + offset; + + /* B.8 Writing Memory, single write check if we write in one go */ + if (count == 1) { /* count is in bytes here */ + value = buf_get_u64(t_buffer, 0, 8 * size); + + 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); + return ERROR_OK; + } + + /*B.8 Writing Memory, using autoincrement*/ + + 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); + + /*2)set the value according to the size required and write*/ + for (riscv_addr_t i = 0; i < count; ++i) { + offset = size*i; + /* for monitoring only */ + t_addr = address + offset; + t_buffer = buffer + offset; + + value = buf_get_u64(t_buffer, 0, 8 * size); + LOG_DEBUG("SAB:autoincrement: expected address: 0x%08x value: 0x%08x" + PRIx64, (uint32_t)t_addr, (uint32_t)value); + dmi_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); + + 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) +{ + RISCV013_INFO(info); + + if (riscv_xlen(target) < size * 8) { + LOG_ERROR("XLEN (%d) is too short for %d-bit memory write.", + riscv_xlen(target), size * 8); + return ERROR_FAIL; + } + + LOG_DEBUG("writing %d words of %d bytes to 0x%08lx", count, size, (long)address); + + // select_dmi(target); + + 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 write to + * s1 holds the next data value to write + */ + + int result = ERROR_OK; + 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 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); + + 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 (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); + + result = riscv_program_ebreak(&program); + if (result != ERROR_OK) + goto error; + riscv_program_write(&program); + + riscv_addr_t cur_addr = address; + riscv_addr_t distance = (riscv_addr_t)count * size; + bool setup_needed = true; + LOG_DEBUG("writing until final address 0x%016" PRIx64, cur_addr + distance); + while (cur_addr - address < distance) { + LOG_DEBUG("transferring burst starting at address 0x%016" PRIx64, + cur_addr); + + struct riscv_batch *batch = riscv_batch_alloc( + target, + RISCV_BATCH_ALLOC_SIZE, + info->dmi_busy_delay + info->ac_busy_delay); + if (!batch) + goto error; + + /* To write another word, we put it in S1 and execute the program. */ + for (riscv_addr_t offset = cur_addr - address; offset < distance; offset += size) { + const uint8_t *t_buffer = buffer + offset; + + uint64_t value = buf_get_u64(t_buffer, 0, 8 * size); + + log_memory_access(cur_addr, value, size, false); + cur_addr += size; + + if (setup_needed) { + result = register_write_direct(target, GDB_REGNO_S0, + address + offset); + if (result != ERROR_OK) { + riscv_batch_free(batch); + goto error; + } + + /* 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; + } + } else { + } + } + + /* 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. */ + + 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; + } + } + +error: + dmi_write(target, DM_ABSTRACTAUTO, 0); + + /* Restore MSTATUS */ + if (mstatus != mstatus_old) + if (register_write_direct(target, GDB_REGNO_MSTATUS, mstatus_old)) + return ERROR_FAIL; + + if (execute_fence(target) != ERROR_OK) + return ERROR_FAIL; + + return result; +} + +static int write_memory(struct target *target, target_addr_t address, + uint32_t size, uint32_t count, const uint8_t *buffer) +{ + if (size != 1 && size != 2 && size != 4 && size != 8 && size != 16) { + LOG_ERROR("BUG: Unsupported size for memory write: %d", size); + return ERROR_FAIL; + } + + int ret = ERROR_FAIL; + RISCV_INFO(r); + RISCV013_INFO(info); + + char *progbuf_result = "disabled"; + char *sysbus_result = "disabled"; + char *abstract_result = "disabled"; + + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) { + int method = r->mem_access_methods[i]; + + if (method == RISCV_MEM_ACCESS_PROGBUF) { + if (mem_should_skip_progbuf(target, address, size, false, &progbuf_result)) + continue; + + ret = write_memory_progbuf(target, address, size, count, buffer); + + if (ret != ERROR_OK) + progbuf_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_SYSBUS) { + if (mem_should_skip_sysbus(target, address, size, 0, false, &sysbus_result)) + continue; + + if (get_field(info->sbcs, DM_SBCS_SBVERSION) == 0) + ret = write_memory_bus_v0(target, address, size, count, buffer); + + + if (ret != ERROR_OK) + sysbus_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_ABSTRACT) { + if (mem_should_skip_abstract(target, address, size, 0, false, &abstract_result)) + continue; + + ret = write_memory_abstract(target, address, size, count, buffer); + + if (ret != ERROR_OK) + abstract_result = "failed"; + } else if (method == RISCV_MEM_ACCESS_UNSPECIFIED) + /* No further mem access method to try. */ + break; + + log_mem_access_result(target, ret == ERROR_OK, method, false); + + if (ret == ERROR_OK) + return ret; + } + + LOG_ERROR("Target %s: Failed to write memory (addr=0x%" PRIx64 ")", target_name(target), address); + LOG_ERROR(" progbuf=%s, sysbus=%s, abstract=%s", progbuf_result, sysbus_result, abstract_result); + return ret; +} + +static int arch_state(struct target *target) +{ + return ERROR_OK; +} + +struct target_type wch_riscv013_target = { + .name = "riscv", + + .init_target = init_target, + .deinit_target = deinit_target, + .examine = examine, + + .poll = &riscv_openocd_poll, + .halt = &riscv_halt, + .step = &riscv_openocd_step, + + .assert_reset = assert_reset, + .deassert_reset = deassert_reset, + + .write_memory = write_memory, + + .arch_state = arch_state +}; + +/*** 0.13-specific implementations of various RISC-V helper functions. ***/ +static int riscv013_get_register(struct target *target, + riscv_reg_t *value, int rid) +{ + LOG_DEBUG("[%s] reading register %s", target_name(target), + gdb_regno_name(rid)); + + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + + int result = ERROR_OK; + if (rid == GDB_REGNO_PC) { + /* TODO: move this into riscv.c. */ + result = register_read_direct(target, value, GDB_REGNO_DPC); + LOG_TARGET_DEBUG(target, "read PC from DPC: 0x%" PRIx64, *value); + } else if (rid == GDB_REGNO_PRIV) { + uint64_t dcsr; + /* TODO: move this into riscv.c. */ + result = register_read_direct(target, &dcsr, GDB_REGNO_DCSR); + *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_direct(target, value, rid); + if (result != ERROR_OK) + *value = -1; + } + + return result; +} + +static int riscv013_set_register(struct target *target, int rid, uint64_t value) +{ + if (dm013_select_target(target) != ERROR_OK) + return ERROR_FAIL; + LOG_TARGET_DEBUG(target, "writing 0x%" PRIx64 " to register %s", + value, gdb_regno_name(rid)); + + if (rid <= GDB_REGNO_XPR31) { + return register_write_direct(target, rid, value); + } else if (rid == GDB_REGNO_PC) { + LOG_TARGET_DEBUG(target, "writing PC to DPC: 0x%" PRIx64, value); + register_write_direct(target, GDB_REGNO_DPC, value); + uint64_t actual_value; + register_read_direct(target, &actual_value, GDB_REGNO_DPC); + LOG_TARGET_DEBUG(target, " actual DPC written: 0x%016" PRIx64, 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_direct(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); + } + + return ERROR_OK; +} + +static int dm013_select_hart(struct target *target, int hart_index) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + if (hart_index == dm->current_hartid) + return ERROR_OK; + + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, hart_index); + if (dmi_write(target, DM_DMCONTROL, dmcontrol) != ERROR_OK) { + /* Who knows what the state is? */ + dm->current_hartid = -2; + return ERROR_FAIL; + } + 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) +{ + RISCV_INFO(r); + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + if (!dm->hasel_supported) { + r->prepped = false; + dm013_select_target(target); + return ERROR_OK; + } + + 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); + + 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; + riscv_info_t *info = riscv_info(t); + riscv013_info_t *info_013 = get_info(t); + unsigned index = info_013->index; + LOG_DEBUG("index=%d, coreid=%d, prepped=%d", index, t->coreid, info->prepped); + if (info->prepped) { + info_013->selected = true; + hawindow[index / 32] |= 1 << (index % 32); + info->prepped = false; + total_selected++; + selected_index = index; + } + } + + if (total_selected == 0) { + LOG_TARGET_ERROR(target, "No harts were prepped!"); + return ERROR_FAIL; + } else if (total_selected == 1) { + /* Don't use hasel if we only need to talk to one hart. */ + return dm013_select_hart(target, selected_index); + } + + if (dm013_select_hart(target, HART_INDEX_MULTIPLE) != ERROR_OK) + return ERROR_FAIL; + + for (unsigned i = 0; i < hawindow_count; i++) { + if (dmi_write(target, DM_HAWINDOWSEL, i) != ERROR_OK) + return ERROR_FAIL; + if (dmi_write(target, DM_HAWINDOW, hawindow[i]) != ERROR_OK) + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static int riscv013_halt_prep(struct target *target) +{ + return ERROR_OK; +} + +static int riscv013_halt_go(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + return ERROR_FAIL; + + if (select_prepped_harts(target) != ERROR_OK) + return ERROR_FAIL; + + LOG_TARGET_DEBUG(target, "halting hart"); + + /* Issue the halt command, and then wait for the current hart to halt. */ + uint32_t dmcontrol = DM_DMCONTROL_DMACTIVE | DM_DMCONTROL_HALTREQ; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + dmi_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; + /* When no harts are running, there's no point in continuing this loop. */ + if (!get_field(dmstatus, DM_DMSTATUS_ALLRUNNING)) + 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 (dmi_read(target, &dmcontrol, DM_DMCONTROL) != ERROR_OK) + return ERROR_FAIL; + + 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); + + if (dm->current_hartid == HART_INDEX_MULTIPLE) { + target_list_t *entry; + 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; + } + } + /* The "else" case is handled in halt_go(). */ + + return ERROR_OK; +} + +static int riscv013_resume_go(struct target *target) +{ + if (select_prepped_harts(target) != ERROR_OK) + return ERROR_FAIL; + + 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); +} + +static int riscv013_resume_prep(struct target *target) +{ + return riscv013_on_step_or_resume(target, false); +} + +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) +{ + RISCV013_INFO(info); + + uint32_t dmstatus; + if (dm013_select_target(target) != ERROR_OK) + return false; + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + return false; + if (get_field(dmstatus, DM_DMSTATUS_ANYUNAVAIL)) + LOG_TARGET_ERROR(target, "Hart is unavailable."); + if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT)) + LOG_TARGET_ERROR(target, "Hart doesn't exist."); + if (get_field(dmstatus, DM_DMSTATUS_ANYHAVERESET)) { + LOG_TARGET_INFO(target, "Hart unexpectedly reset!"); + /* 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; + 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_direct(target, &dcsr, GDB_REGNO_DCSR); + if (result != ERROR_OK) + return RISCV_HALT_UNKNOWN; + + 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_EBREAK: + return RISCV_HALT_BREAKPOINT; + 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); + return RISCV_HALT_TRIGGER; + case CSR_DCSR_CAUSE_STEP: + return RISCV_HALT_SINGLESTEP; + 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%" PRIx32, (uint32_t) dcsr); + return RISCV_HALT_UNKNOWN; +} + +static int riscv013_write_debug_buffer(struct target *target, unsigned index, riscv_insn_t data) +{ + if (dmi_write(target, DM_PROGBUF0 + index, data) != ERROR_OK) + return ERROR_FAIL; + + return ERROR_OK; +} + +static riscv_insn_t riscv013_read_debug_buffer(struct target *target, unsigned index) +{ + uint32_t value; + dmi_read(target, &value, DM_PROGBUF0 + index); + return value; +} + +static int riscv013_invalidate_cached_debug_buffer(struct target *target) +{ + dm013_info_t *dm = get_dm(target); + if (!dm) + 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; +} + +static int riscv013_execute_debug_buffer(struct target *target) +{ + uint32_t run_program = 0; + run_program = set_field(run_program, AC_ACCESS_REGISTER_AARSIZE, 2); + run_program = set_field(run_program, AC_ACCESS_REGISTER_POSTEXEC, 1); + 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); +} + + 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 void riscv013_fill_dmi_nop_u64(struct target *target, char *buf) +{ + +} + +/* Helper function for riscv013_test_sba_config_reg */ +static int get_max_sbaccess(struct target *target) +{ + RISCV013_INFO(info); + + uint32_t sbaccess128 = get_field(info->sbcs, DM_SBCS_SBACCESS128); + uint32_t sbaccess64 = get_field(info->sbcs, DM_SBCS_SBACCESS64); + uint32_t sbaccess32 = get_field(info->sbcs, DM_SBCS_SBACCESS32); + uint32_t sbaccess16 = get_field(info->sbcs, DM_SBCS_SBACCESS16); + uint32_t sbaccess8 = get_field(info->sbcs, DM_SBCS_SBACCESS8); + + if (sbaccess128) + return 4; + else if (sbaccess64) + return 3; + else if (sbaccess32) + return 2; + else if (sbaccess16) + return 1; + else if (sbaccess8) + return 0; + else + return -1; +} + +static uint32_t get_num_sbdata_regs(struct target *target) +{ + RISCV013_INFO(info); + + uint32_t sbaccess128 = get_field(info->sbcs, DM_SBCS_SBACCESS128); + uint32_t sbaccess64 = get_field(info->sbcs, DM_SBCS_SBACCESS64); + uint32_t sbaccess32 = get_field(info->sbcs, DM_SBCS_SBACCESS32); + + if (sbaccess128) + return 4; + else if (sbaccess64) + return 2; + else if (sbaccess32) + return 1; + else + return 0; +} + +static int riscv013_test_sba_config_reg(struct target *target, + target_addr_t legal_address, uint32_t num_words, + target_addr_t illegal_address, bool run_sbbusyerror_test) +{ + LOG_INFO("Testing System Bus Access as defined by RISC-V Debug Spec v0.13"); + + uint32_t tests_failed = 0; + + uint32_t rd_val; + uint32_t sbcs_orig; + dmi_read(target, &sbcs_orig, DM_SBCS); + + uint32_t sbcs = sbcs_orig; + bool test_passed; + + int max_sbaccess = get_max_sbaccess(target); + + if (max_sbaccess == -1) { + LOG_ERROR("System Bus Access not supported in this config."); + return ERROR_FAIL; + } + + if (get_field(sbcs, DM_SBCS_SBVERSION) != 1) { + LOG_ERROR("System Bus Access unsupported SBVERSION (%d). Only version 1 is supported.", + get_field(sbcs, DM_SBCS_SBVERSION)); + return ERROR_FAIL; + } + + uint32_t num_sbdata_regs = get_num_sbdata_regs(target); + assert(num_sbdata_regs); + + uint32_t rd_buf[num_sbdata_regs]; + + /* Test 1: Simple write/read test */ + test_passed = true; + sbcs = set_field(sbcs_orig, DM_SBCS_SBAUTOINCREMENT, 0); + dmi_write(target, DM_SBCS, sbcs); + + uint32_t test_patterns[4] = {0xdeadbeef, 0xfeedbabe, 0x12345678, 0x08675309}; + for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) { + sbcs = set_field(sbcs, DM_SBCS_SBACCESS, sbaccess); + dmi_write(target, DM_SBCS, sbcs); + + uint32_t compare_mask = (sbaccess == 0) ? 0xff : (sbaccess == 1) ? 0xffff : 0xffffffff; + + for (uint32_t i = 0; i < num_words; i++) { + uint32_t addr = legal_address + (i << sbaccess); + uint32_t wr_data[num_sbdata_regs]; + for (uint32_t j = 0; j < num_sbdata_regs; j++) + wr_data[j] = test_patterns[j] + i; + write_memory_sba_simple(target, addr, wr_data, num_sbdata_regs, sbcs); + } + + for (uint32_t i = 0; i < num_words; i++) { + uint32_t addr = legal_address + (i << sbaccess); + read_memory_sba_simple(target, addr, rd_buf, num_sbdata_regs, sbcs); + for (uint32_t j = 0; j < num_sbdata_regs; j++) { + if (((test_patterns[j]+i)&compare_mask) != (rd_buf[j]&compare_mask)) { + LOG_ERROR("System Bus Access Test 1: Error reading non-autoincremented address %x," + "expected val = %x, read val = %x", addr, test_patterns[j]+i, rd_buf[j]); + test_passed = false; + tests_failed++; + } + } + } + } + if (test_passed) + LOG_INFO("System Bus Access Test 1: Simple write/read test PASSED."); + + /* Test 2: Address autoincrement test */ + target_addr_t curr_addr; + target_addr_t prev_addr; + test_passed = true; + sbcs = set_field(sbcs_orig, DM_SBCS_SBAUTOINCREMENT, 1); + dmi_write(target, DM_SBCS, sbcs); + + for (uint32_t sbaccess = 0; sbaccess <= (uint32_t)max_sbaccess; sbaccess++) { + sbcs = set_field(sbcs, DM_SBCS_SBACCESS, sbaccess); + dmi_write(target, DM_SBCS, sbcs); + + dmi_write(target, DM_SBADDRESS0, legal_address); + read_sbcs_nonbusy(target, &sbcs); + curr_addr = legal_address; + for (uint32_t i = 0; i < num_words; i++) { + prev_addr = curr_addr; + read_sbcs_nonbusy(target, &sbcs); + curr_addr = sb_read_address(target); + if ((curr_addr - prev_addr != (uint32_t)(1 << sbaccess)) && (i != 0)) { + LOG_ERROR("System Bus Access Test 2: Error with address auto-increment, sbaccess = %x.", sbaccess); + test_passed = false; + tests_failed++; + } + dmi_write(target, DM_SBDATA0, i); + } + + read_sbcs_nonbusy(target, &sbcs); + + dmi_write(target, DM_SBADDRESS0, legal_address); + + uint32_t val; + sbcs = set_field(sbcs, DM_SBCS_SBREADONDATA, 1); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &val, DM_SBDATA0); /* Dummy read to trigger first system bus read */ + curr_addr = legal_address; + for (uint32_t i = 0; i < num_words; i++) { + prev_addr = curr_addr; + read_sbcs_nonbusy(target, &sbcs); + curr_addr = sb_read_address(target); + if ((curr_addr - prev_addr != (uint32_t)(1 << sbaccess)) && (i != 0)) { + LOG_ERROR("System Bus Access Test 2: Error with address auto-increment, sbaccess = %x", sbaccess); + test_passed = false; + tests_failed++; + } + dmi_read(target, &val, DM_SBDATA0); + read_sbcs_nonbusy(target, &sbcs); + if (i != val) { + LOG_ERROR("System Bus Access Test 2: Error reading auto-incremented address," + "expected val = %x, read val = %x.", i, val); + test_passed = false; + tests_failed++; + } + } + } + if (test_passed) + LOG_INFO("System Bus Access Test 2: Address auto-increment test PASSED."); + + /* Test 3: Read from illegal address */ + read_memory_sba_simple(target, illegal_address, rd_buf, 1, sbcs_orig); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 2) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 2); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) + LOG_INFO("System Bus Access Test 3: Illegal address read test PASSED."); + else + LOG_ERROR("System Bus Access Test 3: Illegal address read test FAILED, unable to clear to 0."); + } else { + LOG_ERROR("System Bus Access Test 3: Illegal address read test FAILED, unable to set error code."); + } + + /* Test 4: Write to illegal address */ + write_memory_sba_simple(target, illegal_address, test_patterns, 1, sbcs_orig); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 2) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 2); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) + LOG_INFO("System Bus Access Test 4: Illegal address write test PASSED."); + else { + LOG_ERROR("System Bus Access Test 4: Illegal address write test FAILED, unable to clear to 0."); + tests_failed++; + } + } else { + LOG_ERROR("System Bus Access Test 4: Illegal address write test FAILED, unable to set error code."); + tests_failed++; + } + + /* Test 5: Write with unsupported sbaccess size */ + uint32_t sbaccess128 = get_field(sbcs_orig, DM_SBCS_SBACCESS128); + + if (sbaccess128) { + LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED, all sbaccess sizes supported."); + } else { + sbcs = set_field(sbcs_orig, DM_SBCS_SBACCESS, 4); + + write_memory_sba_simple(target, legal_address, test_patterns, 1, sbcs); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 4) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 4); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) + LOG_INFO("System Bus Access Test 5: SBCS sbaccess error test PASSED."); + else { + LOG_ERROR("System Bus Access Test 5: SBCS sbaccess error test FAILED, unable to clear to 0."); + tests_failed++; + } + } else { + LOG_ERROR("System Bus Access Test 5: SBCS sbaccess error test FAILED, unable to set error code."); + tests_failed++; + } + } + + /* Test 6: Write to misaligned address */ + sbcs = set_field(sbcs_orig, DM_SBCS_SBACCESS, 1); + + write_memory_sba_simple(target, legal_address+1, test_patterns, 1, sbcs); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 3) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBERROR, 3); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBERROR) == 0) + LOG_INFO("System Bus Access Test 6: SBCS address alignment error test PASSED"); + else { + LOG_ERROR("System Bus Access Test 6: SBCS address alignment error test FAILED, unable to clear to 0."); + tests_failed++; + } + } else { + LOG_ERROR("System Bus Access Test 6: SBCS address alignment error test FAILED, unable to set error code."); + tests_failed++; + } + + /* Test 7: Set sbbusyerror, only run this case in simulation as it is likely + * impossible to hit otherwise */ + if (run_sbbusyerror_test) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBREADONADDR, 1); + dmi_write(target, DM_SBCS, sbcs); + + for (int i = 0; i < 16; i++) + dmi_write(target, DM_SBDATA0, 0xdeadbeef); + + for (int i = 0; i < 16; i++) + dmi_write(target, DM_SBADDRESS0, legal_address); + + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBBUSYERROR)) { + sbcs = set_field(sbcs_orig, DM_SBCS_SBBUSYERROR, 1); + dmi_write(target, DM_SBCS, sbcs); + dmi_read(target, &rd_val, DM_SBCS); + if (get_field(rd_val, DM_SBCS_SBBUSYERROR) == 0) + LOG_INFO("System Bus Access Test 7: SBCS sbbusyerror test PASSED."); + else { + LOG_ERROR("System Bus Access Test 7: SBCS sbbusyerror test FAILED, unable to clear to 0."); + tests_failed++; + } + } else { + LOG_ERROR("System Bus Access Test 7: SBCS sbbusyerror test FAILED, unable to set error code."); + tests_failed++; + } + } + + if (tests_failed == 0) { + LOG_INFO("ALL TESTS PASSED"); + return ERROR_OK; + } else { + LOG_ERROR("%d TESTS FAILED", tests_failed); + return ERROR_FAIL; + } + +} + +static void write_memory_sba_simple(struct target *target, target_addr_t addr, + uint32_t *write_data, uint32_t write_size, uint32_t sbcs) +{ + RISCV013_INFO(info); + + uint32_t rd_sbcs; + uint32_t masked_addr; + + uint32_t sba_size = get_field(info->sbcs, DM_SBCS_SBASIZE); + + read_sbcs_nonbusy(target, &rd_sbcs); + + uint32_t sbcs_no_readonaddr = set_field(sbcs, DM_SBCS_SBREADONADDR, 0); + dmi_write(target, DM_SBCS, sbcs_no_readonaddr); + + for (uint32_t i = 0; i < sba_size/32; i++) { + masked_addr = (addr >> 32*i) & 0xffffffff; + + if (i != 3) + dmi_write(target, DM_SBADDRESS0+i, masked_addr); + else + dmi_write(target, DM_SBADDRESS3, masked_addr); + } + + /* Write SBDATA registers starting with highest address, since write to + * SBDATA0 triggers write */ + for (int i = write_size-1; i >= 0; i--) + dmi_write(target, DM_SBDATA0+i, write_data[i]); +} + +static void read_memory_sba_simple(struct target *target, target_addr_t addr, + uint32_t *rd_buf, uint32_t read_size, uint32_t sbcs) +{ + RISCV013_INFO(info); + + uint32_t rd_sbcs; + uint32_t masked_addr; + + uint32_t sba_size = get_field(info->sbcs, DM_SBCS_SBASIZE); + + read_sbcs_nonbusy(target, &rd_sbcs); + + uint32_t sbcs_readonaddr = set_field(sbcs, DM_SBCS_SBREADONADDR, 1); + dmi_write(target, DM_SBCS, sbcs_readonaddr); + + /* Write addresses starting with highest address register */ + for (int i = sba_size/32-1; i >= 0; i--) { + masked_addr = (addr >> 32*i) & 0xffffffff; + + if (i != 3) + dmi_write(target, DM_SBADDRESS0+i, masked_addr); + else + dmi_write(target, DM_SBADDRESS3, masked_addr); + } + + read_sbcs_nonbusy(target, &rd_sbcs); + + for (uint32_t i = 0; i < read_size; i++) + dmi_read(target, &(rd_buf[i]), DM_SBDATA0+i); +} + +static int riscv013_dmi_write_u64_bits(struct target *target) +{ + RISCV013_INFO(info); + return info->abits + DTM_DMI_DATA_LENGTH + DTM_DMI_OP_LENGTH; +} + +static int maybe_execute_fence_i(struct target *target) +{ + if (has_sufficient_progbuf(target, 3)) + return execute_fence(target); + return ERROR_OK; +} +/* Helper Functions. */ +static int riscv013_on_step_or_resume(struct target *target, bool step) +{ + if (maybe_execute_fence_i(target) != ERROR_OK) + return ERROR_FAIL; + dmi_write(target,WCH_DMOD,0); + /* We want to twiddle some bits in the debug CSR so debugging works. */ + riscv_reg_t dcsr; + int result = register_read_direct(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); + if (riscv_set_register(target, GDB_REGNO_DCSR, dcsr) != 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) +{ + 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; + dmcontrol = set_dmcontrol_hartsel(dmcontrol, dm->current_hartid); + dmi_write(target, DM_DMCONTROL, dmcontrol); + + dmcontrol = set_field(dmcontrol, DM_DMCONTROL_RESUMEREQ, 0); + + uint32_t dmstatus; + for (size_t i = 0; i < 256; ++i) { + usleep(10); + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + 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); + return ERROR_OK; + } + + dmi_write(target, DM_DMCONTROL, dmcontrol); + + LOG_TARGET_ERROR(target, "unable to resume"); + if (dmstatus_read(target, &dmstatus, true) != ERROR_OK) + return ERROR_FAIL; + LOG_TARGET_ERROR(target, " dmstatus=0x%08x", dmstatus); + + if (step) { + LOG_ERROR(" was stepping, halting"); + riscv_halt(target); + return ERROR_OK; + } + + return ERROR_FAIL; +} + +static void 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); +} diff --git a/src/target/wch_riscv.c b/src/target/wch_riscv.c new file mode 100755 index 000000000..9919d5ccd --- /dev/null +++ b/src/target/wch_riscv.c @@ -0,0 +1,3030 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wch_riscv.h" + + +static int wch_riscv_flush_registers(struct target *target); + struct trigger { + uint64_t address; + uint32_t length; + uint64_t mask; + uint64_t value; + bool read, write, execute; + int unique_id; +}; +extern int write_flash_data(struct target *target, target_addr_t address,uint32_t size, uint32_t count, uint8_t *buffer); + + +static enum { + RO_NORMAL, + RO_REVERSED +} resume_order; + +static int riscv_resume_go_all_harts(struct target *target); +extern unsigned char riscvchip; +extern unsigned int chip_type; + +static struct target_type *get_target_type(struct target *target) +{ + riscv_info_t *info = (riscv_info_t *) target->arch_info; + + if (!info) { + LOG_ERROR("Target has not been initialized"); + return NULL; + } + + switch (info->dtm_version) { + case 0: + case 1: + return &wch_riscv013_target; + default: + LOG_ERROR("[%s] Unsupported DTM version: %d", + target_name(target), info->dtm_version); + return NULL; + } +} + +static int wch_riscv_create_target(struct target *target, Jim_Interp *interp) +{ + LOG_DEBUG("wch_riscv_create_target()"); + target->arch_info = calloc(1, sizeof(riscv_info_t)); + if (!target->arch_info) { + LOG_ERROR("Failed to allocate RISC-V target structure."); + return ERROR_FAIL; + } + riscv_info_init(target, target->arch_info); + return ERROR_OK; +} + +static int wch_riscv_init_target(struct command_context *cmd_ctx, + struct target *target) +{ + LOG_DEBUG("wch_riscv_init_target()"); + RISCV_INFO(info); + info->cmd_ctx = cmd_ctx; + + riscv_semihosting_init(target); + + target->debug_reason = DBG_REASON_DBGRQ; + + return ERROR_OK; +} + +static void riscv_free_registers(struct target *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); + } + free(target->reg_cache); + } +} + +static void wch_riscv_deinit_target(struct target *target) +{ + LOG_DEBUG("wch_riscv_deinit_target()"); + + riscv_info_t *info = target->arch_info; + struct target_type *tt = get_target_type(target); + + if (wch_riscv_flush_registers(target) != ERROR_OK) + LOG_ERROR("[%s] Failed to flush registers. Ignoring this error.", target_name(target)); + + if (tt && info->version_specific) + tt->deinit_target(target); + + riscv_free_registers(target); + + range_list_t *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &info->expose_csr, list) { + free(entry->name); + free(entry); + } + + list_for_each_entry_safe(entry, tmp, &info->expose_custom, list) { + free(entry->name); + free(entry); + } + + free(info->reg_names); + free(target->arch_info); + + target->arch_info = NULL; +} + +static void trigger_from_breakpoint(struct trigger *trigger, + const struct breakpoint *breakpoint) +{ + trigger->address = breakpoint->address; + trigger->length = breakpoint->length; + trigger->mask = ~0LL; + trigger->read = false; + trigger->write = false; + trigger->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) +{ + RISCV_INFO(r); + + const uint32_t bpcontrol_x = 1<<0; + const uint32_t bpcontrol_w = 1<<1; + const uint32_t bpcontrol_r = 1<<2; + const uint32_t bpcontrol_u = 1<<3; + const uint32_t bpcontrol_s = 1<<4; + const uint32_t bpcontrol_h = 1<<5; + const uint32_t bpcontrol_m = 1<<6; + const uint32_t bpcontrol_bpmatch = 0xf << 7; + const uint32_t bpcontrol_bpaction = 0xff << 11; + + 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 = set_field(tdata1, bpcontrol_bpaction, 0); /* cause bp exception */ + + riscv_set_register(target, GDB_REGNO_TDATA1, tdata1); + + 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); + + 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; + } + + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + + return ERROR_OK; +} + +static int maybe_add_trigger_t2(struct target *target, + struct trigger *trigger, uint64_t tdata1) +{ + RISCV_INFO(r); + + /* tselect is already set */ + if (tdata1 & (CSR_MCONTROL_EXECUTE | CSR_MCONTROL_STORE | CSR_MCONTROL_LOAD)) { + /* Trigger is already in use, presumably by user code. */ + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + /* address/data match trigger */ + + tdata1 = set_field(tdata1, CSR_MCONTROL_ACTION, + CSR_MCONTROL_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL_MATCH, CSR_MCONTROL_MATCH_EQUAL); + tdata1 |= CSR_MCONTROL_M; + if (r->misa & (1 << ('S' - 'A'))) + tdata1 |= CSR_MCONTROL_S; + if (r->misa & (1 << ('U' - 'A'))) + tdata1 |= CSR_MCONTROL_U; + + if (trigger->execute) + tdata1 |= CSR_MCONTROL_EXECUTE; + if (trigger->read) + tdata1 |= CSR_MCONTROL_LOAD; + if (trigger->write) + tdata1 |= CSR_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); + + 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; + } + + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + + return ERROR_OK; +} + +static int maybe_add_trigger_t6(struct target *target, + struct trigger *trigger, uint64_t tdata1) +{ + 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; + } + + /* address/data match trigger */ + tdata1 |= CSR_MCONTROL6_DMODE(riscv_xlen(target)); + tdata1 = set_field(tdata1, CSR_MCONTROL6_ACTION, + CSR_MCONTROL6_ACTION_DEBUG_MODE); + tdata1 = set_field(tdata1, CSR_MCONTROL6_MATCH, CSR_MCONTROL6_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); + + 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; + } + + riscv_set_register(target, GDB_REGNO_TDATA2, trigger->address); + + return ERROR_OK; +} + +static int add_trigger(struct target *target, struct trigger *trigger) +{ + RISCV_INFO(r); + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + + unsigned int i; + for (i = 0; i < r->trigger_count; i++) { + if (r->trigger_unique_id[i] != -1) + continue; + + riscv_set_register(target, GDB_REGNO_TSELECT, i); + + uint64_t tdata1; + int result = riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1); + if (result != ERROR_OK) + return result; + int type = get_field(tdata1, CSR_TDATA1_TYPE(riscv_xlen(target))); + + result = ERROR_OK; + switch (type) { + case 1: + result = maybe_add_trigger_t1(target, trigger, tdata1); + break; + case 2: + result = maybe_add_trigger_t2(target, trigger, tdata1); + break; + case 6: + result = maybe_add_trigger_t6(target, trigger, tdata1); + break; + default: + LOG_DEBUG("trigger %d has unknown type %d", i, type); + continue; + } + + if (result != ERROR_OK) + continue; + + 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; + } + + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); + + if (i >= r->trigger_count) { + LOG_ERROR("Couldn't find an available hardware trigger."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + return ERROR_OK; +} + +/** + * Write one memory item of given "size". Use memory access of given "access_size". + * Utilize read-modify-write, if needed. + * */ +static int write_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_write_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Modify and write back */ + memcpy(helper_buf + offset_head, buffer, size); + return target_write_memory(target, address - offset_head, access_size, n_blocks, helper_buf); +} + +/** + * Read one memory item of given "size". Use memory access of given "access_size". + * Read larger section of memory and pick out the required portion, if needed. + * */ +static int read_by_given_size(struct target *target, target_addr_t address, + uint32_t size, uint8_t *buffer, uint32_t access_size) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + assert(access_size == 1 || access_size == 2 || access_size == 4 || access_size == 8); + + if (access_size <= size && address % access_size == 0) + /* Can do the memory access directly without a helper buffer. */ + return target_read_memory(target, address, access_size, size / access_size, buffer); + + unsigned int offset_head = address % access_size; + unsigned int n_blocks = ((size + offset_head) <= access_size) ? 1 : 2; + uint8_t helper_buf[n_blocks * access_size]; + + /* Read from memory */ + if (target_read_memory(target, address - offset_head, access_size, n_blocks, helper_buf) != ERROR_OK) + return ERROR_FAIL; + + /* Pick the requested portion from the buffer */ + memcpy(buffer, helper_buf + offset_head, size); + return ERROR_OK; +} + +int wch_riscv_read_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + + assert(size == 1 || size == 2 || size == 4 || size == 8); + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (read_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (read_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} +/** + * Write one memory item using any memory access size that will work. + * Utilize read-modify-write, if needed. + * */ +int wch_riscv_write_by_any_size(struct target *target, target_addr_t address, uint32_t size, uint8_t *buffer) +{ + assert(size == 1 || size == 2 || size == 4 || size == 8); + if(address<0x20000000){ + if((address>=0x08000000)&&(riscvchip!=0x09) ) + address -= 0x08000000; + + int ret=write_flash_data(target,address,size,1,buffer); + return ret; + } + /* Find access size that correspond to data size and the alignment. */ + unsigned int preferred_size = size; + while (address % preferred_size != 0) + preferred_size /= 2; + + /* First try the preferred (most natural) access size. */ + if (write_by_given_size(target, address, size, buffer, preferred_size) == ERROR_OK) + return ERROR_OK; + + /* On failure, try other access sizes. + Minimize the number of accesses by trying first the largest size. */ + for (unsigned int access_size = 8; access_size > 0; access_size /= 2) { + if (access_size == preferred_size) + /* Already tried this size. */ + continue; + + if (write_by_given_size(target, address, size, buffer, access_size) == ERROR_OK) + return ERROR_OK; + } + + /* No access attempt succeeded. */ + return ERROR_FAIL; +} + + +int wch_riscv_add_breakpoint(struct target *target, struct breakpoint *breakpoint) +{ + LOG_TARGET_DEBUG(target, "@0x%" TARGET_PRIxADDR, breakpoint->address); + + assert(breakpoint); + if((riscvchip==6 ||(((uint16_t)chip_type) ==0x050c)||(riscvchip==0x0b)||(riscvchip==0x0c)||(riscvchip==0x0e)) && breakpoint->type == BKPT_HARD && target->breakpoints ){ + struct breakpoint *p= target->breakpoints->next; + int len=0; + while(p){ + len++; + p=p->next; + } + if(len>3) + breakpoint->type = BKPT_SOFT; + } + if(riscvchip==1||riscvchip==2||riscvchip==3 ||riscvchip==7 ||riscvchip==9 ||riscvchip==0x0a ||((uint16_t)chip_type) ==0x0500){ + breakpoint->type = BKPT_SOFT; + } + 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); + return ERROR_FAIL; + } + + if (0 != (breakpoint->address % 2)) { + LOG_ERROR("Invalid breakpoint alignment for address 0x%" TARGET_PRIxADDR, breakpoint->address); + return ERROR_FAIL; + } + + /* Read the original instruction. */ + if (wch_riscv_read_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { + LOG_ERROR("Failed to read original instruction at 0x%" TARGET_PRIxADDR, + breakpoint->address); + return ERROR_FAIL; + } + uint8_t buff[4] = { 0 }; + buf_set_u32(buff, 0, breakpoint->length * CHAR_BIT, breakpoint->length == 4 ? ebreak() : ebreak_c()); + /* Write the ebreak instruction. */ + if (wch_riscv_write_by_any_size(target, breakpoint->address, breakpoint->length, buff) != ERROR_OK) { + LOG_ERROR("Failed to write %d-byte breakpoint instruction at 0x%" + TARGET_PRIxADDR, breakpoint->length, breakpoint->address); + return ERROR_FAIL; + } + + } else if (breakpoint->type == BKPT_HARD) { + struct trigger trigger; + trigger_from_breakpoint(&trigger, breakpoint); + int const result = add_trigger(target, &trigger); + if (result != ERROR_OK) + return result; + } else { + LOG_INFO("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) +{ + 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_TARGET_RESOURCE_NOT_AVAILABLE; + } + 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); + riscv_set_register(target, GDB_REGNO_TSELECT, tselect); + r->trigger_unique_id[i] = -1; + + return ERROR_OK; +} + +int wch_riscv_remove_breakpoint(struct target *target, + struct breakpoint *breakpoint) +{ + if (breakpoint->type == BKPT_SOFT) { + uint8_t buf[32]; + + + /* Write the original instruction. */ + if (wch_riscv_write_by_any_size( + target, breakpoint->address, breakpoint->length, breakpoint->orig_instr) != ERROR_OK) { + LOG_ERROR("Failed to restore instruction for %d-byte breakpoint at " + "0x%" TARGET_PRIxADDR, breakpoint->length, breakpoint->address); + return ERROR_FAIL; + } + // wch_riscv_read_by_any_size(target, 0x08000000, 4, buf); + } else if (breakpoint->type == BKPT_HARD) { + struct trigger trigger; + trigger_from_breakpoint(&trigger, breakpoint); + int result = remove_trigger(target, &trigger); + if (result != ERROR_OK) + return result; + + } else { + LOG_INFO("OpenOCD only supports hardware and software breakpoints."); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + + breakpoint->is_set = false; + + return ERROR_OK; +} + +static void trigger_from_watchpoint(struct trigger *trigger, + const struct watchpoint *watchpoint) +{ + trigger->address = watchpoint->address; + 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; + /* unique_id is unique across both breakpoints and watchpoints. */ + trigger->unique_id = watchpoint->unique_id; +} + +int wch_riscv_add_watchpoint(struct target *target, struct watchpoint *watchpoint) +{ + struct trigger trigger; + trigger_from_watchpoint(&trigger, watchpoint); + + int result = add_trigger(target, &trigger); + if (result != ERROR_OK) + return result; + watchpoint->is_set = true; + + return ERROR_OK; +} + +int wch_riscv_remove_watchpoint(struct target *target, + struct watchpoint *watchpoint) +{ + LOG_DEBUG("[%d] @0x%" TARGET_PRIxADDR, target->coreid, watchpoint->address); + + struct trigger trigger; + trigger_from_watchpoint(&trigger, watchpoint); + + int result = remove_trigger(target, &trigger); + if (result != ERROR_OK) + return result; + watchpoint->is_set = false; + + 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 1: + /* Doesn't support hit bit. */ + break; + case 2: + hit_mask = CSR_MCONTROL_HIT; + break; + case 6: + hit_mask = CSR_MCONTROL6_HIT; + break; + default: + LOG_DEBUG("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_DEBUG("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; +} + + + +static int oldriscv_step(struct target *target, int current, uint32_t address, + int handle_breakpoints) +{ + struct target_type *tt = get_target_type(target); + 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) +{ + RISCV_INFO(r); + LOG_DEBUG("handle_breakpoints=%d", handle_breakpoints); + if (!r->is_halted) + return oldriscv_step(target, current, address, handle_breakpoints); + else + return wch_riscv_openocd_step(target, current, address, handle_breakpoints); +} + +static int wch_riscv_examine(struct target *target) +{ + LOG_DEBUG("[%s]", target_name(target)); + if (target_was_examined(target)) { + LOG_DEBUG("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); + info->dtm_version = 1; + LOG_DEBUG(" version=0x%x", info->dtm_version); + + struct target_type *tt = get_target_type(target); + if (!tt) + return ERROR_FAIL; + + int result = tt->init_target(info->cmd_ctx, target); + if (result != ERROR_OK) + return result; + + return tt->examine(target); +} + +static int oldriscv_poll(struct target *target) +{ + struct target_type *tt = get_target_type(target); + return tt->poll(target); +} + +static int old_or_new_riscv_poll(struct target *target) +{ + RISCV_INFO(r); + if (!r->is_halted) + return oldriscv_poll(target); + else + return wch_riscv_openocd_poll(target); +} + +static int wch_riscv_flush_registers(struct target *target) +{ + RISCV_INFO(r); + + if (!target->reg_cache) + return ERROR_OK; + + LOG_DEBUG("[%s]", target_name(target)); + + for (uint32_t number = 0; number < target->reg_cache->num_regs; number++) { + struct reg *reg = &target->reg_cache->reg_list[number]; + if (reg->valid && reg->dirty) { + uint64_t value = buf_get_u64(reg->value, 0, reg->size); + LOG_DEBUG("[%s] %s is dirty; write back 0x%" PRIx64, + target_name(target), reg->name, value); + int result = r->set_register(target, number, value); + if (result != ERROR_OK) + return ERROR_FAIL; + reg->dirty = false; + } + } + + return ERROR_OK; +} + + +int wch_halt_prep(struct target *target) +{ + RISCV_INFO(r); + + LOG_DEBUG("[%s] prep hart, debug_reason=%d", target_name(target), + target->debug_reason); + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted (debug_reason=%d).", + target_name(target), target->debug_reason); + if (target->debug_reason == DBG_REASON_NOTHALTED) { + 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_prep(target) != ERROR_OK) + return ERROR_FAIL; + r->prepped = true; + } + + return ERROR_OK; +} + +int wch_riscv_halt_go_all_harts(struct target *target) +{ + RISCV_INFO(r); + + if (riscv_is_halted(target)) { + LOG_DEBUG("[%s] Hart is already halted.", target_name(target)); + } else { + if (r->halt_go(target) != ERROR_OK) + return ERROR_FAIL; + + riscv_invalidate_register_cache(target); + } + + return ERROR_OK; +} + +int wch_halt_go(struct target *target) +{ + riscv_info_t *r = riscv_info(target); + int result; + if (!r->is_halted) { + struct target_type *tt = get_target_type(target); + result = tt->halt(target); + } else { + result = wch_riscv_halt_go_all_harts(target); + } + target->state = TARGET_HALTED; + if (target->debug_reason == DBG_REASON_NOTHALTED) + target->debug_reason = DBG_REASON_DBGRQ; + + return result; +} + +static int halt_finish(struct target *target) +{ + return target_call_event_callbacks(target, TARGET_EVENT_HALTED); +} + +int wch_riscv_halt(struct target *target) +{ + RISCV_INFO(r); + + if (!r->is_halted) { + struct target_type *tt = get_target_type(target); + return tt->halt(target); + } + + LOG_TARGET_DEBUG(target, "halting all harts"); + + int result = ERROR_OK; + if (target->smp) { + struct target_list *tlist; + foreach_smp_target(tlist, target->smp_targets) { + struct target *t = tlist->target; + if (wch_halt_prep(t) != ERROR_OK) + result = ERROR_FAIL; + } + + foreach_smp_target(tlist, target->smp_targets) { + struct target *t = tlist->target; + riscv_info_t *i = riscv_info(t); + if (i->prepped) { + if (wch_halt_go(t) != ERROR_OK) + result = ERROR_FAIL; + } + } + + foreach_smp_target(tlist, target->smp_targets) { + struct target *t = tlist->target; + if (halt_finish(t) != ERROR_OK) + return ERROR_FAIL; + } + + } else { + if (wch_halt_prep(target) != ERROR_OK) + result = ERROR_FAIL; + if (wch_halt_go(target) != ERROR_OK) + result = ERROR_FAIL; + if (halt_finish(target) != ERROR_OK) + return ERROR_FAIL; + } + + return result; +} + +static int riscv_assert_reset(struct target *target) +{ + LOG_DEBUG("[%d]", target->coreid); + struct target_type *tt = get_target_type(target); + riscv_invalidate_register_cache(target); + return tt->assert_reset(target); +} + +static int riscv_deassert_reset(struct target *target) +{ + LOG_DEBUG("[%d]", target->coreid); + struct target_type *tt = get_target_type(target); + return tt->deassert_reset(target); +} + +/* state must be riscv_reg_t state[RISCV_MAX_HWBPS] = {0}; */ +static int disable_triggers(struct target *target, riscv_reg_t *state) +{ + RISCV_INFO(r); + + LOG_DEBUG("deal with triggers"); + + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + + if (r->manual_hwbp_set) { + /* Look at every trigger that may have been set. */ + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + for (unsigned int t = 0; t < r->trigger_count; t++) { + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + return ERROR_FAIL; + riscv_reg_t tdata1; + if (riscv_get_register(target, &tdata1, GDB_REGNO_TDATA1) != ERROR_OK) + return ERROR_FAIL; + 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; + } + } + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + } else { + /* Just go through the triggers we manage. */ + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_DEBUG("watchpoint %d: set=%d", i, watchpoint->is_set); + state[i] = watchpoint->is_set; + if (watchpoint->is_set) { + if (wch_riscv_remove_watchpoint(target, watchpoint) != ERROR_OK) + return ERROR_FAIL; + } + watchpoint = watchpoint->next; + i++; + } + } + + return ERROR_OK; +} + +static int enable_triggers(struct target *target, riscv_reg_t *state) +{ + RISCV_INFO(r); + + if (r->manual_hwbp_set) { + /* Look at every trigger that may have been set. */ + riscv_reg_t tselect; + if (riscv_get_register(target, &tselect, GDB_REGNO_TSELECT) != ERROR_OK) + return ERROR_FAIL; + for (unsigned int t = 0; t < r->trigger_count; t++) { + if (state[t] != 0) { + if (riscv_set_register(target, GDB_REGNO_TSELECT, t) != ERROR_OK) + return ERROR_FAIL; + if (riscv_set_register(target, GDB_REGNO_TDATA1, state[t]) != ERROR_OK) + return ERROR_FAIL; + } + } + if (riscv_set_register(target, GDB_REGNO_TSELECT, tselect) != ERROR_OK) + return ERROR_FAIL; + + } else { + struct watchpoint *watchpoint = target->watchpoints; + int i = 0; + while (watchpoint) { + LOG_DEBUG("watchpoint %d: cleared=%" PRId64, i, state[i]); + if (state[i]) { + if (wch_riscv_add_watchpoint(target, watchpoint) != ERROR_OK) + return ERROR_FAIL; + } + watchpoint = watchpoint->next; + i++; + } + } + + return ERROR_OK; +} + +/** + * Get everything ready to resume. + */ +static int resume_prep(struct target *target, int current, + target_addr_t address, int handle_breakpoints, int debug_execution) +{ + RISCV_INFO(r); + LOG_TARGET_DEBUG(target, "target->state=%d", target->state); + + 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 (enable_triggers(target, trigger_state) != ERROR_OK) + return ERROR_FAIL; + } + + if (r->is_halted) { + if (r->resume_prep(target) != ERROR_OK) + return ERROR_FAIL; + } + + LOG_DEBUG("[%d] mark as prepped", target->coreid); + r->prepped = true; + + return ERROR_OK; +} + +/** + * Resume all the harts that have been prepped, as close to instantaneous as + * possible. + */ +static int resume_go(struct target *target, int current, + target_addr_t address, int handle_breakpoints, int debug_execution) +{ + riscv_info_t *r = riscv_info(target); + int result; + if (!r->is_halted) { + struct target_type *tt = get_target_type(target); + result = tt->resume(target, current, address, handle_breakpoints, + debug_execution); + } else { + result = riscv_resume_go_all_harts(target); + } + + return result; +} + +static int resume_finish(struct target *target, int debug_execution) +{ + register_cache_invalidate(target->reg_cache); + + target->state = debug_execution ? TARGET_DEBUG_RUNNING : TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + return target_call_event_callbacks(target, + debug_execution ? TARGET_EVENT_DEBUG_RESUMED : TARGET_EVENT_RESUMED); +} + +/** + * @par single_hart When true, only resume a single hart even if SMP is + * configured. This is used to run algorithms on just one hart. + */ +int wch_riscv_resume( + struct target *target, + int current, + target_addr_t address, + int handle_breakpoints, + 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; + riscv_info_t *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, target->smp_targets) { + struct target *t = tlist->target; + if (resume_finish(t, debug_execution) != ERROR_OK) + result = ERROR_FAIL; + } + + } else { + if (resume_prep(target, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + if (resume_go(target, current, address, handle_breakpoints, + debug_execution) != ERROR_OK) + result = ERROR_FAIL; + if (resume_finish(target, debug_execution) != ERROR_OK) + return ERROR_FAIL; + } + + return result; +} + +static int riscv_target_resume(struct target *target, int current, target_addr_t address, + int handle_breakpoints, int debug_execution) +{ + return wch_riscv_resume(target, current, address, handle_breakpoints, + debug_execution, false); +} + +static int riscv_mmu(struct target *target, int *enabled) +{ + if (!riscv_enable_virt2phys) { + *enabled = 0; + 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."); + 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."); + return ERROR_FAIL; + } + + 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; + return ERROR_OK; + } + + riscv_reg_t satp; + if (riscv_get_register(target, &satp, GDB_REGNO_SATP) != ERROR_OK) { + LOG_DEBUG("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; + } else { + LOG_DEBUG("MMU is enabled."); + *enabled = 1; + } + + return ERROR_OK; +} + +static int riscv_address_translate(struct target *target, + 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; + + 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); + + /* verify bits xlen-1:va_bits-1 are all equal */ + 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 " + "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; + 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); + uint8_t buffer[8]; + assert(info->pte_shift <= 3); + int retval = r->read_memory(target, pte_address, + 4, (1 << info->pte_shift) / 4, buffer, 4); + if (retval != ERROR_OK) + return ERROR_FAIL; + + if (info->pte_shift == 2) + pte = buf_get_u32(buffer, 0, 32); + else + pte = buf_get_u64(buffer, 0, 64); + + LOG_DEBUG("i=%d; PTE @0x%" TARGET_PRIxADDR " = 0x%" PRIx64, i, + pte_address, pte); + + if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) + return ERROR_FAIL; + + if ((pte & PTE_R) || (pte & PTE_X)) /* Found leaf PTE. */ + break; + + i--; + if (i < 0) + break; + ppn_value = pte >> PTE_PPN_SHIFT; + table_address = ppn_value << RISCV_PGSHIFT; + } + + if (i < 0) { + LOG_ERROR("Couldn't find the PTE."); + return ERROR_FAIL; + } + + /* Make sure to clear out the high bits that may be set. */ + *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]; + *physical &= ~(((target_addr_t)info->pa_ppn_mask[i]) << + info->pa_ppn_shift[i]); + *physical |= (ppn_value << info->pa_ppn_shift[i]); + i++; + } + LOG_DEBUG("0x%" TARGET_PRIxADDR " -> 0x%" TARGET_PRIxADDR, virtual, + *physical); + + return ERROR_OK; +} + +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_address_translate(target, virtual, physical) == ERROR_OK) + return ERROR_OK; + } + + return ERROR_FAIL; +} + +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); + return r->read_memory(target, phys_address, size, count, buffer, size); +} + +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); + return ERROR_OK; + } + + target_addr_t physical_addr; + if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) + address = physical_addr; + + RISCV_INFO(r); + return r->read_memory(target, address, 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) +{ + struct target_type *tt = get_target_type(target); + return tt->write_memory(target, phys_address, size, count, buffer); +} + +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); + return ERROR_OK; + } + + target_addr_t physical_addr; + if (target->type->virt2phys(target, address, &physical_addr) == ERROR_OK) + address = physical_addr; + + struct target_type *tt = get_target_type(target); + return tt->write_memory(target, address, size, count, buffer); +} + + + +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) +{ + LOG_TARGET_DEBUG(target, "reg_class=%d, read=%d", reg_class, read); + + if (!target->reg_cache) { + LOG_ERROR("Target not initialized. Return ERROR_FAIL."); + return ERROR_FAIL; + } + + switch (reg_class) { + case REG_CLASS_GENERAL: + *reg_list_size = 33; + break; + case REG_CLASS_ALL: + *reg_list_size = target->reg_cache->num_regs; + break; + default: + LOG_ERROR("Unsupported reg_class: %d", reg_class); + return ERROR_FAIL; + } + + *reg_list = calloc(*reg_list_size, sizeof(struct reg *)); + if (!*reg_list) + return ERROR_FAIL; + + for (int i = 0; i < *reg_list_size; i++) { + 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 && + target->reg_cache->reg_list[i].exist && + !target->reg_cache->reg_list[i].valid) { + if (target->reg_cache->reg_list[i].type->get( + &target->reg_cache->reg_list[i]) != ERROR_OK) + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int riscv_get_gdb_reg_list_noread(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size, + reg_class, false); +} + +static int riscv_get_gdb_reg_list(struct target *target, + struct reg **reg_list[], int *reg_list_size, + enum target_register_class reg_class) +{ + return riscv_get_gdb_reg_list_internal(target, reg_list, reg_list_size, + reg_class, true); +} + +static int riscv_arch_state(struct target *target) +{ + struct target_type *tt = get_target_type(target); + return tt->arch_state(target); +} + +/* Algorithm must end with a software breakpoint instruction. */ +static int riscv_run_algorithm(struct target *target, int num_mem_params, + struct mem_param *mem_params, int num_reg_params, + struct reg_param *reg_params, target_addr_t entry_point, + target_addr_t exit_point, int timeout_ms, void *arch_info) +{ + 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_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* 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); + + uint64_t saved_regs[32]; + for (int i = 0; i < num_reg_params; i++) { + LOG_DEBUG("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); + return ERROR_FAIL; + } + + if (r->size != reg_params[i].size) { + LOG_ERROR("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."); + return ERROR_FAIL; + } + + if (r->type->get(r) != ERROR_OK) + return ERROR_FAIL; + saved_regs[r->number] = buf_get_u64(r->value, 0, r->size); + + if (reg_params[i].direction == PARAM_OUT || reg_params[i].direction == PARAM_IN_OUT) { + if (r->type->set(r, reg_params[i].value) != ERROR_OK) + return ERROR_FAIL; + } + } + + /* Disable Interrupts before attempting to run the algorithm. */ + uint64_t current_mstatus; + uint64_t irq_disabled_mask = MSTATUS_MIE | MSTATUS_HIE | MSTATUS_SIE | MSTATUS_UIE; + if (riscv_interrupts_disable(target, irq_disabled_mask, ¤t_mstatus) != ERROR_OK) + return ERROR_FAIL; + + /* Run algorithm */ + LOG_DEBUG("resume at 0x%" TARGET_PRIxADDR, entry_point); + if (wch_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()"); + int64_t now = timeval_ms(); + if (now - start > timeout_ms) { + LOG_ERROR("Algorithm timed out after %" PRId64 " ms.", now - start); + wch_riscv_halt(target); + old_or_new_riscv_poll(target); + enum gdb_regno regnums[] = { + GDB_REGNO_RA, GDB_REGNO_SP, GDB_REGNO_GP, GDB_REGNO_TP, + GDB_REGNO_T0, GDB_REGNO_T1, GDB_REGNO_T2, GDB_REGNO_FP, + GDB_REGNO_S1, GDB_REGNO_A0, GDB_REGNO_A1, GDB_REGNO_A2, + GDB_REGNO_A3, GDB_REGNO_A4, GDB_REGNO_A5, GDB_REGNO_A6, + GDB_REGNO_A7, GDB_REGNO_S2, GDB_REGNO_S3, GDB_REGNO_S4, + GDB_REGNO_S5, GDB_REGNO_S6, GDB_REGNO_S7, GDB_REGNO_S8, + GDB_REGNO_S9, GDB_REGNO_S10, GDB_REGNO_S11, GDB_REGNO_T3, + GDB_REGNO_T4, GDB_REGNO_T5, GDB_REGNO_T6, + GDB_REGNO_PC, + GDB_REGNO_MSTATUS, GDB_REGNO_MEPC, GDB_REGNO_MCAUSE, + }; + for (unsigned i = 0; i < ARRAY_SIZE(regnums); i++) { + enum gdb_regno regno = regnums[i]; + riscv_reg_t reg_value; + if (riscv_get_register(target, ®_value, regno) != ERROR_OK) + break; + LOG_ERROR("%s = 0x%" PRIx64, gdb_regno_name(regno), reg_value); + } + return ERROR_TARGET_TIMEOUT; + } + + int result = old_or_new_riscv_poll(target); + if (result != ERROR_OK) + return result; + } + + /* 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%" + TARGET_PRIxADDR, final_pc, exit_point); + return ERROR_FAIL; + } + + /* Restore Interrupts */ + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) + return ERROR_FAIL; + + /* Restore registers */ + uint8_t buf[8] = { 0 }; + buf_set_u64(buf, 0, info->xlen, saved_pc); + if (reg_pc->type->set(reg_pc, buf) != ERROR_OK) + return ERROR_FAIL; + + for (int i = 0; i < num_reg_params; i++) { + if (reg_params[i].direction == PARAM_IN || + 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); + return ERROR_FAIL; + } + buf_cpy(r->value, reg_params[i].value, reg_params[i].size); + } + LOG_DEBUG("restore %s", reg_params[i].reg_name); + struct reg *r = register_get_by_name(target->reg_cache, reg_params[i].reg_name, false); + buf_set_u64(buf, 0, info->xlen, saved_regs[r->number]); + if (r->type->set(r, buf) != ERROR_OK) { + LOG_ERROR("set(%s) failed", r->name); + return ERROR_FAIL; + } + } + + return ERROR_OK; +} + +static int riscv_checksum_memory(struct target *target, + target_addr_t address, uint32_t count, + uint32_t *checksum) +{ + struct working_area *crc_algorithm; + struct reg_param reg_params[2]; + int retval; + + LOG_DEBUG("address=0x%" TARGET_PRIxADDR "; count=0x%" PRIx32, address, count); + + static const uint8_t riscv32_crc_code[] = { +#include "../../contrib/loaders/checksum/riscv32_crc.inc" + }; + static const uint8_t riscv64_crc_code[] = { +#include "../../contrib/loaders/checksum/riscv64_crc.inc" + }; + + static const uint8_t *crc_code; + + unsigned xlen = riscv_xlen(target); + unsigned crc_code_size; + if (xlen == 32) { + crc_code = riscv32_crc_code; + crc_code_size = sizeof(riscv32_crc_code); + } else { + crc_code = riscv64_crc_code; + crc_code_size = sizeof(riscv64_crc_code); + } + + if (count < crc_code_size * 4) { + /* Don't use the algorithm for relatively small buffers. It's faster + * just to read the memory. target_checksum_memory() will take care of + * that if we fail. */ + return ERROR_FAIL; + } + + retval = target_alloc_working_area(target, crc_code_size, &crc_algorithm); + if (retval != ERROR_OK) + return retval; + + if (crc_algorithm->address + crc_algorithm->size > address && + crc_algorithm->address < address + count) { + /* Region to checksum overlaps with the work area we've been assigned. + * Bail. (Would be better to manually checksum what we read there, and + * use the algorithm for the rest.) */ + target_free_working_area(target, crc_algorithm); + return ERROR_FAIL; + } + + 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", + crc_algorithm->address, retval); + target_free_working_area(target, crc_algorithm); + return retval; + } + + init_reg_param(®_params[0], "a0", xlen, PARAM_IN_OUT); + init_reg_param(®_params[1], "a1", xlen, PARAM_OUT); + buf_set_u64(reg_params[0].value, 0, xlen, address); + buf_set_u64(reg_params[1].value, 0, xlen, count); + + /* 20 second timeout/megabyte */ + int timeout = 20000 * (1 + (count / (1024 * 1024))); + + retval = target_run_algorithm(target, 0, NULL, 2, reg_params, + crc_algorithm->address, + 0, /* Leave exit point unspecified because we don't know. */ + timeout, NULL); + + if (retval == ERROR_OK) + *checksum = buf_get_u32(reg_params[0].value, 0, 32); + else + LOG_ERROR("error executing RISC-V CRC algorithm"); + + destroy_reg_param(®_params[0]); + destroy_reg_param(®_params[1]); + + target_free_working_area(target, crc_algorithm); + + LOG_DEBUG("checksum=0x%" PRIx32 ", result=%d", *checksum, retval); + + return retval; +} + +/*** OpenOCD Helper Functions ***/ + + static enum riscv_poll_hart { + RPH_NO_CHANGE, + RPH_DISCOVERED_HALTED, + RPH_DISCOVERED_RUNNING, + RPH_ERROR +}; +static enum riscv_poll_hart riscv_poll_hart(struct target *target, int hartid) +{ + RISCV_INFO(r); + + LOG_TARGET_DEBUG(target, "polling, target->state=%d", 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 (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 (wch_riscv_flush_registers(target) != ERROR_OK) + return ERROR_FAIL; + } + + 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 && target->state != TARGET_DEBUG_RUNNING && !halted) { + LOG_DEBUG(" triggered running"); + target->state = TARGET_RUNNING; + target->debug_reason = DBG_REASON_NOTHALTED; + return RPH_DISCOVERED_RUNNING; + } + + return RPH_NO_CHANGE; +} + +int wch_sample_memory(struct target *target) +{ + RISCV_INFO(r); + + if (!r->sample_buf.buf || !r->sample_config.enabled) + return ERROR_OK; + + LOG_DEBUG("buf used/size: %d/%d", r->sample_buf.used, r->sample_buf.size); + + uint64_t start = timeval_ms(); + riscv_sample_buf_maybe_add_timestamp(target, true); + int result = ERROR_OK; + if (r->sample_memory) { + result = r->sample_memory(target, &r->sample_buf, &r->sample_config, + start + TARGET_DEFAULT_POLLING_INTERVAL); + if (result != ERROR_NOT_IMPLEMENTED) + goto exit; + } + + /* Default slow path. */ + while (timeval_ms() - start < TARGET_DEFAULT_POLLING_INTERVAL) { + for (unsigned int i = 0; i < ARRAY_SIZE(r->sample_config.bucket); i++) { + if (r->sample_config.bucket[i].enabled && + r->sample_buf.used + 1 + r->sample_config.bucket[i].size_bytes < r->sample_buf.size) { + assert(i < RISCV_SAMPLE_BUF_TIMESTAMP_BEFORE); + r->sample_buf.buf[r->sample_buf.used] = i; + result = riscv_read_phys_memory( + target, r->sample_config.bucket[i].address, + r->sample_config.bucket[i].size_bytes, 1, + r->sample_buf.buf + r->sample_buf.used + 1); + if (result == ERROR_OK) + r->sample_buf.used += 1 + r->sample_config.bucket[i].size_bytes; + else + goto exit; + } + } + } + +exit: + riscv_sample_buf_maybe_add_timestamp(target, false); + if (result != ERROR_OK) { + LOG_INFO("Turning off memory sampling because it failed."); + r->sample_config.enabled = false; + } + return result; +} + +/*** OpenOCD Interface ***/ +int wch_riscv_openocd_poll(struct target *target) +{ + LOG_DEBUG("polling all harts"); + enum target_state old_state = target->state; + + 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; + if (!target_was_examined(t)) + continue; + enum riscv_poll_hart out = riscv_poll_hart(t, t->coreid); + 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); + if (set_debug_reason(t, halt_reason) != ERROR_OK) + return ERROR_FAIL; + + if (halt_reason == RISCV_HALT_BREAKPOINT) { + int retval; + switch (riscv_semihosting(t, &retval)) { + case SEMI_NONE: + case SEMI_WAITING: + /* This hart should remain halted. */ + should_remain_halted++; + break; + case SEMI_HANDLED: + /* This hart should be resumed, along with any other + * harts that halted due to haltgroups. */ + should_resume++; + break; + case SEMI_ERROR: + return retval; + } + } else if (halt_reason != RISCV_HALT_GROUP) { + should_remain_halted++; + } + break; + + case RPH_ERROR: + return ERROR_FAIL; + } + } + + 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"); + wch_riscv_halt(target); + } else if (should_resume) { + LOG_DEBUG("resume all"); + wch_riscv_resume(target, true, 0, 0, 0, false); + } + + /* Sample memory if any target is running. */ + foreach_smp_target(list, target->smp_targets) { + struct target *t = list->target; + if (t->state == TARGET_RUNNING) { + wch_sample_memory(target); + break; + } + } + + return ERROR_OK; + + } else { + enum riscv_poll_hart out = riscv_poll_hart(target, target->coreid); + if (out == RPH_NO_CHANGE || out == RPH_DISCOVERED_RUNNING) { + if (target->state == TARGET_RUNNING) + wch_sample_memory(target); + return ERROR_OK; + } else if (out == RPH_ERROR) { + return ERROR_FAIL; + } + + LOG_TARGET_DEBUG(target, "hart 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; + target->state = TARGET_HALTED; + } + + if (target->debug_reason == DBG_REASON_BREAKPOINT) { + int retval; + switch (riscv_semihosting(target, &retval)) { + case SEMI_NONE: + case SEMI_WAITING: + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + break; + case SEMI_HANDLED: + if (wch_riscv_resume(target, true, 0, 0, 0, false) != ERROR_OK) + return ERROR_FAIL; + break; + case SEMI_ERROR: + return retval; + } + } else { + if (old_state == TARGET_DEBUG_RUNNING) + target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED); + else + target_call_event_callbacks(target, TARGET_EVENT_HALTED); + } + + return ERROR_OK; +} + +int wch_riscv_openocd_step(struct target *target, int current, + target_addr_t address, int handle_breakpoints) +{ + LOG_TARGET_DEBUG(target, "stepping hart"); + + if (!current) { + if (riscv_set_register(target, GDB_REGNO_PC, address) != ERROR_OK) + return ERROR_FAIL; + } + + 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 && (wch_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; + + 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, + ¤t_mstatus) != ERROR_OK) { + success = false; + LOG_ERROR("unable to disable interrupts"); + goto _exit; + } + } + + if (riscv_step_rtos_hart(target) != ERROR_OK) { + success = false; + LOG_ERROR("unable to step rtos hart"); + } + + register_cache_invalidate(target->reg_cache); + + if (info->isrmask_mode == RISCV_ISRMASK_STEPONLY) + if (riscv_interrupts_restore(target, current_mstatus) != ERROR_OK) { + success = false; + LOG_ERROR("unable to restore interrupts"); + } + +_exit: + if (enable_triggers(target, trigger_state) != ERROR_OK) { + success = false; + LOG_ERROR("unable to enable triggers"); + } + + if (breakpoint && (wch_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; + 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 success ? ERROR_OK : ERROR_FAIL; +} + +/* Command Handlers */ +COMMAND_HANDLER(wch_riscv_set_command_timeout_sec) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + int timeout = atoi(CMD_ARGV[0]); + if (timeout <= 0) { + LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); + return ERROR_FAIL; + } + + riscv_command_timeout_sec = timeout; + + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_reset_timeout_sec) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + int timeout = atoi(CMD_ARGV[0]); + if (timeout <= 0) { + LOG_ERROR("%s is not a valid integer argument for command.", CMD_ARGV[0]); + return ERROR_FAIL; + } + + riscv_reset_timeout_sec = timeout; + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_prefer_sba) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + bool prefer_sba; + LOG_WARNING("`riscv set_prefer_sba` is deprecated. Please use `riscv set_mem_access` instead."); + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], prefer_sba); + if (prefer_sba) { + /* Use system bus with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + } else { + /* Use progbuf with highest priority */ + r->mem_access_methods[0] = RISCV_MEM_ACCESS_PROGBUF; + r->mem_access_methods[1] = RISCV_MEM_ACCESS_SYSBUS; + r->mem_access_methods[2] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_mem_access) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + int progbuf_cnt = 0; + int sysbus_cnt = 0; + int abstract_cnt = 0; + + if (CMD_ARGC < 1 || CMD_ARGC > RISCV_NUM_MEM_ACCESS_METHODS) { + LOG_ERROR("Command takes 1 to %d parameters", RISCV_NUM_MEM_ACCESS_METHODS); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Check argument validity */ + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) { + progbuf_cnt++; + } else if (strcmp("sysbus", CMD_ARGV[i]) == 0) { + sysbus_cnt++; + } else if (strcmp("abstract", CMD_ARGV[i]) == 0) { + abstract_cnt++; + } else { + LOG_ERROR("Unknown argument '%s'. " + "Must be one of: 'progbuf', 'sysbus' or 'abstract'.", CMD_ARGV[i]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + if (progbuf_cnt > 1 || sysbus_cnt > 1 || abstract_cnt > 1) { + LOG_ERROR("Syntax error - duplicate arguments to `riscv set_mem_access`."); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + /* Args are valid, store them */ + for (unsigned int i = 0; i < RISCV_NUM_MEM_ACCESS_METHODS; i++) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_UNSPECIFIED; + for (unsigned int i = 0; i < CMD_ARGC; i++) { + if (strcmp("progbuf", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_PROGBUF; + else if (strcmp("sysbus", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_SYSBUS; + else if (strcmp("abstract", CMD_ARGV[i]) == 0) + r->mem_access_methods[i] = RISCV_MEM_ACCESS_ABSTRACT; + } + + /* Reset warning flags */ + r->mem_access_progbuf_warn = true; + r->mem_access_sysbus_warn = true; + r->mem_access_abstract_warn = true; + + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_enable_virtual) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virtual); + return ERROR_OK; +} + + +COMMAND_HANDLER(wch_riscv_set_expose_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->expose_csr, CMD_ARGV[i], "csr", 0xfff); + if (ret != ERROR_OK) + break; + } + + return ret; +} + +COMMAND_HANDLER(wch_riscv_set_expose_custom) +{ + 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->expose_custom, CMD_ARGV[i], "custom", 0x3fff); + if (ret != ERROR_OK) + break; + } + + return ret; +} + +COMMAND_HANDLER(wch_riscv_authdata_read) +{ + unsigned int index = 0; + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + } else { + LOG_ERROR("Command takes at most one parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + if (!target) { + LOG_ERROR("target is NULL!"); + return ERROR_FAIL; + } + + RISCV_INFO(r); + if (!r) { + LOG_ERROR("riscv_info is NULL!"); + return ERROR_FAIL; + } + + if (r->authdata_read) { + uint32_t value; + if (r->authdata_read(target, &value, index) != ERROR_OK) + return ERROR_FAIL; + command_print_sameline(CMD, "0x%08" PRIx32, value); + return ERROR_OK; + } else { + LOG_ERROR("authdata_read is not implemented for this target."); + return ERROR_FAIL; + } +} + +COMMAND_HANDLER(wch_riscv_authdata_write) +{ + uint32_t value; + unsigned int index = 0; + + if (CMD_ARGC == 0) { + /* nop */ + } else if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], value); + } else if (CMD_ARGC == 2) { + COMMAND_PARSE_NUMBER(uint, CMD_ARGV[0], index); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + } else { + LOG_ERROR("Command takes at most 2 arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + if (r->authdata_write) { + return r->authdata_write(target, value, index); + } else { + LOG_ERROR("authdata_write is not implemented for this target."); + return ERROR_FAIL; + } +} + +COMMAND_HANDLER(wch_riscv_dmi_read) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + if (!target) { + LOG_ERROR("target is NULL!"); + return ERROR_FAIL; + } + + RISCV_INFO(r); + if (!r) { + LOG_ERROR("riscv_info is NULL!"); + return ERROR_FAIL; + } + + 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."); + return ERROR_FAIL; + } +} + + +COMMAND_HANDLER(wch_riscv_dmi_write) +{ + if (CMD_ARGC != 2) { + LOG_ERROR("Command takes exactly 2 arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + uint32_t address, value; + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value); + + if (r->dmi_write) { + /* Perform the DMI write */ + int retval = r->dmi_write(target, 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. + */ + bool progbufTouched = (address >= DM_PROGBUF0 && address <= DM_PROGBUF15); + bool dmDeactivated = (address == DM_DMCONTROL && (value & DM_DMCONTROL_DMACTIVE) == 0); + if (progbufTouched || dmDeactivated) { + if (r->invalidate_cached_debug_buffer) + r->invalidate_cached_debug_buffer(target); + } + + return retval; + } + + LOG_ERROR("dmi_write is not implemented for this target."); + return ERROR_FAIL; +} + +COMMAND_HANDLER(wch_riscv_test_sba_config_reg) +{ + if (CMD_ARGC != 4) { + LOG_ERROR("Command takes exactly 4 arguments"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + target_addr_t legal_address; + uint32_t num_words; + target_addr_t illegal_address; + bool run_sbbusyerror_test; + + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[0], legal_address); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], num_words); + COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[2], illegal_address); + COMMAND_PARSE_ON_OFF(CMD_ARGV[3], run_sbbusyerror_test); + + if (r->test_sba_config_reg) { + return r->test_sba_config_reg(target, legal_address, num_words, + illegal_address, run_sbbusyerror_test); + } else { + LOG_ERROR("test_sba_config_reg is not implemented for this target."); + return ERROR_FAIL; + } +} + +COMMAND_HANDLER(wch_riscv_reset_delays) +{ + int wait = 0; + + if (CMD_ARGC > 1) { + LOG_ERROR("Command takes at most one argument"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (CMD_ARGC == 1) + COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], wait); + + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + r->reset_delays_wait = wait; + return ERROR_OK; +} + + + +COMMAND_HANDLER(wch_riscv_resume_order) +{ + if (CMD_ARGC > 1) { + LOG_ERROR("Command takes at most one argument"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (!strcmp(CMD_ARGV[0], "normal")) { + resume_order = RO_NORMAL; + } else if (!strcmp(CMD_ARGV[0], "reversed")) { + resume_order = RO_REVERSED; + } else { + LOG_ERROR("Unsupported resume order: %s", CMD_ARGV[0]); + return ERROR_FAIL; + } + + return ERROR_OK; +} + + + + +COMMAND_HANDLER(wch_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(wch_riscv_set_enable_virt2phys) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_enable_virt2phys); + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_ebreakm) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreakm); + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_ebreaks) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaks); + return ERROR_OK; +} + +COMMAND_HANDLER(wch_riscv_set_ebreaku) +{ + if (CMD_ARGC != 1) { + LOG_ERROR("Command takes exactly 1 parameter"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + COMMAND_PARSE_ON_OFF(CMD_ARGV[0], riscv_ebreaku); + return ERROR_OK; +} + +COMMAND_HANDLER(wch_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(wch_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 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_ERROR("Max bucket number is %d.", (unsigned) 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_ERROR("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(wch_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_ERROR("Failed base64 encode!"); + result = ERROR_FAIL; + goto error; + } + command_print(CMD, "%s", encoded); + free(encoded); + } else { + unsigned 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_ERROR("Found invalid size in bucket %d: %d", command, + r->sample_config.bucket[command].size_bytes); + result = ERROR_FAIL; + goto error; + } + } else { + LOG_ERROR("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; +} + + + +COMMAND_HANDLER(wch_handle_info) +{ + struct target *target = get_current_target(CMD_CTX); + RISCV_INFO(r); + + /* This output format can be fed directly into TCL's "array set". */ + + riscv_print_info_line(CMD, "hart", "xlen", riscv_xlen(target)); + riscv_enumerate_triggers(target); + riscv_print_info_line(CMD, "hart", "trigger_count", + r->trigger_count); + + if (r->print_info) + return CALL_COMMAND_HANDLER(r->print_info, target); + + return 0; +} + +static const struct command_registration riscv_exec_command_handlers[] = { + { + .name = "dump_sample_buf", + .handler = wch_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 = wch_handle_info, + .mode = COMMAND_ANY, + .usage = "", + .help = "Displays some information OpenOCD detected about the target." + }, + { + .name = "memory_sample", + .handler = wch_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 = wch_handle_repeat_read, + .mode = COMMAND_ANY, + .usage = "count address [size=4]", + .help = "Repeatedly read the value at address." + }, + { + .name = "set_command_timeout_sec", + .handler = wch_riscv_set_command_timeout_sec, + .mode = COMMAND_ANY, + .usage = "[sec]", + .help = "Set the wall-clock timeout (in seconds) for individual commands" + }, + { + .name = "set_reset_timeout_sec", + .handler = wch_riscv_set_reset_timeout_sec, + .mode = COMMAND_ANY, + .usage = "[sec]", + .help = "Set the wall-clock timeout (in seconds) after reset is deasserted" + }, + { + .name = "set_prefer_sba", + .handler = wch_riscv_set_prefer_sba, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "When on, prefer to use System Bus Access to access memory. " + "When off (default), prefer to use the Program Buffer to access memory." + }, + { + .name = "set_mem_access", + .handler = wch_riscv_set_mem_access, + .mode = COMMAND_ANY, + .usage = "method1 [method2] [method3]", + .help = "Set which memory access methods shall be used and in which order " + "of priority. Method can be one of: 'progbuf', 'sysbus' or 'abstract'." + }, + { + .name = "set_enable_virtual", + .handler = wch_riscv_set_enable_virtual, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "When on, memory accesses are performed on physical or virtual " + "memory depending on the current system configuration. " + "When off (default), all memory accessses are performed on physical memory." + }, + { + .name = "expose_csrs", + .handler = wch_riscv_set_expose_csrs, + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", + .help = "Configure a list of inclusive ranges for CSRs to expose in " + "addition to the standard ones. This must be executed before " + "`init`." + }, + { + .name = "expose_custom", + .handler = wch_riscv_set_expose_custom, + .mode = COMMAND_CONFIG, + .usage = "n0[-m0|=name0][,n1[-m1|=name1]]...", + .help = "Configure a list of inclusive ranges for custom registers to " + "expose. custom0 is accessed as abstract register number 0xc000, " + "etc. This must be executed before `init`." + }, + { + .name = "authdata_read", + .handler = wch_riscv_authdata_read, + .usage = "[index]", + .mode = COMMAND_ANY, + .help = "Return the 32-bit value read from authdata or authdata0 " + "(index=0), or authdata1 (index=1)." + }, + { + .name = "authdata_write", + .handler = wch_riscv_authdata_write, + .mode = COMMAND_ANY, + .usage = "[index] value", + .help = "Write the 32-bit value to authdata or authdata0 (index=0), " + "or authdata1 (index=1)." + }, + { + .name = "dmi_read", + .handler = wch_riscv_dmi_read, + .mode = COMMAND_ANY, + .usage = "address", + .help = "Perform a 32-bit DMI read at address, returning the value." + }, + { + .name = "dmi_write", + .handler = wch_riscv_dmi_write, + .mode = COMMAND_ANY, + .usage = "address value", + .help = "Perform a 32-bit DMI write of value at address." + }, + { + .name = "test_sba_config_reg", + .handler = wch_riscv_test_sba_config_reg, + .mode = COMMAND_ANY, + .usage = "legal_address num_words " + "illegal_address run_sbbusyerror_test[on/off]", + .help = "Perform a series of tests on the SBCS register. " + "Inputs are a legal, 128-byte aligned address and a number of words to " + "read/write starting at that address (i.e., address range [legal address, " + "legal_address+word_size*num_words) must be legally readable/writable), " + "an illegal, 128-byte aligned address for error flag/handling cases, " + "and whether sbbusyerror test should be run." + }, + { + .name = "reset_delays", + .handler = wch_riscv_reset_delays, + .mode = COMMAND_ANY, + .usage = "[wait]", + .help = "OpenOCD learns how many Run-Test/Idle cycles are required " + "between scans to avoid encountering the target being busy. This " + "command resets those learned values after `wait` scans. It's only " + "useful for testing OpenOCD itself." + }, + { + .name = "resume_order", + .handler = wch_riscv_resume_order, + .mode = COMMAND_ANY, + .usage = "normal|reversed", + .help = "Choose the order that harts are resumed in when `hasel` is not " + "supported. Normal order is from lowest hart index to highest. " + "Reversed order is from highest hart index to lowest." + }, + + { + .name = "set_maskisr", + .handler = wch_riscv_set_maskisr, + .mode = COMMAND_EXEC, + .help = "mask riscv interrupts", + .usage = "['off'|'steponly']", + }, + { + .name = "set_enable_virt2phys", + .handler = wch_riscv_set_enable_virt2phys, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "When on (default), enable translation from virtual address to " + "physical address." + }, + { + .name = "set_ebreakm", + .handler = wch_riscv_set_ebreakm, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreakm. When off, M-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, + { + .name = "set_ebreaks", + .handler = wch_riscv_set_ebreaks, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreaks. When off, S-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, + { + .name = "set_ebreaku", + .handler = wch_riscv_set_ebreaku, + .mode = COMMAND_ANY, + .usage = "on|off", + .help = "Control dcsr.ebreaku. When off, U-mode ebreak instructions " + "don't trap to OpenOCD. Defaults to on." + }, + COMMAND_REGISTRATION_DONE +}; + +/* + * To be noted that RISC-V targets use the same semihosting commands as + * ARM targets. + * + * The main reason is compatibility with existing tools. For example the + * Eclipse OpenOCD/SEGGER J-Link/QEMU plug-ins have several widgets to + * configure semihosting, which generate commands like `arm semihosting + * enable`. + * A secondary reason is the fact that the protocol used is exactly the + * one specified by ARM. If RISC-V will ever define its own semihosting + * protocol, then a command like `riscv semihosting enable` will make + * sense, but for now all semihosting commands are prefixed with `arm`. + */ +extern const struct command_registration semihosting_common_handlers[]; + +const struct command_registration wch_riscv_command_handlers[] = { + { + .name = "riscv", + .mode = COMMAND_ANY, + .help = "RISC-V Command Group", + .usage = "", + .chain = riscv_exec_command_handlers + }, + { + .name = "arm", + .mode = COMMAND_ANY, + .help = "ARM Command Group", + .usage = "", + .chain = semihosting_common_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +static unsigned riscv_xlen_nonconst(struct target *target) +{ + return riscv_xlen(target); +} + +static unsigned int riscv_data_bits(struct target *target) +{ + RISCV_INFO(r); + if (r->data_bits) + return r->data_bits(target); + return riscv_xlen(target); +} + +struct target_type wch_riscv_target = { + .name = "wch_riscv", + + .target_create = wch_riscv_create_target, + .init_target = wch_riscv_init_target, + .deinit_target = wch_riscv_deinit_target, + .examine = wch_riscv_examine, + + /* poll current target status */ + .poll = old_or_new_riscv_poll, + + .halt = wch_riscv_halt, + .resume = riscv_target_resume, + .step = old_or_new_riscv_step, + + .assert_reset = riscv_assert_reset, + .deassert_reset = riscv_deassert_reset, + + .read_memory = riscv_read_memory, + .write_memory = riscv_write_memory, + .read_phys_memory = riscv_read_phys_memory, + .write_phys_memory = riscv_write_phys_memory, + + .checksum_memory = riscv_checksum_memory, + + .mmu = riscv_mmu, + .virt2phys = riscv_virt2phys, + + .get_gdb_arch = riscv_get_gdb_arch, + .get_gdb_reg_list = riscv_get_gdb_reg_list, + .get_gdb_reg_list_noread = riscv_get_gdb_reg_list_noread, + + .add_breakpoint = wch_riscv_add_breakpoint, + .remove_breakpoint = wch_riscv_remove_breakpoint, + + .add_watchpoint = wch_riscv_add_watchpoint, + .remove_watchpoint = wch_riscv_remove_watchpoint, + .hit_watchpoint = riscv_hit_watchpoint, + + .arch_state = riscv_arch_state, + + .run_algorithm = riscv_run_algorithm, + + .commands = wch_riscv_command_handlers, + + .address_bits = riscv_xlen_nonconst, + .data_bits = riscv_data_bits +}; + + + +static int riscv_resume_go_all_harts(struct target *target) +{ + RISCV_INFO(r); + + LOG_TARGET_DEBUG(target, "resuming hart, state=%d", target->state); + if (riscv_is_halted(target)) { + if (r->resume_go(target) != ERROR_OK) + return ERROR_FAIL; + } else { + LOG_DEBUG("[%s] hart requested resume, but was already resumed", + target_name(target)); + } + + riscv_invalidate_register_cache(target); + return ERROR_OK; +} + + + + + +/** + * If write is true: + * return true iff we are guaranteed that the register will contain exactly + * the value we just wrote when it's read. + * If write is false: + * 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) +{ + /* GPRs, FPRs, vector registers are just normal data stores. */ + if (regno <= GDB_REGNO_XPR31 || + (regno >= GDB_REGNO_FPR0 && regno <= GDB_REGNO_FPR31) || + (regno >= GDB_REGNO_V0 && regno <= GDB_REGNO_V31)) + return true; + + /* Most CSRs won't change value on us, but we can't assume it about arbitrary + * CSRs. */ + switch (regno) { + case GDB_REGNO_DPC: + return true; + + case GDB_REGNO_VSTART: + case GDB_REGNO_VXSAT: + case GDB_REGNO_VXRM: + case GDB_REGNO_VLENB: + case GDB_REGNO_VL: + case GDB_REGNO_VTYPE: + case GDB_REGNO_MISA: + case GDB_REGNO_DCSR: + case GDB_REGNO_DSCRATCH0: + case GDB_REGNO_MSTATUS: + case GDB_REGNO_MEPC: + case GDB_REGNO_MCAUSE: + case GDB_REGNO_SATP: + /* + * WARL registers might not contain the value we just wrote, but + * these ones won't spontaneously change their value either. * + */ + return !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. */ + default: + return false; + } +} + + + +static int register_get(struct reg *reg) +{ + riscv_reg_info_t *reg_info = reg->arch_info; + struct target *target = reg_info->target; + RISCV_INFO(r); + + 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)); + return ERROR_FAIL; + } + + if (r->get_register_buf(target, reg->value, reg->number) != ERROR_OK) + return ERROR_FAIL; + } else { + uint64_t value; + int result = riscv_get_register(target, &value, reg->number); + if (result != ERROR_OK) + 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); + free(str); + return ERROR_OK; +} + +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; + 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); + free(str); + + /* Exit early for writing x0, which on the hardware would be ignored, and we + * don't want to update our cache. */ + if (reg->number == GDB_REGNO_ZERO) + return ERROR_OK; + + memcpy(reg->value, buf, DIV_ROUND_UP(reg->size, 8)); + reg->valid = gdb_regno_cacheable(reg->number, true); + + if (reg->number == GDB_REGNO_TDATA1 || + reg->number == GDB_REGNO_TDATA2) { + r->manual_hwbp_set = true; + /* When enumerating triggers, we clear any triggers with DMODE set, + * assuming they were left over from a previous debug session. So make + * sure that is done before a user might be setting their own triggers. + */ + if (riscv_enumerate_triggers(target) != ERROR_OK) + return ERROR_FAIL; + } + + 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)); + return ERROR_FAIL; + } + + if (r->set_register_buf(target, reg->number, reg->value) != ERROR_OK) + return ERROR_FAIL; + } else { + uint64_t value = buf_get_u64(buf, 0, reg->size); + if (riscv_set_register(target, reg->number, value) != ERROR_OK) + return ERROR_FAIL; + } + + return ERROR_OK; +} + +static struct reg_arch_type riscv_reg_arch_type = { + .get = register_get, + .set = register_set +}; + +static struct csr_info { + unsigned number; + const char *name; +}; + +static int cmp_csr_info(const void *p1, const void *p2) +{ + return (int) (((struct csr_info *)p1)->number) - (int) (((struct csr_info *)p2)->number); +} + + + + + diff --git a/src/target/wch_riscv.h b/src/target/wch_riscv.h new file mode 100755 index 000000000..24c06c00a --- /dev/null +++ b/src/target/wch_riscv.h @@ -0,0 +1,26 @@ +#include + +#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask))) + +extern const virt2phys_info_t sv32; +extern const virt2phys_info_t sv39; +extern const virt2phys_info_t sv48; + + +extern void riscv_sample_buf_maybe_add_timestamp(struct target *target, bool before); +extern const char *riscv_get_gdb_arch(struct target *target); +extern int parse_ranges(struct list_head *ranges, const char *tcl_arg, const char *reg_type, unsigned int max_val); + +extern const char *gdb_regno_name(enum gdb_regno regno); +extern struct target_type wch_riscv013_target; + + + +extern bool riscv_enable_virt2phys ; +extern bool riscv_ebreakm ; +extern bool riscv_ebreaks ; +extern bool riscv_ebreaku ; + +extern bool riscv_enable_virtual; +extern int wch_riscv_openocd_poll(struct target *target); \ No newline at end of file diff --git a/src/target/x86_32_common.c b/src/target/x86_32_common.c index a009bfe92..d119be148 100644 --- a/src/target/x86_32_common.c +++ b/src/target/x86_32_common.c @@ -862,7 +862,7 @@ int x86_32_common_remove_watchpoint(struct target *t, struct watchpoint *wp) { if (check_not_halted(t)) return ERROR_TARGET_NOT_HALTED; - if (wp->set) + if (wp->is_set) unset_watchpoint(t, wp); return ERROR_OK; } @@ -883,7 +883,7 @@ int x86_32_common_remove_breakpoint(struct target *t, struct breakpoint *bp) LOG_DEBUG("type=%d, addr=" TARGET_ADDR_FMT, bp->type, bp->address); if (check_not_halted(t)) return ERROR_TARGET_NOT_HALTED; - if (bp->set) + if (bp->is_set) unset_breakpoint(t, bp); return ERROR_OK; @@ -995,7 +995,7 @@ static int set_hwbp(struct target *t, struct breakpoint *bp) } if (set_debug_regs(t, bp->address, hwbp_num, DR7_BP_EXECUTE, 1) != ERROR_OK) return ERROR_FAIL; - bp->set = hwbp_num + 1; + breakpoint_hw_set(bp, hwbp_num); debug_reg_list[hwbp_num].used = 1; debug_reg_list[hwbp_num].bp_value = bp->address; LOG_USER("%s hardware breakpoint %" PRIu32 " set at 0x%08" PRIx32 " (hwreg=%" PRIu8 ")", __func__, @@ -1007,9 +1007,9 @@ static int unset_hwbp(struct target *t, struct breakpoint *bp) { struct x86_32_common *x86_32 = target_to_x86_32(t); struct x86_32_dbg_reg *debug_reg_list = x86_32->hw_break_list; - int hwbp_num = bp->set - 1; + int hwbp_num = bp->number; - if ((hwbp_num < 0) || (hwbp_num >= x86_32->num_hw_bpoints)) { + if (hwbp_num >= x86_32->num_hw_bpoints) { LOG_ERROR("%s invalid breakpoint number=%d, bpid=%" PRIu32, __func__, hwbp_num, bp->unique_id); return ERROR_OK; @@ -1055,7 +1055,7 @@ static int set_swbp(struct target *t, struct breakpoint *bp) __func__, readback, *bp->orig_instr); return ERROR_FAIL; } - bp->set = SW_BP_OPCODE; /* just non 0 */ + bp->is_set = true; /* add the memory patch */ struct swbp_mem_patch *new_patch = malloc(sizeof(struct swbp_mem_patch)); @@ -1134,7 +1134,7 @@ static int set_breakpoint(struct target *t, struct breakpoint *bp) int error = ERROR_OK; struct x86_32_common *x86_32 = target_to_x86_32(t); LOG_DEBUG("type=%d, addr=" TARGET_ADDR_FMT, bp->type, bp->address); - if (bp->set) { + if (bp->is_set) { LOG_ERROR("breakpoint already set"); return error; } @@ -1164,7 +1164,7 @@ static int set_breakpoint(struct target *t, struct breakpoint *bp) static int unset_breakpoint(struct target *t, struct breakpoint *bp) { LOG_DEBUG("type=%d, addr=" TARGET_ADDR_FMT, bp->type, bp->address); - if (!bp->set) { + if (!bp->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } @@ -1182,7 +1182,7 @@ static int unset_breakpoint(struct target *t, struct breakpoint *bp) return ERROR_FAIL; } } - bp->set = 0; + bp->is_set = false; return ERROR_OK; } @@ -1193,7 +1193,7 @@ static int set_watchpoint(struct target *t, struct watchpoint *wp) int wp_num = 0; LOG_DEBUG("type=%d, addr=" TARGET_ADDR_FMT, wp->rw, wp->address); - if (wp->set) { + if (wp->is_set) { LOG_ERROR("%s watchpoint already set", __func__); return ERROR_OK; } @@ -1233,7 +1233,7 @@ static int set_watchpoint(struct target *t, struct watchpoint *wp) LOG_ERROR("%s only 'access' or 'write' watchpoints are supported", __func__); break; } - wp->set = wp_num + 1; + watchpoint_set(wp, wp_num); debug_reg_list[wp_num].used = 1; debug_reg_list[wp_num].bp_value = wp->address; LOG_USER("'%s' watchpoint %d set at " TARGET_ADDR_FMT " with length %" PRIu32 " (hwreg=%d)", @@ -1248,13 +1248,13 @@ static int unset_watchpoint(struct target *t, struct watchpoint *wp) struct x86_32_common *x86_32 = target_to_x86_32(t); struct x86_32_dbg_reg *debug_reg_list = x86_32->hw_break_list; LOG_DEBUG("type=%d, addr=" TARGET_ADDR_FMT, wp->rw, wp->address); - if (!wp->set) { + if (!wp->is_set) { LOG_WARNING("watchpoint not set"); return ERROR_OK; } - int wp_num = wp->set - 1; - if ((wp_num < 0) || (wp_num >= x86_32->num_hw_bpoints)) { + int wp_num = wp->number; + if (wp_num >= x86_32->num_hw_bpoints) { LOG_DEBUG("Invalid FP Comparator number in watchpoint"); return ERROR_OK; } @@ -1263,7 +1263,7 @@ static int unset_watchpoint(struct target *t, struct watchpoint *wp) debug_reg_list[wp_num].used = 0; debug_reg_list[wp_num].bp_value = 0; - wp->set = 0; + wp->is_set = false; LOG_USER("'%s' watchpoint %d removed from " TARGET_ADDR_FMT " with length %" PRIu32 " (hwreg=%d)", wp->rw == WPT_READ ? "read" : wp->rw == WPT_WRITE ? diff --git a/src/target/xscale.c b/src/target/xscale.c index dd383b6e4..78bd09922 100644 --- a/src/target/xscale.c +++ b/src/target/xscale.c @@ -1087,7 +1087,7 @@ static void xscale_enable_watchpoints(struct target *target) struct watchpoint *watchpoint = target->watchpoints; while (watchpoint) { - if (watchpoint->set == 0) + if (!watchpoint->is_set) xscale_set_watchpoint(target, watchpoint); watchpoint = watchpoint->next; } @@ -1099,7 +1099,7 @@ static void xscale_enable_breakpoints(struct target *target) /* set any pending breakpoints */ while (breakpoint) { - if (breakpoint->set == 0) + if (!breakpoint->is_set) xscale_set_breakpoint(target, breakpoint); breakpoint = breakpoint->next; } @@ -1506,7 +1506,7 @@ static int xscale_deassert_reset(struct target *target) /* mark all hardware breakpoints as unset */ while (breakpoint) { if (breakpoint->type == BKPT_HARD) - breakpoint->set = 0; + breakpoint->is_set = false; breakpoint = breakpoint->next; } @@ -2088,7 +2088,7 @@ static int xscale_set_breakpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (breakpoint->set) { + if (breakpoint->is_set) { LOG_WARNING("breakpoint already set"); return ERROR_OK; } @@ -2098,11 +2098,13 @@ static int xscale_set_breakpoint(struct target *target, if (!xscale->ibcr0_used) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR0], value); xscale->ibcr0_used = 1; - breakpoint->set = 1; /* breakpoint set on first breakpoint register */ + /* breakpoint set on first breakpoint register */ + breakpoint_hw_set(breakpoint, 0); } else if (!xscale->ibcr1_used) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR1], value); xscale->ibcr1_used = 1; - breakpoint->set = 2; /* breakpoint set on second breakpoint register */ + /* breakpoint set on second breakpoint register */ + breakpoint_hw_set(breakpoint, 1); } else {/* bug: availability previously verified in xscale_add_breakpoint() */ LOG_ERROR("BUG: no hardware comparator available"); return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; @@ -2133,7 +2135,7 @@ static int xscale_set_breakpoint(struct target *target, if (retval != ERROR_OK) return retval; } - breakpoint->set = 1; + breakpoint->is_set = true; xscale_send_u32(target, 0x50); /* clean dcache */ xscale_send_u32(target, xscale->cache_clean_address); @@ -2176,20 +2178,20 @@ static int xscale_unset_breakpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (!breakpoint->set) { + if (!breakpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } if (breakpoint->type == BKPT_HARD) { - if (breakpoint->set == 1) { + if (breakpoint->number == 0) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR0], 0x0); xscale->ibcr0_used = 0; - } else if (breakpoint->set == 2) { + } else if (breakpoint->number == 1) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_IBCR1], 0x0); xscale->ibcr1_used = 0; } - breakpoint->set = 0; + breakpoint->is_set = false; } else { /* restore original instruction (kept in target endianness) */ if (breakpoint->length == 4) { @@ -2203,7 +2205,7 @@ static int xscale_unset_breakpoint(struct target *target, if (retval != ERROR_OK) return retval; } - breakpoint->set = 0; + breakpoint->is_set = false; xscale_send_u32(target, 0x50); /* clean dcache */ xscale_send_u32(target, xscale->cache_clean_address); @@ -2223,7 +2225,7 @@ static int xscale_remove_breakpoint(struct target *target, struct breakpoint *br return ERROR_TARGET_NOT_HALTED; } - if (breakpoint->set) + if (breakpoint->is_set) xscale_unset_breakpoint(target, breakpoint); if (breakpoint->type == BKPT_HARD) @@ -2279,13 +2281,13 @@ static int xscale_set_watchpoint(struct target *target, xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR0], watchpoint->address); dbcon_value |= enable; xscale_set_reg_u32(dbcon, dbcon_value); - watchpoint->set = 1; + watchpoint_set(watchpoint, 0); xscale->dbr0_used = 1; } else if (!xscale->dbr1_used) { xscale_set_reg_u32(&xscale->reg_cache->reg_list[XSCALE_DBR1], watchpoint->address); dbcon_value |= enable << 2; xscale_set_reg_u32(dbcon, dbcon_value); - watchpoint->set = 2; + watchpoint_set(watchpoint, 1); xscale->dbr1_used = 1; } else { LOG_ERROR("BUG: no hardware comparator available"); @@ -2349,12 +2351,12 @@ static int xscale_unset_watchpoint(struct target *target, return ERROR_TARGET_NOT_HALTED; } - if (!watchpoint->set) { + if (!watchpoint->is_set) { LOG_WARNING("breakpoint not set"); return ERROR_OK; } - if (watchpoint->set == 1) { + if (watchpoint->number == 0) { if (watchpoint->length > 4) { dbcon_value &= ~0x103; /* clear DBCON[M] as well */ xscale->dbr1_used = 0; /* DBR1 was used for mask */ @@ -2363,12 +2365,12 @@ static int xscale_unset_watchpoint(struct target *target, xscale_set_reg_u32(dbcon, dbcon_value); xscale->dbr0_used = 0; - } else if (watchpoint->set == 2) { + } else if (watchpoint->number == 1) { dbcon_value &= ~0xc; xscale_set_reg_u32(dbcon, dbcon_value); xscale->dbr1_used = 0; } - watchpoint->set = 0; + watchpoint->is_set = false; return ERROR_OK; } @@ -2382,7 +2384,7 @@ static int xscale_remove_watchpoint(struct target *target, struct watchpoint *wa return ERROR_TARGET_NOT_HALTED; } - if (watchpoint->set) + if (watchpoint->is_set) xscale_unset_watchpoint(target, watchpoint); if (watchpoint->length > 4) diff --git a/src/transport/transport.h b/src/transport/transport.h old mode 100644 new mode 100755 index e04f78063..b51e51339 --- a/src/transport/transport.h +++ b/src/transport/transport.h @@ -97,7 +97,7 @@ bool transport_is_swd(void); bool transport_is_dapdirect_jtag(void); bool transport_is_dapdirect_swd(void); bool transport_is_swim(void); - +bool transport_is_sdi(void); #if BUILD_HLADAPTER bool transport_is_hla(void); #else diff --git a/tcl/board/at91cap7a-stk-sdram.cfg b/tcl/board/at91cap7a-stk-sdram.cfg index 8a371e064..182a4067f 100644 --- a/tcl/board/at91cap7a-stk-sdram.cfg +++ b/tcl/board/at91cap7a-stk-sdram.cfg @@ -32,8 +32,7 @@ $_TARGETNAME configure -event reset-start { } proc peek32 {address} { - mem2array t 32 $address 1 - return $t(0) + return [read_memory $address 32 1] } # Wait for an expression to be true with a timeout diff --git a/tcl/board/at91sam9g20-ek.cfg b/tcl/board/at91sam9g20-ek.cfg index e1cbb9120..04d9a197c 100644 --- a/tcl/board/at91sam9g20-ek.cfg +++ b/tcl/board/at91sam9g20-ek.cfg @@ -40,9 +40,7 @@ at91sam9 rdy_busy 0 0xfffff800 13 at91sam9 ce 0 0xfffff800 14 proc read_register {register} { - set result "" - mem2array result 32 $register 1 - return $result(0) + return [read_memory $register 32 1] } proc at91sam9g20_reset_start { } { diff --git a/tcl/board/embedded-artists_lpc2478-32.cfg b/tcl/board/embedded-artists_lpc2478-32.cfg index 38f5e1b8e..a73d83263 100644 --- a/tcl/board/embedded-artists_lpc2478-32.cfg +++ b/tcl/board/embedded-artists_lpc2478-32.cfg @@ -8,9 +8,7 @@ source [find target/lpc2478.cfg] # Helper # proc read_register {register} { - set result "" - mem2array result 32 $register 1 - return $result(0) + return [read_memory $register 32 1] } proc init_board {} { diff --git a/tcl/board/esp32c2-ftdi.cfg b/tcl/board/esp32c2-ftdi.cfg new file mode 100644 index 000000000..bc2b82f5a --- /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-ftdi.cfg b/tcl/board/esp32c3-ftdi.cfg new file mode 100644 index 000000000..55953742c --- /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/evb-lan9255.cfg b/tcl/board/evb-lan9255.cfg new file mode 100644 index 000000000..3fd6f603b --- /dev/null +++ b/tcl/board/evb-lan9255.cfg @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Microchip LAN9255 evaluation board +# https://www.microchip.com/en-us/development-tool/EV25Y25A +# + +set CHIPNAME same53 + +source [find target/atsame5x.cfg] + +reset_config srst_only diff --git a/tcl/board/hilscher_nxhx10.cfg b/tcl/board/hilscher_nxhx10.cfg index 1875dacc0..6e2eba79e 100644 --- a/tcl/board/hilscher_nxhx10.cfg +++ b/tcl/board/hilscher_nxhx10.cfg @@ -26,9 +26,7 @@ proc flash_init { } { } proc mread32 {addr} { - set value(0) 0 - mem2array value 32 $addr 1 - return $value(0) + return [read_memory $addr 32 1] } proc init_clocks { } { diff --git a/tcl/board/icnova_sam9g45_sodimm.cfg b/tcl/board/icnova_sam9g45_sodimm.cfg index 8a0736b1f..91e0107c2 100644 --- a/tcl/board/icnova_sam9g45_sodimm.cfg +++ b/tcl/board/icnova_sam9g45_sodimm.cfg @@ -43,9 +43,7 @@ flash bank $_FLASHNAME cfi 0x10000000 0x00800000 2 2 $_TARGETNAME proc read_register {register} { - set result "" - mem2array result 32 $register 1 - return $result(0) + return [read_memory $register 32 1] } proc at91sam9g45_start { } { diff --git a/tcl/board/lemaker_hikey.cfg b/tcl/board/lemaker_hikey.cfg index ee677c34b..325b6fdda 100644 --- a/tcl/board/lemaker_hikey.cfg +++ b/tcl/board/lemaker_hikey.cfg @@ -17,7 +17,7 @@ proc core_up { args } { global _TARGETNAME # examine remaining cores - foreach _core [set args] { + foreach _core $args { ${_TARGETNAME}$_core arp_examine } } diff --git a/tcl/board/nxp_frdm-k64f.cfg b/tcl/board/nxp_frdm-k64f.cfg new file mode 100644 index 000000000..1581c9594 --- /dev/null +++ b/tcl/board/nxp_frdm-k64f.cfg @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# This is an NXP Freedom eval board with a single MK64FN1M0VLL12 chip. +# https://www.nxp.com/design/development-boards/freedom-development-boards/mcu-boards/freedom-development-platform-for-kinetis-k64-k63-and-k24-mcus:FRDM-K64F +# + +source [find interface/cmsis-dap.cfg] + +# Set working area to 16 KiB +set WORKAREASIZE 0x4000 + +set CHIPNAME k64f +reset_config srst_only + +source [find target/kx.cfg] diff --git a/tcl/board/nxp_rdb-ls1046a.cfg b/tcl/board/nxp_rdb-ls1046a.cfg new file mode 100644 index 000000000..fde1829fb --- /dev/null +++ b/tcl/board/nxp_rdb-ls1046a.cfg @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# NXP LS1046ARDB (Reference Design Board) +# This is for the "console" USB port on the front panel +# You must ensure that SW4-7 is in the "off" position + +# NXP K20 +# The firmware implements the old CMSIS-DAP v1 USB HID interface +# You must pass --enable-cmsis-dap to ./configure to enable it +source [find interface/cmsis-dap.cfg] + +transport select jtag +reset_config srst_only + +source [find target/ls1046a.cfg] + +# The adapter can't handle 10MHz +adapter speed 5000 diff --git a/tcl/board/nxp_rdb-ls1088a.cfg b/tcl/board/nxp_rdb-ls1088a.cfg new file mode 100644 index 000000000..40483f2d6 --- /dev/null +++ b/tcl/board/nxp_rdb-ls1088a.cfg @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# NXP LS1088ARDB (Reference Design Board) +# This is for the "main" JTAG connector J55 + +transport select jtag +reset_config srst_only + +# To access the CPLD, populate J48 and add `-c 'set CWTAP 1'` to your command +# line. At the time of this writing, programming is unsupported. +if { [info exists CWTAP] } { + source [find cpld/altera-epm240.cfg] +} else { + source [find target/ls1088a.cfg] +} diff --git a/tcl/board/ti_am625evm.cfg b/tcl/board/ti_am625evm.cfg new file mode 100644 index 000000000..4906fd096 --- /dev/null +++ b/tcl/board/ti_am625evm.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2021-2022 Texas Instruments Incorporated - http://www.ti.com/ +# +# Texas Instruments am625 EVM/SK +# Link: https://www.ti.com/lit/zip/sprr448 +# + +# AM625 EVM has an xds110 onboard. +source [find interface/xds110.cfg] + +transport select jtag + +# default JTAG configuration has only SRST and no TRST +reset_config srst_only srst_push_pull + +# delay after SRST goes inactive +adapter srst delay 20 + +if { ![info exists SOC] } { + set SOC am625 +} + +source [find target/ti_k3.cfg] + +adapter speed 2500 diff --git a/tcl/board/ti_j721s2evm.cfg b/tcl/board/ti_j721s2evm.cfg new file mode 100644 index 000000000..72418b57b --- /dev/null +++ b/tcl/board/ti_j721s2evm.cfg @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Copyright (C) 2021 Texas Instruments Incorporated - http://www.ti.com/ +# +# Texas Instruments J721s2 EVM +# Link(SoM): https://www.ti.com/lit/zip/sprr439 +# + +# J721s2 EVM has an xds110 onboard. +source [find interface/xds110.cfg] + +transport select jtag + +# default JTAG configuration has only SRST and no TRST +reset_config srst_only srst_push_pull + +# delay after SRST goes inactive +adapter srst delay 20 + +if { ![info exists SOC] } { + set SOC j721s2 +} + +source [find target/ti_k3.cfg] + +adapter speed 2500 diff --git a/tcl/board/tocoding_poplar.cfg b/tcl/board/tocoding_poplar.cfg index 6d2e6354e..36d5aec47 100644 --- a/tcl/board/tocoding_poplar.cfg +++ b/tcl/board/tocoding_poplar.cfg @@ -19,7 +19,7 @@ proc core_up { args } { global _TARGETNAME # examine remaining cores - foreach _core [set args] { + foreach _core $args { ${_TARGETNAME}$_core arp_examine } } diff --git a/tcl/board/vd_a53x2_jtag.cfg b/tcl/board/vd_a53x2_jtag.cfg new file mode 100644 index 000000000..869bc4db0 --- /dev/null +++ b/tcl/board/vd_a53x2_jtag.cfg @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm Cortex A53x2 through JTAG + +source [find interface/vdebug.cfg] + +set _CORES 2 +set _CHIPNAME a53 +set _MEMSTART 0x00000000 +set _MEMSIZE 0x1000000 +set _CPUTAPID 0x5ba00477 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 50000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 10ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.u_memory.mem_array $_MEMSTART $_MEMSIZE + +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_aarch64.cfg] diff --git a/tcl/board/vd_m4_jtag.cfg b/tcl/board/vd_m4_jtag.cfg new file mode 100644 index 000000000..ca21476d2 --- /dev/null +++ b/tcl/board/vd_m4_jtag.cfg @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm Cortex m4 through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME m4 +set _MEMSTART 0x00000000 +set _MEMSIZE 0x10000 +set _CPUTAPID 0x4ba00477 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 25000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 20ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.u_mcu.u_sys.u_rom.rom $_MEMSTART $_MEMSIZE + +jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_cortex_m.cfg] diff --git a/tcl/board/vd_pulpissimo_jtag.cfg b/tcl/board/vd_pulpissimo_jtag.cfg new file mode 100644 index 000000000..69dd9e6db --- /dev/null +++ b/tcl/board/vd_pulpissimo_jtag.cfg @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV Ibex core with Pulpissimo through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME ibex +set _HARTID 0x20 +set _CPUTAPID 0x249511c3 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 12500 +adapter srst delay 10 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 40ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2_pri\[0\].sram_i.mem_array 0x1c000000 0x8000 +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2_pri\[1\].sram_i.mem_array 0x1c008000 0x8000 +vdebug mem_path tbench.soc_domain_i.pulp_soc_i.gen_mem_l2\[0\].sram_i.mem_array 0x1c010000 0x80000 + +# need to explicitly define riscv tap, autoprobing does not work for icapture != 0x01 +jtag newtap $_CHIPNAME cpu -irlen 5 -ircapture 0x05 -irmask 0x1f -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_riscv.cfg] diff --git a/tcl/board/vd_swerv_jtag.cfg b/tcl/board/vd_swerv_jtag.cfg new file mode 100644 index 000000000..ff6c6835f --- /dev/null +++ b/tcl/board/vd_swerv_jtag.cfg @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV swerv core with Swerv through JTAG + +source [find interface/vdebug.cfg] + +set _CHIPNAME rv32 +set _HARTID 0x00 +set _CPUTAPID 0x1000008b +set _MEMSTART 0x00000000 +set _MEMSIZE 0x10000 + +# vdebug select transport +#transport select jtag + +# JTAG reset config, frequency and reset delay +reset_config trst_and_srst +adapter speed 50000 +adapter srst delay 5 + +# BFM hierarchical path and input clk period +vdebug bfm_path tbench.u_vd_jtag_bfm 10ns + +# DMA Memories to access backdoor (up to 4) +vdebug mem_path tbench.i_ahb_ic.mem $_MEMSTART $_MEMSIZE + +# need to explicitly define riscv tap, autoprobing does not work for icapture != 0x01 +jtag newtap $_CHIPNAME cpu -irlen 5 -ircapture 0x01 -irmask 0x1f -expected-id $_CPUTAPID + +jtag arp_init-reset + +source [find target/vd_riscv.cfg] diff --git a/tcl/chip/atmel/at91/aic.tcl b/tcl/chip/atmel/at91/aic.tcl index b0b100270..8b8a48f3b 100644 --- a/tcl/chip/atmel/at91/aic.tcl +++ b/tcl/chip/atmel/at91/aic.tcl @@ -54,36 +54,36 @@ proc show_AIC_IMR_helper { NAME ADDR VAL } { proc show_AIC { } { global AIC_SMR - if [catch { mem2array aaa 32 $AIC_SMR [expr {32 * 4}] } msg ] { + if [catch { set aaa [read_memory $AIC_SMR 32 [expr {32 * 4}]] } msg ] { error [format "%s (%s)" $msg AIC_SMR] } echo "AIC_SMR: Mode & Type" global AT91C_ID for { set x 0 } { $x < 32 } { } { echo -n " " - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) $aaa($x)] + echo [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) [lindex $aaa $x]] incr x } global AIC_SVR - if [catch { mem2array aaa 32 $AIC_SVR [expr {32 * 4}] } msg ] { + if [catch { set aaa [read_memory $AIC_SVR 32 [expr {32 * 4}]] } msg ] { error [format "%s (%s)" $msg AIC_SVR] } echo "AIC_SVR: Vectors" for { set x 0 } { $x < 32 } { } { echo -n " " - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) $aaa($x)] + echo -n [format "%2d: %5s 0x%08x | " $x $AT91C_ID($x) [lindex $aaa $x]] incr x - echo [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) $aaa($x)] + echo [format "%2d: %5s 0x%08x" $x $AT91C_ID($x) [lindex $aaa $x]] incr x } diff --git a/tcl/cpld/altera-epm240.cfg b/tcl/cpld/altera-epm240.cfg index 62f2b73b7..ece02bbef 100644 --- a/tcl/cpld/altera-epm240.cfg +++ b/tcl/cpld/altera-epm240.cfg @@ -1,6 +1,23 @@ # Altera MAXII EPM240T100C CPLD + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME epm240 +} + # see MAX II Device Handbook # Table 3-3: 32-Bit MAX II Device IDCODE # Version Part Number Manuf. ID LSB # 0000 0010 0000 1010 0001 000 0110 1110 1 -jtag newtap epm240 tap -expected-id 0x020a10dd -irlen 10 +jtag newtap $_CHIPNAME tap -irlen 10 \ + -expected-id 0x020a10dd \ + -expected-id 0x020a20dd \ + -expected-id 0x020a30dd \ + -expected-id 0x020a40dd \ + -expected-id 0x020a50dd \ + -expected-id 0x020a60dd + +# 200ns seems like a good speed +# c.f. Table 5-34: MAX II JTAG Timing Parameters +adapter speed 5000 diff --git a/tcl/cpu/arc/common.tcl b/tcl/cpu/arc/common.tcl index e9a915717..b31e31a34 100644 --- a/tcl/cpu/arc/common.tcl +++ b/tcl/cpu/arc/common.tcl @@ -29,9 +29,8 @@ proc arc_common_reset { {target ""} } { # vector located at the interrupt vector base address, which is the first # entry (offset 0x00) in the vector table. set int_vector_base [arc jtag get-aux-reg 0x25] - set start_pc "" - mem2array start_pc 32 $int_vector_base 1 - arc jtag set-aux-reg 0x6 $start_pc(0) + set start_pc [read_memory $int_vector_base 32 1] + arc jtag set-aux-reg 0x6 $start_pc # It is OK to do uncached writes - register cache will be invalidated by # the reset_assert() function. diff --git a/tcl/interface/ftdi/digilent-hs2-cjtag.cfg b/tcl/interface/ftdi/digilent-hs2-cjtag.cfg new file mode 100644 index 000000000..3daf46185 --- /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/vdebug.cfg b/tcl/interface/vdebug.cfg new file mode 100644 index 000000000..9cca6aaab --- /dev/null +++ b/tcl/interface/vdebug.cfg @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface + +if { [info exists VDEBUGHOST] } { + set _VDEBUGHOST $VDEBUGHOST +} else { + set _VDEBUGHOST localhost +} +if { [info exists VDEBUGPORT] } { + set _VDEBUGPORT $VDEBUGPORT +} else { + set _VDEBUGPORT 8192 +} + +adapter driver vdebug +# vdebug server:port +vdebug server $_VDEBUGHOST:$_VDEBUGPORT + +# example config debug level and log +#debug_level 3 +#log_output vd_ocd.log + +# example config listen on all interfaces, disable tcl/telnet server +bindto 0.0.0.0 +#gdb_port 3333 +#telnet_port disabled +tcl_port disabled + +# transaction batching: 0 - no batching, 1 - (default) wr, 2 - rw +vdebug batching 1 + +# Polling values +vdebug polling 100 1000 \ No newline at end of file diff --git a/tcl/interface/wlink.cfg b/tcl/interface/wlink.cfg new file mode 100644 index 000000000..73707ac27 --- /dev/null +++ b/tcl/interface/wlink.cfg @@ -0,0 +1,3 @@ +adapter driver wlinke +adapter speed 6000 +transport select sdi diff --git a/tcl/mem_helper.tcl b/tcl/mem_helper.tcl index 9ea285a22..1c860119a 100644 --- a/tcl/mem_helper.tcl +++ b/tcl/mem_helper.tcl @@ -2,9 +2,7 @@ # mrw: "memory read word", returns value of $reg proc mrw {reg} { - set value "" - mem2array value 32 $reg 1 - return $value(0) + return [read_memory $reg 32 1] } add_usage_text mrw "address" @@ -12,9 +10,7 @@ add_help_text mrw "Returns value of word in memory." # mrh: "memory read halfword", returns value of $reg proc mrh {reg} { - set value "" - mem2array value 16 $reg 1 - return $value(0) + return [read_memory $reg 16 1] } add_usage_text mrh "address" @@ -22,9 +18,7 @@ add_help_text mrh "Returns value of halfword in memory." # mrb: "memory read byte", returns value of $reg proc mrb {reg} { - set value "" - mem2array value 8 $reg 1 - return $value(0) + return [read_memory $reg 8 1] } add_usage_text mrb "address" diff --git a/tcl/memory.tcl b/tcl/memory.tcl index 8d50ba853..ac273451d 100644 --- a/tcl/memory.tcl +++ b/tcl/memory.tcl @@ -79,108 +79,96 @@ proc address_info { ADDRESS } { } proc memread32 {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 32 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 32 1] } msg ] { + return $foo } else { error "memread32: $msg" } } proc memread16 {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 16 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 16 1] } msg ] { + return $foo } else { error "memread16: $msg" } } proc memread8 {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 8 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 8 1] } msg ] { + return $foo } else { error "memread8: $msg" } } proc memwrite32 {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 32 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 32 $DATA } msg ] { + return $DATA } else { error "memwrite32: $msg" } } proc memwrite16 {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 16 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 16 $DATA } msg ] { + return $DATA } else { error "memwrite16: $msg" } } proc memwrite8 {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 8 $ADDR 1 } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 8 $DATA } msg ] { + return $DATA } else { error "memwrite8: $msg" } } proc memread32_phys {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 32 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 32 1 phys] } msg ] { + return $foo } else { error "memread32: $msg" } } proc memread16_phys {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 16 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 16 1 phys] } msg ] { + return $foo } else { error "memread16: $msg" } } proc memread8_phys {ADDR} { - set foo(0) 0 - if ![ catch { mem2array foo 8 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { set foo [read_memory $ADDR 8 1 phys] } msg ] { + return $foo } else { error "memread8: $msg" } } proc memwrite32_phys {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 32 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 32 $DATA phys } msg ] { + return $DATA } else { error "memwrite32: $msg" } } proc memwrite16_phys {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 16 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 16 $DATA phys } msg ] { + return $DATA } else { error "memwrite16: $msg" } } proc memwrite8_phys {ADDR DATA} { - set foo(0) $DATA - if ![ catch { array2mem foo 8 $ADDR 1 phys } msg ] { - return $foo(0) + if ![ catch { write_memory $ADDR 8 $DATA phys } msg ] { + return $DATA } else { error "memwrite8: $msg" } diff --git a/tcl/target/bluefield.cfg b/tcl/target/bluefield.cfg index 62b1e3165..dcebb2fb1 100644 --- a/tcl/target/bluefield.cfg +++ b/tcl/target/bluefield.cfg @@ -72,7 +72,7 @@ proc core_up { args } { global _TARGETNAME # Examine remaining cores - foreach _core [set args] { + foreach _core $args { ${_TARGETNAME}$_core arp_examine } } diff --git a/tcl/target/c100helper.tcl b/tcl/target/c100helper.tcl index bdcfd8cf5..ecd7edf11 100644 --- a/tcl/target/c100helper.tcl +++ b/tcl/target/c100helper.tcl @@ -29,9 +29,7 @@ source [find mem_helper.tcl] # read a 64-bit register (memory mapped) proc mr64bit {reg} { - set value "" - mem2array value 32 $reg 2 - return $value + return [read_memory $reg 32 2] } @@ -117,19 +115,19 @@ proc showAmbaClk {} { set PLL_CLK_BYPASS [regs PLL_CLK_BYPASS] echo [format "CLKCORE_AHB_CLK_CNTRL (0x%x): 0x%x" $CLKCORE_AHB_CLK_CNTRL [mrw $CLKCORE_AHB_CLK_CNTRL]] - mem2array value 32 $CLKCORE_AHB_CLK_CNTRL 1 + set value [read_memory $CLKCORE_AHB_CLK_CNTRL 32 1] # see if the PLL is in bypass mode - set bypass [expr {($value(0) & $PLL_CLK_BYPASS) >> 24}] + set bypass [expr {($value & $PLL_CLK_BYPASS) >> 24}] echo [format "PLL bypass bit: %d" $bypass] if {$bypass == 1} { echo [format "Amba Clk is set to REFCLK: %d (MHz)" [expr {$CFG_REFCLKFREQ/1000000}]] } else { # nope, extract x,y,w and compute the PLL output freq. - set x [expr {($value(0) & 0x0001F0000) >> 16}] + set x [expr {($value & 0x0001F0000) >> 16}] echo [format "x: %d" $x] - set y [expr {($value(0) & 0x00000007F)}] + set y [expr {($value & 0x00000007F)}] echo [format "y: %d" $y] - set w [expr {($value(0) & 0x000000300) >> 8}] + set w [expr {($value & 0x000000300) >> 8}] echo [format "w: %d" $w] echo [format "Amba PLL Clk: %d (MHz)" [expr {($CFG_REFCLKFREQ * $y / (($w + 1) * ($x + 1) * 2))/1000000}]] } @@ -192,19 +190,19 @@ proc showArmClk {} { set PLL_CLK_BYPASS [regs PLL_CLK_BYPASS] echo [format "CLKCORE_ARM_CLK_CNTRL (0x%x): 0x%x" $CLKCORE_ARM_CLK_CNTRL [mrw $CLKCORE_ARM_CLK_CNTRL]] - mem2array value 32 $CLKCORE_ARM_CLK_CNTRL 1 + set value [read_memory $CLKCORE_ARM_CLK_CNTRL 32 1] # see if the PLL is in bypass mode - set bypass [expr {($value(0) & $PLL_CLK_BYPASS) >> 24}] + set bypass [expr {($value & $PLL_CLK_BYPASS) >> 24}] echo [format "PLL bypass bit: %d" $bypass] if {$bypass == 1} { echo [format "Amba Clk is set to REFCLK: %d (MHz)" [expr {$CFG_REFCLKFREQ/1000000}]] } else { # nope, extract x,y,w and compute the PLL output freq. - set x [expr {($value(0) & 0x0001F0000) >> 16}] + set x [expr {($value & 0x0001F0000) >> 16}] echo [format "x: %d" $x] - set y [expr {($value(0) & 0x00000007F)}] + set y [expr {($value & 0x00000007F)}] echo [format "y: %d" $y] - set w [expr {($value(0) & 0x000000300) >> 8}] + set w [expr {($value & 0x000000300) >> 8}] echo [format "w: %d" $w] echo [format "Arm PLL Clk: %d (MHz)" [expr {($CFG_REFCLKFREQ * $y / (($w + 1) * ($x + 1) * 2))/1000000}]] } diff --git a/tcl/target/efm32.cfg b/tcl/target/efm32.cfg index c789efc72..d2e4eb329 100644 --- a/tcl/target/efm32.cfg +++ b/tcl/target/efm32.cfg @@ -43,6 +43,8 @@ $_TARGETNAME configure -work-area-phys 0x10000000 -work-area-size $_WORKAREASIZE set _FLASHNAME $_CHIPNAME.flash flash bank $_FLASHNAME efm32 0 0 0 0 $_TARGETNAME +flash bank userdata.flash efm32 0x0FE00000 0 0 0 $_TARGETNAME +flash bank lockbits.flash efm32 0x0FE04000 0 0 0 $_TARGETNAME if {![using_hla]} { # if srst is not fitted use SYSRESETREQ to diff --git a/tcl/target/esp32c2.cfg b/tcl/target/esp32c2.cfg new file mode 100644 index 000000000..2af6dd292 --- /dev/null +++ b/tcl/target/esp32c2.cfg @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# The ESP32-C2 only supports JTAG. +transport select jtag + +# Source the ESP common configuration file +source [find target/esp_common.cfg] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME esp32c2 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x0000cc25 +} + +set _TARGETNAME $_CHIPNAME +set _CPUNAME cpu +set _TAPNAME $_CHIPNAME.$_CPUNAME + +jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID + +proc esp32c2_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 +} + +# This is almost identical with the esp32c3_soc_reset. +# Will be refactored with the other common settings. +proc esp32c2_soc_reset { } { + # 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 0x10 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 + esp32c2_wdt_disable + + # Here debugger reads allresumeack and allhalted bits as set (0x330a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write 0x10 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write 0x10 0x80000003 +} + +if { $_RTOS == "none" } { + target create $_TARGETNAME riscv -chain-position $_TAPNAME +} else { + target create $_TARGETNAME riscv -chain-position $_TAPNAME -rtos $_RTOS +} + +$_TARGETNAME configure -event reset-assert-post { esp32c2_soc_reset } +$_TARGETNAME configure -event halted { + esp32c2_wdt_disable +} +$_TARGETNAME configure -event examine-end { + # Need this to handle 'apptrace init' syscall correctly because semihosting is not enabled by default + arm semihosting enable + arm semihosting_resexit enable + if { [info exists _SEMIHOST_BASEDIR] } { + if { $_SEMIHOST_BASEDIR != "" } { + # TODO: cherry-pick from upstream + # https://review.openocd.org/c/openocd/+/6888 + # https://review.openocd.org/c/openocd/+/7005 + # arm semihosting_basedir $_SEMIHOST_BASEDIR + } + } +} +$_TARGETNAME configure -event gdb-attach { + halt 1000 + # by default mask interrupts while stepping + riscv set_maskisr steponly +} + +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 diff --git a/tcl/target/esp32c3.cfg b/tcl/target/esp32c3.cfg new file mode 100644 index 000000000..ecb9bdf53 --- /dev/null +++ b/tcl/target/esp32c3.cfg @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# The ESP32-C3 only supports JTAG. +transport select jtag + +# Source the ESP common configuration file +source [find target/esp_common.cfg] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME esp32c3 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x00005c25 +} + +set _TARGETNAME $_CHIPNAME +set _CPUNAME cpu +set _TAPNAME $_CHIPNAME.$_CPUNAME + +jtag newtap $_CHIPNAME $_CPUNAME -irlen 5 -expected-id $_CPUTAPID + +proc esp32c3_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 esp32c3_soc_reset { } { + # 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 0x10 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 + esp32c3_wdt_disable + + # Here debugger reads allresumeack and allhalted bits as set (0x330a2) + # We will clean allhalted state by resuming the core. + riscv dmi_write 0x10 0x40000001 + + # Put the hart back into reset state. Note that we need to keep haltreq set. + riscv dmi_write 0x10 0x80000003 +} + +if { $_RTOS == "none" } { + target create $_TARGETNAME riscv -chain-position $_TAPNAME +} else { + target create $_TARGETNAME riscv -chain-position $_TAPNAME -rtos $_RTOS +} + +$_TARGETNAME configure -event reset-assert-post { esp32c3_soc_reset } +$_TARGETNAME configure -event halted { + esp32c3_wdt_disable +} +$_TARGETNAME configure -event examine-end { + # Need this to handle 'apptrace init' syscall correctly because semihosting is not enabled by default + arm semihosting enable + arm semihosting_resexit enable + if { [info exists _SEMIHOST_BASEDIR] } { + if { $_SEMIHOST_BASEDIR != "" } { + # TODO: cherry-pick from upstream + # https://review.openocd.org/c/openocd/+/6888 + # https://review.openocd.org/c/openocd/+/7005 + # arm semihosting_basedir $_SEMIHOST_BASEDIR + } + } +} +$_TARGETNAME configure -event gdb-attach { + halt 1000 + # by default mask interrupts while stepping + riscv set_maskisr steponly +} + +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 diff --git a/tcl/target/esp_common.cfg b/tcl/target/esp_common.cfg new file mode 100644 index 000000000..c02bcb2b7 --- /dev/null +++ b/tcl/target/esp_common.cfg @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Common ESP chips definitions + +if { [info exists ESP_RTOS] } { + set _RTOS "$ESP_RTOS" +} else { + set _RTOS "FreeRTOS" +} + +if { [info exists ESP_SEMIHOST_BASEDIR] } { + set _SEMIHOST_BASEDIR $ESP_SEMIHOST_BASEDIR +} else { + # by default current dir (when OOCD has been started) + set _SEMIHOST_BASEDIR "." +} diff --git a/tcl/target/gd32vf103.cfg b/tcl/target/gd32vf103.cfg index c7af88d81..b00e5e9b7 100644 --- a/tcl/target/gd32vf103.cfg +++ b/tcl/target/gd32vf103.cfg @@ -22,7 +22,7 @@ default_mem_access $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 0x1000 -work-area-backup 1 set _FLASHNAME $_CHIPNAME.flash -flash bank $_FLASHNAME gd32vf103 0x08000000 0 0 0 $_TARGETNAME +flash bank $_FLASHNAME stm32f1x 0x08000000 0 0 0 $_TARGETNAME # 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 diff --git a/tcl/target/hilscher_netx500.cfg b/tcl/target/hilscher_netx500.cfg index 6d919f9dd..131bef221 100644 --- a/tcl/target/hilscher_netx500.cfg +++ b/tcl/target/hilscher_netx500.cfg @@ -26,9 +26,7 @@ set _TARGETNAME $_CHIPNAME.cpu target create $_TARGETNAME arm926ejs -endian $_ENDIAN -chain-position $_TARGETNAME proc mread32 {addr} { - set value(0) 0 - mem2array value 32 $addr 1 - return $value(0) + return [read_memory $addr 32 1] } # This function must be called on netX100/500 right after halt diff --git a/tcl/target/ls1046a.cfg b/tcl/target/ls1046a.cfg new file mode 100644 index 000000000..3d96a994e --- /dev/null +++ b/tcl/target/ls1046a.cfg @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# NXP LS1046A + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME ls1046a +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x5ba00477 +} + +if { [info exists SAP_TAPID] } { + set _SAP_TAPID $SAP_TAPID +} else { + set _SAP_TAPID 0x06b3001d +} + +jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap + +target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 0 + +set _CPU_BASE 0x80400000 +set _CPU_STRIDE 0x100000 +set _CPU_DBGOFF 0x10000 +set _CPU_CTIOFF 0x20000 + +set _TARGETS {} +for {set i 0} {$i < 4} {incr i} { + set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}] + cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 1 \ + -baseaddr [expr {$_BASE + $_CPU_CTIOFF}] + target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \ + -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \ + -coreid $i {*}[expr {$i ? {-defer-examine} : {-rtos hwthread} }] + lappend _TARGETS $_CHIPNAME.cpu$i +} + +target smp {*}$_TARGETS + +jtag newtap $_CHIPNAME sap -irlen 8 -expected-id $_SAP_TAPID +target create $_CHIPNAME.sap ls1_sap -chain-position $_CHIPNAME.sap -endian big + +proc core_up { args } { + foreach core $args { + $::_CHIPNAME.cpu$core arp_examine + } +} + +targets $_CHIPNAME.cpu0 + +adapter speed 10000 diff --git a/tcl/target/ls1088a.cfg b/tcl/target/ls1088a.cfg new file mode 100644 index 000000000..f9ae9a134 --- /dev/null +++ b/tcl/target/ls1088a.cfg @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# NXP LS1088A + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME ls1088a +} + +if { [info exists DAP_TAPID] } { + set _DAP_TAPID $DAP_TAPID +} else { + set _DAP_TAPID 0x5ba00477 +} + +jtag newtap $_CHIPNAME dap -irlen 4 -expected-id $_DAP_TAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.dap + +target create $_CHIPNAME.axi mem_ap -dap $_CHIPNAME.dap -ap-num 1 + +set _CPU_BASE 0x81000000 +set _CPU_STRIDE 0x100000 +set _CPU_DBGOFF 0x10000 +set _CPU_CTIOFF 0x20000 + +set _TARGETS {} +for {set i 0} {$i < 8} {incr i} { + set _BASE [expr {$_CPU_BASE + $_CPU_STRIDE * $i}] + cti create $_CHIPNAME.cti$i -dap $_CHIPNAME.dap -ap-num 0 \ + -baseaddr [expr {$_BASE + $_CPU_CTIOFF}] + target create $_CHIPNAME.cpu$i aarch64 -dap $_CHIPNAME.dap \ + -cti $_CHIPNAME.cti$i -dbgbase [expr {$_BASE + $_CPU_DBGOFF}] \ + {*}[expr {$i ? "-coreid $i" : "-rtos hwthread" }] + lappend _TARGETS $_CHIPNAME.cpu$i +} + +target smp {*}$_TARGETS + +# Service processor +target create $_CHIPNAME.sp cortex_a -dap $_CHIPNAME.dap -ap-num 0 -dbgbase 0x80138000 + +# Normally you will not need to call this, but if you are using the hard-coded +# Reset Configuration Word (RCW) you will need to call this manually. The CPU's +# reset vector is 0, and the boot ROM at that location contains ARMv7-A 32-bit +# instructions. This will cause the CPU to almost immediately execute an +# illegal instruction. +# +# This code is idempotent; releasing a released CPU has no effect, although it +# will halt/resume the service processor. +add_help_text release_cpu "Release a cpu which is held off" +proc release_cpu {cpu} { + set RST_BRRL 0x1e60060 + + set old [target current] + targets $::_CHIPNAME.sp + set not_halted [string compare halted [$::_CHIPNAME.sp curstate]] + if {$not_halted} { + halt + } + + # Release the cpu; it will start executing something bogus + mem2array regs 32 $RST_BRRL 1 + mww $RST_BRRL [expr {$regs(0) | 1 << $cpu}] + + if {$not_halted} { + resume + } + targets $old +} + +targets $_CHIPNAME.cpu0 + +# Seems to work OK in testing +adapter speed 10000 diff --git a/tcl/target/psoc4.cfg b/tcl/target/psoc4.cfg index cffcbc753..40f2fcab3 100644 --- a/tcl/target/psoc4.cfg +++ b/tcl/target/psoc4.cfg @@ -74,22 +74,22 @@ if {![using_hla]} { } proc psoc4_get_family_id {} { - set err [catch "mem2array romtable_pid 32 0xF0000FE0 3"] + set err [catch {set romtable_pid [read_memory 0xF0000FE0 32 3]}] if { $err } { return 0 } - if { [expr {$romtable_pid(0) & 0xffffff00 }] - || [expr {$romtable_pid(1) & 0xffffff00 }] - || [expr {$romtable_pid(2) & 0xffffff00 }] } { + if { [expr {[lindex $romtable_pid 0] & 0xffffff00 }] + || [expr {[lindex $romtable_pid 1] & 0xffffff00 }] + || [expr {[lindex $romtable_pid 2] & 0xffffff00 }] } { echo "Unexpected data in ROMTABLE" return 0 } - set designer_id [expr {(( $romtable_pid(1) & 0xf0 ) >> 4) | (( $romtable_pid(2) & 0xf ) << 4 ) }] + set designer_id [expr {(( [lindex $romtable_pid 1] & 0xf0 ) >> 4) | (( [lindex $romtable_pid 2] & 0xf ) << 4 ) }] if { $designer_id != 0xb4 } { echo [format "ROMTABLE Designer ID 0x%02x is not Cypress" $designer_id] return 0 } - set family_id [expr {( $romtable_pid(0) & 0xff ) | (( $romtable_pid(1) & 0xf ) << 8 ) }] + set family_id [expr {( [lindex $romtable_pid 0] & 0xff ) | (( [lindex $romtable_pid 1] & 0xf ) << 8 ) }] return $family_id } @@ -193,9 +193,9 @@ proc ocd_process_reset_inner { MODE } { } # Set registers to reset vector values - mem2array value 32 0 2 - reg pc [expr {$value(1) & 0xfffffffe} ] - reg msp $value(0) + set value [read_memory 0x0 32 2] + reg pc [expr {[lindex $value 1] & 0xfffffffe}] + reg msp [lindex $value 0] if { $PSOC4_TEST_MODE_WORKAROUND } { catch { mww $TEST_MODE 0 } diff --git a/tcl/target/stm32f4x.cfg b/tcl/target/stm32f4x.cfg index 2228de72f..aa2816e76 100644 --- a/tcl/target/stm32f4x.cfg +++ b/tcl/target/stm32f4x.cfg @@ -1,7 +1,7 @@ # script for stm32f4x family # -# stm32 devices support both JTAG and SWD transports. +# stm32f4 devices support both JTAG and SWD transports. # source [find target/swj-dp.tcl] source [find mem_helper.tcl] diff --git a/tcl/target/stm32h7x.cfg b/tcl/target/stm32h7x.cfg index f2a5c42c6..ca685c2f2 100644 --- a/tcl/target/stm32h7x.cfg +++ b/tcl/target/stm32h7x.cfg @@ -232,9 +232,7 @@ if {[set $_CHIPNAME.DUAL_CORE]} { # like mrw, but with target selection proc stm32h7x_mrw {used_target reg} { - set value "" - $used_target mem2array value 32 $reg 1 - return $value(0) + return [$used_target read_memory $reg 32 1] } # like mmw, but with target selection diff --git a/tcl/target/stm32l4x.cfg b/tcl/target/stm32l4x.cfg index 589979193..9bd7e37ba 100644 --- a/tcl/target/stm32l4x.cfg +++ b/tcl/target/stm32l4x.cfg @@ -15,11 +15,11 @@ if { [info exists CHIPNAME] } { set _ENDIAN little # Work-area is a space in RAM used for flash programming -# Smallest current target has 64kB ram, use 32kB by default to avoid surprises +# By default use 40kB (Available RAM in smallest device STM32L412) if { [info exists WORKAREASIZE] } { set _WORKAREASIZE $WORKAREASIZE } else { - set _WORKAREASIZE 0x8000 + set _WORKAREASIZE 0xa000 } #jtag scan chain @@ -38,6 +38,8 @@ if { [info exists CPUTAPID] } { swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu +tpiu create $_CHIPNAME.tpiu -dap $_CHIPNAME.dap -ap-num 0 -baseaddr 0xE0040000 + if {[using_jtag]} { jtag newtap $_CHIPNAME bs -irlen 5 } @@ -47,8 +49,9 @@ target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap $_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 -flash bank $_CHIPNAME.flash stm32l4x 0x08000000 0 0 0 $_TARGETNAME -flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME +set _FLASHNAME $_CHIPNAME.flash +flash bank $_FLASHNAME stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x1fff7000 0 0 0 $_TARGETNAME if { [info exists QUADSPI] && $QUADSPI } { set a [llength [flash list]] @@ -88,12 +91,54 @@ if {![using_hla]} { cortex_m reset_config sysresetreq } +$_TARGETNAME configure -event examine-end { + # Enable debug during low power modes (uses more power) + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP + mmw 0xE0042004 0x00000007 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0xE0042008 0x00001800 0 +} + +proc proc_post_enable {_chipname} { + targets $_chipname.cpu + + if { [$_chipname.tpiu cget -protocol] eq "sync" } { + switch [$_chipname.tpiu cget -port-width] { + 1 { + mmw 0xE0042004 0x00000060 0x000000c0 + mmw 0x48001020 0x00000000 0x0000ff00 + mmw 0x48001000 0x000000a0 0x000000f0 + mmw 0x48001008 0x000000f0 0x00000000 + } + 2 { + mmw 0xE0042004 0x000000a0 0x000000c0 + mmw 0x48001020 0x00000000 0x000fff00 + mmw 0x48001000 0x000002a0 0x000003f0 + mmw 0x48001008 0x000003f0 0x00000000 + } + 4 { + mmw 0xE0042004 0x000000e0 0x000000c0 + mmw 0x48001020 0x00000000 0x0fffff00 + mmw 0x48001000 0x00002aa0 0x00003ff0 + mmw 0x48001008 0x00003ff0 0x00000000 + } + } + } else { + mmw 0xE0042004 0x00000020 0x000000c0 + } +} + +$_CHIPNAME.tpiu configure -event post-enable "proc_post_enable $_CHIPNAME" + $_TARGETNAME configure -event reset-init { # CPU comes out of reset with MSI_ON | MSI_RDY | MSI Range 6 (4 MHz). # Use MSI 24 MHz clock, compliant even with VOS == 2. # 3 WS compliant with VOS == 2 and 24 MHz. mww 0x40022000 0x00000103 ;# FLASH_ACR = PRFTBE | 3(Latency) mww 0x40021000 0x00000099 ;# RCC_CR = MSI_ON | MSIRGSEL | MSI Range 9 + # Boost JTAG frequency adapter speed 4000 } @@ -102,19 +147,3 @@ $_TARGETNAME configure -event reset-start { # Reset clock is MSI (4 MHz) adapter speed 500 } - -$_TARGETNAME configure -event examine-end { - # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP - mmw 0xE0042004 0x00000007 0 - - # Stop watchdog counters during halt - # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP - mmw 0xE0042008 0x00001800 0 -} - -$_TARGETNAME configure -event trace-config { - # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync - # change this value accordingly to configure trace pins - # assignment - mmw 0xE0042004 0x00000020 0 -} diff --git a/tcl/target/stm32l5x.cfg b/tcl/target/stm32l5x.cfg index 0616df1cb..c43b699d2 100644 --- a/tcl/target/stm32l5x.cfg +++ b/tcl/target/stm32l5x.cfg @@ -1,10 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later # script for stm32l5x family +# stm32l5x devices support both JTAG and SWD transports. -# -# stm32l5 devices support both JTAG and SWD transports. -# source [find target/swj-dp.tcl] source [find mem_helper.tcl] @@ -14,78 +12,10 @@ if { [info exists CHIPNAME] } { set _CHIPNAME stm32l5x } -set _ENDIAN little +source [find target/stm32x5x_common.cfg] -# Work-area is a space in RAM used for flash programming -# By default use 64kB -if { [info exists WORKAREASIZE] } { - set _WORKAREASIZE $WORKAREASIZE -} else { - set _WORKAREASIZE 0x10000 -} - -#jtag scan chain -if { [info exists CPUTAPID] } { - set _CPUTAPID $CPUTAPID -} else { - if { [using_jtag] } { - # See STM Document RM0438 - # RM0438 Rev5, Section 52.2.8 JTAG debug port - Table 425. JTAG-DP data registers - # Corresponds to Cortex®-M33 JTAG debug port ID code - set _CPUTAPID 0x0ba04477 - } { - # SWD IDCODE (single drop, arm) - set _CPUTAPID 0x0be12477 - } -} - -swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID -dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu - -if {[using_jtag]} { - jtag newtap $_CHIPNAME bs -irlen 5 -} - -set _TARGETNAME $_CHIPNAME.cpu -target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap - -# use non-secure RAM by default -$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 - -# create sec/ns flash and otp memories (sizes will be probed) -flash bank $_CHIPNAME.flash_ns stm32l4x 0x08000000 0 0 0 $_TARGETNAME -flash bank $_CHIPNAME.flash_alias_s stm32l4x 0x0C000000 0 0 0 $_TARGETNAME -flash bank $_CHIPNAME.otp stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME - -# Common knowledges tells JTAG speed should be <= F_CPU/6. -# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on -# the safe side. -# -# Note that there is a pretty wide band where things are -# more or less stable, see http://openocd.zylin.com/#/c/3366/ -adapter speed 500 - -adapter srst delay 100 -if {[using_jtag]} { - jtag_ntrst_delay 100 -} - -reset_config srst_nogate - -if {![using_hla]} { - # if srst is not fitted use SYSRESETREQ to - # perform a soft reset - cortex_m reset_config sysresetreq -} - -proc is_secure {} { - # read Debug Security Control and Status Regsiter (DSCSR) and check CDS (bit 16) - set DSCSR [mrw 0xE000EE08] - return [expr {($DSCSR & (1 << 16)) != 0}] -} - -proc clock_config_110_mhz {} { - set offset [expr {[is_secure] ? 0x10000000 : 0}] +proc stm32l5x_clock_config {} { + set offset [expr {[stm32x5x_is_secure] ? 0x10000000 : 0}] # MCU clock is MSI (4MHz) after reset, set MCU freq at 110 MHz with PLL # RCC_APB1ENR1 = PWREN mww [expr {0x40021058 + $offset}] 0x10000000 @@ -111,90 +41,8 @@ proc clock_config_110_mhz {} { while {([mrw [expr {0x40021008 + $offset}]] & 0x0C) != 0x0C} {} } -proc ahb_ap_non_secure_access {} { - # SPROT=1=Non Secure access, Priv=1 - [[target current] cget -dap] apcsw 0x4B000000 0x4F000000 -} - -proc ahb_ap_secure_access {} { - # SPROT=0=Secure access, Priv=1 - [[target current] cget -dap] apcsw 0x0B000000 0x4F000000 -} - $_TARGETNAME configure -event reset-init { - clock_config_110_mhz + stm32l5x_clock_config # Boost JTAG frequency adapter speed 4000 } - -$_TARGETNAME configure -event reset-start { - # Reset clock is MSI (4 MHz) - adapter speed 480 -} - -$_TARGETNAME configure -event examine-end { - # DBGMCU_CR |= DBG_STANDBY | DBG_STOP - mmw 0xE0044004 0x00000006 0 - - # Stop watchdog counters during halt - # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP - mmw 0xE0044008 0x00001800 0 -} - -$_TARGETNAME configure -event halted { - set secure [is_secure] - - if {$secure} { - set secure_str "Secure" - ahb_ap_secure_access - } else { - set secure_str "Non-Secure" - ahb_ap_non_secure_access - } - - # print the secure state only when it changes - set _TARGETNAME [target current] - global $_TARGETNAME.secure - - if {![info exists $_TARGETNAME.secure] || $secure != [set $_TARGETNAME.secure]} { - echo "CPU in $secure_str state" - # update saved security state - set $_TARGETNAME.secure $secure - } -} - -$_TARGETNAME configure -event gdb-flash-erase-start { - set use_secure_workarea 0 - # check if FLASH_OPTR.TZEN is enabled - set FLASH_OPTR [mrw 0x40022040] - if {[expr {$FLASH_OPTR & 0x80000000}] == 0} { - echo "TZEN option bit disabled" - ahb_ap_non_secure_access - } { - ahb_ap_secure_access - echo "TZEN option bit enabled" - - # check if FLASH_OPTR.RDP is not Level 0.5 - if {[expr {$FLASH_OPTR & 0xFF}] != 0x55} { - set use_secure_workarea 1 - } - } - - set workarea_addr [$_TARGETNAME cget -work-area-phys] - echo "workarea_addr $workarea_addr" - - if {$use_secure_workarea} { - set workarea_addr [expr {$workarea_addr | 0x10000000}] - } { - set workarea_addr [expr {$workarea_addr & ~0x10000000}] - } - - $_TARGETNAME configure -work-area-phys $workarea_addr -} - -$_TARGETNAME configure -event trace-config { - # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync - # change this value accordingly to configure trace pins - # assignment - mmw 0xE0044004 0x00000020 0 -} diff --git a/tcl/target/stm32mp15x.cfg b/tcl/target/stm32mp15x.cfg index 639fbabe0..afd5d2413 100644 --- a/tcl/target/stm32mp15x.cfg +++ b/tcl/target/stm32mp15x.cfg @@ -109,8 +109,8 @@ proc toggle_cpu0_dbg_claim0 {} { } proc detect_cpu1 {} { - $::_CHIPNAME.ap1 mem2array cpu1_prsr 32 0xE00D2314 1 - set dual_core [expr {$cpu1_prsr(0) & 1}] + set cpu1_prsr [$::_CHIPNAME.ap1 read_memory 0xE00D2314 32 1] + set dual_core [expr {$cpu1_prsr & 1}] if {! $dual_core} {$::_CHIPNAME.cpu1 configure -defer-examine} } diff --git a/tcl/target/stm32u5x.cfg b/tcl/target/stm32u5x.cfg index 2c2c0e037..44b51e2b6 100644 --- a/tcl/target/stm32u5x.cfg +++ b/tcl/target/stm32u5x.cfg @@ -1,10 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-or-later # script for stm32u5x family +# stm32u5x devices support both JTAG and SWD transports. -# -# stm32u5 devices support both JTAG and SWD transports. -# source [find target/swj-dp.tcl] source [find mem_helper.tcl] @@ -14,99 +12,34 @@ if { [info exists CHIPNAME] } { set _CHIPNAME stm32u5x } -set _ENDIAN little +source [find target/stm32x5x_common.cfg] -# Work-area is a space in RAM used for flash programming -# By default use 64kB -if { [info exists WORKAREASIZE] } { - set _WORKAREASIZE $WORKAREASIZE -} else { - set _WORKAREASIZE 0x10000 -} - -#jtag scan chain -if { [info exists CPUTAPID] } { - set _CPUTAPID $CPUTAPID -} else { - if { [using_jtag] } { - # See STM Document RM0438 - # RM0456 Rev1, Section 65.2.8 JTAG debug port - Table 661. JTAG-DP data registers - # Corresponds to Cortex®-M33 JTAG debug port ID code - set _CPUTAPID 0x0ba04477 - } { - # SWD IDCODE (single drop, arm) - set _CPUTAPID 0x0be12477 - } -} - -swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID -dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu - -if {[using_jtag]} { - jtag newtap $_CHIPNAME bs -irlen 5 -} - -set _TARGETNAME $_CHIPNAME.cpu -target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap - -# use non-secure RAM by default -$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 - -# create sec/ns flash and otp memories (sizes will be probed) -flash bank $_CHIPNAME.flash_ns stm32l4x 0x08000000 0 0 0 $_TARGETNAME -flash bank $_CHIPNAME.flash_alias_s stm32l4x 0x0C000000 0 0 0 $_TARGETNAME -flash bank $_CHIPNAME.otp stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME - -# Common knowledges tells JTAG speed should be <= F_CPU/6. -# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on -# the safe side. -# -# Note that there is a pretty wide band where things are -# more or less stable, see http://openocd.zylin.com/#/c/3366/ -adapter speed 500 - -adapter srst delay 100 -if {[using_jtag]} { - jtag_ntrst_delay 100 -} - -reset_config srst_nogate - -if {![using_hla]} { - # if srst is not fitted use SYSRESETREQ to - # perform a soft reset - cortex_m reset_config sysresetreq -} - -proc is_secure {} { - # read Debug Security Control and Status Regsiter (DSCSR) and check CDS (bit 16) - set DSCSR [mrw 0xE000EE08] - return [expr {($DSCSR & (1 << 16)) != 0}] -} - -proc clock_config_160_mhz {} { - set offset [expr {[is_secure] ? 0x10000000 : 0}] +proc stm32u5x_clock_config {} { + set offset [expr {[stm32x5x_is_secure] ? 0x10000000 : 0}] # MCU clock is at MSI 4MHz after reset, set MCU freq at 160 MHz with PLL # Enable voltage range 1 for frequency above 100 Mhz # RCC_AHB3ENR = PWREN mww [expr {0x46020C94 + $offset}] 0x00000004 # delay for register clock enable (read back reg) - mrw [expr {0x56020C94 + $offset}] + mrw [expr {0x46020C94 + $offset}] # PWR_VOSR : VOS Range 1 - mww [expr {0x4602080C + $offset}] 0x00030000 - # delay for register write (read back reg) - mrw [expr {0x4602080C + $offset}] + mmw [expr {0x4602080C + $offset}] 0x00030000 0 + # while !(PWR_VOSR & VOSRDY) + while {!([mrw [expr {0x4602080C + $offset}]] & 0x00008000)} {} # FLASH_ACR : 4 WS for 160 MHz HCLK mww [expr {0x40022000 + $offset}] 0x00000004 - # RCC_PLL1CFGR => PLL1M=0000=/1, PLL1SRC=MSI 4MHz - mww [expr {0x46020C28 + $offset}] 0x00000001 + # RCC_PLL1CFGR => PLL1MBOOST=0, PLL1M=0=/1, PLL1FRACEN=0, PLL1SRC=MSI 4MHz + # PLL1REN=1, PLL1RGE => VCOInputRange=PLLInputRange_4_8 + mww [expr {0x46020C28 + $offset}] 0x00040009 + # Enable EPOD Booster + mmw [expr {0x4602080C + $offset}] 0x00040000 0 + # while !(PWR_VOSR & BOOSTRDY) + while {!([mrw [expr {0x4602080C + $offset}]] & 0x00004000)} {} # RCC_PLL1DIVR => PLL1P=PLL1Q=PLL1R=000001=/2, PLL1N=0x4F=80 # fVCO = 4 x 80 /1 = 320 # SYSCLOCK = fVCO/PLL1R = 320/2 = 160 MHz - mmw [expr {0x46020C34 + $offset}] 0x0000004F 0 - # RCC_PLL1CFGR => PLL1REN=1 - mmw [expr {0x46020C28 + $offset}] 0x00040000 0 + mww [expr {0x46020C34 + $offset}] 0x0101024F # RCC_CR |= PLL1ON mmw [expr {0x46020C00 + $offset}] 0x01000000 0 # while !(RCC_CR & PLL1RDY) @@ -117,91 +50,8 @@ proc clock_config_160_mhz {} { while {([mrw [expr {0x46020C1C + $offset}]] & 0x0C) != 0x0C} {} } -proc ahb_ap_non_secure_access {} { - # SPROT=1=Non Secure access, Priv=1 - [[target current] cget -dap] apcsw 0x4B000000 0x4F000000 -} - -proc ahb_ap_secure_access {} { - # SPROT=0=Secure access, Priv=1 - [[target current] cget -dap] apcsw 0x0B000000 0x4F000000 -} - $_TARGETNAME configure -event reset-init { - clock_config_160_mhz + stm32u5x_clock_config # Boost JTAG frequency adapter speed 4000 } - -$_TARGETNAME configure -event reset-start { - # Reset clock is MSI (4 MHz) - adapter speed 480 -} - -$_TARGETNAME configure -event examine-end { - # DBGMCU_CR |= DBG_STANDBY | DBG_STOP - mmw 0xE0044004 0x00000006 0 - - # Stop watchdog counters during halt - # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP - mmw 0xE0044008 0x00001800 0 -} - -$_TARGETNAME configure -event halted { - set secure [is_secure] - - if {$secure} { - set secure_str "Secure" - ahb_ap_secure_access - } else { - set secure_str "Non-Secure" - ahb_ap_non_secure_access - } - - # print the secure state only when it changes - set _TARGETNAME [target current] - global $_TARGETNAME.secure - - if {![info exists $_TARGETNAME.secure] || $secure != [set $_TARGETNAME.secure]} { - echo "CPU in $secure_str state" - # update saved security state - set $_TARGETNAME.secure $secure - } -} - -$_TARGETNAME configure -event gdb-flash-erase-start { - set use_secure_workarea 0 - # check if FLASH_OPTR.TZEN is enabled - set FLASH_OPTR [mrw 0x40022040] - if {[expr {$FLASH_OPTR & 0x80000000}] == 0} { - echo "TZEN option bit disabled" - ahb_ap_non_secure_access - } else { - ahb_ap_secure_access - echo "TZEN option bit enabled" - - # check if FLASH_OPTR.RDP is not Level 0.5 - if {[expr {$FLASH_OPTR & 0xFF}] != 0x55} { - set use_secure_workarea 1 - } - } - - set _TARGETNAME [target current] - set workarea_addr [$_TARGETNAME cget -work-area-phys] - echo "workarea_addr $workarea_addr" - - if {$use_secure_workarea} { - set workarea_addr [expr {$workarea_addr | 0x10000000}] - } else { - set workarea_addr [expr {$workarea_addr & ~0x10000000}] - } - - $_TARGETNAME configure -work-area-phys $workarea_addr -} - -$_TARGETNAME configure -event trace-config { - # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync - # change this value accordingly to configure trace pins - # assignment - mmw 0xE0044004 0x00000020 0 -} diff --git a/tcl/target/stm32wlx.cfg b/tcl/target/stm32wlx.cfg index fafe9bcba..75f6f0288 100644 --- a/tcl/target/stm32wlx.cfg +++ b/tcl/target/stm32wlx.cfg @@ -156,9 +156,7 @@ proc stm32wlx_get_chipname {} { # like mrw, but with target selection proc stm32wlx_mrw {used_target reg} { - set value "" - $used_target mem2array value 32 $reg 1 - return $value(0) + return [$used_target read_memory $reg 32 1] } # like mmw, but with target selection diff --git a/tcl/target/stm32x5x_common.cfg b/tcl/target/stm32x5x_common.cfg new file mode 100644 index 000000000..276d0cca0 --- /dev/null +++ b/tcl/target/stm32x5x_common.cfg @@ -0,0 +1,154 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# common script for stm32l5x and stm32u5x families + +# Work-area is a space in RAM used for flash programming +# By default use 64kB +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x10000 +} + +#jtag scan chain +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + if { [using_jtag] } { + # STM32L5x: RM0438 Rev5, Section 52.2.8 JTAG debug port - Table 425. JTAG-DP data registers + # STM32U5x: RM0456 Rev1, Section 65.2.8 JTAG debug port - Table 661. JTAG-DP data registers + # Corresponds to Cortex®-M33 JTAG debug port ID code + set _CPUTAPID 0x0ba04477 + } { + # SWD IDCODE (single drop, arm) + set _CPUTAPID 0x0be12477 + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID +dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu + +if {[using_jtag]} { + jtag newtap $_CHIPNAME bs -irlen 5 +} + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian little -dap $_CHIPNAME.dap + +# use non-secure RAM by default +$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# create sec/ns flash and otp memories (sizes will be probed) +flash bank $_CHIPNAME.flash_ns stm32l4x 0x08000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.flash_alias_s stm32l4x 0x0C000000 0 0 0 $_TARGETNAME +flash bank $_CHIPNAME.otp stm32l4x 0x0BFA0000 0 0 0 $_TARGETNAME + +# Common knowledge tells JTAG speed should be <= F_CPU/6. +# F_CPU after reset is MSI 4MHz, so use F_JTAG = 500 kHz to stay on +# the safe side. +# +# Note that there is a pretty wide band where things are +# more or less stable, see http://review.openocd.org/3366 +adapter speed 500 + +adapter srst delay 100 +if {[using_jtag]} { + jtag_ntrst_delay 100 +} + +reset_config srst_nogate + +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +proc stm32x5x_is_secure {} { + # read Debug Security Control and Status Register (DSCSR) and check CDS (bit 16) + set DSCSR [mrw 0xE000EE08] + return [expr {($DSCSR & (1 << 16)) != 0}] +} + +proc stm32x5x_ahb_ap_non_secure_access {} { + # SPROT=1=Non Secure access, Priv=1 + [[target current] cget -dap] apcsw 0x4B000000 0x4F000000 +} + +proc stm32x5x_ahb_ap_secure_access {} { + # SPROT=0=Secure access, Priv=1 + [[target current] cget -dap] apcsw 0x0B000000 0x4F000000 +} + +$_TARGETNAME configure -event reset-start { + # Reset clock is MSI (4 MHz) + adapter speed 480 +} + +$_TARGETNAME configure -event examine-end { + # DBGMCU_CR |= DBG_STANDBY | DBG_STOP + mmw 0xE0044004 0x00000006 0 + + # Stop watchdog counters during halt + # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP + mmw 0xE0044008 0x00001800 0 +} + +$_TARGETNAME configure -event halted { + set secure [stm32x5x_is_secure] + + if {$secure} { + set secure_str "Secure" + stm32x5x_ahb_ap_secure_access + } else { + set secure_str "Non-Secure" + stm32x5x_ahb_ap_non_secure_access + } + + # print the secure state only when it changes + set _TARGETNAME [target current] + global $_TARGETNAME.secure + + if {![info exists $_TARGETNAME.secure] || $secure != [set $_TARGETNAME.secure]} { + echo "CPU in $secure_str state" + # update saved security state + set $_TARGETNAME.secure $secure + } +} + +$_TARGETNAME configure -event gdb-flash-erase-start { + set use_secure_workarea 0 + # check if FLASH_OPTR.TZEN is enabled + set FLASH_OPTR [mrw 0x40022040] + if {[expr {$FLASH_OPTR & 0x80000000}] == 0} { + echo "TZEN option bit disabled" + stm32x5x_ahb_ap_non_secure_access + } else { + stm32x5x_ahb_ap_secure_access + echo "TZEN option bit enabled" + + # check if FLASH_OPTR.RDP is not Level 0.5 + if {[expr {$FLASH_OPTR & 0xFF}] != 0x55} { + set use_secure_workarea 1 + } + } + + set _TARGETNAME [target current] + set workarea_addr [$_TARGETNAME cget -work-area-phys] + echo "workarea_addr $workarea_addr" + + if {$use_secure_workarea} { + set workarea_addr [expr {$workarea_addr | 0x10000000}] + } else { + set workarea_addr [expr {$workarea_addr & ~0x10000000}] + } + + $_TARGETNAME configure -work-area-phys $workarea_addr +} + +$_TARGETNAME configure -event trace-config { + # Set TRACE_IOEN; TRACE_MODE is set to async; when using sync + # change this value accordingly to configure trace pins + # assignment + mmw 0xE0044004 0x00000020 0 +} diff --git a/tcl/target/ti_cc3220sf.cfg b/tcl/target/ti_cc3220sf.cfg index 74269aa66..c0a7b568d 100644 --- a/tcl/target/ti_cc3220sf.cfg +++ b/tcl/target/ti_cc3220sf.cfg @@ -26,11 +26,11 @@ proc ocd_process_reset_inner { MODE } { soft_reset_halt # Initialize MSP, PSP, and PC from vector table at flash 0x01000800 - mem2array boot 32 0x01000800 2 + set boot [read_memory 0x01000800 32 2] - reg msp $boot(0) - reg psp $boot(0) - reg pc $boot(1) + reg msp [lindex $boot 0] + reg psp [lindex $boot 0] + reg pc [lindex $boot 1] if { 0 == [string compare $MODE run ] } { resume diff --git a/tcl/target/ti_k3.cfg b/tcl/target/ti_k3.cfg index d2aa53160..254bb6971 100644 --- a/tcl/target/ti_k3.cfg +++ b/tcl/target/ti_k3.cfg @@ -27,11 +27,11 @@ if { [info exists V8_SMP_DEBUG] } { # Common Definitions -# CM3 the very first processor - all current SoCs have it. +# System Controller is the very first processor - all current SoCs have it. set CM3_CTIBASE {0x3C016000} -# M3 power-ap unlock offsets -set _m3_ap_unlock_offsets {0xf0 0x44} +# sysctrl power-ap unlock offsets +set _sysctrl_ap_unlock_offsets {0xf0 0x44} # All the ARMV8s are the next processors. # CL0,CORE0 CL0,CORE1 CL1,CORE0 CL1,CORE1 @@ -42,14 +42,15 @@ set ARMV8_CTIBASE {0x90420000 0x90520000 0x90820000 0x90920000} # (0)MCU 0 (1)MCU 1 (2)MAIN_0_0 (3)MAIN_0_1 (4)MAIN_1_0 (5)MAIN_1_1 set R5_DBGBASE {0x9d010000 0x9d012000 0x9d410000 0x9d412000 0x9d510000 0x9d512000} set R5_CTIBASE {0x9d018000 0x9d019000 0x9d418000 0x9d419000 0x9d518000 0x9d519000} +set R5_NAMES {mcu_r5.0 mcu_r5.1 main0_r5.0 main0_r5.1 main1_r5.0 main1_r5.1} -# Finally an M4F +# Finally an General Purpose(GP) MCU set CM4_CTIBASE {0x20001000} -# M4 may be present on some very few SoCs -set _mcu_m4_cores 0 -# M4 power-ap unlock offsets -set _m4_ap_unlock_offsets {0xf0 0x60} +# General Purpose MCU (M4) may be present on some very few SoCs +set _gp_mcu_cores 0 +# General Purpose MCU power-ap unlock offsets +set _gp_mcu_ap_unlock_offsets {0xf0 0x60} # Set configuration overrides for each SOC switch $_soc { @@ -63,15 +64,10 @@ switch $_soc { # AM654 has 1 cluster of 2 R5s cores. set _r5_cores 2 - set _mcu_r5_cores 2 - set _mcu_base_core_id 0 - set _main0_r5_cores 0 - set _main0_base_core_id 0 - set _main1_r5_cores 0 - set _main1_base_core_id 0 - - # M3 power-ap unlock offsets - set _m3_ap_unlock_offsets {0xf0 0x50} + set R5_NAMES {mcu_r5.0 mcu_r5.1} + + # Sysctrl power-ap unlock offsets + set _sysctrl_ap_unlock_offsets {0xf0 0x50} } am642 { set _CHIPNAME am642 @@ -85,17 +81,37 @@ switch $_soc { # AM642 has 2 cluster of 2 R5s cores. set _r5_cores 4 - set _mcu_r5_cores 0 - set _mcu_base_core_id 0 - set _main0_r5_cores 2 - set _main0_base_core_id 0 - set _main1_r5_cores 2 - set _main1_base_core_id 2 + set R5_NAMES {main0_r5.0 main0_r5.1 main1_r5.0 main1_r5.1} set R5_DBGBASE {0x9d410000 0x9d412000 0x9d510000 0x9d512000} set R5_CTIBASE {0x9d418000 0x9d419000 0x9d518000 0x9d519000} # M4 processor - set _mcu_m4_cores 1 + set _gp_mcu_cores 1 + } + am625 { + set _CHIPNAME am625 + set _K3_DAP_TAPID 0x0bb7e02f + + # AM625 has 1 clusters of 4 A53 cores. + set _armv8_cpu_name a53 + set _armv8_cores 4 + set ARMV8_DBGBASE {0x90010000 0x90110000 0x90210000 0x90310000} + set ARMV8_CTIBASE {0x90020000 0x90120000 0x90220000 0x90320000} + + # AM625 has 1 cluster of 1 R5s core. + set _r5_cores 1 + set R5_NAMES {main0_r5.0} + set R5_DBGBASE {0x9d410000} + set R5_CTIBASE {0x9d418000} + + # sysctrl CTI base + set CM3_CTIBASE {0x20001000} + # Sysctrl power-ap unlock offsets + set _sysctrl_ap_unlock_offsets {0xf0 0x78} + + # M4 processor + set _gp_mcu_cores 1 + set _gp_mcu_ap_unlock_offsets {0xf0 0x7c} } j721e { set _CHIPNAME j721e @@ -106,12 +122,6 @@ switch $_soc { # J721E has 3 clusters of 2 R5 cores each. set _r5_cores 6 - set _mcu_r5_cores 2 - set _mcu_base_core_id 0 - set _main0_r5_cores 2 - set _main0_base_core_id 2 - set _main1_r5_cores 2 - set _main1_base_core_id 4 } j7200 { set _CHIPNAME j7200 @@ -123,18 +133,32 @@ switch $_soc { # J7200 has 2 clusters of 2 R5 cores each. set _r5_cores 4 - set _mcu_r5_cores 2 - set _mcu_base_core_id 0 - set _main0_r5_cores 2 - set _main0_base_core_id 2 - set _main1_r5_cores 0 - set _main1_base_core_id 0 set R5_DBGBASE {0x9d010000 0x9d012000 0x9d110000 0x9d112000} set R5_CTIBASE {0x9d018000 0x9d019000 0x9d118000 0x9d119000} # M3 CTI base set CM3_CTIBASE {0x20001000} } + j721s2 { + set _CHIPNAME j721s2 + set _K3_DAP_TAPID 0x0bb7502f + + # J721s2 has 1 cluster of 2 A72 cores. + set _armv8_cpu_name a72 + set _armv8_cores 2 + + # J721s2 has 3 clusters of 2 R5 cores each. + set _r5_cores 6 + + # sysctrl CTI base + set CM3_CTIBASE {0x20001000} + # Sysctrl power-ap unlock offsets + set _sysctrl_ap_unlock_offsets {0xf0 0x78} + + # M4 processor + set _gp_mcu_cores 1 + set _gp_mcu_ap_unlock_offsets {0xf0 0x7c} + } default { echo "'$_soc' is invalid!" } @@ -147,18 +171,42 @@ set _TARGETNAME $_CHIPNAME.cpu set _CTINAME $_CHIPNAME.cti -# M3 is always present -cti create $_CTINAME.m3 -dap $_CHIPNAME.dap -ap-num 7 -baseaddr [lindex $CM3_CTIBASE 0] -target create $_TARGETNAME.m3 cortex_m -dap $_CHIPNAME.dap -ap-num 7 -defer-examine -$_TARGETNAME.m3 configure -event reset-assert { } +# sysctrl is always present +cti create $_CTINAME.sysctrl -dap $_CHIPNAME.dap -ap-num 7 -baseaddr [lindex $CM3_CTIBASE 0] +target create $_TARGETNAME.sysctrl cortex_m -dap $_CHIPNAME.dap -ap-num 7 -defer-examine +$_TARGETNAME.sysctrl configure -event reset-assert { } -proc m3_up { args } { - # To access M3, we need to enable the JTAG access for the same. +proc sysctrl_up {} { + # To access sysctrl, we need to enable the JTAG access for the same. # Ensure Power-AP unlocked - $::_CHIPNAME.dap apreg 3 [lindex $::_m3_ap_unlock_offsets 0] 0x00190000 - $::_CHIPNAME.dap apreg 3 [lindex $::_m3_ap_unlock_offsets 1] 0x00102098 + $::_CHIPNAME.dap apreg 3 [lindex $::_sysctrl_ap_unlock_offsets 0] 0x00190000 + $::_CHIPNAME.dap apreg 3 [lindex $::_sysctrl_ap_unlock_offsets 1] 0x00102098 + + $::_TARGETNAME.sysctrl arp_examine +} - $::_TARGETNAME.m3 arp_examine +$_TARGETNAME.sysctrl configure -event gdb-attach { + sysctrl_up + # gdb-attach default rule + halt 1000 +} + +proc _cpu_no_smp_up {} { + set _current_target [target current] + set _current_type [$_current_target cget -type] + + $_current_target arp_examine + $_current_target $_current_type dbginit +} + +proc _armv8_smp_up {} { + for { set _core 0 } { $_core < $::_armv8_cores } { incr _core } { + $::_TARGETNAME.$::_armv8_cpu_name.$_core arp_examine + $::_TARGETNAME.$::_armv8_cpu_name.$_core aarch64 dbginit + $::_TARGETNAME.$::_armv8_cpu_name.$_core aarch64 smp on + } + # Set Default target as core 0 + targets $::_TARGETNAME.$::_armv8_cpu_name.0 } set _v8_smp_targets "" @@ -172,6 +220,20 @@ for { set _core 0 } { $_core < $_armv8_cores } { incr _core } { -dbgbase [lindex $ARMV8_DBGBASE $_core] -cti $_CTINAME.$_armv8_cpu_name.$_core -defer-examine set _v8_smp_targets "$_v8_smp_targets $_TARGETNAME.$_armv8_cpu_name.$_core" + + if { $_v8_smp_debug == 0 } { + $_TARGETNAME.$_armv8_cpu_name.$_core configure -event gdb-attach { + _cpu_no_smp_up + # gdb-attach default rule + halt 1000 + } + } else { + $_TARGETNAME.$_armv8_cpu_name.$_core configure -event gdb-attach { + _armv8_smp_up + # gdb-attach default rule + halt 1000 + } + } } # Setup ARMV8 proc commands based on CPU to prevent people confusing SoCs @@ -181,76 +243,59 @@ set _armv8_smp_cmd "$_armv8_cpu_name"_smp if { $_v8_smp_debug == 0 } { proc $_armv8_up_cmd { args } { - foreach { _core } [set args] { - $::_TARGETNAME.$::_armv8_cpu_name.$_core arp_examine - $::_TARGETNAME.$::_armv8_cpu_name.$_core aarch64 dbginit + foreach _core $args { + targets $_core + _cpu_no_smp_up } } } else { proc $_armv8_smp_cmd { args } { - for { set _core 0 } { $_core < $::_armv8_cores } { incr _core } { - $::_TARGETNAME.$::_armv8_cpu_name.$_core arp_examine - $::_TARGETNAME.$::_armv8_cpu_name.$_core aarch64 dbginit - $::_TARGETNAME.$::_armv8_cpu_name.$_core aarch64 smp on - } - # Set Default target are core 0 - targets $::_TARGETNAME.$::_armv8_cpu_name.0 + _armv8_smp_up } - # Declare SMP target smp $:::_v8_smp_targets } for { set _core 0 } { $_core < $_r5_cores } { incr _core } { - cti create $_CTINAME.r5.$_core -dap $_CHIPNAME.dap -ap-num 1 \ + set _r5_name [lindex $R5_NAMES $_core] + cti create $_CTINAME.$_r5_name -dap $_CHIPNAME.dap -ap-num 1 \ -baseaddr [lindex $R5_CTIBASE $_core] # inactive core examination will fail - wait till startup of additional core - target create $_TARGETNAME.r5.$_core cortex_r4 -dap $_CHIPNAME.dap \ + target create $_TARGETNAME.$_r5_name cortex_r4 -dap $_CHIPNAME.dap \ -dbgbase [lindex $R5_DBGBASE $_core] -ap-num 1 -defer-examine -} - -if { $_mcu_r5_cores != 0 } { - proc mcu_r5_up { args } { - foreach { _core } [set args] { - set _core [expr {$_core + $::_mcu_base_core_id}] - $::_TARGETNAME.r5.$_core arp_examine - $::_TARGETNAME.r5.$_core cortex_r4 dbginit - } - } -} -if { $_main0_r5_cores != 0 } { - proc main0_r5_up { args } { - foreach { _core } [set args] { - set _core [expr {$_core + $::_main0_base_core_id}] - $::_TARGETNAME.r5.$_core arp_examine - $::_TARGETNAME.r5.$_core cortex_r4 dbginit - } + $_TARGETNAME.$_r5_name configure -event gdb-attach { + _cpu_no_smp_up + # gdb-attach default rule + halt 1000 } } -if { $_main1_r5_cores != 0 } { - proc main1_r5_up { args } { - foreach { _core } [set args] { - set _core [expr {$_core + $::_main1_base_core_id}] - $::_TARGETNAME.r5.$_core arp_examine - $::_TARGETNAME.r5.$_core cortex_r4 dbginit - } +proc r5_up { args } { + foreach _core $args { + targets $_core + _cpu_no_smp_up } } -if { $_mcu_m4_cores != 0 } { - cti create $_CTINAME.m4 -dap $_CHIPNAME.dap -ap-num 8 -baseaddr [lindex $CM4_CTIBASE 0] - target create $_TARGETNAME.m4 cortex_m -dap $_CHIPNAME.dap -ap-num 8 -defer-examine - $_TARGETNAME.m4 configure -event reset-assert { } +if { $_gp_mcu_cores != 0 } { + cti create $_CTINAME.gp_mcu -dap $_CHIPNAME.dap -ap-num 8 -baseaddr [lindex $CM4_CTIBASE 0] + target create $_TARGETNAME.gp_mcu cortex_m -dap $_CHIPNAME.dap -ap-num 8 -defer-examine + $_TARGETNAME.gp_mcu configure -event reset-assert { } - proc m4_up { args } { - # To access M4, we need to enable the JTAG access for the same. + proc gp_mcu_up {} { + # To access GP MCU, we need to enable the JTAG access for the same. # Ensure Power-AP unlocked - $::_CHIPNAME.dap apreg 3 [lindex $::_m4_ap_unlock_offsets 0] 0x00190000 - $::_CHIPNAME.dap apreg 3 [lindex $::_m4_ap_unlock_offsets 1] 0x00102098 + $::_CHIPNAME.dap apreg 3 [lindex $::_gp_mcu_ap_unlock_offsets 0] 0x00190000 + $::_CHIPNAME.dap apreg 3 [lindex $::_gp_mcu_ap_unlock_offsets 1] 0x00102098 + + $::_TARGETNAME.gp_mcu arp_examine + } - $::_TARGETNAME.m4 arp_examine + $_TARGETNAME.gp_mcu configure -event gdb-attach { + gp_mcu_up + # gdb-attach default rule + halt 1000 } } diff --git a/tcl/target/vd_aarch64.cfg b/tcl/target/vd_aarch64.cfg new file mode 100644 index 000000000..619134aa6 --- /dev/null +++ b/tcl/target/vd_aarch64.cfg @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# Arm v8 64b Cortex A + +if {![info exists _CORES]} { + set _CORES 1 +} +if {![info exists _CHIPNAME]} { + set _CHIPNAME aarch64 +} +set _TARGETNAME $_CHIPNAME.cpu +set _CTINAME $_CHIPNAME.cti + +set DBGBASE {0x80810000 0x80910000} +set CTIBASE {0x80820000 0x80920000} + +dap create $_CHIPNAME.dap -chain-position $_TARGETNAME +$_CHIPNAME.dap apsel 1 + +for { set _core 0 } { $_core < $_CORES } { incr _core } \ +{ + cti create $_CTINAME.$_core -dap $_CHIPNAME.dap -ap-num 1 -baseaddr [lindex $CTIBASE $_core] + set _command "target create $_TARGETNAME.$_core aarch64 -dap $_CHIPNAME.dap \ + -dbgbase [lindex $DBGBASE $_core] -cti $_CTINAME.$_core -coreid $_core" + if { $_core != 0 } { + # non-boot core examination may fail + set _command "$_command -defer-examine" + set _smp_command "$_smp_command $_TARGETNAME.$_core" + } else { + set _smp_command "target smp $_TARGETNAME.$_core" + } + eval $_command +} +eval $_smp_command + +# default target is core 0 +targets $_TARGETNAME.0 diff --git a/tcl/target/vd_cortex_m.cfg b/tcl/target/vd_cortex_m.cfg new file mode 100644 index 000000000..4d7b0df26 --- /dev/null +++ b/tcl/target/vd_cortex_m.cfg @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# ARM Cortex M + +if {![info exists _CHIPNAME]} { + set _CHIPNAME cortex_m +} +set _TARGETNAME $_CHIPNAME.cpu + +dap create $_CHIPNAME.dap -chain-position $_TARGETNAME + +target create $_TARGETNAME cortex_m -dap $_CHIPNAME.dap diff --git a/tcl/target/vd_riscv.cfg b/tcl/target/vd_riscv.cfg new file mode 100644 index 000000000..b42b25a3a --- /dev/null +++ b/tcl/target/vd_riscv.cfg @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# Cadence virtual debug interface +# RISCV core + +if {![info exists _HARTID]} { + set _HARTID 0x00 +} +if {![info exists _CHIPNAME]} { + set _CHIPNAME riscv +} +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME riscv -chain-position $_TARGETNAME -coreid $_HARTID + +riscv set_reset_timeout_sec 120 +riscv set_command_timeout_sec 120 +# prefer to use sba for system bus access +riscv set_prefer_sba on diff --git a/tcl/target/wch-riscv.cfg b/tcl/target/wch-riscv.cfg new file mode 100644 index 000000000..35131c306 --- /dev/null +++ b/tcl/target/wch-riscv.cfg @@ -0,0 +1,13 @@ +transport select sdi + +wlink_set_address 0x00000000 +set _CHIPNAME wch_riscv +sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001 + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME +$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1 +set _FLASHNAME $_CHIPNAME.flash + +flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0 diff --git a/tcl/target/xilinx_zynqmp.cfg b/tcl/target/xilinx_zynqmp.cfg index e66289a70..2df7a4ff9 100644 --- a/tcl/target/xilinx_zynqmp.cfg +++ b/tcl/target/xilinx_zynqmp.cfg @@ -99,7 +99,7 @@ targets $_TARGETNAME.0 proc core_up { args } { global _TARGETNAME - foreach { core } [set args] { + foreach core $args { $_TARGETNAME.$core arp_examine } } diff --git a/tcl/tools/test_cpu_speed.tcl b/tcl/tools/test_cpu_speed.tcl new file mode 100644 index 000000000..cef2bbbd7 --- /dev/null +++ b/tcl/tools/test_cpu_speed.tcl @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Description: +# Measure the CPU clock frequency of an ARM Cortex-M based device. +# +# Return: +# The CPU clock frequency in Hz. A negative value indicates that the loop +# counter was saturated. +# +# Note: +# You may need to adapt the number of cycles for your device. +# +add_help_text cortex_m_test_cpu_speed "Measure the CPU clock frequency of an ARM Cortex-M based device" +add_usage_text cortex_m_test_cpu_speed {address [timeout [cycles_per_loop]]} +proc cortex_m_test_cpu_speed { address { timeout 200 } { cycles_per_loop 4 } } { + set loop_counter_start 0xffffffff + + halt + + # Backup registers and memory. + set backup_regs [get_reg -force {pc r0 xPSR}] + set backup_mem [read_memory $address 16 3] + + # We place the following code at the given address to measure the + # CPU clock frequency: + # + # 3801: subs r0, #1 + # d1fd: bne #-2 + # e7fe: b #-4 + write_memory $address 16 {0x3801 0xd1fd 0xe7fe} + + set_reg "pc $address r0 $loop_counter_start" + resume + sleep $timeout + halt + + # Get the loop counter value from register r0. + set loop_counter_end [dict values [get_reg r0]] + set loop_counter_diff [expr {$loop_counter_start - $loop_counter_end}] + + # Restore registers and memory. + set_reg $backup_regs + write_memory $address 16 $backup_mem + + if { [expr {$loop_counter_end == 0}] } { + return -1 + } + + return [expr {double($loop_counter_diff) * $cycles_per_loop / $timeout * 1000}] +} diff --git a/testing/examples/cortex/cm3-ftest.cfg b/testing/examples/cortex/cm3-ftest.cfg index 6f3fa5c81..02c8da11a 100644 --- a/testing/examples/cortex/cm3-ftest.cfg +++ b/testing/examples/cortex/cm3-ftest.cfg @@ -50,7 +50,7 @@ proc load_and_run { name halfwords n_instr } { echo "# code to trigger $name vector" set addr 0x20000000 - # array2mem should be faster, though we'd need to + # write_memory should be faster, though we'd need to # compute the resulting $addr ourselves foreach opcode $halfwords { mwh $addr $opcode diff --git a/tools/filter_openocd_log.py b/tools/filter_openocd_log.py index cd38efdb4..666e166dd 100755 --- a/tools/filter_openocd_log.py +++ b/tools/filter_openocd_log.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import re @@ -38,7 +38,7 @@ def shorten_buffer(outfd, buf, current_repetition): # Look for repeated sequences... repetitions = [] - for length in range(1, len(buf)/2): + 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:]): @@ -50,9 +50,9 @@ def shorten_buffer(outfd, buf, current_repetition): repetitions.append((matched_lines + length, length)) if repetitions: - repetitions.sort(key=lambda entry: (entry[0] * (entry[1] / entry[0]), -entry[1])) + repetitions.sort(key=lambda entry: -entry[1]) matched_lines, length = repetitions[-1] - repeated = matched_lines / length + repeated = int(matched_lines / length) if repeated * length >= 3: sequence = buf[:length] del buf[:repeated * length] @@ -74,9 +74,9 @@ def shorten_buffer(outfd, buf, current_repetition): buf.pop(0) if length_before <= len(buf): - print "Buffer:" + print("Buffer:") for entry in buf: - print "%r" % entry[0] + print("%r" % entry[0]) assert False return None