Compare commits

...

12 Commits

Author SHA1 Message Date
Antonio Borneo
1f5da25ed1 configure.ac: rename 'adapterTuple' as 'adapter_driver'
Commit ce3bf664c8 ("configure.ac: rename M4 macro 'adapter' to
prevent accidental conflicts") renames the macro as 'adapterTuple'
but since the macro name is printed in error messages, this
creates cryptic errors like:
	configure: error: header sys/mman.h is required
	for adapterTuple "Bitbanging on EP93xx-based SBCs".

Rename it as 'adapter_driver'. It keeps valid the purpose of the
former renaming, while keeping readable the error message.

Change-Id: Idd68270fbdf879153cd59f4cacf5036aa599b251
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Fixes: ce3bf664c8 ("configure.ac: rename M4 macro 'adapter' to prevent accidental conflicts")
Reviewed-on: https://review.openocd.org/c/openocd/+/9160
Tested-by: jenkins
2025-10-11 16:17:45 +00:00
Antonio Borneo
7c16c38eda configure.ac: add adapter dependency from sys/mman.h
The adapter's driver that require the header file sys/mman.h
should check for it and don't compile if it is not present.

Add the check for sys/mman.h in configure.ac and prevent the
build of the adapter's driver that depend on it.

Change-Id: If0a518069e8fef9b41a67b633ec20e2f142a8b14
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/9159
Tested-by: jenkins
2025-10-11 16:17:22 +00:00
Marc Schink
910e6ba2f0 adapter/parport: Add device file support
Allow to specify the parallel port by its device file. Deprecate port
number support but keep it for backward compatibility.

This is one necessary step to remove direct I/O support for the parallel
port driver.

While at it, consistently return ERROR_JTAG_INIT_FAILED in case of a
failure in parport_init().

Change-Id: Ie68087f05ece4b32ccab9d9bdfbf7e1a779e9031
Signed-off-by: Marc Schink <dev@zapb.de>
Reviewed-on: https://review.openocd.org/c/openocd/+/9152
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-10-11 16:15:10 +00:00
Marc Schink
39ed0b0bba adapter/parport: Deprecate direct I/O support
We deprecate direct I/O support in favor of ppdev for the following
reasons:

  - Linux supports ppdev since ~2.4 (released ~24 years ago) and it is
    enabled by default on major distros (Ubuntu, Fedora). So it is
    effectively ubiquitous
  - FreeBSD provides no direct I/O support, so ppdev (ppi) is the only
    viable option
  - Direct I/O requires root/elevated privileges which is inadvisable
  - Removing direct I/O reduces build and driver complexity and yields
    a smaller, easier-to-maintain codebase
  - Supporting only ppdev allows us to simplify the codebase by using
    device files (e.g., /dev/parport0) instead of numeric identifiers

Windows is the only rationale to keep direct I/O, but the user base
appears minimal to nonexistent and no active contributors can test the
Windows driver.

Change-Id: Ia6d5ed6e8c5faa2a9b4919ca97c5cf9033372a64
Signed-off-by: Marc Schink <dev@zapb.de>
Reviewed-on: https://review.openocd.org/c/openocd/+/9151
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-10-11 16:14:07 +00:00
Marc Schink
6d51e6b900 tcl/interface: Remove flashlink config
Remove the configuration file, a replacement is already available.

Link the old filename to the new configuration file to ensure backwards
compatibility.

Change-Id: I77cbd62d805b1c9b9bb8f56a823c3f6476d1a5a9
Signed-off-by: Marc Schink <dev@zapb.de>
Reviewed-on: https://review.openocd.org/c/openocd/+/9150
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-10-11 16:13:44 +00:00
Marc Schink
4805ecad76 tcl/interface: Remove chameleon config
Remove the configuration file, a replacement is already available.

Link the old filename to the new configuration file to ensure backwards
compatibility.

Change-Id: I11361e471bb7ec277a850f956e51cd7d0fab408d
Signed-off-by: Marc Schink <dev@zapb.de>
Reviewed-on: https://review.openocd.org/c/openocd/+/9149
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-10-11 16:13:34 +00:00
Marek Kraus
382f067b6e tcl/target: add Bouffalo Lab BL616 chip series support
Adds support for BL616 series of chips, BL616 and BL618.
No flash bank support yet.

BL616 in comparison with BL602-series have new architecture,
using T-Head E907 RISC-V cores, instead of SiFive ones.

As BL602-series, the ndmreset bit in RISC-V Debug Module
does not reset the chip as it should, so we need to do it
manually with registers almost the same way as in BL602.

Additionally, JTAG Debug Transport Module in the chip have wrongly
implemented Test-Logic-Reset state, causing automatic chain scan
not working at all after initial JTAG usage. This is because
Test-Logic-State do not set IR instruction to IDCODE,
as it should by JTAG spec. We can fix this by getting state machine
to known state and configure IR instruction manually to IDCODE.
This bug was so far found in T-Head C906 and E907 IP cores.

This patch was tested heavily and works reliably on
BL616, BL618 and QCC74X.

Change-Id: Idc80a702e817d78fc0ca925572c68d4d0c28ce4e
Signed-off-by: Marek Kraus <gamelaster@outlook.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/9145
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
2025-10-11 16:07:02 +00:00
Antonio Borneo
34d76b805e README: add instructions for missing INSTALL file
The file INSTALL has been dropped in 2009 with [1] and it has been
replaced by the INSTALL file generated by automake.
The file INSTALL is only present in official releases of OpenOCD
and not in the code cloned from git.

Add a note in README to report this inconsistency.

Change-Id: I881b4282550a80bd5a6c61baff05543714a2e9c5
Reported-by: Mats <mats_w@users.sourceforge.net>
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Fixes: https://sourceforge.net/p/openocd/tickets/451/
Link: [1] commit ef733b48e9 ("Switch automake handling to use
	--gnu mode, not --foreign. Remove INSTALL file; automake
	generates a copy of the latest version.")
Reviewed-on: https://review.openocd.org/c/openocd/+/9121
Tested-by: jenkins
2025-10-11 16:02:39 +00:00
EasyDevKits
5fa74d4ee8 jtag/ch347: Refine driver and configs for EasyDevKits adapters
This commit improves support for CH347-based JTAG adapters:

- configure.ac: removed "Mode3" restriction (CH347F does not require mode).
- configs: added board config for ESP32-WROVER-E WCH JTAG DevKit and
  ESP32-WROVER-E FTDI JTAG DevKit
- ch347 driver: removed `ch347 activity_led` command; activity LED
  is now controlled via the generic `adapter gpio led` command.
- doc/openocd.texi: updated documentation accordingly.

Change-Id: I5524290297adcc004e00af919181868d2b6303af
Signed-off-by: EasyDevKits <info@easydevkits.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/9015
Reviewed-by: zapb <dev@zapb.de>
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
2025-10-11 16:02:18 +00:00
Antonio Borneo
8b43a967e5 target: cortex_m: add comment for breakpoint of length 3
Add a comment in the breakpoint code to clarify the check for the
odd breakpoint length of 3 bytes, introduced by [1].

[1]: commit 0a5e03c12a ("cortex_m.c: Use two byte breakpoint for
     32bit Thumb-2 request").

Change-Id: I024863d10078b5d9062c876aa59ccf70a81bf641
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/9139
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins
2025-10-11 15:57:06 +00:00
Antonio Borneo
04da6e2c62 target: cortex-m: add support for armv8m caches
Cores like Cortex-M7, Cortex-M55 and Cortex-M85 can have either
D-Cache and/or I-Cache.
Using SW breakpoints in RAM requires handling these caches.

Detect the presence of cache at examine.
Detect cache state (enable/disable) at debug entry.
Take care of caches synchronization through the PoC (usually the
SRAM) while setting and removing SW breakpoints.
Add command 'cache_info' to check cache presence and size.

Change-Id: Ice637c215fe3042c8fff57edefbab1b86515ef4b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: https://review.openocd.org/c/openocd/+/9077
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins
2025-10-11 15:56:41 +00:00
Marc Schink
2abf8daa80 target/cortex_m: Remove echo of 'reset_config' command
Do not echo the selected reset config. This is one of many changes to
make the behavior of Tcl commands more consistent.

This also avoids stray and confusing messages in the output of OpenOCD.
For example, the "reset_config" line here:

  Open On-Chip Debugger 0.12.0+dev-00802-gb7f0145fc-dirty
  Licensed under GNU GPL v2
  For bug reports, read
  	http://openocd.org/doc/doxygen/bugs.html
  cortex_m reset_config sysresetreq
  Info : Listening on port 6666 for tcl connections
  Info : Listening on port 4444 for telnet connections

While at it, fix some coding style and command handling issues.

Change-Id: I3b3d8687af1d23a2dc1764f29b52dc607b80cb59
Signed-off-by: Marc Schink <dev@zapb.de>
Reviewed-on: https://review.openocd.org/c/openocd/+/8638
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Tested-by: jenkins
2025-10-11 15:51:37 +00:00
17 changed files with 859 additions and 157 deletions

5
README
View File

@@ -191,6 +191,11 @@ and compiling the OpenOCD source code. That file is provided by
default for all GNU autotools packages. If you are not familiar with
the GNU autotools, then you should read those instructions first.
Note: if the INSTALL file is not present, it means you are using the
source code from a development branch, not from an OpenOCD release.
In this case, follow the instructions 'Compiling OpenOCD' below and
the file will be created by the first command './bootstrap'.
The remainder of this document tries to provide some instructions for
those looking for a quick-install.

View File

@@ -69,6 +69,7 @@ AC_CHECK_HEADERS([netdb.h])
AC_CHECK_HEADERS([poll.h])
AC_CHECK_HEADERS([strings.h])
AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([sys/mman.h])
AC_CHECK_HEADERS([sys/param.h])
AC_CHECK_HEADERS([sys/select.h])
AC_CHECK_HEADERS([sys/stat.h])
@@ -129,7 +130,7 @@ m4_define([ADAPTER_OPT], [m4_translit(ADAPTER_ARG($1), [_], [-])])
m4_define([USB1_ADAPTERS],
[[[ftdi], [MPSSE mode of FTDI based devices], [FTDI]],
[[ch347], [Mode 3 of CH347 based devices], [CH347]],
[[ch347], [CH347 based devices], [CH347]],
[[stlink], [ST-Link Programmer], [HLADAPTER_STLINK]],
[[ti_icdi], [TI ICDI JTAG Programmer], [HLADAPTER_ICDI]],
[[ulink], [Keil ULINK JTAG Programmer], [ULINK]],
@@ -300,15 +301,15 @@ AS_IF([test "x$debug_malloc" = "xyes" -a "x$have_glibc" = "xyes"], [
])
m4_define([AC_ARG_ADAPTERS], [
m4_foreach([adapterTuple], [$1],
[AC_ARG_ENABLE(ADAPTER_OPT([adapterTuple]),
AS_HELP_STRING([--enable-ADAPTER_OPT([adapterTuple])[[[=yes/no/auto]]]],
[Enable building support for the ]ADAPTER_DESC([adapterTuple])[ (default is $2)]),
m4_foreach([adapter_driver], [$1],
[AC_ARG_ENABLE(ADAPTER_OPT([adapter_driver]),
AS_HELP_STRING([--enable-ADAPTER_OPT([adapter_driver])[[[=yes/no/auto]]]],
[Enable building support for the ]ADAPTER_DESC([adapter_driver])[ (default is $2)]),
[case "${enableval}" in
yes|no|auto) ;;
*) AC_MSG_ERROR([Option --enable-ADAPTER_OPT([adapterTuple]) has invalid value "${enableval}".]) ;;
*) AC_MSG_ERROR([Option --enable-ADAPTER_OPT([adapter_driver]) has invalid value "${enableval}".]) ;;
esac],
[ADAPTER_VAR([adapterTuple])=$2])
[ADAPTER_VAR([adapter_driver])=$2])
])
])
@@ -356,7 +357,7 @@ AC_ARG_ENABLE([parport_ppdev],
AC_ARG_ENABLE([parport_giveio],
AS_HELP_STRING([--enable-parport-giveio],
[Enable use of giveio for parport (for CygWin only)]),
[Enable use of giveio for parport (deprecated, for CygWin only)]),
[parport_use_giveio=$enableval], [parport_use_giveio=])
AC_ARG_ENABLE([gw16012],
@@ -606,23 +607,24 @@ PKG_CHECK_MODULES([LIBJAYLINK], [libjaylink >= 0.2],
# Arg $3: What prerequisites are missing, to be shown in an error message
# if an adapter was requested but cannot be enabled.
m4_define([PROCESS_ADAPTERS], [
m4_foreach([adapterTuple], [$1], [
m4_foreach([adapter_driver], [$1], [
AS_IF([test $2], [
AS_IF([test "x$ADAPTER_VAR([adapterTuple])" != "xno"], [
AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [1],
[1 if you want the ]ADAPTER_DESC([adapterTuple]).)
AS_IF([test "x$ADAPTER_VAR([adapter_driver])" != "xno"], [
AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [1],
[1 if you want the ]ADAPTER_DESC([adapter_driver]).)
], [
AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [0],
[0 if you do not want the ]ADAPTER_DESC([adapterTuple]).)
AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [0],
[0 if you do not want the ]ADAPTER_DESC([adapter_driver]).)
])
], [
AS_IF([test "x$ADAPTER_VAR([adapterTuple])" = "xyes"], [
AC_MSG_ERROR([$3 is required for [adapterTuple] "ADAPTER_DESC([adapterTuple])".])
AS_IF([test "x$ADAPTER_VAR([adapter_driver])" = "xyes"], [
AC_MSG_ERROR([$3 is required for [adapter_driver] "ADAPTER_DESC([adapter_driver])".])
])
ADAPTER_VAR([adapterTuple])=no
AC_DEFINE([BUILD_]ADAPTER_SYM([adapterTuple]), [0], [0 if you do not want the ]ADAPTER_DESC([adapterTuple]).)
ADAPTER_VAR([adapter_driver])=no
AC_DEFINE([BUILD_]ADAPTER_SYM([adapter_driver]), [0],
[0 if you do not want the ]ADAPTER_DESC([adapter_driver]).)
])
AM_CONDITIONAL(ADAPTER_SYM([adapterTuple]), [test "x$ADAPTER_VAR([adapterTuple])" != "xno"])
AM_CONDITIONAL(ADAPTER_SYM([adapter_driver]), [test "x$ADAPTER_VAR([adapter_driver])" != "xno"])
])
])
@@ -632,12 +634,14 @@ PROCESS_ADAPTERS([HIDAPI_USB1_ADAPTERS], ["x$use_hidapi" = "xyes" -a "x$use_libu
PROCESS_ADAPTERS([LIBFTDI_ADAPTERS], ["x$use_libftdi" = "xyes"], [libftdi])
PROCESS_ADAPTERS([LIBFTDI_USB1_ADAPTERS], ["x$use_libftdi" = "xyes" -a "x$use_libusb1" = "xyes"], [libftdi and libusb-1.x])
PROCESS_ADAPTERS([LIBGPIOD_ADAPTERS], ["x$use_libgpiod" = "xyes"], [Linux libgpiod])
PROCESS_ADAPTERS([DMEM_ADAPTER], ["x$is_linux" = "xyes"], [Linux /dev/mem])
PROCESS_ADAPTERS([DMEM_ADAPTER], ["x$is_linux" = "xyes" -a "x$ac_cv_header_sys_mman_h" = "xyes"], [Linux /dev/mem])
PROCESS_ADAPTERS([SYSFSGPIO_ADAPTER], ["x$is_linux" = "xyes"], [Linux sysfs])
PROCESS_ADAPTERS([REMOTE_BITBANG_ADAPTER], [true], [unused])
PROCESS_ADAPTERS([CMSIS_DAP_TCP_ADAPTER], [true], [unused])
PROCESS_ADAPTERS([LIBJAYLINK_ADAPTERS], ["x$use_internal_libjaylink" = "xyes" -o "x$use_libjaylink" = "xyes"], [libjaylink-0.2])
PROCESS_ADAPTERS([XVC_ADAPTERS], ["x$is_linux" = "xyes" -a "x$ac_cv_header_linux_pci_h" = "xyes"], [Linux build])
PROCESS_ADAPTERS([XVC_ADAPTERS],
["x$is_linux" = "xyes" -a "x$ac_cv_header_linux_pci_h" = "xyes" -a "x$ac_cv_header_sys_mman_h" = "xyes"],
[Linux build with headers linux/pci.h and sys/mman.h])
PROCESS_ADAPTERS([SERIAL_PORT_ADAPTERS], ["x$can_build_buspirate" = "xyes"],
[internal error: validation should happen beforehand])
PROCESS_ADAPTERS([PARALLEL_PORT_ADAPTER], [true], [unused])
@@ -649,8 +653,8 @@ PROCESS_ADAPTERS([JTAG_VPI_ADAPTER], [true], [unused])
PROCESS_ADAPTERS([RSHIM_ADAPTER], ["x$can_build_rshim" = "xyes"],
[internal error: validation should happen beforehand])
PROCESS_ADAPTERS([AMTJTAGACCEL_ADAPTER], [true], [unused])
PROCESS_ADAPTERS([HOST_ARM_BITBANG_ADAPTERS], [true], [unused])
PROCESS_ADAPTERS([HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS], [true], [unused])
PROCESS_ADAPTERS([HOST_ARM_BITBANG_ADAPTERS], ["x$ac_cv_header_sys_mman_h" = "xyes"], [header sys/mman.h])
PROCESS_ADAPTERS([HOST_ARM_OR_AARCH64_BITBANG_ADAPTERS], ["x$ac_cv_header_sys_mman_h" = "xyes"], [header sys/mman.h])
PROCESS_ADAPTERS([DUMMY_ADAPTER], [true], [unused])
AS_IF([test "x$enable_linuxgpiod" != "xno"], [
@@ -825,11 +829,17 @@ AS_IF([test "x$build_gw16012" = "xyes"], [
AC_MSG_WARN([Gateworks GW16012 JTAG adapter is deprecated and support will be removed in the next release!])
])
AS_IF([test "x$parport_use_giveio" = "xyes" || test [ "x$enable_parport" != "xno" -a "x$parport_use_ppdev" = "xno"]], [
echo
echo
AC_MSG_WARN([Parallel port access with direct I/O is deprecated and support will be removed in the next release!])
])
echo
echo
echo OpenOCD configuration summary
echo ---------------------------------------------------
m4_foreach([adapterTuple], [USB1_ADAPTERS,
m4_foreach([adapter_driver], [USB1_ADAPTERS,
HIDAPI_ADAPTERS, HIDAPI_USB1_ADAPTERS, LIBFTDI_ADAPTERS,
LIBFTDI_USB1_ADAPTERS,
LIBGPIOD_ADAPTERS,
@@ -851,8 +861,8 @@ m4_foreach([adapterTuple], [USB1_ADAPTERS,
DUMMY_ADAPTER,
OPTIONAL_LIBRARIES,
COVERAGE],
[s=m4_format(["%-49s"], ADAPTER_DESC([adapterTuple]))
AS_CASE([$ADAPTER_VAR([adapterTuple])],
[s=m4_format(["%-49s"], ADAPTER_DESC([adapter_driver]))
AS_CASE([$ADAPTER_VAR([adapter_driver])],
[auto], [
echo "$s"yes '(auto)'
],
@@ -864,8 +874,8 @@ m4_foreach([adapterTuple], [USB1_ADAPTERS,
],
[
AC_MSG_ERROR(m4_normalize([
Error in [adapterTuple] "ADAPTER_ARG([adapterTuple])": Variable "ADAPTER_VAR([adapterTuple])"
has invalid value "$ADAPTER_VAR([adapterTuple])".]))
Error in [adapter_driver] "ADAPTER_ARG([adapter_driver])": Variable "ADAPTER_VAR([adapter_driver])"
has invalid value "$ADAPTER_VAR([adapter_driver])".]))
])
])
echo

View File

@@ -2590,6 +2590,9 @@ mitigates the problem.
@end itemize
@end itemize
The driver supports activity LED through the generic
command @ref{adapter gpio, @command{adapter gpio led}}.
This driver has these driver-specific command:
@deffn {Config Command} {ch347 vid_pid} [vid pid]+
@@ -2609,17 +2612,6 @@ and product ID will be connected.
ch347 device_desc "EasyDevKit"
@end example
@end deffn
@deffn {Config Command} {ch347 activity_led} [n]gpio_number
If specified the drive let an activity LED blink during JTAG operations.
The number is the GPIO number of the CH347T chip. If prefixed with "n",
then this GPIO should be low active. The example configures GPIO4 as
low active activity LED. For the CH347T chip only GPIO3 (Pin11 / SCL),
GPIO4 (Pin15 / ACT), GPIO5 (Pin9 / TRST) and GPIO6 (Pin2 / CTS1) are possible.
@example
ch347 activity_led n4
@end example
@end deffn
@end deffn
@deffn {Interface Driver} {cmsis-dap}
@@ -3225,14 +3217,16 @@ The pin direction is given in the following table.
@tab 2
@end multitable
@deffn {Config Command} {parport port} port_number
Configure the number of the parallel port.
@deffn {Config Command} {parport port} file
Specify the device file of the parallel port device.
The parallel port device file is usually @file{/dev/parportX} on Linux and @file{/dev/ppiX} on FreeBSD.
When using PPDEV to access the parallel port, use the number of the parallel port file @file{/dev/parport} (Linux) or @file{/dev/ppi} (FreeBSD).
The default port number is 0.
For legacy reason, the port number @var{X} can be specified instead of the device file.
@b{Note:} Using the port number is a deprecated feature and will be removed in the future.
When using direct I/O, the number is the I/O port number.
The default port number is 0x378 (LTP1).
@b{Note:} Direct I/O support is deprecated and will be removed in the future.
@end deffn
@deffn {Config Command} {parport write_on_exit} (@option{on}|@option{off})
@@ -11120,6 +11114,10 @@ Enable or disable trace output for all ITM stimulus ports.
@subsection Cortex-M specific commands
@cindex Cortex-M
@deffn {Command} {cortex_m cache_info}
Report information about the type and size of the cache, if present.
@end deffn
@deffn {Command} {cortex_m maskisr} (@option{auto}|@option{on}|@option{off}|@option{steponly})
Control masking (disabling) interrupts during target step/resume.

View File

@@ -48,6 +48,7 @@
#include <jtag/interface.h>
#include <jtag/commands.h>
#include <jtag/swd.h>
#include <jtag/adapter.h>
#include <helper/time_support.h>
#include <helper/replacements.h>
#include <helper/list.h>
@@ -1805,32 +1806,6 @@ COMMAND_HANDLER(ch347_handle_device_desc_command)
return ERROR_OK;
}
/**
* @brief The command handler for configuring which GPIO pin is used as activity LED
*
* @return ERROR_OK at success; ERROR_COMMAND_SYNTAX_ERROR otherwise
*/
COMMAND_HANDLER(ch347_handle_activity_led_command)
{
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
uint8_t gpio;
if (CMD_ARGV[0][0] == 'n') {
COMMAND_PARSE_NUMBER(u8, &CMD_ARGV[0][1], gpio);
ch347_activity_led_active_high = false;
} else {
COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], gpio);
ch347_activity_led_active_high = true;
}
if (gpio >= GPIO_CNT || (BIT(gpio) & USEABLE_GPIOS) == 0)
return ERROR_COMMAND_ARGUMENT_INVALID;
ch347_activity_led_gpio_pin = gpio;
return ERROR_OK;
}
static const struct command_registration ch347_subcommand_handlers[] = {
{
.name = "vid_pid",
@@ -1846,13 +1821,6 @@ static const struct command_registration ch347_subcommand_handlers[] = {
.help = "set the USB device description of the CH347 device",
.usage = "description_string",
},
{
.name = "activity_led",
.handler = &ch347_handle_activity_led_command,
.mode = COMMAND_CONFIG,
.help = "if set this CH347 GPIO pin is the JTAG activity LED; start with n for active low output",
.usage = "[n]gpio_number",
},
COMMAND_REGISTRATION_DONE
};
@@ -1867,6 +1835,25 @@ static const struct command_registration ch347_command_handlers[] = {
COMMAND_REGISTRATION_DONE
};
/**
* @brief Configure which GPIO pin is used as the activity LED.
*
* Updates the global activity LED GPIO pin and polarity settings
* based on the provided configuration. If the given GPIO is not
* usable, the function returns without making changes.
*
* @param led_config Pointer to the GPIO configuration structure for the LED pin
*/
static void ch347_configure_activity_led(const struct adapter_gpio_config *led_config)
{
uint8_t gpio = led_config->gpio_num;
if (gpio >= GPIO_CNT || (BIT(gpio) & USEABLE_GPIOS) == 0)
return;
ch347_activity_led_gpio_pin = gpio;
ch347_activity_led_active_high = !led_config->active_low;
}
/**
* @brief CH347 Initialization function
*
@@ -1893,6 +1880,8 @@ static int ch347_init(void)
ch347.pack_size = UNSET;
ch347_configure_activity_led(&adapter_gpio_get_config()[ADAPTER_GPIO_IDX_LED]);
if (!swd_mode) {
tap_set_state(TAP_RESET);
} else {

View File

@@ -47,8 +47,11 @@
static const struct adapter_gpio_config *adapter_gpio_config;
#if PARPORT_USE_PPDEV == 0
static uint16_t parport_port;
#endif
static bool parport_write_exit_state;
static char *parport_device_file;
static uint32_t parport_toggling_time_ns = 1000;
static int wait_states;
@@ -258,13 +261,13 @@ static int parport_init(void)
if (gpio.gpio_num < 10 || gpio.gpio_num > 15 || gpio.gpio_num == 14) {
LOG_ERROR("The '%s' signal pin must be 10, 11, 12, 13, or 15",
adapter_gpio_get_name(gpio_index));
return ERROR_FAIL;
goto init_fail;
}
} else {
if (gpio.gpio_num < 2 || gpio.gpio_num > 9) {
LOG_ERROR("The '%s' signal pin must be 2, 3, 4, 5, 6, 7, 8, or 9",
adapter_gpio_get_name(gpio_index));
return ERROR_FAIL;
goto init_fail;
}
}
}
@@ -292,35 +295,37 @@ static int parport_init(void)
#if PARPORT_USE_PPDEV == 1
if (device_handle > 0) {
LOG_ERROR("Parallel port is already open");
return ERROR_JTAG_INIT_FAILED;
goto init_fail;
}
char device_path[256];
if (!parport_device_file) {
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
snprintf(device_path, sizeof(device_path), "/dev/ppi%d", parport_port);
parport_device_file = strdup("/dev/ppi0");
#else
snprintf(device_path, sizeof(device_path), "/dev/parport%d", parport_port);
#endif /* __FreeBSD__, __FreeBSD_kernel__ */
parport_device_file = strdup("/dev/parport0");
#endif
LOG_WARNING("No parallel port specified, using %s", parport_device_file);
LOG_WARNING("DEPRECATED! The lack of a parallel port specification is deprecated and will no longer work in the future");
}
LOG_DEBUG("Using parallel port %s", device_path);
LOG_DEBUG("Using parallel port %s", parport_device_file);
device_handle = open(device_path, O_WRONLY);
device_handle = open(parport_device_file, O_WRONLY);
if (device_handle < 0) {
int err = errno;
LOG_ERROR("Failed to open parallel port %s (errno = %d)", device_path,
err);
LOG_ERROR("Failed to open parallel port %s (errno = %d)",
parport_device_file, err);
LOG_ERROR("Check whether the device exists and if you have the required access rights");
return ERROR_JTAG_INIT_FAILED;
goto init_fail;
}
#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
int retval = ioctl(device_handle, PPCLAIM);
if (retval < 0) {
LOG_ERROR("Failed to claim parallel port %s", device_path);
return ERROR_JTAG_INIT_FAILED;
LOG_ERROR("Failed to claim parallel port %s", parport_device_file);
goto init_fail;
}
int value = PARPORT_MODE_COMPAT;
@@ -328,7 +333,7 @@ static int parport_init(void)
if (retval < 0) {
LOG_ERROR("Cannot set compatible mode to device");
return ERROR_JTAG_INIT_FAILED;
goto init_fail;
}
value = IEEE1284_MODE_COMPAT;
@@ -336,11 +341,13 @@ static int parport_init(void)
if (retval < 0) {
LOG_ERROR("Cannot set compatible 1284 mode to device");
return ERROR_JTAG_INIT_FAILED;
goto init_fail;
}
#endif /* not __FreeBSD__, __FreeBSD_kernel__ */
#else /* not PARPORT_USE_PPDEV */
LOG_WARNING("DEPRECATED: Parallel port access with direct I/O is deprecated and support will be removed in the next release");
if (!parport_port) {
parport_port = 0x378;
LOG_WARNING("No parallel port specified, using default 0x378 (LPT1)");
@@ -357,7 +364,7 @@ static int parport_init(void)
if (ioperm(dataport, 3, 1) != 0) {
#endif /* PARPORT_USE_GIVEIO */
LOG_ERROR("Missing privileges for direct I/O");
return ERROR_JTAG_INIT_FAILED;
goto init_fail;
}
// Make sure parallel port is in right mode (clear tristate and interrupt).
@@ -370,21 +377,27 @@ static int parport_init(void)
#endif /* PARPORT_USE_PPDEV */
if (parport_reset(0, 0) != ERROR_OK)
return ERROR_FAIL;
goto init_fail;
if (parport_write(0, 0, 0) != ERROR_OK)
return ERROR_FAIL;
goto init_fail;
if (parport_led(true) != ERROR_OK)
return ERROR_FAIL;
goto init_fail;
bitbang_interface = &parport_bitbang;
return ERROR_OK;
init_fail:
free(parport_device_file);
return ERROR_JTAG_INIT_FAILED;
}
static int parport_quit(void)
{
free(parport_device_file);
// Deinitialize signal pin states.
for (size_t i = 0; i < ARRAY_SIZE(all_signals); i++) {
const enum adapter_gpio_config_index gpio_index = all_signals[i].gpio_index;
@@ -419,13 +432,46 @@ COMMAND_HANDLER(parport_handle_port_command)
if (CMD_ARGC != 1)
return ERROR_COMMAND_SYNTAX_ERROR;
// Only if the port wasn't overwritten by command-line.
#if PARPORT_USE_PPDEV == 1
if (parport_device_file) {
LOG_ERROR("The parallel port device file is already configured");
return ERROR_FAIL;
}
char *tmp;
// We do not use the parse_xxx() or COMMAND_PARSE_xxx() functions here since
// they generate an error message if parsing fails.
char *endptr = NULL;
unsigned long port_number = strtoul(CMD_ARGV[0], &endptr, 0);
if (*endptr == '\0' && endptr != CMD_ARGV[0]) {
LOG_WARNING("DEPRECATED! Using a port number is deprecated, use the device file instead");
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
tmp = alloc_printf("/dev/ppi%lu", port_number);
#else
tmp = alloc_printf("/dev/parport%lu", port_number);
#endif
} else {
tmp = strdup(CMD_ARGV[0]);
}
if (!tmp) {
LOG_ERROR("Failed to allocate memory");
return ERROR_FAIL;
}
free(parport_device_file);
parport_device_file = tmp;
#else
if (parport_port > 0) {
command_print(CMD, "The parallel port is already configured");
return ERROR_FAIL;
}
COMMAND_PARSE_NUMBER(u16, CMD_ARGV[0], parport_port);
#endif
return ERROR_OK;
}
@@ -487,9 +533,8 @@ static const struct command_registration parport_subcommand_handlers[] = {
.name = "port",
.handler = parport_handle_port_command,
.mode = COMMAND_CONFIG,
.help = "Configure the address of the I/O port (e.g. 0x378) "
"or the number of the '/dev/parport' (Linux) or '/dev/ppi' (FreeBSD) device used",
.usage = "port_number",
.help = "Specify the device file of the parallel port",
.usage = "file",
},
{
.name = "cable",

View File

@@ -75,6 +75,7 @@ ARMV6_SRC = \
ARMV7_SRC = \
%D%/armv7m.c \
%D%/armv7m_cache.c \
%D%/armv7m_trace.c \
%D%/cortex_m.c \
%D%/armv7a.c \
@@ -183,6 +184,7 @@ ARC_SRC = \
%D%/armv4_5_cache.h \
%D%/armv7a.h \
%D%/armv7m.h \
%D%/armv7m_cache.h \
%D%/armv7m_trace.h \
%D%/armv8.h \
%D%/armv8_dpm.h \

View File

@@ -15,6 +15,7 @@
#define OPENOCD_TARGET_ARMV7M_H
#include "arm.h"
#include "armv7m_cache.h"
#include "armv7m_trace.h"
struct adiv5_ap;
@@ -239,6 +240,8 @@ struct armv7m_common {
/* hla_target uses a high level adapter that does not support all functions */
bool is_hla_target;
struct armv7m_cache_common armv7m_cache;
struct armv7m_trace_config trace_config;
/* Direct processor core register read and writes */

284
src/target/armv7m_cache.c Normal file
View File

@@ -0,0 +1,284 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025 by STMicroelectronics
* Copyright (C) 2025 by Antonio Borneo <borneo.antonio@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdint.h>
#include <helper/align.h>
#include <helper/bitfield.h>
#include <helper/bits.h>
#include <helper/command.h>
#include <helper/log.h>
#include <helper/types.h>
#include <target/arm_adi_v5.h>
#include <target/armv7m_cache.h>
#include <target/cortex_m.h>
static int get_cache_info(struct adiv5_ap *ap, unsigned int cl,
unsigned int ind, uint32_t *ccsidr)
{
uint32_t csselr = FIELD_PREP(CSSELR_LEVEL_MASK, cl)
| FIELD_PREP(CSSELR_IND_MASK, ind);
int retval = mem_ap_write_u32(ap, CSSELR, csselr);
if (retval != ERROR_OK)
return retval;
return mem_ap_read_u32(ap, CCSIDR, ccsidr);
}
static int get_d_u_cache_info(struct adiv5_ap *ap, unsigned int cl,
uint32_t *ccsidr)
{
return get_cache_info(ap, cl, CSSELR_IND_DATA_OR_UNIFIED_CACHE, ccsidr);
}
static int get_i_cache_info(struct adiv5_ap *ap, unsigned int cl,
uint32_t *ccsidr)
{
return get_cache_info(ap, cl, CSSELR_IND_INSTRUCTION_CACHE, ccsidr);
}
static struct armv7m_cache_size decode_ccsidr(uint32_t ccsidr)
{
struct armv7m_cache_size size;
size.line_len = 16 << FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr);
size.associativity = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr) + 1;
size.num_sets = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr) + 1;
size.cache_size = size.line_len * size.associativity * size.num_sets / 1024;
// compute info for set way operation on cache
size.index_shift = FIELD_GET(CCSIDR_LINESIZE_MASK, ccsidr) + 2;
size.index = FIELD_GET(CCSIDR_NUMSETS_MASK, ccsidr);
size.way = FIELD_GET(CCSIDR_ASSOCIATIVITY_MASK, ccsidr);
unsigned int i = 0;
while (((size.way << i) & 0x80000000) == 0)
i++;
size.way_shift = i;
return size;
}
int armv7m_identify_cache(struct target *target)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
uint32_t clidr;
int retval = mem_ap_read_u32(armv7m->debug_ap, CLIDR, &clidr);
if (retval != ERROR_OK)
return retval;
uint32_t ctr;
retval = mem_ap_read_u32(armv7m->debug_ap, CTR, &ctr);
if (retval != ERROR_OK)
return retval;
// retrieve selected cache for later restore
uint32_t csselr;
retval = mem_ap_read_atomic_u32(armv7m->debug_ap, CSSELR, &csselr);
if (retval != ERROR_OK)
return retval;
if (clidr == 0) {
LOG_TARGET_DEBUG(target, "No cache detected");
return ERROR_OK;
}
if (FIELD_GET(CTR_FORMAT_MASK, ctr) != CTR_FORMAT_PROVIDED) {
LOG_ERROR("Wrong value in CTR register");
return ERROR_FAIL;
}
cache->i_min_line_len = 4UL << FIELD_GET(CTR_IMINLINE_MASK, ctr);
cache->d_min_line_len = 4UL << FIELD_GET(CTR_DMINLINE_MASK, ctr);
LOG_TARGET_DEBUG(target,
"ctr=0x%" PRIx32 " ctr.i_min_line_len=%" PRIu32 " ctr.d_min_line_len=%" PRIu32,
ctr, cache->i_min_line_len, cache->d_min_line_len);
cache->loc = FIELD_GET(CLIDR_LOC_MASK, clidr);
LOG_TARGET_DEBUG(target,
"clidr=0x%" PRIx32 " Number of cache levels to PoC=%" PRIu32,
clidr, cache->loc);
// retrieve all available inner caches
uint32_t d_u_ccsidr[8], i_ccsidr[8];
for (unsigned int cl = 0; cl < cache->loc; cl++) {
unsigned int ctype = FIELD_GET(CLIDR_CTYPE_MASK(cl + 1), clidr);
// skip reserved values
if (ctype > CLIDR_CTYPE_UNIFIED_CACHE)
continue;
cache->arch[cl].ctype = ctype;
// separate d or unified d/i cache at this level ?
if (ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) {
// retrieve d-cache info
retval = get_d_u_cache_info(armv7m->debug_ap, cl, &d_u_ccsidr[cl]);
if (retval != ERROR_OK)
break;
}
if (ctype & CLIDR_CTYPE_I_CACHE) {
// retrieve i-cache info
retval = get_i_cache_info(armv7m->debug_ap, cl, &i_ccsidr[cl]);
if (retval != ERROR_OK)
break;
}
}
// restore selected cache
int retval1 = mem_ap_write_atomic_u32(armv7m->debug_ap, CSSELR, csselr);
if (retval != ERROR_OK)
return retval;
if (retval1 != ERROR_OK)
return retval1;
for (unsigned int cl = 0; cl < cache->loc; cl++) {
unsigned int ctype = cache->arch[cl].ctype;
// separate d or unified d/i cache at this level ?
if (ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE)) {
cache->has_d_u_cache = true;
cache->arch[cl].d_u_size = decode_ccsidr(d_u_ccsidr[cl]);
LOG_TARGET_DEBUG(target,
"data/unified cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
cache->arch[cl].d_u_size.index,
cache->arch[cl].d_u_size.index_shift,
cache->arch[cl].d_u_size.way,
cache->arch[cl].d_u_size.way_shift);
LOG_TARGET_DEBUG(target,
"cache line %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
cache->arch[cl].d_u_size.line_len,
cache->arch[cl].d_u_size.cache_size,
cache->arch[cl].d_u_size.associativity);
}
if (ctype & CLIDR_CTYPE_I_CACHE) {
cache->has_i_cache = true;
cache->arch[cl].i_size = decode_ccsidr(i_ccsidr[cl]);
LOG_TARGET_DEBUG(target,
"instruction cache index %" PRIu32 " << %" PRIu32 ", way %" PRIu32 " << %" PRIu32,
cache->arch[cl].i_size.index,
cache->arch[cl].i_size.index_shift,
cache->arch[cl].i_size.way,
cache->arch[cl].i_size.way_shift);
LOG_TARGET_DEBUG(target,
"cache line %" PRIu32 " bytes %" PRIu32 " KBytes asso %" PRIu32 " ways",
cache->arch[cl].i_size.line_len,
cache->arch[cl].i_size.cache_size,
cache->arch[cl].i_size.associativity);
}
}
cache->info_valid = true;
return ERROR_OK;
}
int armv7m_d_cache_flush(struct target *target, uint32_t address,
unsigned int length)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
if (!cache->info_valid || !cache->has_d_u_cache)
return ERROR_OK;
uint32_t line_len = cache->d_min_line_len;
uint32_t addr_line = ALIGN_DOWN(address, line_len);
uint32_t addr_end = address + length;
while (addr_line < addr_end) {
int retval = mem_ap_write_u32(armv7m->debug_ap, DCCIMVAC, addr_line);
if (retval != ERROR_OK)
return retval;
addr_line += line_len;
keep_alive();
}
return dap_run(armv7m->debug_ap->dap);
}
int armv7m_i_cache_inval(struct target *target, uint32_t address,
unsigned int length)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
if (!cache->info_valid || !cache->has_i_cache)
return ERROR_OK;
uint32_t line_len = cache->i_min_line_len;
uint32_t addr_line = ALIGN_DOWN(address, line_len);
uint32_t addr_end = address + length;
while (addr_line < addr_end) {
int retval = mem_ap_write_u32(armv7m->debug_ap, ICIMVAU, addr_line);
if (retval != ERROR_OK)
return retval;
addr_line += line_len;
keep_alive();
}
return dap_run(armv7m->debug_ap->dap);
}
int armv7m_handle_cache_info_command(struct command_invocation *cmd,
struct target *target)
{
struct armv7m_common *armv7m = target_to_armv7m(target);
struct armv7m_cache_common *cache = &armv7m->armv7m_cache;
if (!target_was_examined(target)) {
command_print(cmd, "Target not examined yet");
return ERROR_FAIL;
}
if (!cache->info_valid) {
command_print(cmd, "No cache detected");
return ERROR_OK;
}
for (unsigned int cl = 0; cl < cache->loc; cl++) {
struct armv7m_arch_cache *arch = &cache->arch[cl];
if (arch->ctype & CLIDR_CTYPE_I_CACHE)
command_print(cmd,
"L%d I-Cache: line length %" PRIu32 ", associativity %" PRIu32
", num sets %" PRIu32 ", cache size %" PRIu32 " KBytes",
cl + 1,
arch->i_size.line_len,
arch->i_size.associativity,
arch->i_size.num_sets,
arch->i_size.cache_size);
if (arch->ctype & (CLIDR_CTYPE_UNIFIED_CACHE | CLIDR_CTYPE_D_CACHE))
command_print(cmd,
"L%d %c-Cache: line length %" PRIu32 ", associativity %" PRIu32
", num sets %" PRIu32 ", cache size %" PRIu32 " KBytes",
cl + 1,
(arch->ctype & CLIDR_CTYPE_D_CACHE) ? 'D' : 'U',
arch->d_u_size.line_len,
arch->d_u_size.associativity,
arch->d_u_size.num_sets,
arch->d_u_size.cache_size);
}
return ERROR_OK;
}

57
src/target/armv7m_cache.h Normal file
View File

@@ -0,0 +1,57 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2025 by STMicroelectronics
* Copyright (C) 2025 by Antonio Borneo <borneo.antonio@gmail.com>
*/
#ifndef OPENOCD_TARGET_ARMV7M_CACHE_H
#define OPENOCD_TARGET_ARMV7M_CACHE_H
#include <stdbool.h>
#include <stdint.h>
#include <helper/types.h>
struct target;
struct armv7m_cache_size {
// cache dimensioning
uint32_t line_len;
uint32_t associativity;
uint32_t num_sets;
uint32_t cache_size;
// info for set way operation on cache
uint32_t index;
uint32_t index_shift;
uint32_t way;
uint32_t way_shift;
};
// information about one architecture cache at any level
struct armv7m_arch_cache {
unsigned int ctype; // cache type, CLIDR encoding
struct armv7m_cache_size d_u_size; // data cache
struct armv7m_cache_size i_size; // instruction cache
};
// common cache information
struct armv7m_cache_common {
bool info_valid;
bool has_i_cache;
bool has_d_u_cache;
unsigned int loc; // level of coherency
uint32_t d_min_line_len; // minimum d-cache line_len
uint32_t i_min_line_len; // minimum i-cache line_len
struct armv7m_arch_cache arch[6]; // cache info, L1 - L7
};
int armv7m_identify_cache(struct target *target);
int armv7m_d_cache_flush(struct target *target, uint32_t address,
unsigned int length);
int armv7m_i_cache_inval(struct target *target, uint32_t address,
unsigned int length);
int armv7m_handle_cache_info_command(struct command_invocation *cmd,
struct target *target);
#endif /* OPENOCD_TARGET_ARMV7M_CACHE_H */

View File

@@ -21,6 +21,7 @@
#include "jtag/interface.h"
#include "breakpoints.h"
#include "cortex_m.h"
#include "armv7m_cache.h"
#include "target_request.h"
#include "target_type.h"
#include "arm_adi_v5.h"
@@ -30,6 +31,7 @@
#include "arm_semihosting.h"
#include "smp.h"
#include <helper/nvp.h>
#include <helper/string_choices.h>
#include <helper/time_support.h>
#include <rtt/rtt.h>
@@ -873,6 +875,14 @@ static int cortex_m_debug_entry(struct target *target)
return retval;
}
// read caches state
uint32_t ccr = 0;
if (armv7m->armv7m_cache.info_valid) {
retval = mem_ap_read_u32(armv7m->debug_ap, CCR, &ccr);
if (retval != ERROR_OK)
return retval;
}
/* Load all registers to arm.core_cache */
if (!cortex_m->slow_register_read) {
retval = cortex_m_fast_read_all_regs(target);
@@ -926,6 +936,11 @@ static int cortex_m_debug_entry(struct target *target)
secure_state ? "Secure" : "Non-Secure",
target_state_name(target));
if (armv7m->armv7m_cache.info_valid)
LOG_TARGET_DEBUG(target, "D-Cache %s, I-Cache %s",
str_enabled_disabled(ccr & CCR_DC_MASK),
str_enabled_disabled(ccr & CCR_IC_MASK));
/* Errata 3092511 workaround
* Cortex-M7 can halt in an incorrect address when breakpoint
* and exception occurs simultaneously */
@@ -1938,12 +1953,25 @@ int cortex_m_set_breakpoint(struct target *target, struct breakpoint *breakpoint
breakpoint->orig_instr);
if (retval != ERROR_OK)
return retval;
// make sure data cache is cleaned & invalidated down to PoC
retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
retval = target_write_memory(target,
breakpoint->address & 0xFFFFFFFE,
breakpoint->length, 1,
code);
if (retval != ERROR_OK)
return retval;
// update i-cache at breakpoint location
retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
retval = armv7m_i_cache_inval(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
breakpoint->is_set = true;
}
@@ -1986,12 +2014,25 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi
target_write_u32(target, comparator_list[fp_num].fpcr_address,
comparator_list[fp_num].fpcr_value);
} else {
// make sure data cache is cleaned & invalidated down to PoC
retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
/* restore original instruction (kept in target endianness) */
retval = target_write_memory(target, breakpoint->address & 0xFFFFFFFE,
breakpoint->length, 1,
breakpoint->orig_instr);
if (retval != ERROR_OK)
return retval;
// update i-cache at breakpoint location
retval = armv7m_d_cache_flush(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
retval = armv7m_i_cache_inval(target, breakpoint->address, breakpoint->length);
if (retval != ERROR_OK)
return retval;
}
breakpoint->is_set = false;
@@ -2000,6 +2041,15 @@ int cortex_m_unset_breakpoint(struct target *target, struct breakpoint *breakpoi
int cortex_m_add_breakpoint(struct target *target, struct breakpoint *breakpoint)
{
/*
* GDB packets Z0 and z0 provide the 'kind' parameter that is target-specific
* and typically indicates the size in bytes of the breakpoint.
* But for 32-bit Thumb mode (Thumb-2) breakpoint, GDB provides 'kind = 3' to
* be used to derive the length information. See:
* https://sourceware.org/gdb/current/onlinedocs/gdb.html/ARM-Breakpoint-Kinds.html
* Since there isn't a four byte Thumb-2 breakpoint instruction, always use
* the two bytes breakpoint instruction.
*/
if (breakpoint->length == 3) {
LOG_TARGET_DEBUG(target, "Using a two byte breakpoint for 32bit Thumb-2 request");
breakpoint->length = 2;
@@ -2906,6 +2956,12 @@ int cortex_m_examine(struct target *target)
LOG_TARGET_INFO(target, "target has %d breakpoints, %d watchpoints",
cortex_m->fp_num_code,
cortex_m->dwt_num_comp);
retval = armv7m_identify_cache(target);
if (retval != ERROR_OK) {
LOG_ERROR("Cannot detect cache");
return retval;
}
}
return ERROR_OK;
@@ -3194,47 +3250,60 @@ COMMAND_HANDLER(handle_cortex_m_reset_config_command)
{
struct target *target = get_current_target(CMD_CTX);
struct cortex_m_common *cortex_m = target_to_cm(target);
int retval;
char *reset_config;
retval = cortex_m_verify_pointer(CMD, cortex_m);
int retval = cortex_m_verify_pointer(CMD, cortex_m);
if (retval != ERROR_OK)
return retval;
if (CMD_ARGC > 0) {
if (strcmp(*CMD_ARGV, "sysresetreq") == 0)
cortex_m->soft_reset_config = CORTEX_M_RESET_SYSRESETREQ;
if (!CMD_ARGC) {
char *reset_config;
else if (strcmp(*CMD_ARGV, "vectreset") == 0) {
if (target_was_examined(target)
&& !cortex_m->vectreset_supported)
LOG_TARGET_WARNING(target, "VECTRESET is not supported on your Cortex-M core!");
else
cortex_m->soft_reset_config = CORTEX_M_RESET_VECTRESET;
switch (cortex_m->soft_reset_config) {
case CORTEX_M_RESET_SYSRESETREQ:
reset_config = "sysresetreq";
break;
} else
return ERROR_COMMAND_SYNTAX_ERROR;
case CORTEX_M_RESET_VECTRESET:
reset_config = "vectreset";
break;
default:
reset_config = "unknown";
break;
}
command_print(CMD, "%s", reset_config);
return ERROR_OK;
} else if (CMD_ARGC != 1) {
return ERROR_COMMAND_SYNTAX_ERROR;
}
switch (cortex_m->soft_reset_config) {
case CORTEX_M_RESET_SYSRESETREQ:
reset_config = "sysresetreq";
break;
case CORTEX_M_RESET_VECTRESET:
reset_config = "vectreset";
break;
default:
reset_config = "unknown";
break;
if (!strcmp(CMD_ARGV[0], "sysresetreq")) {
cortex_m->soft_reset_config = CORTEX_M_RESET_SYSRESETREQ;
} else if (!strcmp(CMD_ARGV[0], "vectreset")) {
if (target_was_examined(target)
&& !cortex_m->vectreset_supported)
LOG_TARGET_WARNING(target, "VECTRESET is not supported on your Cortex-M core");
else
cortex_m->soft_reset_config = CORTEX_M_RESET_VECTRESET;
} else {
command_print(CMD, "invalid reset config '%s'", CMD_ARGV[0]);
return ERROR_COMMAND_ARGUMENT_INVALID;
}
command_print(CMD, "cortex_m reset_config %s", reset_config);
return ERROR_OK;
}
COMMAND_HANDLER(handle_cortex_m_cache_info_command)
{
if (CMD_ARGC)
return ERROR_COMMAND_SYNTAX_ERROR;
struct target *target = get_current_target(CMD_CTX);
return armv7m_handle_cache_info_command(CMD, target);
}
static const struct command_registration cortex_m_exec_command_handlers[] = {
{
.name = "maskisr",
@@ -3257,6 +3326,13 @@ static const struct command_registration cortex_m_exec_command_handlers[] = {
.help = "configure software reset handling",
.usage = "['sysresetreq'|'vectreset']",
},
{
.name = "cache_info",
.handler = handle_cortex_m_cache_info_command,
.mode = COMMAND_EXEC,
.help = "display information about target caches",
.usage = "",
},
{
.chain = smp_command_handlers,
},

View File

@@ -15,6 +15,7 @@
#define OPENOCD_TARGET_CORTEX_M_H
#include "armv7m.h"
#include "helper/bitfield.h"
#include "helper/bits.h"
#define CORTEX_M_COMMON_MAGIC 0x1A451A45U
@@ -114,6 +115,45 @@ struct cortex_m_part_info {
#define FPU_FPCAR 0xE000EF38
#define FPU_FPDSCR 0xE000EF3C
// Cache
#define CCR 0xE000ED14
#define CLIDR 0xE000ED78
#define CTR 0xE000ED7C
#define CCSIDR 0xE000ED80
#define CSSELR 0xE000ED84
#define ICIMVAU 0xE000EF58
#define DCCIMVAC 0xE000EF70
#define CCR_IC_MASK BIT(17)
#define CCR_DC_MASK BIT(16)
#define CLIDR_ICB_MASK GENMASK(31, 30)
#define CLIDR_LOUU_MASK GENMASK(29, 27)
#define CLIDR_LOC_MASK GENMASK(26, 24)
#define CLIDR_LOUIS_MASK GENMASK(23, 21)
#define CLIDR_CTYPE_MASK(i) (GENMASK(2, 0) << (3 * (i) - 3))
#define CLIDR_CTYPE_I_CACHE BIT(0)
#define CLIDR_CTYPE_D_CACHE BIT(1)
#define CLIDR_CTYPE_UNIFIED_CACHE BIT(2)
#define CTR_FORMAT_MASK GENMASK(31, 29)
#define CTR_CWG_MASK GENMASK(27, 24)
#define CTR_ERG_MASK GENMASK(23, 20)
#define CTR_DMINLINE_MASK GENMASK(19, 16)
#define CTR_IMINLINE_MASK GENMASK(3, 0)
#define CTR_FORMAT_PROVIDED 0x04
#define CCSIDR_NUMSETS_MASK GENMASK(27, 13)
#define CCSIDR_ASSOCIATIVITY_MASK GENMASK(12, 3)
#define CCSIDR_LINESIZE_MASK GENMASK(2, 0)
#define CSSELR_LEVEL_MASK GENMASK(3, 1)
#define CSSELR_IND_MASK BIT(0)
#define CSSELR_IND_DATA_OR_UNIFIED_CACHE 0
#define CSSELR_IND_INSTRUCTION_CACHE 1
#define TPIU_SSPSR 0xE0040000
#define TPIU_CSPSR 0xE0040004
#define TPIU_ACPR 0xE0040010

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for the EasyDevKits ESP32-WROVER-E FTDI JTAG DevKit.
#
# For example, OpenOCD can be started for ESP32 debugging on
#
# openocd -f board/esp32-wrover-e-ftdi-jtag-devkit.cfg
#
# Select the FTDI JTAG driver
adapter driver ftdi
# Identify the device
ftdi device_desc "EasyDevKit"
ftdi vid_pid 0x0403 0x6010
# interface 0 is JTAG; interface 1 is the uart
ftdi channel 0
# TCK, TDI, TDO, TMS: ADBUS0-3
# activity LED: ADBUS4
ftdi layout_init 0x0008 0x001b
ftdi layout_signal LED -data 0x0010
# Source the ESP32 configuration file
source [find target/esp32.cfg]
# ---------------------------------------------------------------------------
# JTAG speed (in kHz)
#
# If you encounter DSR/DIR errors that are not caused by OpenOCD
# attempting to read unmapped memory regions, try lowering this value.
#
# Recommended settings for EasyDevKits:
# - Do not exceed 20 MHz.
# - Best results are typically achieved at 20 MHz.
# ---------------------------------------------------------------------------
adapter speed 20000

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Example OpenOCD configuration file for the EasyDevKits ESP32-WROVER-E WCH JTAG DevKit.
#
# For example, OpenOCD can be started for ESP32 debugging on
#
# openocd -f board/esp32-wrover-e-wch-jtag-devkit.cfg
#
# Select the CH347 JTAG driver
adapter driver ch347
# Identify the device
ch347 device_desc "EasyDevKit"
ch347 vid_pid 0x1a86 0x55dd
# Configure activity LED
# Note: The LED is active-low on GPIO4.
adapter gpio led 4 -active-low
# Source the ESP32 configuration file
source [find target/esp32.cfg]
# ---------------------------------------------------------------------------
# JTAG speed (in kHz)
#
# If you encounter DSR/DIR errors that are not caused by OpenOCD
# attempting to read unmapped memory regions, try lowering this value.
#
# Recommended settings for EasyDevKits:
# - Do not exceed 30 MHz.
# - Best results are typically achieved at 15 MHz.
#
# Supported frequencies (kHz):
# 469, 938, 1875, 3750, 7500, 15000, 30000, 60000
# ---------------------------------------------------------------------------
adapter speed 15000

View File

@@ -20,4 +20,6 @@ set _file_renaming {
board/stm32mp13x_dk.cfg board/st/stm32mp135f-dk.cfg
board/stm32mp15x_dk2.cfg board/st/stm32mp157f-dk2.cfg
board/sifive-hifive1-revb.cfg board/sifive/hifive1-rev-b.cfg
interface/chameleon.cfg interface/parport/chameleon.cfg
interface/flashlink.cfg interface/parport/flashlink.cfg
}

View File

@@ -1,10 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Amontec Chameleon POD
#
# http://www.amontec.com/chameleon.shtml
#
adapter driver parport
parport cable chameleon

View File

@@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# ST FlashLINK JTAG parallel cable
#
# http://www.st.com/internet/evalboard/product/94023.jsp
# http://www.st.com/stonline/products/literature/um/7889.pdf
#
if { [info exists PARPORTADDR] } {
set _PARPORTADDR $PARPORTADDR
} else {
set _PARPORTADDR 0
}
adapter driver parport
parport port $_PARPORTADDR
parport cable flashlink

145
tcl/target/bl616.cfg Normal file
View File

@@ -0,0 +1,145 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Author: Marek Kraus <gamelaster@gami.ee>
#
# Bouffalo Labs BL616 and BL618 target
#
# Default JTAG pins: (if not changed by eFuse configuration)
# TMS - GPIO0
# TCK - GPIO1
# TDO - GPIO2
# TDI - GPIO3
#
source [find mem_helper.tcl]
transport select jtag
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME bl616
}
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10000b6f
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
riscv set_mem_access progbuf
riscv set_enable_virt2phys off
$_TARGETNAME configure -work-area-phys 0x40000000 -work-area-size 0x10000 -work-area-backup 1
adapter speed 4000
# Useful functions
set dmcontrol 0x10
set dmcontrol_dmactive [expr {1 << 0}]
set dmcontrol_haltreq [expr {1 << 31}]
# By spec, ndmreset should reset whole chip. This implementation resets only few parts of the chip.
# CTRL_PWRON_RESET register in GLB core triggers full "power-on like" reset, so we use it instead
# for full software reset.
$_TARGETNAME configure -event reset-assert {
halt
# To stay in BootROM until JTAG re-attaches, we are using BootROM functionality
# to force ISP mode, so BootROM looks out for external ISP communication.
# In HBN_RSV2, set HBN_RELEASE_CORE to HBN_RELEASE_CORE_FLAG (4)
# and HBN_USER_BOOT_SEL to 1 (ISP)
mww 0x2000f108 0x44000000
# Switch clock to internal RC32M
# In HBN_GLB, set ROOT_CLK_SEL = 0
mmw 0x2000f030 0x0 0x00000002
# In GLB_SYS_CFG0, set REG_BCLK_DIV and REG_HCLK_DIV = 0
mmw 0x20000090 0x0 0x00FFFF00
# Trigger BCLK ACT pulse
# In GLB_SYS_CFG1, set BCLK_DIV_ACT_PULSE = 1
mmw 0x20000094 0x1 0x00000001
# In GLB_SYS_CFG1, wait for GLB_STS_BCLK_PROT_DONE to become 1
while { [expr {[mrw 0x20000094] & 4}] == 0 } { sleep 1 }
# In GLB_SWRST_CFG2, clear CTRL_PWRON_RESET
mmw 0x20000548 0x0 0x00000001
# This Software reset method resets everything, so CPU as well.
# It does that in not much good way, resulting in Debug Module being reset as well.
# This also means, that right after CPU and Debug Module are turned on, we need to
# enable Debug Module and halt CPU if needed. Additionally, we trigger this SW reset
# through program buffer access directly with DMI commands, to avoid errors printed by
# OpenOCD about unsuccessful register write.
# In GLB_SWRST_CFG2, set CTRL_PWRON_RESET to 1
set_reg {fp 0x20000548 s1 0x01}
riscv dmi_write 0x20 0x00942023
riscv dmi_write 0x17 0x40000
# We need to wait for chip to finish reset and execute BootROM
sleep 10
# JTAG Debug Transport Module is reset as well, so we need to get into RUN/IDLE state
runtest 10
# We need to enable Debug Module and halt the CPU, so we can reset Program Counter
# and to do additional clean-ups. If reset was called without halt, resume is handled
# by reset-deassert-post event handler.
# In Debug Module Control (dmcontrol), set dmactive to 1 and then haltreq to 1
riscv dmi_write $::dmcontrol $::dmcontrol_dmactive
riscv dmi_write $::dmcontrol [ expr {$::dmcontrol_dmactive | $::dmcontrol_haltreq} ]
}
$_TARGETNAME configure -event reset-deassert-post {
# Set Program Counter to start of BootROM and execute one instruction
step 0x90000000
# When using default JTAG pinout, BOOT pin is the same as JTAG TDO pin.
# Since after reset we set PC to start of the BootROM,
# BootROM will execute also check of BOOT pin, which will disable TDO pin,
# to check the BOOT pin state. This leads to temporary loss of JTAG access
# and causes (recoverable) errors in OpenOCD. We can bypass the BOOT pin check
# function, by forcing booting from Media/SPI Flash.
# In HBN_RSV2, set HBN_RELEASE_CORE to HBN_RELEASE_CORE_FLAG (4)
# and HBN_USER_BOOT_SEL to 2 (Media/SPI Flash)
mww 0x2000f108 0x48000000
# Resume the processor if reset was triggered without halt request
if {$halt == 0} {
resume
}
}
# According to JTAG spec (IEEE 1149.1), when chip enters "Test-Logic-Reset" state,
# the IR instruction should be set to "IDCODE" or "BYPASS" (when chip does not have IDCODE).
# This is done so automatic chain scan can detect all the chips within JTAG chain without knowing IDCODE.
# JTAG Debug Transport Module (DTM) used in this chip, developed by T-Head (formerly C-Sky)
# does not implement this, so OpenOCD can't detect the chip anymore after the IR instruction is changed.
# This workaround gets chip into known state, and manually set IR instruction to IDCODE,
# which is 0x01, standardized by RISC-V Debug Specification.
proc init_reset { mode } {
if {[using_jtag]} {
# Get JTAG SM to known state
runtest 10
# Set IR to IDCODE
irscan $::_CHIPNAME.cpu 0x01
jtag arp_init-reset
}
}
proc jtag_init {} {
# Get JTAG SM to known state
runtest 10
# Set IR to IDCODE
irscan $::_CHIPNAME.cpu 0x01
if {[catch {jtag arp_init} err]!=0} {
# try resetting additionally
init_reset startup
}
}